frank-primary: prefix caching OFF — the fork's QFN align-mode GDN state fault

Measured 2026-10-04 18:46:07 PDT (live podman logs): 48 gdn_hip state
faults in one 0.6 s burst — 'state out of ssm-cache range: max|S|=0 with
non-finite values (slot=42 head=3|8)', one per TP rank, 23 GDN layers
each — then the process-lifetime report cap. The guard's own text in
this build names it: 'known Qwen3.8-Flash-Next (QFN) flaw under prefix
caching … Mitigation: --no-enable-prefix-caching'.

Mechanism: prefix caching puts QFN on mamba_cache_mode 'align', which
checkpoints each sequence's GDN recurrent state into a mamba page per
3136-token block and re-seeds it from that page when a request resumes
from a cached prefix. The NaN state landed at the 13th boundary
(13 x 3136 = 40,768 — the reported ~40k mark) and persisted in the page:
every later turn of that conversation re-seeded the same non-finite
state for two heads, silently (fault-report cap already spent).

RAM ruled out the same day: zero MCE/EDAC records since the boot under
test, 8 GiB in-band pattern stable under live load, 4800 MT/s is the
JEDEC floor on 5600-rated DIMMs, and the two-head single-slot NaN
signature is a state-path fault, not scattered bit corruption.

Fix: FRANK_PRIMARY_NO_PREFIX_CACHING=1 -> --no-enable-prefix-caching
(flag renders in both directions from the knob). Cost: every turn
re-prefills the whole history instead of at most 3135 tail tokens.

Seam updated: the prefix-cache pin now asserts the conditional render
and the shipped OFF value. Runbook carries the evidence + acceptance
criteria; all 368 tests pass.
This commit is contained in:
Frank
2026-10-05 03:45:26 -07:00
parent a1c4701d7e
commit 9d8b18553d
4 changed files with 130 additions and 3 deletions
@@ -291,8 +291,73 @@ FRANK_PRIMARY_GPU_MAX_HW_QUEUES=2 -e GPU_MAX_HW_QUEUES=2 (HSA queues/GPU)
FRANK_PRIMARY_CACHE_DIR=/home/dank/.cache/frank-primary mounted at /root/.cache
R4D_LRU_GATHER_CHUNKS=32 / R4D_LRU_GATHER_LANES=64 (container env)
NO --speculative-config, NO --expert-prefetch
FRANK_PRIMARY_NO_PREFIX_CACHING=1 --no-enable-prefix-caching (2026-10-05, the QFN align-mode GDN state fault — below)
```
### 2026-10-05 — prefix caching OFF: the fork's QFN align-mode GDN state fault
**Symptom (Frank, 2026-10-04):** output turned repetitive/corrupted at
~40k tokens into a long conversation; short sessions stayed clean.
**Evidence (live `podman logs frank-primary-qwen`, the 2026-10-04 18:19
boot):** one 0.6 s burst at 18:46:07 PDT — 5.6 h into service, one
running request, and the engine's own 18:46:08 log line reading
"Prefix cache hit rate: 48.1%". 48 `gdn_hip` faults, then the
process-lifetime silence cap:
```
[gdn_hip] ERROR gdn_decode_r: state out of ssm-cache range: max|S|=0
with non-finite values (slot=42 head=3 limit=3.40282e+38) fault #1-23
[gdn_hip] ERROR gdn_decode_r: ... (slot=42 head=8 ...) fault #24-46
[gdn_hip] ERROR: state fault report limit (32) reached; further faults
are counted silently
```
One physical SSM slot (42), two heads (3 and 8 — one per TP rank), 23
GDN layers each, the state non-finite (NaN). The guard's own text in
this build: *"known Qwen3.8-Flash-Next (QFN) flaw under prefix caching
and is being actively worked; output quality for this request may
degrade. Mitigation: --no-enable-prefix-caching."*
**Mechanism.** With prefix caching on, the fork defaults QFN to
`mamba_cache_mode=align` (boot log: "Mamba cache mode is set to 'align'
for Qwen4ExpForConditionalGeneration … when prefix caching is enabled";
`config/cache.py`: "none" is the no-prefix-cache mode, "all" is
deprecated — align is the only sound mode WITH the flag). Align mode
checkpoints each sequence's GDN (Gated DeltaNet) recurrent state into a
mamba page per block and RE-SEEDS the state from that page when a
request resumes from a cached prefix. The block size is raised to
**3136 tokens** for the page-size equality (boot log: "Setting
attention block size to 3136 tokens to ensure that attention page size
is >= mamba page size" + "Padding mamba page size by 0.13% … exactly
equal"). The corrupt state landed at the **13th boundary: 13 × 3136 =
40,768 tokens** — the reported "~40k" mark. The NaN persisted in the
mamba page, so every later turn of that conversation re-seeded the same
non-finite state for those two heads (silently — the fault-report cap
was already spent) while fresh short sessions looked fine.
**RAM ruled out the same day** (the 2026-10-04 17:17 host reboot is when
the current memory speed took effect): zero MCE/EDAC kernel records
since boot; an 8 GiB in-band pattern check stable over 60 s under live
load; 4800 MT/s is the JEDEC DDR5 floor on 5600 MT/s-rated DIMMs (not an
overclock); and a two-head, single-slot NaN state is a state-path fault,
not scattered finite bit corruption.
**Fix.** `FRANK_PRIMARY_NO_PREFIX_CACHING=1` → `--no-enable-prefix-caching`
(the fork-named mitigation; the flag renders in both directions from the
knob so a fork default change cannot silently flip the contract). Cost:
no prefix reuse — every turn re-prefills the whole history instead of at
most 3135 tail tokens. A silently corrupted conversation is worse than a
slower first token.
**Acceptance (2026-10-05 deploy):** boot healthy with the expert plan
unchanged (arena 17.77 GiB / 7,762 slots — the flag touches no plan
term), "GPU KV cache size" still clears 400,700 tokens (if the
align-mode block-size raise disappears the mamba per-token price moves
and the pool may need `FRANK_PRIMARY_KV_BYTES` up a notch), zero
`gdn_hip` lines, and one request whose context crosses 40,768 tokens
in-session decodes coherently.
### Shape F/G (2026-10-03): where the free VRAM actually is, and what forcing it cost
`rocm-smi` showed **3.57 GiB free per R9700** at the deployed shape E (28.28 GiB