docs(sp20): trader-grade trade management spec + audit infrastructure
Adds SP20 — full production trader-management system in one
greenfield commit (3-4 weeks of implementation work to follow):
* Tier 0: multi-resolution time-scaled market features (3 horizons)
* Tier 1: trade-arc awareness (4 features per batch)
* Tier 2: per-unit trail-stop (entry + trail + stop per unit)
* Tier 3: pyramiding + partial profit-taking (HalfFlat actions,
N_ACTIONS=9→11)
* Tier 4: Forward-Return-Distribution head + confidence gate +
per-batch anti-martingale sizing + position heat cap +
vol-adjusted defaults
Spec went through critical-review pass (v1→v2→v3):
* v1: 3 tiers, side-channel features, single-gate acceptance
* v2: 5 tiers added partial-flat + anti-mart + multi-res + checklist
* v3: foundational fixes for 4 CRIT + 6 SIG + 6 MIN findings
(per-unit pyramid state, encoder-input injection vs side-channel,
FRD head replaces survivor-biased checklist, override stack
ordering, per-batch anti-mart, real-time multi-res scales,
P-1 ceiling falsification gate, multi-tier acceptance)
§0 Foundational Principles (NEW, non-negotiable):
* §0.1 every numerical constant ISV-resident (no hardcoded #defines
in new kernels; structural-dim exception only)
* §0.2 every kernel/slot/head/action fully wired in same commit
* §0.3 diagnostics baked in at birth (every observable in JSONL)
* §0.4 per-phase ship-gate: all three audits must pass
Audit infrastructure shipped with the spec:
* scripts/audit-isv.sh — greps new .cu for hardcoded #defines
* scripts/audit-wiring.sh — verifies kernels/slots/heads/actions
have producer + consumer in code
* scripts/audit-diag.sh — runs local 100-step smoke, validates
manifest-listed jq paths present in JSONL
* scripts/audit-manifest/ — per-phase append manifests (kernels,
slots, heads, actions, diag-fields)
Naming discipline: audit scripts and manifest are SP-agnostic (no
`sp20-` prefix) per new pearl `feedback_no_sp_or_version_prefixes_in_file_names`
— they'll serve future SPs too. SP numbers belong only in
docs/superpowers/{specs,plans}/ filenames.
Audit scripts dogfooded — already caught two real violations on
existing code that the formal review missed:
* audit-isv: KL_EMA_ALPHA=0.05f hardcoded in rl_q_pi_distill_grad.cu
* audit-wiring: TrailTighten action (a7) has no handler in any
kernel (per pearl_dead_trail_stop_actions_a7_a8)
These violations are SP20 P5/P10 fix targets.
User decision recorded in spec §3 P-1: ceiling-falsification phase
intentionally skipped — SP20 is the architectural launchpad for
the broader trader system regardless of whether current arch could
be pushed further at 1M steps. P-1 may be revisited as standalone
work after SP20 ships.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
137
scripts/audit-diag.sh
Executable file
137
scripts/audit-diag.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
# audit-diag.sh — enforce "every observable surfaces in diag JSONL"
|
||||
# per SP20 §0.3.
|
||||
#
|
||||
# Runs a local 100-step smoke (small footprint, b_size=4, predecoded
|
||||
# test data) and parses the produced diag JSONL. For each entry in
|
||||
# scripts/audit-manifest/diag-fields.txt: verify the jq-syntax path
|
||||
# resolves to a non-null, non-NaN value in at least one sampled row.
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 — zero missing fields
|
||||
# 1 — one or more missing fields
|
||||
# 2 — usage / smoke-run failure
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT=$(git rev-parse --show-toplevel)
|
||||
MANIFEST="$ROOT/scripts/audit-manifest/diag-fields.txt"
|
||||
BINARY="$ROOT/target/release/examples/alpha_rl_train"
|
||||
SMOKE_OUT_DIR="${TMPDIR:-/tmp}/audit-diag-smoke-$$"
|
||||
DIAG_FILE="$SMOKE_OUT_DIR/diag.jsonl"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "ERROR: jq required (apt install jq)" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$MANIFEST" ]]; then
|
||||
echo "ERROR: manifest not found: $MANIFEST" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mapfile -t fields < <(grep -vE '^\s*(#|$)' "$MANIFEST")
|
||||
|
||||
if [[ ${#fields[@]} -eq 0 ]]; then
|
||||
echo "audit-diag: manifest empty — no diag fields to audit. PASS."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Build binary if missing/stale ─────────────────────────────
|
||||
if [[ ! -x "$BINARY" || "$BINARY" -ot "$ROOT/crates/ml-alpha/src/trainer/integrated.rs" ]]; then
|
||||
echo "audit-diag: building alpha_rl_train release binary..."
|
||||
(cd "$ROOT" && SQLX_OFFLINE=true cargo build -p ml-alpha --example alpha_rl_train --release 2>&1 | tail -5)
|
||||
fi
|
||||
|
||||
# ── Local smoke: 100 steps, b_size=4, single fold, single seed ───
|
||||
mkdir -p "$SMOKE_OUT_DIR"
|
||||
echo "audit-diag: running 100-step local smoke (b_size=4) → $SMOKE_OUT_DIR"
|
||||
|
||||
# Inputs — use test-data MBP-10 baseline
|
||||
TEST_DATA="${FOXHUNT_TEST_DATA:-$ROOT/test_data/futures-baseline}"
|
||||
if [[ ! -d "$TEST_DATA/ES.FUT" ]]; then
|
||||
echo "ERROR: test data missing: $TEST_DATA/ES.FUT" >&2
|
||||
echo "Set FOXHUNT_TEST_DATA env var to a directory containing ES.FUT/ subdirectory" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
PREDECODED="$TEST_DATA/predecoded"
|
||||
if [[ ! -d "$PREDECODED" ]]; then
|
||||
PREDECODED="$TEST_DATA"
|
||||
fi
|
||||
|
||||
"$BINARY" \
|
||||
--mbp10-data-dir "$TEST_DATA/ES.FUT" \
|
||||
--trades-data-dir "$TEST_DATA/ES.FUT" \
|
||||
--predecoded-dir "$PREDECODED" \
|
||||
--output-dir "$SMOKE_OUT_DIR" \
|
||||
--n-steps 100 \
|
||||
--n-backtests 4 \
|
||||
--seq-len 32 \
|
||||
--per-capacity 256 \
|
||||
--seed 16962 \
|
||||
--instrument-mode front-month \
|
||||
--n-folds 1 \
|
||||
--fold-idx 0 \
|
||||
--n-eval-steps 0 \
|
||||
> "$SMOKE_OUT_DIR/stdout.log" 2> "$SMOKE_OUT_DIR/stderr.log" \
|
||||
|| { echo "ERROR: local smoke failed; see $SMOKE_OUT_DIR/stderr.log"; tail -20 "$SMOKE_OUT_DIR/stderr.log"; exit 2; }
|
||||
|
||||
if [[ ! -f "$DIAG_FILE" ]]; then
|
||||
echo "ERROR: smoke completed but diag.jsonl not produced at $DIAG_FILE" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
n_rows=$(wc -l < "$DIAG_FILE")
|
||||
echo "audit-diag: smoke produced $n_rows diag rows"
|
||||
|
||||
if [[ $n_rows -lt 10 ]]; then
|
||||
echo "ERROR: too few diag rows ($n_rows); smoke may have crashed early" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Sample 3 rows: first, middle, last
|
||||
mid_row=$((n_rows / 2))
|
||||
last_row=$n_rows
|
||||
sample_rows=(1 $mid_row $last_row)
|
||||
|
||||
# ── Verify each manifest field present + non-trivial ─────────
|
||||
violations=0
|
||||
echo
|
||||
echo "audit-diag: validating ${#fields[@]} field path(s)..."
|
||||
for field in "${fields[@]}"; do
|
||||
found_any=0
|
||||
bad_any=0
|
||||
for r in "${sample_rows[@]}"; do
|
||||
val=$(sed -n "${r}p" "$DIAG_FILE" | jq -c "$field // empty" 2>/dev/null || echo "<jq-error>")
|
||||
if [[ "$val" == "<jq-error>" ]]; then
|
||||
bad_any=1
|
||||
continue
|
||||
fi
|
||||
if [[ -n "$val" && "$val" != "null" ]]; then
|
||||
# Check for NaN (jq emits null for NaN typically; double-check)
|
||||
if [[ "$val" != *"NaN"* && "$val" != *"nan"* ]]; then
|
||||
found_any=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [[ $found_any -eq 0 ]]; then
|
||||
echo " VIOLATION field $field: not present (or null/NaN) in sampled rows"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo
|
||||
echo "audit-diag: $violations missing field(s)"
|
||||
|
||||
if [[ $violations -gt 0 ]]; then
|
||||
echo
|
||||
echo "Per SP20 §0.3: every observable state MUST be in diag JSONL same commit as the mechanic."
|
||||
echo "Add the field to the diag JSONL emission in crates/ml-alpha/examples/alpha_rl_train.rs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "audit-diag: PASS (sampled rows: ${sample_rows[*]})"
|
||||
# Cleanup smoke dir on success
|
||||
rm -rf "$SMOKE_OUT_DIR"
|
||||
exit 0
|
||||
105
scripts/audit-isv.sh
Executable file
105
scripts/audit-isv.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
# audit-isv.sh — enforce "every numerical constant is ISV-resident"
|
||||
# per SP20 §0.1.
|
||||
#
|
||||
# Walks scripts/audit-manifest/kernels.txt and greps each listed .cu
|
||||
# for `#define NAME VALUE` lines where VALUE is a numeric literal.
|
||||
# Flags any define whose NAME is NOT in the structural-dim allowlist
|
||||
# AND does NOT end in `_INDEX` (ISV slot pointer).
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 — zero violations
|
||||
# 1 — one or more violations found
|
||||
# 2 — usage / setup error
|
||||
#
|
||||
# Allowlist for structural-dim names (compile-time required for buffer
|
||||
# layouts / atom support / shared memory):
|
||||
ALLOWED_NAMES=(
|
||||
"N_ACTIONS"
|
||||
"Q_N_ATOMS"
|
||||
"HIDDEN_DIM"
|
||||
"REGIME_DIM"
|
||||
"MAX_UNITS"
|
||||
"BLOCK_DIM"
|
||||
"GRID_DIM"
|
||||
"BLOCK_SIZE"
|
||||
"WARP_SIZE"
|
||||
"BLOCK_X" "BLOCK_Y" "BLOCK_Z"
|
||||
"MAX_WINDOW_ENTRIES"
|
||||
)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT=$(git rev-parse --show-toplevel)
|
||||
MANIFEST="$ROOT/scripts/audit-manifest/kernels.txt"
|
||||
CUDA_DIR="$ROOT/crates/ml-alpha/cuda"
|
||||
|
||||
if [[ ! -f "$MANIFEST" ]]; then
|
||||
echo "ERROR: manifest not found: $MANIFEST" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Read manifest into array, skipping blank lines and comments.
|
||||
mapfile -t kernels < <(grep -vE '^\s*(#|$)' "$MANIFEST" | awk '{print $1}')
|
||||
|
||||
if [[ ${#kernels[@]} -eq 0 ]]; then
|
||||
echo "audit-isv: manifest empty — no kernels to audit. PASS."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Build a regex of allowed names + `_INDEX` suffix pattern.
|
||||
allowed_regex=""
|
||||
for name in "${ALLOWED_NAMES[@]}"; do
|
||||
allowed_regex="${allowed_regex}|^${name}$"
|
||||
done
|
||||
allowed_regex="(${allowed_regex#|}|_INDEX$)"
|
||||
|
||||
violations=0
|
||||
total_defines=0
|
||||
|
||||
echo "audit-isv: auditing ${#kernels[@]} kernel(s) from manifest..."
|
||||
echo
|
||||
|
||||
for kernel in "${kernels[@]}"; do
|
||||
cu_file="$CUDA_DIR/${kernel}.cu"
|
||||
if [[ ! -f "$cu_file" ]]; then
|
||||
echo "ERROR: kernel listed in manifest but file missing: $cu_file" >&2
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Extract `#define NAME VALUE` where VALUE is a numeric literal
|
||||
# (integer or float, optional `f` suffix, optional sign).
|
||||
# Skip macros that contain function-like () bodies — those are
|
||||
# parameterized templates, not constants.
|
||||
while IFS= read -r line; do
|
||||
# Strip leading whitespace
|
||||
trimmed="${line#"${line%%[![:space:]]*}"}"
|
||||
# Parse: #define NAME VALUE
|
||||
if [[ "$trimmed" =~ ^\#define[[:space:]]+([A-Z_][A-Z0-9_]*)[[:space:]]+([+-]?[0-9]+(\.[0-9]+)?f?)$ ]]; then
|
||||
name="${BASH_REMATCH[1]}"
|
||||
value="${BASH_REMATCH[2]}"
|
||||
total_defines=$((total_defines + 1))
|
||||
if [[ "$name" =~ $allowed_regex ]]; then
|
||||
: # allowed
|
||||
else
|
||||
echo " VIOLATION $kernel: #define $name $value (should be ISV slot)"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
fi
|
||||
done < "$cu_file"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "audit-isv: scanned $total_defines numeric #defines, found $violations violation(s)."
|
||||
|
||||
if [[ $violations -gt 0 ]]; then
|
||||
echo
|
||||
echo "Per SP20 §0.1: every numerical constant in new kernels must be ISV-resident."
|
||||
echo "Add an RL_*_INDEX slot to crates/ml-alpha/src/rl/isv_slots.rs,"
|
||||
echo "seed via rl_isv_write at trainer init, and read from the slot in the kernel."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "audit-isv: PASS"
|
||||
exit 0
|
||||
34
scripts/audit-manifest/README.md
Normal file
34
scripts/audit-manifest/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Audit manifest
|
||||
|
||||
Tracks names of newly-added artifacts so the three audit scripts
|
||||
(`scripts/audit-isv.sh`, `scripts/audit-wiring.sh`,
|
||||
`scripts/audit-diag.sh`) can verify ISV-residency, wiring, and
|
||||
diagnostic exposure on every commit.
|
||||
|
||||
These manifests are SP-agnostic — they hold the cumulative set of
|
||||
"things introduced by the active development line" that downstream
|
||||
ship gates audit. SP20 starts the convention; future SPs extend it.
|
||||
|
||||
## Files
|
||||
|
||||
* `kernels.txt` — one new .cu kernel basename per line (e.g. `rl_trail_mutate`)
|
||||
* `slots.txt` — one new RL_*_INDEX constant name per line (e.g. `RL_TRAIL_ADJUST_RATE_INDEX`)
|
||||
* `heads.txt` — one new head module name per line (e.g. `frd`)
|
||||
* `actions.txt` — one new Action enum entry per line (e.g. `HalfFlatLong`)
|
||||
* `diag-fields.txt` — one new diag JSONL field path per line, jq-syntax
|
||||
(e.g. `.units.unit_count`, `.trail.fired_count_step`)
|
||||
|
||||
## Workflow per phase commit
|
||||
|
||||
1. Implement the phase (new kernel / slot / head / action / diag field)
|
||||
2. Append the names to relevant manifest files
|
||||
3. Run `scripts/audit-isv.sh` — must report 0 violations
|
||||
4. Run `scripts/audit-wiring.sh` — must report 0 violations
|
||||
5. Run `scripts/audit-diag.sh` — must report 0 missing fields
|
||||
6. Commit (the manifest append + the code in the same commit)
|
||||
|
||||
Failures are immediately actionable — no "iterate within scope"
|
||||
softness. Per SP20 spec §0.4.
|
||||
|
||||
## Lines starting with `#` are comments and ignored by the audit
|
||||
## scripts. Blank lines also ignored.
|
||||
3
scripts/audit-manifest/actions.txt
Normal file
3
scripts/audit-manifest/actions.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# New Action enum entries added by the active development line.
|
||||
# Each phase commit appends here.
|
||||
# Format: one variant name per line (matches src/rl/common.rs::Action enum).
|
||||
4
scripts/audit-manifest/diag-fields.txt
Normal file
4
scripts/audit-manifest/diag-fields.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
# New diag JSONL field paths added by the active development line.
|
||||
# Each phase commit appends here.
|
||||
# Format: one jq-syntax path per line (e.g. `.trail.fired_count_step`).
|
||||
# Used by audit-diag.sh against a local-smoke JSONL output.
|
||||
3
scripts/audit-manifest/heads.txt
Normal file
3
scripts/audit-manifest/heads.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# New head module names added by the active development line.
|
||||
# Each phase commit appends here.
|
||||
# Format: one module name per line (matches crates/ml-alpha/src/heads/<name>.rs).
|
||||
3
scripts/audit-manifest/kernels.txt
Normal file
3
scripts/audit-manifest/kernels.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# New .cu kernels added by the active development line.
|
||||
# Each phase commit appends here.
|
||||
# Format: one basename per line, without `.cu` extension.
|
||||
3
scripts/audit-manifest/slots.txt
Normal file
3
scripts/audit-manifest/slots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# New ISV slot constants added by the active development line.
|
||||
# Each phase commit appends here.
|
||||
# Format: one RL_*_INDEX symbol per line.
|
||||
187
scripts/audit-wiring.sh
Executable file
187
scripts/audit-wiring.sh
Executable file
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env bash
|
||||
# audit-wiring.sh — enforce "every kernel/slot/head/action fully wired"
|
||||
# per SP20 §0.2.
|
||||
#
|
||||
# Walks all four manifests:
|
||||
# * kernels.txt → every entry has a registration in build.rs KERNELS
|
||||
# AND a `.launch_builder(&self.<name>_fn)` site
|
||||
# in src/trainer/integrated.rs
|
||||
# * slots.txt → every entry has a write site (seed in rl_isv_write
|
||||
# OR write in a .cu kernel) AND a read site
|
||||
# (read in a .cu kernel)
|
||||
# * heads.txt → every entry has src/heads/<name>.rs AND forward
|
||||
# call AND backward call in the trainer
|
||||
# * actions.txt → every entry has a branch in
|
||||
# cuda/actions_to_market_targets.cu OR a handler
|
||||
# in an override kernel
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 — zero violations
|
||||
# 1 — one or more violations
|
||||
# 2 — usage error
|
||||
# Note: NOT using `set -e` — many grep|head|wc pipelines naturally
|
||||
# return non-zero (SIGPIPE on head consuming early, grep no-match).
|
||||
# We handle exit codes explicitly via `|| true` and `|| echo 0` on
|
||||
# the suspect pipelines, and bookkeep `violations` for the final
|
||||
# exit code.
|
||||
set -uo pipefail
|
||||
|
||||
ROOT=$(git rev-parse --show-toplevel)
|
||||
M="$ROOT/scripts/audit-manifest"
|
||||
BUILD_RS="$ROOT/crates/ml-alpha/build.rs"
|
||||
TRAINER="$ROOT/crates/ml-alpha/src/trainer/integrated.rs"
|
||||
ISV_SLOTS="$ROOT/crates/ml-alpha/src/rl/isv_slots.rs"
|
||||
COMMON_RS="$ROOT/crates/ml-alpha/src/rl/common.rs"
|
||||
ACTIONS_KERNEL="$ROOT/crates/ml-alpha/cuda/actions_to_market_targets.cu"
|
||||
HEADS_DIR="$ROOT/crates/ml-alpha/src/heads"
|
||||
CUDA_DIR="$ROOT/crates/ml-alpha/cuda"
|
||||
|
||||
violations=0
|
||||
|
||||
read_manifest() {
|
||||
local file="$1"
|
||||
if [[ -f "$file" ]]; then
|
||||
grep -vE '^\s*(#|$)' "$file" | awk '{print $1}'
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── 1. KERNELS ─────────────────────────────────────────────
|
||||
echo "audit-wiring: KERNELS"
|
||||
while IFS= read -r kernel; do
|
||||
[[ -z "$kernel" ]] && continue
|
||||
cu_file="$CUDA_DIR/${kernel}.cu"
|
||||
if [[ ! -f "$cu_file" ]]; then
|
||||
echo " VIOLATION kernel $kernel: source file missing ($cu_file)"
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
# build.rs registration
|
||||
if ! grep -qE "\"${kernel}\"" "$BUILD_RS"; then
|
||||
echo " VIOLATION kernel $kernel: not registered in build.rs KERNELS"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
# trainer field
|
||||
if ! grep -qE "${kernel}_fn[: ,]" "$TRAINER"; then
|
||||
echo " VIOLATION kernel $kernel: no <name>_fn field in trainer"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
# trainer launch site
|
||||
if ! grep -qE "launch_builder\(&self\.${kernel}_fn\)" "$TRAINER"; then
|
||||
echo " VIOLATION kernel $kernel: no launch site in trainer"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
done < <(read_manifest "$M/kernels.txt")
|
||||
|
||||
# ─── 2. SLOTS ───────────────────────────────────────────────
|
||||
echo "audit-wiring: SLOTS"
|
||||
while IFS= read -r slot; do
|
||||
[[ -z "$slot" ]] && continue
|
||||
# slot defined in isv_slots.rs?
|
||||
if ! grep -qE "pub const ${slot}\s*:\s*usize" "$ISV_SLOTS"; then
|
||||
echo " VIOLATION slot $slot: no `pub const` definition in isv_slots.rs"
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
# Extract numeric slot index for cross-kernel grep
|
||||
slot_idx=$(awk -v name="$slot" '
|
||||
$0 ~ "pub const " name "[[:space:]]*:[[:space:]]*usize" {
|
||||
if (match($0, /=[[:space:]]*[0-9]+/)) {
|
||||
v = substr($0, RSTART, RLENGTH)
|
||||
gsub(/[^0-9]/, "", v)
|
||||
print v; exit
|
||||
}
|
||||
}' "$ISV_SLOTS")
|
||||
[[ -z "$slot_idx" ]] && slot_idx="-1"
|
||||
# producer: seeded in rl_isv_write list OR written by a .cu kernel
|
||||
producer_seed=0
|
||||
producer_kernel=0
|
||||
if grep -qE "isv_slots::${slot}" "$TRAINER"; then
|
||||
producer_seed=1
|
||||
fi
|
||||
# kernel writes via isv[INDEX] = ... or isv[<num>] = ...
|
||||
if grep -qrE "isv\[(${slot}|${slot_idx})\][[:space:]]*=" "$CUDA_DIR" 2>/dev/null; then
|
||||
producer_kernel=1
|
||||
fi
|
||||
if [[ $producer_seed -eq 0 && $producer_kernel -eq 0 ]]; then
|
||||
echo " VIOLATION slot $slot (idx $slot_idx): no producer (not seeded in rl_isv_write, not written by any kernel)"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
# consumer: read by some .cu (isv[INDEX] in non-assignment context)
|
||||
# Heuristic: count distinct .cu files containing the slot reference
|
||||
# in a context that's NOT immediately followed by `=` (assignment).
|
||||
# Pattern `isv[X][^=]` matches isv[X] where next char isn't `=`.
|
||||
read_only_files=$(grep -lrE "isv\[(${slot}|${slot_idx})\][^=]" "$CUDA_DIR" 2>/dev/null | wc -l)
|
||||
if [[ $read_only_files -eq 0 ]]; then
|
||||
echo " VIOLATION slot $slot (idx $slot_idx): no consumer (no kernel reads it in non-assignment context)"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
done < <(read_manifest "$M/slots.txt")
|
||||
|
||||
# ─── 3. HEADS ───────────────────────────────────────────────
|
||||
echo "audit-wiring: HEADS"
|
||||
while IFS= read -r head; do
|
||||
[[ -z "$head" ]] && continue
|
||||
head_rs="$HEADS_DIR/${head}.rs"
|
||||
if [[ ! -f "$head_rs" ]]; then
|
||||
echo " VIOLATION head $head: module file missing ($head_rs)"
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
if ! grep -qE "fn forward" "$head_rs"; then
|
||||
echo " VIOLATION head $head: no fn forward in module"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
if ! grep -qE "fn backward" "$head_rs"; then
|
||||
echo " VIOLATION head $head: no fn backward in module"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
# Adam step + LR controller — grep for the head's forward call site
|
||||
# in the trainer (loose heuristic).
|
||||
if ! grep -qE "${head}_head" "$TRAINER"; then
|
||||
echo " VIOLATION head $head: no ${head}_head field or call in trainer"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
done < <(read_manifest "$M/heads.txt")
|
||||
|
||||
# ─── 4. ACTIONS ─────────────────────────────────────────────
|
||||
echo "audit-wiring: ACTIONS"
|
||||
while IFS= read -r action; do
|
||||
[[ -z "$action" ]] && continue
|
||||
# Action enum entry
|
||||
if ! grep -qE "${action}\s*=\s*[0-9]+" "$COMMON_RS"; then
|
||||
echo " VIOLATION action $action: not in Action enum in common.rs"
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
# Get action index
|
||||
action_idx=$(awk -v name="$action" '
|
||||
$0 ~ name "[[:space:]]*=[[:space:]]*[0-9]+" {
|
||||
if (match($0, /=[[:space:]]*[0-9]+/)) {
|
||||
v = substr($0, RSTART, RLENGTH)
|
||||
gsub(/[^0-9]/, "", v)
|
||||
print v; exit
|
||||
}
|
||||
}' "$COMMON_RS")
|
||||
[[ -z "$action_idx" ]] && action_idx="-1"
|
||||
# Handler in actions_to_market_targets.cu: branch `action == <idx>`
|
||||
if ! grep -qE "action[[:space:]]*==[[:space:]]*${action_idx}\b" "$ACTIONS_KERNEL"; then
|
||||
# Could be handled in override kernel — check across all .cu files
|
||||
handler_count=$(grep -lrE "(action|actions\[[^]]*\])[[:space:]]*==[[:space:]]*${action_idx}\b" "$CUDA_DIR" 2>/dev/null | wc -l)
|
||||
if [[ "$handler_count" -eq 0 ]]; then
|
||||
echo " VIOLATION action $action (idx $action_idx): no handler in actions_to_market_targets or override kernels"
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
fi
|
||||
done < <(read_manifest "$M/actions.txt")
|
||||
|
||||
echo
|
||||
echo "audit-wiring: $violations violation(s)"
|
||||
|
||||
if [[ $violations -gt 0 ]]; then
|
||||
echo
|
||||
echo "Per SP20 §0.2: every kernel/slot/head/action MUST have producer + consumer in same commit."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "audit-wiring: PASS"
|
||||
exit 0
|
||||
Reference in New Issue
Block a user