infra(dqn-v2): audit doc scaffolding + pre-commit enforcement
Plan 1 Task 1. Creates the five audit docs plus config/metric-bands.toml
that track Invariants 2, 7, 8 per the DQN v2 spec, and extends the
pre-commit hook with two checks:
- component-adding commits must touch an audit doc (Invariant 7)
- added code may not contain TODO/FIXME/XXX/HACK/TBD/unimplemented!/
todo! markers (Invariant 9)
Tests: manually verified by staging a TODO-marked file; commit
rejected with the correct error message.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
13
config/metric-bands.toml
Normal file
13
config/metric-bands.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
# Regression-detection bands for DQN training metrics.
|
||||
# Invariant 7. Per A.4, bands populated from last 3 known-good runs.
|
||||
# Band = [mean − 3σ, mean + 3σ] per metric; recomputed after baseline promotion.
|
||||
# N consecutive out-of-band epochs → WARN; 2N consecutive → ERROR + SIGTERM-self.
|
||||
|
||||
[settings]
|
||||
consecutive_epochs_for_warn = 3
|
||||
consecutive_epochs_for_error = 6
|
||||
|
||||
# Placeholder bands — populated by A.4.1 nsys baseline run + populate_metric_bands.sh
|
||||
# (populated during Plan 5 execution; this file lands with empty metric entries for Plan 1).
|
||||
|
||||
[bands]
|
||||
14
docs/dqn-gpu-hot-path-audit.md
Normal file
14
docs/dqn-gpu-hot-path-audit.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# DQN GPU Hot-Path Audit
|
||||
|
||||
**Invariant 3 enforcement.** Every cross-boundary call (DtoH, HtoD, memcpy between host and device) in the DQN code path is classified below. No hot-path `memcpy` DtoH/HtoD is allowed — zero tolerance. Pinned + device-mapped memory is the ONLY permitted mechanism for per-step host-visible data.
|
||||
|
||||
**Hot path definition:** every function called from `fused_training.rs::step_fused` or its descendants that executes once per training step (178 times per epoch at batch size 8192), including kernel invocations inside the captured CUDA graph (`adam_grad_child`, `forward_child`, `aux_grad_child`) and host-side orchestration between them.
|
||||
|
||||
**Classification:**
|
||||
- `OK-pinned` — uses the zero-copy pinned + device-mapped pattern (allowed).
|
||||
- `MIGRATE` — uses `memcpy` but should be pinned. Fix during spec impl.
|
||||
- `COLD-PATH` — acceptable placement (checkpoint save/load, fold transition, epoch-boundary validation), annotated with reason.
|
||||
|
||||
| File:line | Call signature | Path class | Annotation | Status |
|
||||
|---|---|---|---|---|
|
||||
| (populated during A.6 audit) | | | | |
|
||||
111
docs/dqn-named-dims.md
Normal file
111
docs/dqn-named-dims.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# DQN Named Dimensions Registry
|
||||
|
||||
**Invariant 8 enforcement.** Every semantic index has a named constant. Raw indices (`[0]`, `[6]`, `[23]`) appear ONLY at definition sites. Consumer code always uses the named constant.
|
||||
|
||||
## State vector offsets
|
||||
|
||||
Defined in `crates/ml/src/cuda_pipeline/state_layout.cuh` and mirrored in Rust via `ml-core::state_layout`.
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|---|---|---|
|
||||
| `SL_STATE_DIM` | 104 | Total state vector length |
|
||||
| `SL_STATE_DIM_PADDED` | 128 | Padded for cuBLAS K-tile alignment |
|
||||
| `SL_MARKET_DIM` | 42 | Market-feature block width |
|
||||
| `SL_OFI_DIM` | 32 | OFI block width |
|
||||
| `SL_MTF_DIM` | 16 | Multi-timeframe block width |
|
||||
| `SL_PORTFOLIO_BASE_DIM` | 8 | Portfolio-base block width |
|
||||
| `SL_PORTFOLIO_PLAN_DIM` | 6 | Plan-ISV block width (extended to 7 in Plan 2) |
|
||||
| `SL_MARKET_START` | 0 | |
|
||||
| `SL_OFI_START` | 42 | |
|
||||
| `SL_MTF_START` | 74 | |
|
||||
| `SL_PORTFOLIO_START` | 90 | |
|
||||
| `SL_PLAN_ISV_START` | 98 | |
|
||||
|
||||
## Portfolio state slots (ps[0..30))
|
||||
|
||||
Defined in `crates/ml/src/cuda_pipeline/experience_kernels.cu` header comments. Migrated to named constants during Task 4 of this plan.
|
||||
|
||||
| Index | Constant | Meaning |
|
||||
|---|---|---|
|
||||
| 0 | `PS_VALUE` | Equity |
|
||||
| 1 | `PS_POSITION` | Current position |
|
||||
| 2 | `PS_CASH` | Available cash |
|
||||
| 3 | `PS_ENTRY_PRICE` | Entry price |
|
||||
| 4 | `PS_MAX_EQUITY` | Peak equity (for drawdown) |
|
||||
| 5 | `PS_HOLD_TIME` | Bars held |
|
||||
| 6 | `PS_CUM_RETURN` | Cumulative return |
|
||||
| 7 | `PS_STEP_COUNT` | Bars in window |
|
||||
| 8 | `PS_SPREAD_COST` | Current spread cost |
|
||||
| 9 | `PS_PREV_EQUITY` | Equity at previous step |
|
||||
| 10 | `PS_HOLD_TIME_TOTAL` | Total hold time |
|
||||
| 11 | `PS_REALIZED_PNL` | Cumulative realised PnL |
|
||||
| 12 | `PS_WIN_COUNT_UNUSED` | (slot retained, not currently used) |
|
||||
| 13 | `PS_LOSS_COUNT_UNUSED` | (slot retained, not currently used) |
|
||||
| 14 | `PS_KELLY_WIN_COUNT` | Kelly stats: winning trades |
|
||||
| 15 | `PS_KELLY_LOSS_COUNT` | Kelly stats: losing trades |
|
||||
| 16 | `PS_KELLY_SUM_WINS` | Kelly stats: sum of winning returns |
|
||||
| 17 | `PS_KELLY_SUM_LOSSES` | Kelly stats: sum of \|losing returns\| |
|
||||
| 18 | `PS_RESERVED_18` | reserved |
|
||||
| 19 | `PS_RESERVED_19` | reserved |
|
||||
| 20 | `PS_INTRA_TRADE_MAX_DD` | worst intra-trade drawdown (v8 reward) |
|
||||
| 21 | `PS_INTRA_TRADE_MAX_PNL` | best intra-trade unrealised PnL (hindsight) |
|
||||
| 22 | `PS_RESERVED_22` | (was interval_sum, slot retained) |
|
||||
| 23 | `PS_PLAN_TARGET_BARS` | plan: max hold bars (>0.5 = plan active) |
|
||||
| 24 | `PS_PLAN_PROFIT_TARGET` | plan: raw profit threshold |
|
||||
| 25 | `PS_PLAN_STOP_LOSS` | plan: raw stop threshold |
|
||||
| 26 | `PS_PLAN_SCALE_AGGRESSION` | plan: position ramp speed |
|
||||
| 27 | `PS_PLAN_CONVICTION` | plan: conviction at entry ∈ [0,1] |
|
||||
| 28 | `PS_PLAN_ASYMMETRY` | plan: profit/stop asymmetry |
|
||||
| 29 | `PS_PLAN_ENTRY_REGIME` | plan: regime_stability at entry (for P12) |
|
||||
|
||||
## Plan_isv dimensions (plan_isv[0..6))
|
||||
|
||||
Will extend to 7 in Plan 2 (D.6 remaining_fraction). These constants live alongside `SL_PLAN_ISV_START` in `state_layout.cuh`.
|
||||
|
||||
| Index | Constant | Meaning |
|
||||
|---|---|---|
|
||||
| 0 | `PLAN_ISV_PROGRESS` | hold_time / target_bars (clamped 2.0) |
|
||||
| 1 | `PLAN_ISV_PNL_VS_TARGET` | unrealized / (target × equity) |
|
||||
| 2 | `PLAN_ISV_PNL_VS_STOP` | -unrealized / (stop × equity) |
|
||||
| 3 | `PLAN_ISV_ENTRY_CONVICTION` | conviction at entry |
|
||||
| 4 | `PLAN_ISV_CONVICTION_DRIFT` | current conviction − entry conviction |
|
||||
| 5 | `PLAN_ISV_REGIME_SHIFT` | \|isv[11] − entry_stability\| |
|
||||
|
||||
## Plan_params output (plan_params[0..6))
|
||||
|
||||
From the trade_plan MLP output.
|
||||
|
||||
| Index | Constant | Meaning |
|
||||
|---|---|---|
|
||||
| 0 | `PLAN_PARAM_TARGET_BARS` | |
|
||||
| 1 | `PLAN_PARAM_PROFIT_TARGET` | |
|
||||
| 2 | `PLAN_PARAM_STOP_LOSS` | |
|
||||
| 3 | `PLAN_PARAM_SCALE_AGGRESSION` | |
|
||||
| 4 | `PLAN_PARAM_CONVICTION` | |
|
||||
| 5 | `PLAN_PARAM_ASYMMETRY` | |
|
||||
|
||||
## Branch indices (4-branch factored action)
|
||||
|
||||
| Index | Constant | Meaning |
|
||||
|---|---|---|
|
||||
| 0 | `BRANCH_DIR` | Direction branch (4 actions) |
|
||||
| 1 | `BRANCH_MAG` | Magnitude branch (3 actions) |
|
||||
| 2 | `BRANCH_ORD` | Order-type branch (3 actions) |
|
||||
| 3 | `BRANCH_URG` | Urgency branch (3 actions) |
|
||||
|
||||
## Direction action sub-indices
|
||||
|
||||
| Index | Constant | Meaning |
|
||||
|---|---|---|
|
||||
| 0 | `DIR_SHORT` | Open short (or close long + open short) |
|
||||
| 1 | `DIR_HOLD` | Keep current position (no-op on position) |
|
||||
| 2 | `DIR_LONG` | Open long (or close short + open long) |
|
||||
| 3 | `DIR_FLAT` | Close any position to zero |
|
||||
|
||||
## Magnitude action sub-indices
|
||||
|
||||
| Index | Constant | Meaning |
|
||||
|---|---|---|
|
||||
| 0 | `MAG_QUARTER` | 0.25× max_position |
|
||||
| 1 | `MAG_HALF` | 0.50× max_position |
|
||||
| 2 | `MAG_FULL` | 1.00× max_position |
|
||||
15
docs/dqn-wire-up-audit.md
Normal file
15
docs/dqn-wire-up-audit.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# DQN v2 Wire-Up Audit
|
||||
|
||||
**Status:** Populated during task #95 (A.5 orphan audit). Updated on every commit per Invariant 7.
|
||||
|
||||
**Legend:**
|
||||
- `Wired` — consumed by production training + val path.
|
||||
- `Partial` — consumed on one side only (training OR val, or forward OR backward).
|
||||
- `Orphan` — built but no production consumer.
|
||||
- `Ghost` — consumer path is stubbed.
|
||||
|
||||
## Components
|
||||
|
||||
| Module / kernel | Consumer path | Classification | Notes |
|
||||
|---|---|---|---|
|
||||
| (populated during A.5 audit) | | | |
|
||||
19
docs/isv-slots.md
Normal file
19
docs/isv-slots.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# ISV Slot Allocation Registry
|
||||
|
||||
**Source of truth for ISV bus slot allocations.** Every slot has a named constant in `gpu_dqn_trainer.rs`. This doc is the cross-reference.
|
||||
|
||||
**Current `ISV_TOTAL_DIM` as of this plan's start:** 37. Post-full DQN v2 rollout: 72.
|
||||
|
||||
| Index | Name constant | Type | Producer | Consumers | Reset-category | Notes |
|
||||
|---|---|---|---|---|---|---|
|
||||
| [0] | `ISV_SCHEMA_VERSION_INDEX` | u32 | construct | load | SchemaContract | Bumped on any slot-layout change |
|
||||
| [1..12) | (pre-existing) | f32 | varies | varies | varies | Legacy — audit during A.2 |
|
||||
| [12] | `LEARNING_HEALTH_INDEX` | f32 | isv_signal_update | many | FoldReset | health score ∈ [0, 1] |
|
||||
| [13..17) | `Q_MAG_MEAN_*_INDEX` | f32 | q_mag_means_reduce | c51 kernels | FoldReset | Quarter/Half/Full mag Q-mean EMAs + |Q| ref |
|
||||
| [17..22) | `Q_DIR_MEAN_*_INDEX` | f32 | q_dir_means_reduce | c51 kernels | FoldReset | Short/Hold/Long/Flat dir Q-mean EMAs + |Q| ref |
|
||||
| [22] | `SHARPE_EMA_INDEX` | f32 | training_loop host | isv_signal_update | FoldReset | Training Sharpe EMA |
|
||||
| [23..31) | `V_{CENTER,HALF}_{DIR,MAG,ORD,URG}_INDEX` | f32 | update_eval_v_range | adaptive_atoms, warm_start | FoldReset | Per-branch Q-support |
|
||||
| [31..35) | `GRAD_NORM_TARGET_*_INDEX` | f32 | grad_balance_isv_update | branch_grad_rescale | SoftReset(decay_bars=500) | Per-branch grad-norm target |
|
||||
| [35] | `GRAD_SCALE_LIMIT_INDEX` | f32 | grad_balance_isv_update | branch_grad_rescale | SoftReset(decay_bars=500) | Scale clamp limit |
|
||||
| [36] | `IQL_BRANCH_SCALE_FLOOR_INDEX` | f32 | construct (static) | iql_per_branch_advantage | SchemaContract | Per-sample branch_scales floor; safety bound |
|
||||
| [37..72) | (reserved for DQN v2) | | | | | Allocated incrementally by Plans 2-5 |
|
||||
18
docs/ml-supervised-to-dqn-concept-audit.md
Normal file
18
docs/ml-supervised-to-dqn-concept-audit.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Supervised → DQN Concept Audit (Part E deliverable)
|
||||
|
||||
Authoritative decisions on which ml-supervised concepts the DQN adopts. Per Invariant 9 (no deferred work), every entry is either `IN`, `OUT`, or `AUDIT-THEN-DECIDE` with the audit producing one of the two terminal classifications during this spec's implementation.
|
||||
|
||||
| Concept | Supervised home | Classification | Action plan | Status |
|
||||
|---|---|---|---|---|
|
||||
| TFT Variable Selection Network | ml-supervised::tft | IN | E.1: extend vsn_mag/vsn_dir to full VSN over state feature groups | pending |
|
||||
| Gated Residual Network | ml-supervised::tft | IN-IF-ABSENT | E.2: if A.5 audit finds absent, adopt; else mark existing as canonical | pending |
|
||||
| TFT multi-quantile heads | ml-supervised::tft | IN | E.3: extend IQN to 5/25/50/75/95 quantile decomposition | pending |
|
||||
| TLOB attention encoder | ml-supervised::tlob | IN | D.8 (Plan 2): wire to DQN trunk | pending |
|
||||
| Mamba2 forward | ml-supervised::mamba2 | WIRED | already in DQN trunk | — |
|
||||
| Mamba2 backward | ml-supervised::mamba2 | IN | D.1 (Plan 2): complete | pending |
|
||||
| Liquid Time-constant | ml-supervised::liquid | AUDIT-THEN-WIRE-OR-DELETE | D.7 (Plan 2): audit decides | pending |
|
||||
| xLSTM | ml-supervised::xlstm | OUT | Redundant with Mamba2 + TLOB; no DQN wiring | — |
|
||||
| KAN | ml-supervised::kan | OUT | Function-approx not a bottleneck; no DQN wiring | — |
|
||||
| Encoder-decoder separation | ml-supervised::tft | IN | E.4: extend D.3 horizon-decomposed V to full trunk/head separation | pending |
|
||||
| Attention-weight interpretability | various | IN | E.5: ISV-expose attention weights from all temporal heads | pending |
|
||||
| Multi-task auxiliary heads | ml-supervised | IN | E.6: next-return + regime-classification auxiliary heads | pending |
|
||||
@@ -69,6 +69,45 @@ if [ -x "$SCRIPT_DIR/gpu-hotpath-guard.sh" ]; then
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# DQN v2 Invariant 7 enforcement: component-adding commits must update audit docs.
|
||||
check_audit_doc_updates() {
|
||||
local staged=$(git diff --cached --name-only)
|
||||
# Detect "component-adding" commits: new .rs or .cu files, or modifications to
|
||||
# files that define ISV slots (gpu_dqn_trainer.rs), kernels (cuda_pipeline/*.cu),
|
||||
# or state layout (state_layout.cuh).
|
||||
local component_changes=$(echo "$staged" | grep -E '(^crates/ml/src/(cuda_pipeline|trainers/dqn)/|^crates/ml/src/cuda_pipeline/state_layout\.cuh$)')
|
||||
if [ -z "$component_changes" ]; then return 0; fi
|
||||
|
||||
local audit_docs_touched=$(echo "$staged" | grep -E '^docs/(dqn-wire-up-audit|isv-slots|dqn-gpu-hot-path-audit|dqn-named-dims|ml-supervised-to-dqn-concept-audit)\.md$')
|
||||
if [ -z "$audit_docs_touched" ]; then
|
||||
echo "❌ Invariant 7 violation: component changes without audit-doc update"
|
||||
echo " Changed components:"
|
||||
echo "$component_changes" | sed 's/^/ /'
|
||||
echo " You must update one of:"
|
||||
echo " docs/dqn-wire-up-audit.md"
|
||||
echo " docs/isv-slots.md"
|
||||
echo " docs/dqn-gpu-hot-path-audit.md"
|
||||
echo " docs/dqn-named-dims.md"
|
||||
echo " docs/ml-supervised-to-dqn-concept-audit.md"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# DQN v2 Invariant 9 enforcement: reject TODO/FIXME/XXX/HACK/TBD markers in added code.
|
||||
check_no_todo_fixme() {
|
||||
local added_lines=$(git diff --cached --diff-filter=AM -U0 -- '*.rs' '*.cu' '*.cuh' \
|
||||
| grep -E '^\+' | grep -vE '^\+\+\+')
|
||||
local bad=$(echo "$added_lines" | grep -E '(TODO|FIXME|\bXXX\b|\bHACK\b|\bTBD\b|unimplemented!|todo!\()')
|
||||
if [ -n "$bad" ]; then
|
||||
echo "❌ Invariant 9 violation: deferred-work markers found in staged code"
|
||||
echo "$bad" | sed 's/^/ /'
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_audit_doc_updates || exit 1
|
||||
check_no_todo_fixme || exit 1
|
||||
|
||||
echo "✅ All pre-commit checks passed!"
|
||||
echo ""
|
||||
echo "Summary:"
|
||||
|
||||
Reference in New Issue
Block a user