Implements spec §4.A.2 structural layout fingerprint with tail placement
rather than head placement (spec alternative: §4.A.2 Step 5.3 alt).
Head placement (ISV[0..2)) was rejected because isv_signals[0] and [1]
are actively written by the isv_signal_update kernel (Q-drift EMA and
gradient-norm EMA). Shifting those would require updating every literal
reference in experience_kernels.cu — a larger change than warranted for
pure contract enforcement. Tail placement leaves all existing indices
intact, touches zero kernel .cu files, and fulfils the same design contract.
Key changes:
- ISV_LAYOUT_FINGERPRINT_LO_INDEX = 37, HI_INDEX = 38 (u64 across 2×f32).
- LAYOUT_FINGERPRINT_CURRENT: u64 = FNV-1a of slot-list seed bytes.
Value: 0x85d4d76b578a7c17. Any slot change updates seed bytes,
which updates the hash automatically.
- Constructor writes fingerprint after zero-init; calls
check_layout_fingerprint() to self-verify before returning.
- check_layout_fingerprint(): reads pinned slots [37..39), recomposes u64,
fails-fast on mismatch with "retrain required" message.
- Error message does NOT mention migration as an option.
- Pre-commit hook rejects `fn migrate_isv|upgrade_isv` names — makes the
no-migration rule structurally enforced (check_no_isv_migrations).
- ISV_TOTAL_DIM: 37 → 39.
- Zero existing index shifts (no kernel literal sites affected).
- StateResetRegistry entry renamed ISV_SCHEMA_VERSION → ISV_LAYOUT_FINGERPRINT.
- ResetCategory::SchemaContract docstring updated to remove "migration" framing.
- docs/isv-slots.md: updated table + design note for tail placement.
Tests: state_reset_registry 3 unit tests pass with renamed entry.
cargo check -p ml clean (pre-existing warnings only).
Plan 1 Task 5. Spec §4.A.2 (tail-placement alternative).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
134 lines
5.4 KiB
Bash
Executable File
134 lines
5.4 KiB
Bash
Executable File
#!/bin/bash
|
||
# Pre-commit hook for Foxhunt HFT system
|
||
# Lightweight checks only — compilation is validated by agents before commit
|
||
|
||
set -e
|
||
|
||
echo "🔍 Running pre-commit quality checks..."
|
||
echo ""
|
||
|
||
# Check for common issues in staged files
|
||
echo "🔎 Checking for common issues..."
|
||
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep "\.rs$" || true)
|
||
|
||
if [ -n "$STAGED_FILES" ]; then
|
||
# Check for unwrap() usage
|
||
UNWRAP_FILES=$(echo "$STAGED_FILES" | xargs grep -l "\.unwrap()" 2>/dev/null || true)
|
||
if [ -n "$UNWRAP_FILES" ]; then
|
||
echo "⚠️ Warning: Found .unwrap() in staged files:"
|
||
echo "$UNWRAP_FILES" | sed 's/^/ /'
|
||
echo " Consider using ? operator or proper error handling"
|
||
echo ""
|
||
fi
|
||
|
||
# Check for expect() with generic messages
|
||
EXPECT_FILES=$(echo "$STAGED_FILES" | xargs grep -l "\.expect(\"\")" 2>/dev/null || true)
|
||
if [ -n "$EXPECT_FILES" ]; then
|
||
echo "⚠️ Warning: Found .expect() with empty message in:"
|
||
echo "$EXPECT_FILES" | sed 's/^/ /'
|
||
echo " Provide descriptive error messages"
|
||
echo ""
|
||
fi
|
||
|
||
# Check for TODO/FIXME comments
|
||
TODO_COUNT=$(echo "$STAGED_FILES" | xargs grep -c "TODO\|FIXME" 2>/dev/null | awk -F: '{sum+=$2} END {print sum}' || echo "0")
|
||
if [ "$TODO_COUNT" -gt 0 ]; then
|
||
echo "ℹ️ Info: Found $TODO_COUNT TODO/FIXME comments in staged files"
|
||
echo ""
|
||
fi
|
||
|
||
# Check for stub patterns in src/ files
|
||
SRC_FILES=$(echo "$STAGED_FILES" | grep "/src/" || true)
|
||
if [ -n "$SRC_FILES" ]; then
|
||
STUB_RETURNS=$(echo "$SRC_FILES" | xargs grep -n 'return 0\.0\b\|return 100\.0\|return 1\.0\b\|return vec!\[\]' 2>/dev/null | grep -v '// ok:' | grep -v '// legitimate' || true)
|
||
if [ -n "$STUB_RETURNS" ]; then
|
||
echo "⚠️ Warning: Possible stub return values in staged files:"
|
||
echo "$STUB_RETURNS" | head -5 | sed 's/^/ /'
|
||
echo " Add '// ok: <reason>' comment to suppress if intentional"
|
||
echo ""
|
||
fi
|
||
|
||
STUB_MARKERS=$(echo "$SRC_FILES" | xargs grep -n '"placeholder"\|"stub"\|"fake"\|"hardcoded"\|"dummy"' 2>/dev/null | grep -v '// ok:' || true)
|
||
if [ -n "$STUB_MARKERS" ]; then
|
||
echo "⚠️ Warning: Stub marker strings in production code:"
|
||
echo "$STUB_MARKERS" | head -5 | sed 's/^/ /'
|
||
echo ""
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# GPU hot-path leak detection
|
||
echo "🔎 Checking for GPU→CPU leaks in hot paths..."
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
if [ -x "$SCRIPT_DIR/gpu-hotpath-guard.sh" ]; then
|
||
if ! "$SCRIPT_DIR/gpu-hotpath-guard.sh" --staged; then
|
||
echo "⛔ GPU hot-path leak detected — fix or suppress with // gpu-ok: <reason>"
|
||
exit 1
|
||
fi
|
||
echo " No GPU→CPU leaks in hot paths"
|
||
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
|
||
}
|
||
|
||
# DQN v2 Invariant (spec §4.A.2): no ISV migration functions allowed.
|
||
# The layout fingerprint is fail-fast only; no migration path exists.
|
||
check_no_isv_migrations() {
|
||
local bad=$(git diff --cached -U0 -- crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs \
|
||
crates/ml/src/trainers/dqn/trainer/ 2>/dev/null | \
|
||
grep -E '^\+.*fn\s+(migrate|upgrade)_isv' || true)
|
||
if [ -n "$bad" ]; then
|
||
echo "❌ Spec §4.A.2 violation: ISV migration functions are forbidden"
|
||
echo " (layout fingerprint is fail-fast only; no migration path exists)"
|
||
echo "$bad" | sed 's/^/ /'
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
check_audit_doc_updates || exit 1
|
||
check_no_todo_fixme || exit 1
|
||
check_no_isv_migrations || exit 1
|
||
|
||
echo "✅ All pre-commit checks passed!"
|
||
echo ""
|
||
echo "Summary:"
|
||
echo " - Code quality checks: ✅"
|
||
echo " - GPU hot-path guard: ✅"
|
||
echo ""
|
||
|
||
exit 0
|