diff --git a/docs/cookbook-index.md b/docs/cookbook-index.md index 671df474d..4906ba081 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 / 382 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 / 387 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. @@ -108,7 +108,7 @@ - **§66d-1** — What transfers between giants is the LOOP, not the PIN L5273 - **§70** — The giv-init base register: walk the PARAMETER, not a copy of it (Phase 29 SESSION-18, `func_801777BC`) L5612 -### structs, block moves & memcpy (28) +### structs, block moves & memcpy (29) - **§3-T2** — Source statement order drives instruction scheduling L78 - **§5** — Known hard-residual classes (instruction-identical, one byte-exact blocker) L199 @@ -138,6 +138,7 @@ - **§3-The** — three-hypothesis trail, because two of them were wrong and the wrongness is instructive L6864 - **§129** — Post-carve, `rtu_match`/`match_one` COUNT THE JUMP TABLE AS INSTRUCTIONS; and a carve must never be committed without its owner (P30 S28, `func_8013BD74`) L8430 - **§129a** — the target instruction count is INFLATED after a carve L8434 +- **§3-The** — DEFINITION-side alias is the only escape when the fleet canon disagrees on a promoting param L9455 ### types, signedness & load/store width (29) @@ -171,7 +172,7 @@ - **§3-Do** — NOT "strip the duplicate typedef" — it breaks the extern that uses it L8030 - **§3-The** — type-form rules L8872 -### declarations, prototypes & K&R (50) +### declarations, prototypes & K&R (51) - **§3-T4** — Branch polarity: invert the source condition to flip gcc's chosen branch L90 - **§8c** — Splitting a TU means rebuilding its DECLARATION ENVIRONMENT, not moving text (Phase 26 session 6) L437 @@ -223,6 +224,7 @@ - **§121** — Synthesise externs for macro-DEFINED callees from the macro's own definition head (Phase 29 T95) L8054 - **§3-The** — declaration surface (integration, not codegen) L8931 - **§136f** — Two declaration sub-cases the reconcile lane surfaced (lane now 15/15 lifetime) L9176 +- **§138** — The propagation lanes: a gate refusal is a DECLARATION, and which lever you owe depends on blast radius L9405 ### jump tables & switches (25) @@ -267,7 +269,7 @@ - **§127a** — §71 (sibling-first) is the strongest `-O0` lever, and it beats the index L8370 - **§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 -### family propagation & sweeps (67) +### family propagation & sweeps (68) - **§8d** — Templating a body INTO a TU must not CHANGE its declaration environment — demote the carried data externs (Phase 26 session 8, byte-proven on `func_8015AE2C` ×133) L483 - **§11** — Cross-binary dedup & code-sharing (Phase 11 — "one match unlocks many") L908 @@ -336,6 +338,7 @@ - **§124a** — a family sweep's `0 matched-exemplar families` may be a FILTER, not a wall L8191 - **§134** — MULTI-LINE BLINDNESS: one root cause, four faces, in `family_remap`'s preamble scanner (P30 S6, 190 zero-crack families) L8704 - **§136c** — SIBLING-FIRST is a DERIVATION shortcut, not just a conflict fix (the fastest route in a family wave) L9059 +- **§138** — The propagation lanes: a gate refusal is a DECLARATION, and which lever you owe depends on blast radius L9405 ### integration / TU plumbing (34) @@ -374,7 +377,7 @@ - **§3-The** — integration idioms (these decide whether a byte-correct draft BANKS) L8797 - **§3-The** — declaration surface (integration, not codegen) L8931 -### build graph, splat & the harness (92) +### build graph, splat & the harness (94) - **§4** — Flag/toolchain gotchas L190 - **Build** — mechanism — per-file opt override (splat resegmentation) L288 @@ -468,8 +471,10 @@ - **§134** — MULTI-LINE BLINDNESS: one root cause, four faces, in `family_remap`'s preamble scanner (P30 S6, 190 zero-crack families) L8704 - **§3-The** — splitting/merging rules (each closed a residual, byte-gated) L8846 - **§137a** — A gate verdict has a TIMESTAMP; re-check it against the draft's mtime L9376 +- **§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 L9472 -### process, measurement & doctrine (52) +### process, measurement & doctrine (53) - **§8e** — The jtbl ALIGNMENT LAW + the pad-spec filter — multi-table .rodata spans (Phase 29, byte-proven; `.run/probe_jtbl/verdict.md`) L530 - **§3-The** — mechanism: game-code dedup is SOURCE-LEVEL, not an object swap (R-D1, the key lesson) L926 @@ -523,8 +528,9 @@ - **§136g** — When the index points at the WRONG lever: two byte-refuted routings (func_801863B4) L9201 - **§136h** — CORRECTION: the zero-crack pool does NOT "refill with cheap work" (my error, byte-measured) L9230 - **§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 L9463 -### (unbucketed — title matched no symptom vocabulary) (107) +### (unbucketed — title matched no symptom vocabulary) (108) - **§3-How** — to use this L30 - **§1** — Idiom catalog (asm pattern → C that produces it) L39 @@ -633,6 +639,7 @@ - **§136d** — Four gcc-2.7.2 levers the redraft lane found (each closed a residual no other lever moved) L9084 - **§136e** — §136c's PRECONDITION, and two more symptom keys (wave 4b batch 3) L9128 - **§136i** — The drafter model LADDER: Haiku → **Sonnet** → Opus → Fable5 (Drew, 2026-08-03) L9260 +- **§3-The** — triage, cheapest first L9411 ## All sections, in order @@ -1019,3 +1026,8 @@ - **§136j** — The failure MIX flips with function size (measured across four bands, one session) L9292 - **§137** — REGALLOC-PERM is a TWO-COMPILE ARITHMETIC PROBLEM, not a permuter job L9334 - **§137a** — A gate verdict has a TIMESTAMP; re-check it against the draft's mtime L9376 +- **§138** — The propagation lanes: a gate refusal is a DECLARATION, and which lever you owe depends on blast radius L9405 +- **§3-The** — triage, cheapest first L9411 +- **§3-The** — DEFINITION-side alias is the only escape when the fleet canon disagrees on a promoting param L9455 +- **Rank** — the lane by measured concentration, not by class count L9463 +- **§134** — again, in a second tool — and the waiter rule corrected L9472 diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index b0e5b441e..e4f83637c 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -9399,3 +9399,96 @@ relocation mask leaves open, without running `make`): Symptom line for the index: **"`match_one`/`rtu_match` say MATCH but the whole-overlay SHA still DIFFs"** has exactly three causes — a stale verdict, a wrong `jal`/`%hi`/`%lo` target the mask hides, or collateral from file-scope decls — and the two oracles above discriminate all three offline. + +--- + +## §138 — The propagation lanes: a gate refusal is a DECLARATION, and which lever you owe depends on blast radius + +**Symptom key:** `dedup_extend` or `dedup_propagate` plans N members and banks **0**, or reports +`PLUMBING: conflicting types for X` / an undiagnosed `DIFF`. Measured this session: a 0/36 lane went +to **31/36**, and every single blocker was a declaration — not one was compiler codegen. + +### The triage, cheapest first + +Capture each failure's **own compiler error** (§136a — classify on the build's OUTPUT, never its exit +status) and bucket by which symbol is named. The named symbols repeat: 9 failures per binary reduced +to **4 distinct symbols** shared across all four binaries. Then pick the *lowest-blast-radius* lever +that is byte-neutral **by construction**: + +| conflict | lever | radius | +|---|---|---| +| macro declares `(void)`, TU declares `(ptr)`, **use is cast** | relax the macro decl to `()` | T2, 1 token | +| TU decl is unused boilerplate (**zero uncast uses**) | conform the TU decl to the fleet canon | T1 | +| TU declares the symbol `volatile`, or any type the macro can't match | **asm-label alias on the DATA** | T2 | +| the fn's own signature (return/arity/param) | **asm-label alias on the DEFINITION** | T2 | + +**Before relaxing to `()`, MEASURE the whole fleet's decl shapes for that symbol.** `()` is illegal +against a prototype carrying a *default-promotion* param (`s8/s16/u8/u16/char/short/float`) — the +documented gcc-2.7.2 dead-end. It is legal against pointers and `s32`. One `grep -rhoE` over `src/` +answers it; 4,020 decls of `func_80146C3C` were all `(void)/()/(u8*)`, so the relax was safe and +bought 8 of 36 for one token. + +**One conflict HIDES the next.** A declaration conflict *aborts the compile*, so the error you see +says nothing about what is behind it (the S29 law). Fixing `func_8012E5CC` immediately revealed +`func_80147364` at the same site. Re-run after every fix; do not price the lane off the first error. + +### `volatile` in the host TU is a SCHEDULING BARRIER — and it looks exactly like a codegen wall + +The four "undiagnosed DIFF"s in `dedup_extend`'s own header were this. Its correctness argument says +an `h_exact` match guarantees byte-identity *including relocs*, so a DIFF should be impossible. +Both halves resolved against the bytes: + +1. **The contract HELD** — `func_80162FF4`'s original bytes are sha1-identical in the failing and the + working overlay (`af1aceb2…`). *Check this first: it splits "the registry is lying" from "the TU + is different" in one command, with no build.* +2. **The TU differed** — the host TU declared `extern volatile s32 D_80127090/94/98` at FILE scope, + which no working overlay's copy of that TU does. Volatile makes the macro's three stores a + barrier, so `addu $a0,$s2,$zero` could not sink into the `jal`'s delay slot: the build emitted it + early **plus a `nop`**, one instruction longer. + +**The tell:** a diff that is a *positional shift with a `nop` appearing at a delay slot* is an +ordering constraint, not a wrong body. Look for a qualifier (`volatile`, `const`) on a symbol the +body touches before reaching for a codegen idiom. The fix is the **data asm-label alias** +(`extern s32 aD_80127090 __asm__("D_80127090")`): a distinct C identifier is immune to any TU's +declaration of that symbol, and is byte-neutral wherever the macro already worked. + +### The DEFINITION-side alias is the only escape when the fleet canon disagrees on a promoting param + +`func_80147364`'s byte-true definition is `(u16, u16)`; **4,046 fleet decls say `(u16, s32)`**. `()` +is illegal (u16 promotes) and conforming the decls would change caller codegen. Author the macro as +`void aF80147364(u16, u16) __asm__("func_80147364");` + a definition of the aliased name: the real +symbol is emitted, every caller keeps its own declaration, blast radius is zero. In-tree precedent: +**1,725 files** already use this form. Banked ×137 first try. + +### Rank the lane by measured concentration, not by class count + +A "45 classes / 20,837 ins" queue was really **5 classes carrying 89% of it**. Re-split the ledger by +`nins × n_stub` before scheduling anything — and note the per-class outcomes diverge wildly +(4,110 banked ×137 · 3,288 banked ×138 · 3,973 dropped · 3,886 at 4/138 · 3,288 tool-gapped), so a +lane average predicts nothing. **`--recover` is not a retry:** the caller-extern reconcile that is +16/16 lifetime *on drafts* banked 4 of 138 on a *propagation*. Probe one excluded member's build +output before re-running any lever that already returned a bad number. + +### §134 again, in a second tool — and the waiter rule corrected + +`dedup_propagate.find_site`'s preamble backscan had the SESSION-18 fix for blank / `//` / +**single-line** `/* … */` lines, and still halted on a **multi-line block comment** (middle lines +start `*`; the last ends `*/` without starting `/*`). Same class S6b fixed three times in +`family_remap`. **Decide skippability on `cdecl._mask`, not on line syntax** — one oracle (R33), +every comment form, immune to a `/*` inside a string, with an R32 assertion that the mask is +length-preserving. Also: a body declaring a draft-local `struct Tag {…}` is unextractable by design; +if the identical layout already exists in `src/shared/engine_types.h`, switching the exemplar to the +shared tag is byte-neutral and unblocks propagation. + +**Waiter rule, corrected (three failures, one mechanism — the signal sampled is not the thing waited +for):** `pgrep -x make` is right for ONE make and **wrong for a campaign** — `dedup_propagate` runs a +*sequence* of `make build BINARY=`, so a poll lands in the gap between two and reports a live +campaign finished. And `pgrep -f ` **self-matches** its own command line, so that waiter can +never exit. Wait on the campaign process by its real argv (`ps -eo args | grep 'python3 tools/…'`) or +on **`tools/treelock.sh --status`**, which is a statement of intent spanning the gaps. Likewise never +wrap a campaign in `nohup … &` inside a backgrounded call: the harness then signals completion of the +*wrapper* — a fleet check "finished" at 63/140. + +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. diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index b78d97af5..ca46f63d8 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -147,7 +147,83 @@ stub on a named wall/behemoth/queue ledger** — 140/140 byte-identical througho --- -# 🛑 SESSION-31/32 CHECKPOINT (2026-08-03) — FRESH SESSION SAFE HERE +# 🛑 SESSION-33 CHECKPOINT (2026-08-04) — FRESH SESSION SAFE HERE +> **Nothing is running. Tree lock FREE. Tree CLEAN** but for the R23 `db.*.gbf` churn — never stage. +> Effort: **ultracode**. **R22 clean-fleet run FOUR times this session, 140/140 every time.** +> HEAD `commit:1380`. **Drew's standing decision: NO phase close — keep grinding.** + +## FLEET — R22 **140 passed / 0 failed of 140** +**96.10% fn-count · 93.7% instr-weighted · 88.0% distinct-code** · dedup **1908/0** · +C1 240807/240807 · **0 NON_MATCHING** (G4). +Phase opened 92.00 / 87.5 / 78.0 ⇒ **+4.10pp fn, +6.2pp instr, +10.0pp distinct.** + +## WHAT S33 DID — lane 2 of the S10 checkpoint (the propagation lag), taken FIRST on the +standing "probe the cheap lever before funding the expensive one" doctrine. **~0 agent tokens.** +- **SC07 EXTEND: 0/36 → 31/36.** Commits `commit:1377` · `commit:1378`. +- **PROPAGATE head: 7,398 of 18,545 ins banked** — `func_80147364` ×137 (4,110) + `func_8012A598` + ×138 (3,288). Commits `commit:1379` · `commit:1380`. +- **Distilled to cookbook §138** (+ index regenerated) — the full triage is there, not here. + +## 🔑 THE FINDING: a gate refusal in these lanes is a DECLARATION, not codegen +**Not one of the 36 EXTEND blockers was compiler codegen.** Nine failures per binary reduced to +**4 distinct symbols** repeated across all four. Two masqueraded as walls: +1. **The 4 "undiagnosed DIFF"s in `dedup_extend`'s own header are a `volatile`.** Its correctness + argument says an h_exact match makes a DIFF impossible — and the contract HELD (original bytes + sha1-identical across the failing and working overlay, `af1aceb2…`, *checked in one command with + no build*). The host TU declared `extern volatile s32 D_80127090/94/98` at FILE scope, which no + working overlay's copy does ⇒ the macro's three stores became a scheduling barrier ⇒ `addu` + could not sink into the `jal` delay slot ⇒ built body emitted it early **plus a `nop`**, one + instruction longer. **Tell: a positional shift with a `nop` at a delay slot is an ORDERING + constraint, not a wrong body.** Fixed by the §37/§124 DATA asm-label alias. 16/16 on retry. +2. **`func_80147364`: definition `(u16,u16)` vs 4,046 fleet decls `(u16,s32)`.** u16 promotes, so + the `()` escape is ILLEGAL. The **DEFINITION-side** asm-label alias is the only zero-radius + escape (1,725 in-tree precedents). Banked ×137 first try. +**Before relaxing a decl to `()`, grep the fleet's decl shapes for that symbol** — `()` is illegal +only against a *default-promotion* param. 4,020 decls of `func_80146C3C` were all `(void)/()/(u8*)` +⇒ safe ⇒ 8 of 36 for ONE token. + +## 🧰 TOOL FIX — §134 multi-line blindness, now in a SECOND tool +`dedup_propagate.find_site`'s preamble backscan had the SESSION-18 fix for blank/`//`/single-line +comments and still halted on a **multi-line block comment**. Same class S6b fixed 3× in +`family_remap`. Now decides on **`cdecl._mask`** (one oracle, R33) with an R32 length-preservation +assertion. Monotone + byte-gate-fed ⇒ can fail to bank, never falsely bank. Paired with switching +the `func_8012A598` exemplar off its draft-local `struct BigCopy164` to the shared `struct BigCopy` +(engine_types.h L312, already used identically at engine_core.h:16158). **Either fix alone leaves +the function written off.** + +## ▶ RESUME HERE — three named items, none diagnosed against a build yet +1. **The PROPAGATE head remainder — 11,147 ins.** `func_8012f274` (3,973, **dropped**), + `func_8016ba68` (3,886, **4 of 138**), `func_801466f0` (3,288, **no source found** — the S6b + **D4** gap: its def at `ov_SC01_077_after.c:495` carries a *wrapped* `__asm__` alias decl that + `_alias_decl_for`'s single-line `rx.match` cannot see, with a file-scope `typedef struct + Rec801466F0` behind it, §100). **`--recover` is NOT a retry** — it banked 4 of 138 here, and the + reconcile lane that is 16/16 lifetime *on drafts* does not transfer to *propagation*. **Probe + ONE excluded overlay's build output (§136a) before re-running any lever.** +2. **The 41-class PROPAGATE tail** — 2,316 ins total. Low value; batch it or ledger it. +3. **EXTEND's last 5:** `func_80144B9C` ×4 — the whale's registry `func` field is a bare name, not + a `DEFINE_` macro, so `write_drafts` emits a CALL (hence "undefined reference" in ov_SC07_010); + it needs the §38 `-O0` shared-header route, **and `dedup_extend` should refuse-and-name the + class per R32** rather than feed the gate a draft that cannot pass. Plus `func_80149954` ×1, + blocked behind `func_80147364`'s u16 params. +Then: **lane 1** (S10's 17 unbanked targets, 26,227 templ ins, Sonnet at ~12-16 concurrency — the +manifest `.run/s10.json` / script `.run/s10.js` are still valid) and **lane 3** (the ×2-9 grind). + +## 🧰 MY PROCESS ERRORS THIS SESSION — all ONE mechanism, all now in §138 +**The signal I sampled was not the thing I was waiting for**, three times: +1. `nohup CMD &` inside a backgrounded call ⇒ the harness signalled the **wrapper**'s exit; the + fleet check stood at **63/140** and I nearly read it as a pass. +2. **CORRECTION to the S10 checkpoint's own rule.** It says use `pgrep -x make`. Right for ONE + make, **wrong for a campaign** of sequential makes — it fired in a gap and called a live + campaign done. And **`pgrep -f ` SELF-MATCHES**, so that waiter can never exit (two + spinning shells, killed). **Wait on the campaign's real argv or on `treelock.sh --status`.** +3. A `corpus.stubs` probe run mid-rebuild returned garbage — R32's coverage assertion refused to + answer rather than hand me a wrong stub set. *A measurement taken during a rebuild is not a + measurement* (S27's law, re-earned). +No bad bytes from any of them — the byte-gate and R22 caught everything. + +--- + +# 🛑 (superseded) SESSION-31/32 CHECKPOINT (2026-08-03) > **Nothing is running. Tree lock FREE. Tree CLEAN** but for the R23 `db.*.gbf` churn — never stage. > Effort: **ultracode**. **R22 clean-fleet run TWELVE times, 140/140 every time.** HEAD `commit:1375`. > **Drew's standing decision: NO phase close — keep grinding** (task #15).