fix(pgate): REFUSE main — an incremental main gate is a FALSE PASS, not just a false diff

S71 ran main through parallel_gate, got "11 banked", committed it, and the R22
clean-fleet verify came back 212/213. main did not compile from clean; once the two
declaration conflicts were reconciled it built and was STILL not byte-identical. All 11
were then re-gated one at a time against a clean build — 11 of 11 REJECT.

The rule was already written down in ox_campaign.gate_main_batch: "main is gated by ONE
CLEAN REBUILD of the whole EXE, never incrementally … main's extract rewrites the linker
script, so an incremental main gate returns a FALSE DIFF." parallel_gate's worker IS
gate_stage, so it inherits that — and S58 recorded the false-DIFF direction while this is
the false-PASS one, which is worse: a false diff wastes drafts, a false pass commits wrong
bytes and reads green until the next clean fleet check (R53's signature — a failed build
leaves the previous object on disk and the SHA check downstream reads it).

Now a refusal naming tools/gate_main.py, not a docstring in the callee (R43).
Cookbook §414, including the two instrument errors made while recovering.
This commit is contained in:
Drew T
2026-09-02 01:45:21 -06:00
parent 6fbdfdb361
commit 2f72f8b20d
3 changed files with 60 additions and 2 deletions
+5 -2
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 / 1081 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 / 1082 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.
@@ -1007,7 +1007,7 @@
- **§398** — ★★★ — `family_remap` CARRIES THE **SOURCE** TU's DECL ENVIRONMENT INTO A DESTINATION THAT ALREADY OWNS THOSE NAMES (P31 S69; measured 3 banked of 22) <sub>L32835</sub>
- **§3-D.** — INTEGRATION IS STILL THE BOTTLENECK, AND THE TU IS THE AUTHORITY <sub>L33140</sub>
### build graph, splat & the harness (191)
### build graph, splat & the harness (192)
- **§4** — Flag/toolchain gotchas <sub>L190</sub>
- **Build** — mechanism — per-file opt override (splat resegmentation) <sub>L307</sub>
@@ -1200,6 +1200,7 @@
- **§332b** — ★★★ — THE §332 "WALLS" ARE A PER-OBJECT ASSEMBLER MODE, NOT A C LIMIT — 6 CLOSE AS REAL C (P31 S69, Fable-3) <sub>L32702</sub>
- **§405** — ★★★ — THE S70 WAVE HARVEST: 130 agents, 113 MATCH, and the laws they brought back <sub>L33092</sub>
- **§3-A.** — THE ORACLE HAS A HOLE: match_one cannot see a jump table (§405-A) <sub>L33098</sub>
- **§414** — ★★★ — `parallel_gate` ON `main` IS A FALSE PASS, AND THE RULE WAS ALREADY WRITTEN DOWN (P31 S71) <sub>L33510</sub>
### process, measurement & doctrine (132)
@@ -2747,6 +2748,7 @@
- **§411** — ★★★ — THE PACK MUST CARRY THAT FUNCTION'S OWN HISTORY (P31 S71; measured 38/39 vs 124/131) <sub>L33386</sub>
- **§412** — ★★★ — §323 CARVE BLOCKER 2 WAS A REGEX THAT COULD NOT SEE PAST `__attribute__` (P31 S71) <sub>L33424</sub>
- **§413** — ★★★ — DIFFICULTY IS THE RESIDUAL CLASS, NOT `nins` — ROUTE THE MODEL TIER OFF HISTORY (P31 S71, Drew) <sub>L33473</sub>
- **§414** — ★★★ — `parallel_gate` ON `main` IS A FALSE PASS, AND THE RULE WAS ALREADY WRITTEN DOWN (P31 S71) <sub>L33510</sub>
---
@@ -3840,3 +3842,4 @@ Notes routinely quote that as a section id. This table resolves it. Grep bait: `
| L33386 | §411 | ★★★ — THE PACK MUST CARRY THAT FUNCTION'S OWN HISTORY (P31 S71; measured 38/39 vs 124/131) |
| L33424 | §412 | ★★★ — §323 CARVE BLOCKER 2 WAS A REGEX THAT COULD NOT SEE PAST `__attribute__` (P31 S71) |
| L33473 | §413 | ★★★ — DIFFICULTY IS THE RESIDUAL CLASS, NOT `nins` — ROUTE THE MODEL TIER OFF HISTORY (P31 |
| L33510 | §414 | ★★★ — `parallel_gate` ON `main` IS A FALSE PASS, AND THE RULE WAS ALREADY WRITTEN DOWN (P3 |
+37
View File
@@ -33506,3 +33506,40 @@ past-attempt fuel can serve one overlay's history to another's target, and any f
rows inherits it. `claude_wave_draft.js`'s `VERDICT` schema now requires `binary`, so new rows are
exact; the historical corpus stays name-keyed and should be read with that caveat.
`check-against-a-known-true-case`, again: the instrument passed because it measured nothing.
## §414 ★★★ — `parallel_gate` ON `main` IS A FALSE PASS, AND THE RULE WAS ALREADY WRITTEN DOWN (P31 S71)
**What happened.** S71 ran `main` through `parallel_gate`. It reported **11 banked**, the merge
committed them, and the run looked like every other successful gate that session. The R22 clean-fleet
verify then came back **212 / 213**: `main` did not compile from clean (`conflicting types for
func_8004355C`, `conflicting types for func_80038FFC`), and once both declarations were reconciled it
built and was **still not byte-identical**. Every one of the 11 was then re-gated the honest way —
apply ONE function to a green `src/800.c`, `make extract`, `make build`, compare SHA1 — and
**11 of 11 REJECTED**.
**The rule already existed, three files away.** `ox_campaign.gate_main_batch`'s docstring:
> *"main is gated by ONE CLEAN REBUILD of the whole EXE, never incrementally. gate_stage/
> sweep_parallel build incrementally, and main's extract rewrites the linker script, so an
> incremental main gate returns a FALSE DIFF. Measured P31 S58: wave `ab` drew 105 main cards and
> banked 0 of them while its non-main cards banked 82% — 105 competent drafts thrown away."*
`parallel_gate`'s worker **is** `gate_stage`, so it inherits that exactly — and S58 recorded the
false-DIFF direction while this is the false-PASS one, which is strictly worse: a false diff wastes
drafts, a false pass commits wrong bytes and reads green until the next clean fleet check.
**Fixed as a refusal, not a note** (R43): `parallel_gate` now returns `REFUSED` for
`binary == 'main'`, naming `tools/gate_main.py`.
**Two instrument lessons from the recovery, both the same shape.**
* The per-function re-gate's FIRST form lifted each function body WITHOUT the `extern` block above
it, and the first three came back REJECT with `cdReq_cdResult undeclared`. That measured the
extractor, not the body — while investigating a gate that had measured itself. Fixed by carrying
the declaration preamble; the verdicts after that are real (they compile and the SHA differs).
* The failure is R53's exact signature: **a failed build leaves the previous object on disk, so a
SHA1 check downstream of it reads green.** R53 was written for a different tool and never applied
here.
**The law.** *A tool that wraps another tool inherits its refusals.* Every constraint documented on
`gate_stage` binds `parallel_gate`, on `harvest_verify`, and on anything else that shells it — and
the place to put that knowledge is a refusal in the wrapper, not a paragraph in the callee.
+18
View File
@@ -320,6 +320,24 @@ def gate_one(idx, pin, job):
# a clean success reporting a TRUE number about an EMPTY world. Measured: 35 binaries / 57 drafts
# "banked 0" in 1-2s each, while the SAME drafts gated in-tree banked 15/16 and 3/6.
drafts = drafts if os.path.isabs(drafts) else os.path.join(REPO, drafts)
# MAIN IS NOT GATEABLE HERE, AND THE RULE PREDATES THIS TOOL (R43, P31 S71).
# `ox_campaign.gate_main_batch` already states it: "main is gated by ONE CLEAN REBUILD of the
# whole EXE, never incrementally. gate_stage/sweep_parallel build incrementally, and main's
# extract rewrites the linker script, so an incremental main gate returns a FALSE DIFF."
# This tool's worker IS gate_stage, so it inherits that exactly — and the failure is not a
# false diff but a false PASS: S71 ran main through here, got "11 banked", committed it, and
# the R22 clean-fleet verify then came back 212/213. The tree did not compile from clean, and
# once the two declaration conflicts were reconciled it was still not byte-identical. Every one
# of the 11 was then re-gated the honest way (substitute -> extract -> build -> compare SHA1,
# one function at a time) and rejected. A tool must refuse an input it cannot handle rather
# than process it wrongly; use tools/gate_main.py.
if binary == 'main':
return {"binary": binary, "banked": [], "files": {}, "ovl": None,
"secs": round(time.time() - t0, 1), "missing_generated": [], "rc": None,
"error": "REFUSED — main cannot be gated incrementally (its extract rewrites the "
"linker script); gate_stage in a worktree reports a FALSE PASS. "
"Use tools/gate_main.py, which does one clean substitute -> extract -> "
"build -> SHA1 cycle for the whole batch."}
try:
# ...and assert the input actually exists before spending a worktree on it (R32/R43): a job
# whose drafts are unreadable is a DEFECT, not a zero-yield result.