From 2f72f8b20df68706da40c4ce9d82f4c7c898cc84 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Wed, 2 Sep 2026 01:45:21 -0600 Subject: [PATCH] =?UTF-8?q?fix(pgate):=20REFUSE=20main=20=E2=80=94=20an=20?= =?UTF-8?q?incremental=20main=20gate=20is=20a=20FALSE=20PASS,=20not=20just?= =?UTF-8?q?=20a=20false=20diff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- docs/cookbook-index.md | 7 +++++-- docs/matching-cookbook.md | 37 +++++++++++++++++++++++++++++++++++++ tools/parallel_gate.py | 18 ++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/docs/cookbook-index.md b/docs/cookbook-index.md index 6d67ee6e4..c094962aa 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 / 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) L32835 - **§3-D.** — INTEGRATION IS STILL THE BOTTLENECK, AND THE TU IS THE AUTHORITY L33140 -### build graph, splat & the harness (191) +### build graph, splat & the harness (192) - **§4** — Flag/toolchain gotchas L190 - **Build** — mechanism — per-file opt override (splat resegmentation) L307 @@ -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) L32702 - **§405** — ★★★ — THE S70 WAVE HARVEST: 130 agents, 113 MATCH, and the laws they brought back L33092 - **§3-A.** — THE ORACLE HAS A HOLE: match_one cannot see a jump table (§405-A) L33098 +- **§414** — ★★★ — `parallel_gate` ON `main` IS A FALSE PASS, AND THE RULE WAS ALREADY WRITTEN DOWN (P31 S71) L33510 ### 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) L33386 - **§412** — ★★★ — §323 CARVE BLOCKER 2 WAS A REGEX THAT COULD NOT SEE PAST `__attribute__` (P31 S71) L33424 - **§413** — ★★★ — DIFFICULTY IS THE RESIDUAL CLASS, NOT `nins` — ROUTE THE MODEL TIER OFF HISTORY (P31 S71, Drew) L33473 +- **§414** — ★★★ — `parallel_gate` ON `main` IS A FALSE PASS, AND THE RULE WAS ALREADY WRITTEN DOWN (P31 S71) L33510 --- @@ -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 | diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index 0b8dc17a9..c79b76970 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -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. diff --git a/tools/parallel_gate.py b/tools/parallel_gate.py index 468b69891..3c914ec77 100644 --- a/tools/parallel_gate.py +++ b/tools/parallel_gate.py @@ -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.