docs: cookbook §384 — a carve-config bank is red until you re-extract; correct the S69 checkpoint

The 'false bank' in the S69 checkpoint was not one. Both instances verify
byte-identical after 'make extract BINARY=<b>'. §384 states the law (verification
must regenerate whatever the gate changed the inputs to), the trap inside it (a
src-only revert of a carve commit produces 'table-count drift vs the carve', which
reads like progress), and the give-away I ignored — the commit diffstat showed
config/overlays.mk and a splat yaml sitting next to the .c.
This commit is contained in:
Drew T
2026-09-01 11:09:31 -06:00
parent 5450f18181
commit 9d7b26523c
3 changed files with 65 additions and 8 deletions
+7 -3
View File
@@ -2,7 +2,7 @@
> **Generated by `tools/cookbook_index.py` — do not hand-edit** (R33). Regenerate after adding a cookbook section.
>
> `docs/matching-cookbook.md` is ~716 KB / 1038 sections. Grepping it blind is how three P30 wave-1 agents each "discovered" an idiom that was already written down. **Start here, then read the section.** A section appears under every symptom it addresses.
> `docs/matching-cookbook.md` is ~716 KB / 1039 sections. Grepping it blind is how three P30 wave-1 agents each "discovered" an idiom that was already written down. **Start here, then read the section.** A section appears under every symptom it addresses.
**How to use:** name what you SEE in the diff (a stolen delay slot, an extra `la`, a swapped register pair, a `conflicting types` error), find that symptom below, read those sections first. If nothing fits, THEN grind — and add a section when you win.
@@ -704,7 +704,7 @@
- **§361** — ★ — A LOOP-TAIL BYTE SIGNATURE THAT NAMES ITS SOURCE SHAPE — AND THE LAW THAT A "SCHEDULING TIE" MAY BE YOUR OWN EARLIER LEVER (P31 S68; byte-proven main/func_800241C0, fable escalation, 19 → 0 in 3 iterations) <sub>L31637</sub>
- **§367** — RECONCILING A DECL CONFLICT BETWEEN TWO DRAFTS FOR THE SAME TU (P31 S68; main) <sub>L31777</sub>
### jump tables & switches (53)
### jump tables & switches (54)
- **§8** — rodata island (compiler jump tables) — the `.data→.rodata→.data` sandwich (Phase 7) <sub>L339</sub>
- **§8a** — rodata island in a flat OVERLAY — the tail sandwich, per matched jr-function (Phase 26 — PoC PROVEN) <sub>L361</sub>
@@ -759,6 +759,7 @@
- **§339** — A 2-CASE SWITCH OMITS THE LOW-BOUND RANGE TEST, SO THE PRESENCE OF `slti/bnez` BETWEEN THE `beq`s IS A **COUNT TELL** FOR A THIRD CASE NODE (P31 S67; byte-proven ov_SC02_005/func_80190538, 197 ins) <sub>L31181</sub>
- **§362** — TWO TRAPS WHEN A CARVE MOVES A STUB INTO THE `-O0` TU (P31 S68; byte-proven, 6 fns / 2,547 ins across ov_MAIN_012 / ov_SC02_037 / ov_SC03_107) <sub>L31682</sub>
- **§371** — ★★ — CARVING A **SINGLE-OBJECT MODULE BINARY**, AND THE spimdisasm RODATA-MIGRATION TRAP THAT FOLLOWS (P31 S68; byte-proven md_MAIN_003, func_800D0D6C 345 ins) <sub>L31890</sub>
- **§384** — ★★★ — A CARVE-CONFIG BANK IS RED UNTIL YOU RE-EXTRACT, AND THAT LOOKS EXACTLY LIKE A FALSE BANK (P31 S69; measured twice, cost one destroyed match) <sub>L32259</sub>
### optimisation level (-O0/-O2) (21)
@@ -1166,7 +1167,7 @@
- **§371** — ★★ — CARVING A **SINGLE-OBJECT MODULE BINARY**, AND THE spimdisasm RODATA-MIGRATION TRAP THAT FOLLOWS (P31 S68; byte-proven md_MAIN_003, func_800D0D6C 345 ins) <sub>L31890</sub>
- **§383** — TWO TOOLCHAIN FACTS THE PACKS DID NOT CARRY (P31 S69) <sub>L32244</sub>
### process, measurement & doctrine (126)
### process, measurement & doctrine (127)
- **§8e** — The jtbl ALIGNMENT LAW + the pad-spec filter — multi-table .rodata spans (Phase 29, byte-proven; `.run/probe_jtbl/verdict.md`) <sub>L549</sub>
- **§3-The** — mechanism: game-code dedup is SOURCE-LEVEL, not an object swap (R-D1, the key lesson) <sub>L945</sub>
@@ -1294,6 +1295,7 @@
- **§361** — ★ — A LOOP-TAIL BYTE SIGNATURE THAT NAMES ITS SOURCE SHAPE — AND THE LAW THAT A "SCHEDULING TIE" MAY BE YOUR OWN EARLIER LEVER (P31 S68; byte-proven main/func_800241C0, fable escalation, 19 → 0 in 3 iterations) <sub>L31637</sub>
- **§370** — ★★ — A **HARD BOUND** FROM sched.c, AND THE reorg SLOT-STEAL DIAGNOSTIC (P31 S68; main/func_8001BC6C, 69 ins, NOT closed — 33 → 28 over ~45 measured compiles) <sub>L31838</sub>
- **§376** — ★★★ — A STANDALONE `match_one` CLOSENESS OF 0 IS A CLAIM ABOUT THE **BODY**, NEVER ABOUT THE **TU** (P31 S69; measured 0/28) <sub>L32055</sub>
- **§384** — ★★★ — A CARVE-CONFIG BANK IS RED UNTIL YOU RE-EXTRACT, AND THAT LOOKS EXACTLY LIKE A FALSE BANK (P31 S69; measured twice, cost one destroyed match) <sub>L32259</sub>
### (unbucketed — title matched no symptom vocabulary) (307)
@@ -2646,6 +2648,7 @@
- **§381** — THE `insn_count` HOIST THRESHOLD IS A DIAL YOU CAN READ WITH `cc1 -dL` (P31 S69; four independent uses in one wave) <sub>L32216</sub>
- **§382** — TWO FOLD REASSOCIATIONS THAT NEED THEIR OWN STATEMENT (P31 S69) <sub>L32232</sub>
- **§383** — TWO TOOLCHAIN FACTS THE PACKS DID NOT CARRY (P31 S69) <sub>L32244</sub>
- **§384** — ★★★ — A CARVE-CONFIG BANK IS RED UNTIL YOU RE-EXTRACT, AND THAT LOOKS EXACTLY LIKE A FALSE BANK (P31 S69; measured twice, cost one destroyed match) <sub>L32259</sub>
---
@@ -3696,3 +3699,4 @@ Notes routinely quote that as a section id. This table resolves it. Grep bait: `
| L32216 | §381 | THE `insn_count` HOIST THRESHOLD IS A DIAL YOU CAN READ WITH `cc1 -dL` (P31 S69; four inde |
| L32232 | §382 | TWO FOLD REASSOCIATIONS THAT NEED THEIR OWN STATEMENT (P31 S69) |
| L32244 | §383 | TWO TOOLCHAIN FACTS THE PACKS DID NOT CARRY (P31 S69) |
| L32259 | §384 | ★★★ — A CARVE-CONFIG BANK IS RED UNTIL YOU RE-EXTRACT, AND THAT LOOKS EXACTLY LIKE A FALSE |
+50
View File
@@ -32255,3 +32255,53 @@ expression", never "add a cast":
`(s16)` `sll`/`sra` pair**. Only an `s16` local assigned in one block and consumed in a distant
one does — and the copy must sit ABOVE the guard, or it coalesces away against the target's
`addu $a1,$v0,$zero` (`func_8002D904`).
## §384 ★★★ — A CARVE-CONFIG BANK IS RED UNTIL YOU RE-EXTRACT, AND THAT LOOKS EXACTLY LIKE A FALSE BANK (P31 S69; measured twice, cost one destroyed match)
**The symptom.** A gate banks a jtbl function, commits, and reports green. You then verify the
binary in the main tree with `make build BINARY=<b>` and get a SHA mismatch:
```
[FAIL] build/ov_SC04_011/ov_SC04_011
got 9c94d36a2cb5d8b56073d859c6e58cf396a5e750
want 8bc09c422de2c28b3d71cf382cb6241c1e39d5db
```
Every instinct says false bank. **It is not.** Both times it happened in S69, the bank was
byte-perfect:
```
make extract BINARY=ov_SC06_025 && make build BINARY=ov_SC06_025 -> BYTE-IDENTICAL
make extract BINARY=ov_SC04_011 && make build BINARY=ov_SC04_011 -> BYTE-IDENTICAL
```
**The mechanism.** Banking a jtbl function changes CARVE CONFIG — `JTBL_PADS` in
`config/overlays.mk` and the binary's splat yaml (`tables=+0x0,+0x20,+0x40,+0x58` gaining a fifth
entry). Those files are splat **inputs**: `asm/` and the linker script are *generated from them*. A
worker gates inside a worktree that regenerated its own state, so it is right. The main tree, after
the merge, holds NEW carve config against OLD extracted state — and links the newly-carved C against
a stale `.ld`. The SHA that comes out is meaningless.
**The trap inside the trap.** Reverting the C alone makes it worse and *changes* the error, which
reads like progress:
```
jtbl_rodata_pads: consumed 4 rodata jump table(s) but 5 pad spec(s) given — table-count drift vs the carve
```
That is the src and the carve state disagreeing — you reverted half a commit. A carve revert must
carry `config/` too (see the `carve-state-files-never-blanket-add` discipline).
**The law.** *Verification after a gate must regenerate whatever that gate changed the inputs to.*
Concretely:
| the gate touched | the valid per-binary check |
|---|---|
| `src/` only | `make build BINARY=<b>` |
| anything under `config/` | **`make extract BINARY=<b> && make build BINARY=<b>`** |
This is the R22 corollary aimed the other way. R22 says a *reverted* config needs a re-extract; a
*landed* config change needs one just as much. It is also R40 in its purest form — the instrument
(a build over stale extract state) was broken, and I attributed the failure to the subject, reverted
a legitimate 96-line match, and wrote a checkpoint calling it a false bank. The give-away I ignored:
the commit's own diffstat showed `config/overlays.mk` and a splat yaml right next to the `.c`.
+8 -5
View File
@@ -4334,11 +4334,14 @@ cast_self_callers --sync-decls # 3: narrow-param, C89-illegal for no-proto (§3
It generalises to the CALLEE named in the diagnostic (banked `main:func_80021D38` that way).
### FOUR OF MY OWN DEFECTS, ALL MEASURED, ALL FIXED
1. **A FALSE BANK reached the tree** (`ov_SC04_011`, sha 9c94d36a vs 8bc09c42). The gate that made it
ran `--no-r22` because agents were live. Reverting needed the CARVE STATE too (JTBL_PADS 4->5 pads
+ splat yaml); a src-only revert gave "table-count drift vs the carve". **It may not even be
false** — the bank changed splat config, and the R22 corollary says a config change needs
`make extract`, not just a rebuild. RETEST with an extract; the draft is preserved.
1. **~~A FALSE BANK reached the tree~~ — CORRECTED: it was never false, MY CHECK WAS BROKEN (§384).**
`ov_SC04_011` and later `ov_SC06_025` both read as SHA mismatches in the main tree. Both are
byte-perfect: `make extract BINARY=<b> && make build` -> BYTE-IDENTICAL, proven on both. A jtbl
bank changes CARVE CONFIG (JTBL_PADS + splat yaml), which are splat INPUTS — so a build-only check
links newly-carved C against STALE extracted state. I reverted a legitimate 96-line match on that
reading (restored in commit:3481) after writing "it may not even be false" and not testing it.
**STANDING FIX: if a gate touched `config/`, the per-binary verify is `make extract && make build`,
never build alone.** Build-only is valid only when nothing under config/ changed.
2. **`cast_self_callers` left casts behind for drafts that did not bank**, and one of them made
`ov_SC07_000` fail to COMPILE at HEAD — so every later gate verdict on it measured a broken
baseline. Found because TWO drafting agents reported BASELINE-RED and I checked their claim.