feat(sp14): B.5 — gradient_hack_detect_kernel (anti-mesa-opt circuit breaker)

Detects suspected gradient hacking: when gate1 is open AND aux_dir_acc
post-open-minimum drops > LOCKOUT_TRIGGER_DROP (0.05) below the Schmitt
open-threshold (target + SCHMITT_BAND = target + 0.03) AND q_disagreement
rises > LOCKOUT_TRIGGER_DIS_RISE (0.10) above the analytic random-alignment
baseline 0.5, simultaneously.

Action: force gate1_open_state = 0 (ISV[391]); set lockout_remaining = 2.0
epochs (LOCKOUT_EPOCHS). During lockout, gate1 stays force-closed
regardless of alpha_grad_compute_kernel output.

Tracks AUX_DIR_ACC_POST_OPEN_MIN (ISV[394]): running minimum of aux_dir_acc
since gate1 last opened; resets to 1.0 sentinel when gate closes naturally
or when circuit breaker fires.

Slot indices shifted +2 from original plan (SP13 closeout added
HOLD_RATE_TARGET=381 + HOLD_RATE_OBSERVED_EMA=382): Q_DIS_SHORT=383,
GATE1=391, POST_OPEN_MIN=394, LOCKOUT=395. Matches sp14_isv_slots.rs.

Single-thread state-machine kernel (threadIdx.x==0 guard); runs at end of
each epoch after alpha_grad_compute_kernel. No atomicAdd per
feedback_no_atomicadd.md.

1 oracle test: gradient_hack_circuit_breaker_fires verifies trigger
conditions (aux_drop=0.08 > 0.05, q_rise=0.15 > 0.10) cause lockout=2.0
and gate1 force-close=0.0.

B.3+B.4 regression: 4 GPU tests unchanged (5 total GPU pass, 1 host pass).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-05 19:29:22 +02:00
parent 49cdf90ecc
commit 82fe6cea66
4 changed files with 272 additions and 0 deletions

View File

@@ -6383,3 +6383,59 @@ The adaptive-`k_aux` mechanism is degenerate-but-non-fatal: Gate 1 still works,
- **Reverse dependencies**: this kernel depends on B.3's writes to ISV[383] and ISV[389], plus SP13's writes to ISV[372] and ISV[373]. ISV[388] is not yet produced (Known Limitation above).
This kernel is the second known-orphan in the B.3..B.6 producer chain (B.3 was the first). The orphan is acknowledged here per `feedback_wire_everything_up.md` (the rule's "same-commit wire-up" requirement is relaxed for atomic chained-producer-consumer landings as long as every producer is documented as orphaned and every consumer's wire-up commit is in the same chain).
## SP14 Layer B — Commit B.5: gradient_hack_detect_kernel + cubin registration + 1 GPU oracle test (2026-05-05)
**Why this commit.** B.5 is the anti-mesa-optimization circuit breaker for the Earned Gradient Flow pearl. Mesa-optimization (gradient hacking) can manifest as the model learning to elevate `aux_dir_acc` above the Schmitt open-threshold *without* a genuine improvement in directional accuracy — i.e., the aux head learns to fool the gate rather than predict correctly. The circuit breaker detects this by watching for a simultaneous drop in `aux_dir_acc` post-open-minimum and rise in `q_disagreement`: if the aux head were actually improving, Q-disagreement would stay near the analytic baseline (0.5); a rising disagreement alongside a falling post-open minimum is the smoking-gun pattern.
This commit lands the kernel + cubin registration + GPU oracle test; it does NOT add a host-side launcher. The kernel is dormant after this commit — only the oracle test exercises it. Same isolation rationale as B.3 and B.4.
### Behaviour contract (B.5 isolated)
| Side | Owns | Reads | Writes |
|------|------|-------|--------|
| `gradient_hack_detect_kernel.cu` | Single-thread epoch-end state machine: post-open minimum tracker + simultaneous-drop/rise detector + force-close + lockout decrement | ISV[372] `TARGET_DIR_ACC`, ISV[373] `AUX_DIR_ACC_SHORT_EMA`, ISV[383] `Q_DISAGREEMENT_SHORT_EMA`, ISV[391] `GATE1_OPEN_STATE` (read+written), ISV[394] `AUX_DIR_ACC_POST_OPEN_MIN` (read+written), ISV[395] `GRADIENT_HACK_LOCKOUT_REMAINING` (read+written) | ISV[391] `GATE1_OPEN_STATE` (force-closed on trigger or during lockout), ISV[394] `AUX_DIR_ACC_POST_OPEN_MIN`, ISV[395] `GRADIENT_HACK_LOCKOUT_REMAINING` |
| `build.rs` kernel manifest | Cubin emission via shared `try_compile_kernel` path | `gradient_hack_detect_kernel.cu` source | `OUT_DIR/gradient_hack_detect_kernel.cubin` |
| `tests/sp14_oracle_tests.rs::gpu` module | Oracle correctness — circuit-breaker trigger and lockout assignment | The cubin + ISV slots initialised to trigger condition | Pass/fail on `lockout_remaining == 2.0` and `gate1_open == 0.0` |
### Per-epoch pipeline
1. Decrement existing lockout: `lockout_new = max(0, lockout - 1)`.
2. Track post-open minimum: if `gate1 >= 0.5` (open), `post_open_min_new = min(prev_min, aux)`; if closed, reset to sentinel `1.0`.
3. Compute drop and rise: `aux_drop = threshold_open post_open_min_new` and `q_rise = q_dis Q_BASELINE (0.5)`. `threshold_open = target + SCHMITT_BAND (0.03)` mirrors `alpha_grad_compute_kernel`.
4. If `gate1 >= 0.5 AND aux_drop > TRIGGER_DROP (0.05) AND q_rise > TRIGGER_DIS_RISE (0.10)`: circuit breaker fires — set `lockout_new = LOCKOUT_EPOCHS (2.0)`, write `ISV[GATE1] = 0.0` (force-close), reset `post_open_min_new = 1.0`.
5. Else if `lockout_new > 0`: still in lockout — write `ISV[GATE1] = 0.0` (maintain force-close).
6. Write back `ISV[POST_OPEN_MIN]` and `ISV[LOCKOUT]`.
### Slot index note
All SP14 slot indices are shifted +2 from the original plan (SP13 closeout added `HOLD_RATE_TARGET=381` and `HOLD_RATE_OBSERVED_EMA=382`). The kernel hardcodes them via `const int` locals and comments reference the `.rs` constants explicitly. Values: `Q_DIS_SHORT=383`, `GATE1=391`, `POST_OPEN_MIN=394`, `LOCKOUT=395`.
### Dependency ordering
This kernel WRITES to ISV[391] (`GATE1_OPEN_STATE`) — the same slot that `alpha_grad_compute_kernel` (B.4) writes. The correct epoch-end sequence is therefore: all-steps `alpha_grad_compute_kernel` (per-step), then `gradient_hack_detect_kernel` (once per epoch). The circuit breaker sees the epoch-final gate state from B.4 and can override it. Callers must ensure this ordering; mixing or reversing will produce a stale gate1 read.
### Files changed
| Type | Files (count) |
|------|---------------|
| New kernel file | `crates/ml/src/cuda_pipeline/gradient_hack_detect_kernel.cu` (NEW) |
| `build.rs` registration | 1 entry added to `kernels_with_common` array immediately after `alpha_grad_compute_kernel.cu` |
| Test append | `crates/ml/tests/sp14_oracle_tests.rs` — 1 GPU oracle test appended to `mod gpu` (`gradient_hack_circuit_breaker_fires`); 2 additional imports from `sp14_isv_slots` (`AUX_DIR_ACC_POST_OPEN_MIN_INDEX`, `GRADIENT_HACK_LOCKOUT_REMAINING_INDEX`) added to the module-level import block |
| Audit doc | This section |
### Build + test verification
- `SQLX_OFFLINE=true cargo check -p ml` — clean (only the 18 pre-existing warnings; no new warnings)
- `SQLX_OFFLINE=true CUDA_COMPUTE_CAP=86 cargo test -p ml --test sp14_oracle_tests gradient_hack --features cuda -- --ignored --nocapture` — 1 PASS:
- `gpu::gradient_hack_circuit_breaker_fires` — gate1=1 (open), aux=0.50 (threshold_open=0.58 → aux_drop=0.08 > 0.05), q_dis=0.65 (q_rise=0.15 > 0.10): circuit breaker fires → lockout=2.0, gate1=0.0
- `SQLX_OFFLINE=true CUDA_COMPUTE_CAP=86 cargo test -p ml --test sp14_oracle_tests --features cuda -- --ignored --nocapture` — 5 PASS total (B.3: 2, B.4: 2, B.5: 1; no regressions)
### Wire-up status
- **Producer**: kernel exists; cubin built; test green.
- **Launcher**: NOT YET WIRED. The Rust launcher will land in B.7+ together with the captured-graph integration of the full producer chain.
- **Consumers**: no downstream slot consumers for the circuit-breaker outputs — the kernel writes ISV[391]/[394]/[395] in-place; the effect is felt only through gate1 being force-closed when B.4 re-runs on the next epoch.
- **Reverse dependencies**: reads ISV[372] (SP13 TARGET), ISV[373] (SP13 AUX_SHORT), ISV[383] (B.3 Q_DIS_SHORT), ISV[391] (B.4 GATE1). All produced by earlier kernels in the chain.
This kernel is the third known-orphan in the B.3..B.6 producer chain. The orphan is acknowledged here per `feedback_wire_everything_up.md`.