From e977205bb88f4dbabec2f48ec4f017e5f46e587e Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Tue, 4 Aug 2026 17:50:05 -0600 Subject: [PATCH] =?UTF-8?q?docs(cookbook):=20=C2=A7139=20=E2=80=94=20a=20g?= =?UTF-8?q?ate=20that=20greps=20for=20verdicts=20must=20assert=201:1=20acc?= =?UTF-8?q?ounting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wave-6 gate printed BANKED 5 / FAILED 1 over SIXTEEN drafts and nothing said so. Captured with the three corollaries that outlive the bug (R30 — written in the session that produced them): (a) the byte-gate is a perfect CORRECTNESS oracle and a NULL COVERAGE oracle (R34 again), so every wave-level tally is a coverage claim and needs its own assertion; (b) a pipeline's exit status is the LAST command's — "family_sweep ... | tail" hid a non-zero exit in this same session; use pipefail/PIPESTATUS or the tail IS the error handler; (c) a reverted CONFIG needs a re-extract before the next measurement means anything (the Phase-20 R22 corollary; skipping it made three genuinely-banked functions read as failures). Plus the inverse-lookup trap: a just-banked head LEAVES its family's members list and becomes exemplar.kind='matched', so a member-list lookup reports "no family" for exactly the heads you just banked. Distinct from §138 rule 4, which governs target SELECTION. Index regenerated: 393 sections, 3 new symptom keys. --- docs/cookbook-index.md | 15 ++++++--- docs/matching-cookbook.md | 66 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/docs/cookbook-index.md b/docs/cookbook-index.md index 131c99481..e06230d76 100644 --- a/docs/cookbook-index.md +++ b/docs/cookbook-index.md @@ -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 / 390 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 / 393 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. @@ -227,7 +227,7 @@ - **§138** — The propagation lanes: a gate refusal is a DECLARATION, and which lever you owe depends on blast radius L9405 - **Reconciling** — a gate-refused draft: which way you edit depends on WHERE the TU's decl is L9555 -### jump tables & switches (25) +### jump tables & switches (26) - **§8** — rodata island (compiler jump tables) — the `.data→.rodata→.data` sandwich (Phase 7) L320 - **§8a** — rodata island in a flat OVERLAY — the tail sandwich, per matched jr-function (Phase 26 — PoC PROVEN) L342 @@ -254,6 +254,7 @@ - **§131** — The jtbl OVER-SPAN: `sltiu N` is ground truth in BOTH directions, and the zero-word rule only guards one (P30 S28, `func_80191C50`) L8526 - **§132** — The `JR-PAIR-IN-ONE-O0-OBJECT` "wall" was TWO instrument defects: a merged-double span the carve could not see, and a truncated object no rule deleted (P30 S29, `func_8013B83C` + `func_8013BD74`) L8563 - **§132b** — When the span's already-matched owner is ITSELF multi-switch: `--span-rel` (P30 S1, `func_8014032C`) L8653 +- **§139** — A GATE THAT GREPS FOR VERDICTS MUST ASSERT 1:1 ACCOUNTING; and a `--src` filter must not survive a carve (P30 S38, wave 6: 10 of 16 drafts vanished) L9584 ### optimisation level (-O0/-O2) (12) @@ -379,7 +380,7 @@ - **§3-The** — declaration surface (integration, not codegen) L8931 - **Reconciling** — a gate-refused draft: which way you edit depends on WHERE the TU's decl is L9555 -### build graph, splat & the harness (95) +### build graph, splat & the harness (96) - **§4** — Flag/toolchain gotchas L190 - **Build** — mechanism — per-file opt override (splat resegmentation) L288 @@ -476,6 +477,7 @@ - **§138** — The propagation lanes: a gate refusal is a DECLARATION, and which lever you owe depends on blast radius L9405 - **§134** — again, in a second tool — and the waiter rule corrected L9516 - **Reconciling** — a gate-refused draft: which way you edit depends on WHERE the TU's decl is L9555 +- **§139** — A GATE THAT GREPS FOR VERDICTS MUST ASSERT 1:1 ACCOUNTING; and a `--src` filter must not survive a carve (P30 S38, wave 6: 10 of 16 drafts vanished) L9584 ### process, measurement & doctrine (53) @@ -533,7 +535,7 @@ - **§136j** — The failure MIX flips with function size (measured across four bands, one session) L9292 - **Rank** — the lane by measured concentration, not by class count L9471 -### (unbucketed — title matched no symptom vocabulary) (110) +### (unbucketed — title matched no symptom vocabulary) (112) - **§3-How** — to use this L30 - **§1** — Idiom catalog (asm pattern → C that produces it) L39 @@ -645,6 +647,8 @@ - **§3-The** — triage, cheapest first L9411 - **THREE** — carry variants hide in one "CARRY-FIXABLE" bucket — and they need different fixes L9480 - **STEP** — 0 of sibling-first: grep `src/` for a distinctive LITERAL from the `.s` L9536 +- **§3-The** — generalisation — three corollaries worth more than the bug L9616 +- **§3-And** — the inverse-lookup trap, same session L9633 ## All sections, in order @@ -1039,3 +1043,6 @@ - **§134** — again, in a second tool — and the waiter rule corrected L9516 - **STEP** — 0 of sibling-first: grep `src/` for a distinctive LITERAL from the `.s` L9536 - **Reconciling** — a gate-refused draft: which way you edit depends on WHERE the TU's decl is L9555 +- **§139** — A GATE THAT GREPS FOR VERDICTS MUST ASSERT 1:1 ACCOUNTING; and a `--src` filter must not survive a carve (P30 S38, wave 6: 10 of 16 drafts vanished) L9584 +- **§3-The** — generalisation — three corollaries worth more than the bug L9616 +- **§3-And** — the inverse-lookup trap, same session L9633 diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index aa07c2f57..de4887169 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -9578,3 +9578,69 @@ source position (`\.[ch]:\d+`), minus the known SHB macro noise. Symptom line for the index: **"a propagation/extend lane plans N and banks 0"** — read each failure's compiler error, bucket by named symbol, and apply the lowest-radius byte-neutral alias; a `volatile` in the host TU and a fleet decl carrying a promoting param are the two that masquerade as codegen. + +--- + +## §139 — A GATE THAT GREPS FOR VERDICTS MUST ASSERT 1:1 ACCOUNTING; and a `--src` filter must not survive a carve (P30 S38, wave 6: 10 of 16 drafts vanished) + +**The symptom** was a gate run that looked ordinary: `BANKED 5 / FAILED 1` — printed over **16** +drafts. Six verdicts for sixteen inputs, and nothing said so. Nine of the ten missing drafts were +claiming MATCH. + +**The chain, both ends of which were ours:** + +1. `harvest_verify._reload_corpus()` exists, by its own docstring, to *"re-derive the stub map after + an isolation moved a stub to a new TU."* It then re-applied the `--src` filter (`x.path == a.src`) + to the reloaded map — **deleting the very stub it had just followed to its new home.** Line 136 of + that same file already labels `--src` *"an optional filter, not a location oracle"*; `_reload_corpus` + was the one place treating it as one. Downstream: + `_stubs` loses `fn` → `render()` raises `KeyError` → **uncaught** → `_jtbl_restore(snap)` never + runs → **the carve is stranded in `config/` + `src/`** → and every *later* group in the same gate + run then builds against a tree the earlier crashes mutated. +2. The gate driver (`.run/s6f_gate.py`) captured the child's output, grepped it for `VERIFIED:` and + `FAILED :`, and **never looked at the returncode.** "Neither line present" was booked as nothing + at all, and the tally printed clean. + +**The fixes are both structural, not cosmetic:** +- the filter never drops a draft under verification, wherever it now lives, plus an R32 loud report + if a working stub vanishes across a reload (which also repairs `_touched`/`baseline` — a carved fn + missing from `_stubs` left its new TU *unbaselined*, so the revert path could not have restored it + either); +- the gate asserts `banked + failed + no-verdict == drafts`, prints the child's rc and output tail for + anything unaccounted, and **exits non-zero** — because a crashed child may have stranded a carve, so + it must never look like success. + +**Proof it was not cosmetic:** `func_8017EA84` (579 ins) carves and banks BYTE-IDENTICAL under the +fixed path. The old tool reported it as *nothing*. + +### The generalisation — three corollaries worth more than the bug + +**(a) The byte-gate is a perfect CORRECTNESS oracle and a null COVERAGE oracle (R34, again).** It +cannot tell you about work it was never asked to do. Every wave-level tally is a *coverage* claim, +and coverage claims need their own assertion. **Any driver that classifies N inputs must prove it +emitted N verdicts.** + +**(b) A pipeline's exit status is the LAST command's.** In the same session `family_sweep … | tail -30` +hid a non-zero exit (`--only` wants comma-separated; space-separated args were rejected). Use +`set -o pipefail` and read `PIPESTATUS`, or the tail *is* the error handler. + +**(c) A reverted CONFIG needs `make extract`, not just the revert.** After undoing the stranded +carves I re-extracted one overlay of sixteen; the next gate run read three genuinely-banked functions +as failures because they were building against asm/ still partitioned by the old carve. This is the +Phase-20 R22 corollary, and it costs a whole gate cycle every time it is skipped. **A gate result +measured against stale asm is not a measurement (R35).** + +### And the inverse-lookup trap, same session + +Propagating a **just-banked** head, `family_hseq.json` moves it **out of its family's `members` list +and into `exemplar` with `kind: "matched"`.** A lookup that searches the member list for the head +therefore returns *"no family"* for every head — while `family_sweep` enumerates those same families +from the same file seconds earlier. Key on the `exemplar` **(ov, addr) pair** (two families can share +an exemplar address in different overlays). This does **not** contradict §138 rule 4 ("never rank off +the exemplar field"): that rule governs *target selection*, where an exemplar pointing at a banked +instance HIDES a family; here the freshly-banked exemplar is precisely what you are looking the +family up BY. + +Symptom lines for the index: **"a gate reports fewer verdicts than it had drafts"** · **"a tool +reports NO FAMILY for a head another tool just enumerated"** · **"a gate result got worse after a +revert"** (→ re-extract, then re-measure).