Commit Graph

341 Commits

Author SHA1 Message Date
Christopher Williams 3cd0f91515 phase12: merge 16 (657 bodies), three-worker per-site gp evidence, C's decline endorsed, A's cross-jump test 2026-09-24 18:45:28 -04:00
Christopher Williams 401c92558e phase12: merge 15 (654 bodies), C's finding-44 direction, and the 3-of-6 reachability expectation 2026-09-24 18:42:41 -04:00
Christopher Williams 5af450c491 phase12: the three-worker triage key was implemented, TESTED, and FAILED -- disarmed, kept as a label
Three workers converged independently on the same split within one hour, which is normally the
signal to act on. This time acting on it would have been wrong, and the reason is measurable.

  D, from its scoreboard: what matters is whether the row has been attempted WITH THE CURRENT LEVER
    SET, not which pool it is in.
  C, from 11 classifications: prio-1 does not distinguish "a name that implies a SOURCE SHAPE" from
    "a name that implies a COMPILER BEHAVIOUR".
  A, from 14 rows touched: what predicts closability is the WORDING of the row's own note, not its
    prio -- and a grep would separate the two groups in one pass.

**The insight is right and is kept. The proposed discriminator is not predictive, and I measured it
rather than arguing about it.** I classified every row A and C named, by outcome, against the pool's
own record:

  MATCHED (8): 0x8010036C 0x800690E4 0x80018284 0x80094370 0x80065494 0x800C3514 0x80022E44 0x8002DF1C
    -> source-shape 2 | unknown 2 | **residual-class 4**
  RESISTED (7): 0x8010400C 0x800A6C34 0x80107CCC 0x80050674 0x80023D40 0x80024630 0x80101E50
    -> source-shape 1 | **unknown 3** | residual-class 3

Precision of "residual-class => will resist": **3/7, worse than a coin flip**, and the rule would have
**deprioritised four of the eight actual successes.** The decisive examples are the proposers' own:
C's `0x800C3514` is classed `alloc+layout (priority selector; ...)` and MATCHED -- the lever was
ARITY; B's `0x8002DF1C` is classed `candidate_bytes=104 = CORRECT length, 58 differing bytes` and
MATCHED -- the lever was a `goto` into the body. Both would have gone to the back of the queue.

So `kind` is DISARMED as an ordering rule and retained only as a LABEL written into every pool row,
with the failure and its numbers in the header so no worker trusts it. A test asserts it is not in
the sort key and cannot drop a row, so it cannot be quietly re-enabled.

**This is defect 5's shape exactly** -- a text heuristic over free prose, on this same field, which
inverted 62 rows. The difference is only that this one was tested before being believed. The real
discriminator is whether a residual has been LOCALISED to an allocation or scheduling decision, and
that requires reading the bytes rather than the note. **Free prose is not evidence about closability.**

Also in this commit: `negatives-d-unattempted.tsv`, the 74-row UN-PUSHED band that worker D asked for
and that D's own numbers justify (2-for-7 on rows a previous session pushed to 1-6 bytes, against
one-spelling closes on rows nobody had pushed). Rows with no mechanism named, not registered, not
excluded, not charter-blocked, not held in the ledger. This is D's criterion applied as a band rather
than as a preference.

Pools after the re-cut: 70 rows (a 16 / b 19 / c 21 / d 14).
2026-09-24 18:41:15 -04:00
Christopher Williams ca5eb6e613 phase12: merge 12-14 (652 bodies) + the phase's first inline-asm row + charter s7's not-counted row
MERGES. +13 bodies across rounds 12-14, each re-verified by me from a fresh --work dir first:
  0x80042CE0 (80, D)  0x80018284 (80, A)  0x80094370 (84, A)  0x8002DF1C (104, B)
  0x80065494 (80, C)  0x800C3514 (88, C)  0x801097A0 (128, D)
Gate every time: differing_bytes=0 result=MATCH, SHA-1 unchanged. make check exit 0.
**652 bodies / 662 regions. Phase: 602 -> 652 = +50.**

**A DEPENDENT ROW WAS RE-VERIFIED WHEN ITS DEPENDENCY LANDED, and this is a new rule.** Worker C's
`0x80091490` was merged as a DEPENDENT claim whose callee `0x80018284` was unreconstructed, with a
3-argument prototype INFERRED from the call site. Worker A then reconstructed `0x80018284`. Merging it
changes `config/symbols.tsv`, which is the environment that row was verified IN -- so I re-ran
`0x80091490` after the merge: still `differing_bytes=0 result=MATCH`. The arity check I promised:
A's `int func_80018284(int *a0, int a1, struct V8 *a2)` vs C's inferred `(int, int, int *)` --
**same arity (3)**, differing only in pointee types on args 1 and 3, which are **byte-invisible** at a
pass-through and a literal-0 site. So the inference held. Rule recorded: **a dependent claim must be
re-verified whenever its dependency is merged**, because the symbol set is part of its input.

**`0x801097A0` -- THE PHASE'S FIRST GENUINE INLINE-ASM ROW IS IN.** Worker D needed seven spellings and
each of the four asm properties is a *measurement with its counter-shape*, which is what makes this an
authorised use rather than a shortcut:
 1. `$31` cannot be WRITTEN from C. `register int ra __asm__("$31"); ra = ...;` makes cc1 keep a frame
    (132 B) and DELETES the assignment -- reading `$31` is a documented binding, writing it is not
    expressible. That is the "provably cannot express" clause demonstrated rather than asserted.
 2. The call must NOT be a C call: as a C call cc1 preserves `$31` itself and adds a prologue and an
    epilogue (140 B), while **the original has NO FRAME AT ALL**. Written as an asm `jal`, cc1 never
    sees a call. This single fact is what makes the row expressible.
 3. Zero operands must be `$0` LITERALLY: `gte_ldOFX(0)` passes `"r"(0)`, cc1 materialises a register,
    and the original's `ctc2 zero,$24` becomes `move` + `ctc2`.
 4. The constant register must be pinned to `$8` (cookbook 160); otherwise the seven constants land in
    `a0` (10 differing bytes = five `li` plus five `ctc2` register fields).
Also measured: maspsx supplies the jump-slot `nop` after the asm `jal` (writing one explicitly gives
132 B -- two nops), and the CP0 constant is `0x40000000`, not `0x4000`: bit 30 is the COP2-enable bit,
and `0x4000` compiles to `ori` and does not match.

**I wrote `include/gtemac.h` myself, because worker D refused to and was RIGHT.** D's charter section 4
forbids it from writing under `include/`, and my authorisation conflicted with the charter. D held its
write scope and handed me the macro text with the note that "I followed the charter because it is a
file and it is unambiguous". **That is the correct resolution of a coordinator error and it is the
behaviour I want**: the charter is a file, it was unambiguous, and D did not quietly exceed it. The two
macros (`gte_ldZSF3`/`gte_ldZSF4`, $29/$30) are written in exactly the form of the existing entries,
with the evidence in the comment -- the NUMBERS are the evidence, not the register names. The open
question D raises is recorded for the next charter: whether `include/` should be a worker write scope
or whether worker-authored macro text passed to the coordinator is the right division of labour.

**CHARTER SECTION 7'S NOT-COUNTED ROW IS NOW EXCLUDED BY NAME, and this one is my miss.** Worker C found
`0x80107C5C` (112 B) in its pool and skipped it because section 7 says it "matches but has no
documented source -- it is NOT counted; do not claim it". **I had READ that sentence earlier in this
session and did not act on it.** A worker who did not know the sentence would have produced a body the
phase does not count. It is now in `NAMED_EXCLUSIONS` -- the one place an exclusion lives -- with a
comment explaining that its reason differs from the rest of the list.

**`sf3_free` CRASHED, worker B caught it, and the fix is in.** My `--claim` addition used `free` and
`failed` without initialising them, so **every FREE address raised NameError and exited 1**. Worker B
reported the reproduction and, critically, the asymmetry that makes it matter: **the crash happened
AFTER the `FREE` line was printed, so a worker reading stdout saw the right answer while `$?` said 1**
-- a false BLOCKED for anyone checking the exit code or running under `set -e`. The harmless direction
of the two, but it costs a row per occurrence and it would have looked like a ledger problem rather
than a tool problem. Fixed (`free: list[int] = []`, `failed = False`), B's exact reproduction now
exits 0, and the 25-test suite passes. **The test suite caught this on the very change that introduced
it** -- recorded because that is the tests earning their keep, and because the tool whose whole purpose
is to be trusted unread is the one that must never be wrong about its exit code.

**`--claim` added so check-and-claim is ONE step.** The race is real and worker B hit it: B appended its
`wip` row in the same shell line as the check, *before* reading the output, so the ledger's last row for
`0x80042CE0` said B while D held it. B caught it, did not touch the row, and restored D's hold by
appending the row back (last-row-wins). **In the safe direction** -- B's own row masked the true holder
as TAKEN -- but the inverse ordering, appending on a stale read, is how two workers end up on one
address. `--claim WORKER` now appends only if every address is free, and a refusal writes nothing.

**Worker C found the FOURTH row where the class name pointed at the right place and the mechanism was
not what the note said.** `0x800C3514`'s note described "all stack loads hoisted, beqz+nop+li groups";
the lever was **ARITY** -- the function takes SIX `unsigned char` parameters, the fifth and sixth
arriving on the stack (`lbu 16(sp)`/`lbu 20(sp)`), which the note read as hoisted loads. Measured: the
four-argument spelling gives 56 bytes, twelve short, with neither stack load present. The tally is now
`alloc-tiebreak` -> a pass-through argument (finding 164) | `constant-materialisation-order` -> source
statement order | `return-merge` -> a literal twin and a compiler revision | `alloc+layout` -> arity.
2026-09-24 18:39:16 -04:00
Christopher Williams ce8e1221ff phase12: record the five pool defects, the convergent worker findings, and D's counter-evidence 2026-09-24 18:31:26 -04:00
Christopher Williams 9053a4dc51 phase12: merge 11 (646 bodies / 655 regions) + sf3_free: claimed is HELD, not FREE
MERGE 11: +7 bodies. All seven re-verified by me from fresh --work dirs BEFORE merging:
  0x800A6B38 (104 B, D)        0x8010036C (56 B, A, maspsx=epilogue)
  0x800690E4 (68 B, A)         0x800FE970 (84 B, C, maspsx=epilogue)
  0x80022E44 (52 B, B)         0x80047468 (72 B, B, gp=-D_80121BFC)
  0x80042D88 (76 B, B)
Gate: c_regions=655 differing_bytes=0 result=MATCH, SHA-1 unchanged. make check exit 0.
Registry: 646 distinct bodies, 0 missing sources, 4 carry cc1bin. Phase: 602 -> 646 = +44.

**sf3_free: a `claimed` ledger row is HELD, not FREE.** Worker D found this and reported it rather
than changing a tracked tool on its own judgement, which is the right instinct. The old rule was
`wip` -> TAKEN, else FREE, so a row whose last ledger row was `claimed` -- a worker holding a
STAGED, UNMERGED claim -- read as free. That is a false FREE in the dangerous direction: the row is
neither registered nor abandoned, so the next worker re-derives it and the merge can then receive two
claims for one address.

**This was not hypothetical. It is exactly what the credit outage produced**: all four workers
stopped with claims in flight, and I re-dispatched the pools underneath them. I recovered those rows
by auditing the staging files, but that was luck of ordering, not a mechanism. D reported the two
live instances it could see (`0x800460AC` held by B, `0x800FE970` held by C). `0x800460AC` I had
already merged, which masked the first one; `0x800FE970` was live when D wrote.

A registered row never reaches the changed branch -- `region_owner()` returns TAKEN first -- so the
new rule only ever fires for a claim whose merge has NOT landed, which is precisely the window at
issue. Measured after the fix:
    0x800FE970  TAKEN by inflight (claim staged, merge pending)
    0x80012A98  FREE (last ledger row 'released')      <- a genuine release still reads FREE
    0x800FECF8  FREE (last ledger row 'released')
    0x800ACA10  FREE (last ledger row 'released')

The residual risk is the opposite direction and is now an orchestrator DUTY, recorded here: if a
claim is ever REJECTED rather than merged, the merge path must append a `released` row, or that
address stays held forever. Nothing has been rejected so far this phase.

Also recorded from this batch:
* **B's `0x80047468` is the second worked case of cookbook 46's per-site override**: the registry
  marks `D_80121BFC` gp, but this row reads it absolutely (`lui v1,0x8012 / lw v1,7164(v1)`), so
  without `gp=-D_80121BFC` the candidate is one instruction SHORT (68 vs 72). C independently hit the
  same class on `0x800A6658` with `D_801226E0`. **The registry's gp marker is per-SYMBOL and the
  access form is per-SITE**, and two workers in one hour each lost time to that.
* **B and C independently converged on the same conclusion about what these negatives are worth:**
  B -- "the recorded class named the SYMPTOM (a merged store, a folded shift, a strength-reduce)
  while the fix was an ORDER or a claim-row option"; C -- "in every one the recorded class NAME was
  right and the recorded MECHANISM was half wrong". Two workers, different partitions, same finding.
  That is the strongest argument yet for dispatching onto named negatives, and simultaneously a
  warning: **the class is a signpost, not an instruction.**
2026-09-24 18:30:21 -04:00
Christopher Williams 7090663275 phase12: sf3_negpool -- kill the five defects that reached the workers, and merge 10 (639 bodies)
MERGE 10: +4 bodies -> 639 bodies / 648 regions. All four re-verified by me from fresh --work dirs
with --symbols first: 0x80099E34 (40 B), 0x800AC9D8 (56 B), 0x800460AC (40 B), 0x80045540 (56 B).
Gate: c_regions=648 differing_bytes=0 result=MATCH, SHA-1 unchanged. make check exit 0.

The tool, and why it exists. The Phase 12 re-dispatch put all four workers on the ~193-row
classified-negatives index. I generated those pools with a one-off script, and that script was
wrong in FIVE ways. Every one of them shipped, and **every one was found by a worker, not by me**:

 1. The index documents its own schema on line 2 -- `# Columns: address<TAB>size<TAB>status<TAB>class`
    -- and my script **never read `status`**. 5 rows `blocked` + 1 `blocked,deferred` are rows
    charter section 7 forbids attempting (trapping arithmetic, the 0x80012xxx primitive-init family,
    the maspsx rare-epilogue mutual exclusion). **Six forbidden rows went out at prio 1, the top of
    the list.** Worker C found three in its own pool and refused them.
 2. `status` is not enough either: `0x80012A48` is status `near-match` with class `primitive-init
    scheduler-bound family`, so it reached prio 1 as well. Blocked families are now matched on the
    CHARTER's own wording, in both the status and the class column -- 7 rows dropped.
    Deliberately NOT blocked: `rare-epilogue-ORDER`. Worker A drew that line precisely (the order is
    work; only the maspsx mutual EXCLUSION is blocked), and cookbook 140/147/165 shipped
    `maspsx=epilogue` for it.
 3. The Makefile's `NAMED_EXCLUSIONS` (12 rows) was invisible to my script: **8 of the 12 leaked back**
    as fresh work -- $gp-switch thunk halves, a fragment, false extent starts. Worker A found two and
    asked whether they should be filtered. Eight were leaking, not two. The list now lives in ONE
    place (`NAMED_EXCLUSIONS`), and both `worklist` and the new `negpool` target are given it.
 4. The workers' own `negatives.tsv` staging was invisible: **94 of the 115 rows they had already
    classified were served as fresh work, at the TOP of the prio-1 order.** Worker C: "they are the
    first four rows in the file's own prio-1 order, so a worker starting at the top spends its first
    hours re-deriving my floor." 58 rows dropped as classified-with-no-mechanism (a second worker
    re-deriving a row that already yielded nothing is pure duplication), and a worker's OWN
    classification is dropped from that worker's own pool (12 rows) while still being dispatched to
    the others, who get the recorded mechanism as a lead.
 5. `named = class not in ('-', '')` is a test for a NON-EMPTY STRING, not for a mechanism. Worker D's
    staging labels 56 of its 92 rows `no-mechanism-yet` and 6 more `no-extent`, so **62 rows were
    ranked prio 1, "cheapest, mechanism already named", when their label says the exact opposite.**
    That is why worker A's pool led with rows it could not close. `names_a_mechanism()` now rejects
    absences, numbers and bare status words, and accepts prose (workers A and C write their class as
    a sentence, so a token-only rule would have missed their mechanisms).

This is a SCHEMA error, not a logic error, and it is worth naming as such: five separate bugs all
trace to generating a dispatch file from a table whose header I had not read.

One coordinator aside that belongs in the record: I had already "fixed" defect 3 by hand, adding
`--exclude 0x8001DC20` to the `worklist` target -- and **the counter caught me**:
`excluded_named_exclusion` did not move, because that row was already excluded from the worklist
under a different rule and had reached the worker through the negatives pool instead. Fixing one row
by hand while the generator leaked was treating a symptom, and the tool said so.

Pools: 176 unregistered -> **93 rows** (a 24 / b 23 / c 28 / d 18), every exclusion and blocked row
gone. C's pool now leads with `index-only` rows, i.e. genuinely fresh work.

Tests: 17 new, `tools/tests/test_sf3_negpool.py`. Fail-first is demonstrated rather than asserted --
the two INTEGRATION tests (which read `NAMED_EXCLUSIONS` out of the Makefile rather than hard-coding
addresses, because the whole defect was two lists that must agree living in different files) were run
against the pools the workers were holding at that moment and **failed, naming all 8 leaked rows**.
Two further bugs found in the process: my own `names_a_mechanism` accepted the numeric dashboard
columns (`'1'` is not a mechanism), which made the rule vacuous and dropped 0 rows instead of ~62; and
one of my tests asserted output that its own `--quiet` flag suppressed.
2026-09-24 18:27:39 -04:00
Christopher Williams ab5b2d3857 phase12: fragment predicate, D's twin correction (coordinator error), and the redundant-flag lesson 2026-09-24 18:18:49 -04:00
Christopher Williams 228ad953e4 phase12: exclude 0x8001DC20 as the second FRAGMENT row, with the class predicate written down
Worker D found it while sweeping the GTE batch, refused to spend spellings on it, and reported it
rather than grinding. I re-derived the conclusion from the bytes before acting: its first
instruction is lw t0,0(t5) (0x8DA80000) and $13 is written ZERO times in the whole 72-byte extent,
so t5 is read but never established and the extent is the tail of a larger function. The
already-excluded 0x800C3490 has the identical signature (also zero writes to $13).

That gives the fragment class a CHECKABLE predicate instead of a vibe: 'a caller-saved temporary
($8-$15) is READ before any instruction in the extent WRITES it.' It is sound because $8-$15 are
never incoming o32 arguments and no compiler emits a read of an uninitialised temporary. It is not
yet an automatic triage rule -- that needs a fail-first test, so it is recorded as an open item
rather than half-implemented at the context cap. Until then fragments are caught by a worker
reading the row, which has now cost two workers a reading budget each.

Excluded rather than left in the dispatch files: 0x800C3490 was re-identified twice by two separate
workers before this.
2026-09-24 18:18:10 -04:00
Christopher Williams e074d947fb phase12: credit-outage recovery (10 bodies recovered, 635/644) and the negatives re-dispatch 2026-09-24 18:16:47 -04:00
Christopher Williams 6da840f3ab phase12: recovery merge 9 — 635 bodies / 644 regions (+33, past the cycle-1 checkpoint)
All four workers stopped when the account ran out of credit. This commit recovers every row they
had produced and verified but not yet reported, and it is the first thing done on resumption.

RECOVERY AUDIT, because work that was produced but never claimed is INVISIBLE to the merge flow
(charter rule 13):
  untracked src/ files produced this phase          11
  distinct addresses claimed in ANY staging file    29
  src/ files claimed by nothing (orphans)            0
  claims whose source file is missing                0

One false alarm worth recording: my first pass globbed `.run/p12/w-*/claims.tsv` and reported
`src/func_801008DC.c` as an orphan. It is claimed -- in `claims-altcc1.tsv`, which is a DELIBERATE
separate pool because those rows need the `cc1bin` override. The audit tool was wrong, not the
worker. A `src/`-vs-claims audit must glob EVERY claims file a charter defines, not just the default
one, or it manufactures orphans.

I re-verified ALL 29 claimed rows from fresh --work directories with `--symbols` (and
`--cc1 gcc-2.8.1-psx/cc1` for the four `cc1bin` rows) BEFORE merging: **29 of 29
`differing_bytes=0 result=MATCH`, exit 0.** Then one `apply --skip-registered` over the combined
input: 19 skipped as already merged, 10 added, 0 rejected.

  sf3_match gate   c_regions=644  differing_bytes=0  result=MATCH
                   sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9  (unchanged)
  make check       exit 0        extents-verify  regions=644 disagreements=0 AGREE
  make worklist    listed=984, excluded_already_registered=642
  registry audit   644 rows, 635 distinct bodies, 0 missing, 4 carry cc1bin

The 10 recovered rows, and they are the phase's best evidence that the dispatch re-cut worked:

  0x8006D3B0 (192 B) and 0x8006AA88 (244 B)  -- worker A, adjacency
  0x8006D250 (172 B) and 0x8006D2FC (180 B)  -- worker B, family/adjacency, sibling with a 5th arg
  0x8002515C (60 B) and 0x800254B0 (60 B)    -- worker D: BOTH twins of the registered 0x80025070,
                                                a THREE-member family, and twins of each other
  0x80101C5C (32 B) and 0x80102FE4 (48 B)    -- worker D, GTE class
  0x80091490 (84 B)                          -- worker C, DEPENDENT, merged on my ruling (below)
  0x801008DC (88 B)                          -- worker C, `cc1bin=gcc-2.8.1-psx`, 3 `jr $31`

**RULING ON THE DEPENDENT CLAIM `0x80091490`.** Worker C flagged it rather than quietly merging it,
correctly applying charter rule 2: its callee `0x80018284` is NOT reconstructed, and the 3-argument
prototype is INFERRED from the call site (the tell is `move a1,zero` -- an integer 0, not a null
pointer in v0). Ruling: MERGE, with the dependency recorded here and in the ledger. Reasoning:
(i) rule 2 forbids verifying against a SUPERSET, and no superset was used; (ii) the whole-binary
gate proves the bytes exact, so the call site's register setup is right; (iii) the residual risk is
that a future `0x80018284` reconstruction disagrees about arity, and **that risk is caught by the
gate the moment it happens** -- the row would stop matching and the cause would be visible. The
alternative, holding a verified body, costs a body for a risk the gate already covers.
**The worker who takes `0x80018284` must treat `0x80091490`'s prototype as a hypothesis to check.**

Also recorded: `excluded_already_registered` still lags the registry by exactly 2 (642 vs 644). The
lag did NOT grow when this merge added four negatives-derived rows, so it is not "negatives rows are
counted elsewhere" -- it is a fixed pair of registry rows the counter never counts. Still open,
still a diagnostic rather than a gate, still to be reconciled at the close with the full census.

Phase position: 602 -> 635 bodies, +33. The cycle-1 checkpoint was +30.
2026-09-24 18:15:57 -04:00
Christopher Williams 3765dc4352 phase12: checkpoint record — 625 bodies / 634 regions and the measured route to them 2026-09-24 17:30:51 -04:00
Christopher Williams b8502c5693 phase12: merges 7-8 — 625 bodies / 634 regions (from 621 / 630)
Merge 7: worker D's 0x8006AD5C (84 B, a GOAL B row, first spelling) and 0x8006D1C4 (140 B).
Merge 8: worker C's GTE pair 0x80010810 / 0x8009C69C (60 B each, on the DEFAULT toolchain, via
the inline-asm hatch). Every row verified from a fresh --work dir against the exact md5, merged to
a candidate, gated whole-binary, promoted only on result=MATCH.

  sf3_match gate     c_regions=634  differing_bytes=0  result=MATCH
                     sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9  (unchanged)
  make check         exit 0     extents-verify  regions=634 disagreements=0 AGREE
  registry audit     634 rows ordered, non-overlapping, 625 distinct sources, 0 missing

INTEGRITY CHECK THAT PASSED, ON A REAL HAZARD. Worker C edited the headers of the three
ALREADY-MERGED cc1bin sources after I merged them, changing their md5s (6afb89a7 / 51ab9083 /
c30c5bfa). The registry points at those paths, so a content change would have made it stale. I
re-ran the full gate on the current registry: MATCH. Comments only. This is exactly what the
md5-in-claim-row guard exists for, and here the whole-binary gate is what settled it.

C'S INLINE-ASM HATCH WAS JUSTIFIED TWICE, AND THE SECOND REASON IS A NEW FINDING.
The GTE pair is the d=0/15 identical pair, so one solve meant two bodies. The hatch was needed
because (1) ~20 spellings and ALL TEN vendored cc1 builds fold the dead `move t0,a1` into the
negation, and `((y ^ -1) + 1)` is the only spelling reaching the right LENGTH while lowering to
nor+addiu -- so length alone was never evidence; and (2) THE ORIGINAL'S NEGATION IS THE TRAPPING
`sub` (funct 0x22), not `subu` (0x23). objdump prints `neg` for the original and `negu` for the
candidate, so a mnemonic comparison cannot see it: a one-byte funct-field difference, cookbook 6's
class. With the copy fixed but the C negation kept, the row sits at differing_bytes=1.

AND C CLOSED THE ALT-CC1 QUESTION AGAINST ITS OWN INTEREST. On the 0x80050674 pair the residual is
differing_bytes=1 on the default cc1 and differing_bytes=22 on BOTH 2.8.1 and 2.91.66-psx, so it is
not a compiler-revision artifact. C then declined to spend the inline-asm hatch on it, on the
grounds that the residual is a mundane operand order inside an otherwise all-C body and the hatch
would be doing COSMETIC work. Ruling: endorsed. The hatch is for shapes plain C provably cannot
express; pinning three registers to win one operand order is not that. The row is classified with
its mechanism and an exhausted dimension instead.

OPEN, RECORDED WITH THE EXPERIMENTS RATHER THAN A GUESS: the workflow's checklist item
`excluded_already_registered == registry size` held exactly through merges 5, 6 and 7 and diverges
at merge 8 (634 registry rows, counter 632). Two hypotheses were tested and BOTH REFUTED: the two
new rows are not extent-graded non-exact (both are `exact`/`term=jr_ra`), and it is not "negatives
rows are counted under the negatives filter instead" (11 registry rows have negatives addresses,
but removing only the 2 newest from the registry reproduces the count exactly). Two failed
root-cause attempts, so per AGENTS.md rule 10 it is recorded with the evidence instead of a third
guess. No correctness risk, and that is measured: the whole-binary gate proves all 634 rows
byte-exact simultaneously, the registry audit is clean, and extents-verify agrees. It is a
diagnostic, not a gate. To be reconciled at the close, where the negatives-index reconciliation
happens anyway and is probably the same question.
2026-09-24 17:30:17 -04:00
Christopher Williams 677d982398 phase12: merge 6 — worker A's 572 B body -> 621 bodies / 630 regions; exclude the 0x800C3490 fragment
Worker A's third claim, verified from a fresh --work dir against the exact md5 in the claim,
merged to a candidate, gated whole-binary, promoted only on result=MATCH. It is the largest body
any worker has taken this phase (572 B).

  sf3_match gate    c_regions=630 differing_bytes=0 result=MATCH
                    sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9  (unchanged)
  make check        exit 0        make extents-verify   regions=630 disagreements=0 AGREE
  make worklist     listed=989, excluded_already_registered=630
  registry audit    630 rows, 621 distinct sources, 0 missing, 3 carry cc1bin

**ADJACENCY IS NOW 3-FOR-3 FOR WORKER A, ACROSS THREE BANDS.** 0x800297F4 (204 B, band 0) ->
0x800298C0 (392 B, band 1) -> 0x80029A48 (572 B, band 2): three CONSECUTIVE bodies, 1168 bytes,
four spellings total, with the density ranker not involved in any of the three picks. The chain
ends where no row starts at the next boundary. That is the strongest single piece of dispatch
evidence in the phase, and it is why the fresh-band files were re-cut to put adjacency above
density inside a band.

Worker A's lever, recorded because it is an operator RECOVERY rule rather than a spelling tip: the
original is `if ((x1 < 0 && x2 > 0) || (x1 > 0 && x2 < 0))`, and the truth-table-equivalent
if/else ladder is EXACTLY ONE INSTRUCTION SHORT. The emitted stream is `bgez x1` / `bgtz x2` /
`blez x1` / `bgez x2` -- each term's first test branches over its own second test -- and the ladder
has no `bgez x1` to emit. So the four BRANCH SENSES let you write the operator down without
guessing; cookbook 83's "branch direction distinguishes && from ||" turned into a recovery rule.
And `(x1 ^ x2) < 0` is the same predicate with the wrong codegen: the original compares.

**0x800C3490 IS EXCLUDED FROM THE WORKLIST.** Cookbook 114 (worker A, Phase 10) records it as a
FRAGMENT: it starts mid-expression, its body is a SHARED TAIL (`addiu sp,sp,48; jr ra`) that also
appears at 0x800C3470-0x800C348C, and it cannot be matched standalone. The extents table still
grades it `exact` with `term=jr_ra` because the boundary walk sees a well-formed terminal, so the
tool cannot catch it -- which is exactly why it needs a recorded exclusion rather than a tool rule.
Worker A recognised it for the SECOND time in Phase 12 and skipped it instead of spending reading
budget, which is the signal that the exclusion belongs in the Makefile: a row that has to be
re-identified by hand every phase is a row the dispatch should not be offering.

Added with its provenance in the Makefile comment, the same mechanism Phase 10 used for
0x8010080C's false extent start. excluded_named_exclusion 8 -> 9.

Ledger updated with merges 2-5, the cc1bin lever and its gate, the mechanism correction, the gp
pair rewrite and both of my failures in it, the dispatch measurement (369 of 993 rows abut SOME
region = noise; only 5 abut a PHASE-12 match = the signal), and the partition-membership fix.
2026-09-24 17:25:51 -04:00
Christopher Williams 63008d5a49 phase12: merges 3-5 — 620 bodies / 629 regions (from 604 / 613), incl. the first cc1bin rows
FIVE claim batches, every row re-verified by the coordinator from a fresh `--work` directory
against the exact md5 in the claim, merged to a candidate, gated whole-binary, promoted only on
`result=MATCH`. Merges 3 and 4 are separated so the every-3rd-merge full audit falls on merge 3.

  sf3_match gate      c_regions=629  differing_bytes=0  result=MATCH
                      sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9  (unchanged)
  make clean && make all   exit 0      (full audit at merge 3)
  cmp                      exit 0
  make check               exit 0
  make extents-verify      regions=629 disagreements=0 result=AGREE
  make worklist            listed=991, excluded_already_registered=629
  registry audit           629 rows, ordered, non-overlapping, 620 distinct sources, 0 missing
  make test                318 tests, OK

**THE FIRST THREE `cc1bin` REGIONS ARE IN THE REGISTRY**, and the gate is byte-exact for the
whole binary with them -- the alternative-cc1 lever works end-to-end, not just in a scratch
harness. All three came from worker C, all three match with gcc-2.8.1-psx/cc1 and NONE matches
with the default (56/60/52 B LENGTH-MISMATCH). They carry `cc1bin=gcc-2.8.1-psx` in the region
row and the gate re-checks the >=2-exit-jump restriction on every build.

  0x800FF43C (64 B) and 0x800FF47C (64 B) — a TWIN PAIR, raw words d=2/16. They are also two of
    the rows worker F left open at the Phase 11 close with a named mechanism and a named untried
    lever. C closed them with the cc1 lever, not a re-spelling.
  0x80108578 (56 B) — needed TWO levers: the cc1 AND a source shape that re-tests the guard each
    iteration (`for(;;){ if (a0==0) return 0; ... }`), because the original's loop back edge
    targets the function's FIRST instruction. A `while` spelling rotates the loop, skips the
    guard, and leaves the 1-byte residual an earlier attempt had recorded.

**AND `0x800298C0` IS AN OVERTURN — the recorded negative, merged as worker A's claim.** Worker A
took it on ADJACENCY (the row immediately after its own previous match) and matched it first try,
overturning cookbook 128's named mechanism. It is also the row Phase 11's close had to release as
STALE-TAKEN because both holders had gone out of context: the row the close flagged as invisible
became a body in the next phase. Cookbook 182 demonstrated end-to-end.

**Worker D's GOAL B produced bodies, not just a classification:** 92/92 of its unclassified rows
are now classified, and TWO of them were raw-word twins of registered bodies and MATCHED
(0x8006AE04 = 1/20 words from 0x8006B66C; 0x80017A38 = 1/18 from 0x80017B50). A Goal B result that
adds zero bodies would still have been met; this one added two.

Worker B's 0x8006B964 is the fourth match in ONE translation unit (0x8006B0A8/B328/B5F0/B964),
which is what prompted the dispatch re-rank below.

MEASURED, AND IT IS THE PHASE'S DISPATCH RESULT SO FAR: every body Phase 12 has produced came from
ADJACENCY, a family/twin finder, or a named structural class -- none from the top of a
density-ranked file, and B hit the leading indicator (four consecutive small-residual near-misses,
no new mechanism) doing exactly that. So the fresh-band files are now ordered band-first, then by
ADJACENCY TO A PHASE-12 MATCH, then density. Plain adjacency is NOT discriminating -- 369 of 993
rows (37%) abut SOME registered region, in every band -- but only FIVE rows abut a region matched
this phase, and the mechanical key independently flagged 0x8006B398, which worker D had already
chosen from B's handoff. The key agrees with the human pick.

PARTITION MEMBERSHIP IS NOW A HASH OF (band, address), not a position in a list. I had this wrong
twice and told the workers membership was stable when it was not: first partitioned by
density-sorted position (correcting the payload bug moved rows; band-0 top-20 overlap 1/20), then
by (band, address) POSITION, which is stable only while the row set is unchanged -- and the set
shrinks by design as rows register (1001 -> 993). Verified: removing 10 rows moves 0 assignments.
2026-09-24 17:23:50 -04:00
Christopher Williams 567b069495 phase12: cc1bin= — the alternative-cc1 lever, RESTRICTED BY THE GATE (developer-approved)
Worker C found that the project's cc1 cannot build a whole class of the original's functions,
and the census is triply verified (C, worker D, coordinator):

    623/623 REGISTERED regions contain EXACTLY ONE `jr $31`. ZERO contain two or more.
    12 of the 193 NEGATIVES rows contain two or more. That is the entire blocked class.
    NOT a flag: 14 flag sets on the default cc1 all yield one exit.
    And it produces bodies: 0x800FF43C / 0x800FF47C / 0x80108578 are byte-exact with
    gcc-2.8.1-psx/cc1 where the default gives 56/60/52 B LENGTH-MISMATCH.

THE RESTRICTION IS ENFORCED HERE, NOT LEFT TO A CONVENTION. Allowing a second compiler
BINARY widens the corpus, and the project's rule against per-function compiler choice exists
because the gate CANNOT catch a wrong choice -- byte-exact is byte-exact. So a region naming
`cc1bin` is REFUSED unless its original body has >= 2 function-exit jumps, i.e. unless it has
a NAMED MECHANISM in the bytes:

    region 0x8006B5F0..0x8006B66C names cc1bin=gcc-2.8.1-psx but its original body has only
    1 function-exit jump(s) (`jr $31`); 2 are required.  -> exit 2

`cc1bin` takes a BARE vendored directory name under tools/old-gcc/ (no slashes, no paths), so
the nameable set is exactly the pinned set already in the repo. Validated by `sf3_merge` at
merge time and enforced by `sf3_match` before anything is compiled.

THE MECHANISM SENTENCE IN THIS FILE WAS WRONG AND IS CORRECTED, NOT DELETED. My first version
said "2.7.2 emits one shared return epilogue while 2.8.x emits one per return". C's 4-return
probe reproduces that and I reproduced C's probe exactly -- but worker D could NOT reproduce
it: on both a 3-return framed probe and a 3-return frameless probe, ALL TEN builds emitted one
shared exit. Both probes are real, so the compiler-side result is SHAPE-DEPENDENT and the
general claim is false. The census and the three bodies justify the lever; the mechanism was a
hypothesis I recorded as a finding. Left in place, marked as corrected, because the next reader
will otherwise re-derive it and believe it.

ALSO IN THIS COMMIT:

* **The 2.8.x gp access pair.** 2.8.x addresses a global as `lui $R,%hi(SYM)` +
  `<op> $r,%lo(SYM)($R)` where 2.7.2 emits a bare `sw $2,SYM`. Both lines were invisible to
  the existing regexes, so a gp-touching region built with an alt cc1 came out 4 BYTES LONG.
  Now rewritten as a pair: the access becomes `%gp_rel(SYM)($gp)` and the `%hi` is dropped.
  My first two attempts at this were both wrong and both are recorded in the code:
  it required the two lines to be ADJACENT and maspsx emits a BLANK LINE between them (so it
  silently did nothing), and it deleted the `%hi` unconditionally, which leaves a later access
  to the same global addressing a register nothing wrote -- right length, plausible, wrong.
  The deletion is now gated on every use of the register before it is redefined being rewritten,
  with redefinition tracked explicitly and `jalr` deliberately treated as non-defining so the
  rule can only ever keep a `%hi` alive, never delete a live one.

* **`check-claims` reported a healthy CUMULATIVE file as a failure.** Worker B hit this: an
  already-registered row is exactly what `apply --skip-registered` exists to skip, and a
  worker's claims file is cumulative, so `result=PROBLEMS`/exit 1 was the NORMAL case for a
  good file. A checker whose healthy output is a failure teaches workers to ignore its exit
  code, and then it catches nothing. Already-registered is now information
  (`already_registered=N`, exit 0); format errors stay exit 2, missing sources and duplicate
  starts stay exit 1.

Tests: 306 -> 318, with the restriction pinned both ways (a 1-exit region is refused, a 2-exit
region is allowed), the census checked on a synthetic payload, and the gp pair rewrite's three
shapes covered. Full gate on the tracked registry stays byte-exact at 623 regions, so none of
this changes the existing corpus. `make check` exit 0.
2026-09-24 17:23:41 -04:00
Christopher Williams d2cf1b8f10 phase12: sf3_cc's maspsx stage NEVER RAN, then doubled the output — fixed (worker B's finding)
Worker B measured this and was right, and the diagnosis is exact. The call was

    maspsx.py --aspsx-version=2.56 "$OUT/$BASE.s" "$OUT/$BASE.ms.s"

but maspsx takes ONE positional (an INPUT) and writes to STDOUT -- it has no output-file
argument, which is precisely why sf3_match runs it as a stdin->stdout filter (run_filter).
So maspsx tried to open the OUTPUT path as its input, failed, and the failure was hidden
TWICE: by `2>/dev/null`, and by a `|| cp` fallback that quietly copied the raw cc1 output
over `.ms.s`.

Measured on src/func_8006B5F0.c before the fix:

  run 1, fresh scratch : .ms.s IDENTICAL to .s (71 lines) -- maspsx NEVER APPLIED
  run 2, scratch exists: 133 lines = TWO body copies with 6 `addu`, because maspsx now read
                         the STALE .ms.s as input, succeeded, printed its real output STRAIGHT
                         TO STDOUT (unredirected), and `cat` printed the stale raw copy after it

After the fix: 62 lines both runs, IDENTICAL to each other, stdout == .ms.s, and .ms.s differs
from .s for the right reason (the stage actually runs).

**So the tool was wrong on the first run and wrong-and-doubled on every later one, from its
Phase 11 promotion (b29963e) until now.** Worker F built it for spelling sweeps and its whole
premise is "one spelling costs ~0.1 s"; the cost was fine, the output was not. Cookbook 185's
description of this tool was wrong for as long as the bug existed.

AND I EDITED THIS VERY FILE EARLIER TODAY to fix `--help` WITHOUT NOTICING, while writing a
LIMIT paragraph about a maspsx stage that was not running at all. Three of four workers had
already tripped on the `--help` path, so I fixed the thing that was visible and documented the
thing I assumed, which is the same failure shape as cookbook 190 an hour earlier: I described a
contract instead of reading the tool that enforces it.

The fix is the correct filter form plus the removal of BOTH things that hid the failure. There
is deliberately NO fallback: a maspsx failure must be loud. A fallback that converts a hard error
into plausible output is worse than no fallback -- it is exactly how a broken tool survives a
whole phase of use by four workers and enters the documentation as working.

Five tests pin it, and two of them are the ones that would have caught this on day one:
  * maspsx must CHANGE the cc1 output (if .ms.s == .s, the stage did not run);
  * two runs into the SAME scratch must agree (catches the stale-read + stdout doubling);
plus exactly one body label emitted, stdout == the staged file, and no `2>/dev/null`/`|| cp`
in the executable body (comments excluded -- the fix's own explanation quotes both patterns).

Concrete cost, per B: it read a cc1-only `jal`/`lw` as a filled delay slot and nearly re-derived
a row that in fact matched. Only `sf3_match range` settled it.

  make test   306 tests, OK   (from 301)
2026-09-24 17:08:29 -04:00
Christopher Williams f5c3426e85 phase12: merge 2 — worker B's 3 rows -> 607 bodies / 616 regions
All three re-verified from fresh --work dirs against the EXACT md5 in the claim rows
(4ad25781c69f611450544ae097936d1a / 7188c87c37bfc87b8e0ebfddd47ded97 /
542de2b2242dd8e6149440e4bb034f77), merged to a candidate, gated whole-binary, promoted only
on result=MATCH.

  sf3_match range (x3, --symbols)    exit 0, differing_bytes=0, result=MATCH
  sf3_merge check-claims             exit 0, problems=0
  sf3_match gate --expect-sha1       exit 0, c_regions=616, differing_bytes=0, result=MATCH
                                     sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9
  make check                         exit 0
  make extents-verify                regions=616 disagreements=0 result=AGREE
  make worklist                      listed=998, excluded_already_registered=616

WORKER B'S CHARTER CORRECTION, CONFIRMED INDEPENDENTLY. The charter's §3d verify command
omitted `--symbols config/symbols.tsv`. Without it this entire family is +4 LENGTH-MISMATCH,
because the gp-marked global D_80121E88 auto-resolves to an absolute address and becomes
lui+lw. Measured on 0x8006B5F0:

  without --symbols:  candidate_bytes=128  result=LENGTH-MISMATCH   exit 1
  with    --symbols:  differing_bytes=0    result=MATCH             exit 0

That is the THIRD defect in the charter I wrote for this phase (after the claims-file format
and the evidence-file name). A verify command that does not match the configuration the gate
uses is a command that reports a false negative, and it would have had workers discarding
correct spellings as LENGTH-MISMATCH.

TWO REUSABLE MECHANISMS FROM B, both named rather than ground out:

(a) A REGISTER RESIDUAL CAN BE ARGUMENT ARITY. 0x8006B328 came out at correct length with
    exactly 4 differing bytes, all the a1-vs-v1 register field of the record pointer. Cause:
    the callee was called with TWO arguments. With one argument cc1 allocates the record
    pointer to v1; with two, both values are already in a0/a1, the call emits NO setup at all,
    and the emitted code differs only in that field. Diagnostic: correct length + the only
    residual is one value in an argument register => try declaring an extra parameter.

(b) A LOOP-INVARIANT CONSTANT MUST BE A NAMED LOCAL DECLARED INSIDE THE GUARDED SCOPE, and
    the `s4` save is the tell. On 0x8006B0A8 (frame 40, saves ra+s4+s3+s2+s1+s0): a literal
    `&= -2049` rematerialises the `li` inside the loop and the whole s4 save/restore pair
    vanishes; a named local before the `while` hoists the `li` above the guard test and
    reschedules the entry block (20 differing bytes); declaring it inside
    `if (p != 0) { int hmask = ...; while (...) }` MATCHES. The guard wrapper is what matters,
    not the loop form (do/while is byte-identical to while). A SECOND mask in the same function
    must stay a literal -- the hoist is per-statement, not per-function.

  make test   306 tests, OK   (from 301)
2026-09-24 17:08:21 -04:00
Christopher Williams 1e620cde50 phase12: a CHARTER is a file every worker copies — my claims format was wrong (cookbook 190)
The phase's FIRST merge was rejected: "expected three or four fields", while all four workers
were already staging files in the format my charter had given them.

  charter said:  range<TAB>source<TAB>md5<TAB>differing_bytes<TAB>result<TAB>options
                 with range written 0xSTART..0xEND
  tool reads:    start<TAB>end<TAB>source[<TAB>overrides]

Finding 179 says a rule every worker must follow belongs in a TRACKED tool, not in a file each
worker copies, because a copied artefact cannot be fixed for the people who already copied it.
Phase 11 earned that from a worker's free.sh. **A charter IS a file each worker copies**, so
writing a FORMAT into prose recreates the defect one level up — and this time the unfixable
copied artefact was the coordinator's.

AND THE TOOL HAD ALREADY LEARNED IT. validate_overrides exists because a Phase 11 worker put
md5= in the 4th column, and its error message says in terms: "Per-claim metadata such as a
source md5 belongs in report.tsv, not in the registry row." The convention was documented
INSIDE THE TOOL, I did not read it, and I wrote prose contradicting it — including renaming
the evidence file to evidence.tsv when report.tsv is the established name in the tool's own
error string and in every Phase 8/9/11 worker's staging directory.

Standing rule: before writing a staging format, an interface, or an exit-code contract into a
charter, READ THE TOOL THAT ENFORCES IT. "The tool is the contract" is not advice for workers
only. Workers compute addresses rather than eyeballing them; the coordinator must derive
formats rather than inventing them.

FIXED AS A COMMAND, NOT AS CORRECTED PROSE. New `sf3_merge check-claims --claims F [--regions R]`
validates the format, flags a duplicate start, a missing source file and an already-registered
row, so a worker answers "is my staging mergeable?" itself before reporting. 11 tests, one of
which asserts THE CHARTER'S OWN WRONG FORMAT IS REJECTED, so the message stays honest for the
next coordinator — who will also write prose.

Charter §10 rewritten to specify the tool's format by READING THE TOOL, to separate claims.tsv
(the merge input) from report.tsv (the evidence, where the md5 lives), and to name
check-claims as a required pre-report step; §5 gained rule 17.

What worked: fail-fast validation caught it in under a second at the merge, not as a confusing
failure at the gate. Phase 11's override-key guard paid off again. A staging format a tool can
CHECK is worth more than one that is documented well.

Cost: one rejected merge, four correction messages, ~10 minutes. Cheap because the tool refuses
to guess.

  make test   301 tests, OK   (from 290)
2026-09-24 17:01:48 -04:00
Christopher Williams b3e3ec3ea7 phase12: merge 1 — worker C's two ADJACENT-TWIN negatives -> 604 bodies / 613 regions
The phase's first two bodies, and both are recorded negatives rather than worklist rows.
Worker C found them with the NAMED UNTRIED LEVER I handed it: tools/sf3_family driven over the
101 negative rows against the 611 registered regions. I measured candidates=0 for the fresh
band and told C that said nothing about its own pool. It didn't:

  score >= 1.000 :  3 rows   (NOT 0)
  score >= 0.99  :  4 rows
  score >= 0.95  : 15 rows

  -> 0x800518BC (88 B) -> 0x80051864   MATCHED (this merge)
  -> 0x800B34A4 (88 B) -> 0x800B34FC   MATCHED (this merge)
  -> 0x8010804C (16 B) -> 0x80085B80   blocked class (gp-thunk), correctly not attempted

Transfer rate 2/2 on the unblocked score-1.000 rows, against finding 166's 7/7.

I re-verified BOTH claims from fresh --work directories before merging (differing_bytes=0,
result=MATCH, exit 0 each), merged to a candidate, gated the whole binary, and promoted only on
result=MATCH.

  ./tools/sf3_merge apply ...                      exit 0, added_regions=2
  ./tools/sf3_match gate --expect-sha1 e173...     exit 0, c_regions=613
                                                   differing_bytes=0, result=MATCH
                                                   sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9
  make check                                       exit 0
  make extents-verify                              regions=613 disagreements=0 result=AGREE
  registry audit                                  613 rows, ordered, non-overlapping,
                                                   604 distinct sources, 0 missing files

Worklist regenerated. It still lists 1001 rows, and the reason is worth recording because it
looks like nothing happened:

  excluded_already_registered   611 -> 613   (+2)
  excluded_recorded_negative    169 -> 167   (-2)

The two rows were NEVER in the worklist -- they were excluded as recorded negatives. Registering
them MOVES each row from the negatives-exclusion bucket to the registered bucket, so `listed`
is unchanged while both counts shift by exactly 2. excluded_already_registered == the registry
size, as required. No partition refilter is needed: both rows were in C's negatives partition,
not in any fresh partition.

WORKER C'S FINDING, which is worth more than the two bodies and is a Goal-B-grade result: BOTH
rows' recorded class strings were WRONG ABOUT THEIR OWN MECHANISM.

  0x800518BC was classed "return-merge/sltiu", with a note that three spellings all came out
    80 B and sltiu fixed the compare byte but not the 8-byte layout. RAW WORDS: 21 of 22 words
    are IDENTICAL to matched 0x80051864. The sole difference is the forward `j` to the shared
    return, and its target differs only because `j` encodes an ABSOLUTE address and the two
    bases are 0x58 apart. The body is a literal twin; no re-spelling was ever needed.

  0x800B34A4 was classed "alloc-tiebreak" (bit-index/base register pair a0/a1 vs cc1 a0/v1).
    RAW WORDS: 20 of 22 identical to matched 0x800B34FC; both differences are the OFFSET
    CONSTANT (`li a0,3` vs `li a0,20`). The register pair the note asked for was already there.
    The lever is the constant, not the allocator.

Both rows abut their matched twin, so the negatives index contains whole RUNS of repeated
bodies -- and C's operational conclusion is that the cheapest finder is a raw-word HAMMING scan
against the 611 registered regions, which the histogram tool only half-finds. C is running that
scan across all 101 rows now. That map, not the bodies, is likely the largest thing this phase
produces.

Note the discipline this vindicates: an unclassified negative is not overturnable (finding 182),
but these two WERE classified -- wrongly, in the same dimension, by earlier sessions that then
wrote off the row. Cookbook 182's rule needs its other half: a recorded mechanism is a HYPOTHESIS,
and a WRONG named mechanism is as unattemptable as no mechanism at all until someone reads the
raw words.
2026-09-24 16:58:39 -04:00
Christopher Williams f4b1f59b85 phase12: the stale patch confirmed end to end — 52 bytes = 4 x 13, and the arithmetic closes
T0's defect was proven three ways (the patch rejects, the guard tests fail, the transform emits
an extra nop). This adds the end-to-end one, and its arithmetic closes exactly.

Gating the CURRENT 611-region registry with the SUPERSEDED transform:

  rebuilt_bytes=1886260   original_bytes=1886208   ->  52 bytes LONG
  rebuilt_sha1=24c4c2a9c99cc7f5daf88c0a30107bda2d16340f
  original_sha1=e173426c157384ebf1b6caf8c6fea18a85a14af9
  result=DIFF

52 bytes is 13 instructions, and 13 was derived INDEPENDENTLY from the original payload: the 15
maspsx=epilogue regions split 2 shapes A / 13 shapes B, classified by asking whether the word
before the `jr $31` is a nop (A) or a real instruction (B), with the frame release after the jump
in both cases. Each shape-B region gets one extra nop, so 4 x 13 = 52.

A count taken from the bytes and a count taken from the failing gate AGREE. That is what makes
this a measurement rather than an argument, and it is why the T0 record does not rest on a single
line of tool output.

ALSO RECORDED, because it is the trap this phase's own documentation warns about: my demonstration
command piped the gate through `tail` and then printed `$?`, so the `>>> gate exit=0` line in that
log is MEANINGLESS -- `$status` is a fish variable and the pipeline masked the gate's real status.
`result=DIFF` is the authoritative field and the byte arithmetic is the independent check. Workflow
section 6's rule is "use the gate's EXIT CODE as the gate"; I broke it while quoting it. Standing
note for every future gate call: capture the exit code directly, never through a pipeline.
2026-09-24 16:53:07 -04:00
Christopher Williams 4750d466b4 phase12: T1 — phase open, roster spawned and chartered, ledger started
Roster (fresh, spawned by the orchestrator; tab w1:t5, 2x2 grid):

  A  w1:pC  01a0d529        A1  lever-a.tsv      334 rows
  B  w1:pE  01a0d52a-2c40   A1  lever-b.tsv      334 rows
  C  w1:pD  01a0d52a-55d3   A2  negatives-c.tsv  the 101 NAMED negatives
  D  w1:pF  01a0d52a-6198   A1+B lever-d.tsv 333 + negatives-d.tsv the 92 UNCLASSIFIED

Four agent_pane_not_found errors on first start, all cleared on retry after a few seconds --
the documented transient, not a bad id.

PARTITIONS ARE PROVEN, NOT ASSERTED (.run/p12/partition-proof.txt):
  worklist rows 1001; a=334 b=334 d=333; sum 1001; distinct union 1001; overlaps 0;
  union == worklist True; negatives 193 (c=101 d=92); negatives overlap worklist 0;
  negatives c/d overlap 0.

The phase's structural premise was verified by set comparison: 0 of the 193 negatives rows
appear in the 1001-row worklist, because sf3_triage plan --negatives excludes them. The
negatives are an INDEX, not a queue, and worker C is the first worker in this project whose
partition is that index.

THE MEASURED RANKING, and it produced a real number. Band first, density second, with the
density statistic IMPORTED from the tracked tools/sf3_rank (which declares itself the reference
implementation and warns scores are not comparable across implementations -- reimplementing it
would silently break that guarantee):

  band            rows   median density   max    size range
  0 (<=244 B)      410      0.359         0.912   4-244 B
  1 (245-400 B)    225      0.491         0.920   248-400 B
  2 (401-800 B)    221      0.604         0.896   404-796 B
  3 (>800 B)       145      0.720         0.940   804-11516 B

Median density rises MONOTONICALLY with band. That is worker D's warning ("density is
correlated with size, which biases the top of the list") measured on this corpus, and it is
exactly why a pure density ranking is wrong when the milestone counts BODIES rather than bytes:
one body at 80 B and one at 8000 B are worth the same. Band-first suppresses the bias; density
then orders within the band, where it is the measured cost signal.

MEASURED NEGATIVE, recorded so three workers do not each rediscover it: tools/sf3_family
--top 25 returns candidates=0 against the fresh band at ratio 1.000. The family lever is
EXHAUSTED for the worklist (Phase 11's 7 rows were all of it). It has NEVER been run against
the 193 negatives -- sf3_family reads the worklist, which excludes them -- so that is a named,
UNTRIED lever handed to workers C and D, who were both told to report who runs it.

Baseline revalidated from clean before dispatch: make clean/all exit 0; cmp exit 0; SHA-1
e173426c157384ebf1b6caf8c6fea18a85a14af9 unchanged; make test 290 OK (253 -> 281 -> 290);
extents-verify regions=611 disagreements=0 AGREE; gate c_regions=611 differing_bytes=0 MATCH.

CURRENT_PHASE.md rewritten for the phase, with the milestone warning stated plainly (+148 is
larger than any phase so far) and the cycle-1 requirement that at least 8 bodies must come from
the negatives pool -- so a low overturn rate is measured in cycle 1, not discovered at the close.
2026-09-24 16:51:37 -04:00
Christopher Williams afce5aa108 phase12: sf3_cc --help was broken — three of four workers tripped on it
Worker B, worker C and worker D all probed `tools/sf3_cc` with `--help` during the Phase 12
capability probe, and all three got a raw coreutils `basename` error instead of help:

  ./tools/sf3_cc: line 29: .run/sf3_cc/Usage: basename NAME [SUFFIX] ... : No such file

ANY leading-`-` argument was taken as a source path. `basename --help .c` prints coreutils'
own help text, and that entire text then became the output filename, so the failure surfaced
as a confusing redirection/cpp error. Every worker lost time on it, on the first command they
tried, and worker D spent probe effort diagnosing it as a "genuinely broken path".

This is cookbook 179 from the other side. That finding says a rule every worker must follow
belongs in a TRACKED tool rather than a file each worker copies. Here the tool WAS tracked --
and it was still unusable the way every single worker reaches for it first. Tracking a tool
is necessary but not sufficient; it also has to survive first contact.

Fixed: `--help`/`-h` print real help and exit 0, an unknown option is rejected BY NAME, and a
missing source file says so rather than failing inside cpp. The compile path is unchanged.

Also documented its LIMIT, which was previously unwritten and is easy to misread as a match:
the maspsx stage always runs with DEFAULT options and no region option is passed, so this
shows a SPELLING's shape and NOT a region's final bytes. A region needing `maspsx=epilogue`,
`nopmarker`, `moves`, `regread`, `off` or `gp=`/`cc1=`/`as=` looks different here than under
`sf3_match range`. Sweep spellings with this; decide matches with `sf3_match range` -- and
decide an `epilogue` token from the CANDIDATE's tail, never the original's (finding 165/180).

9 new tests (tools/tests/test_sf3_cc.py): the argument-handling cases run without a toolchain,
including one that pins the exact symptom (no coreutils help text in the output, and no file
created named after it); the compile-path test skips cleanly when the ignored toolchain or any
src/func_*.c is absent, and one test pins the documented LIMIT so it cannot be dropped.

  make test   290 tests, OK   (from 281)
2026-09-24 16:50:50 -04:00
Christopher Williams 98988e04ec phase12: PLAN approved — milestone 750 bodies (+148), stretch 800
Approved by the developer 2026-09-24: milestone 750, stretch 800; both harness repairs
authorised; Goal B (the 92-unclassified negatives) in scope and bounded; 4 workers —
two on the fresh band, one on the 101 named negatives, one on the third fresh slice plus
the 92 unclassified as the Goal B worker; compaction-first.

THE ONE STRUCTURAL FACT THIS PLAN IS BUILT ON, measured at the open:

  0 of the 193 near_match_negatives rows appear in the 1001-row worklist.

The negatives index is held OUT of the worklist (sf3_triage plan --negatives) and is not a
queue — it is an index, and a worker dispatched only from the worklist can never reach it.
So the negatives pool becomes a first-class partition with its own worker, and the phase's
structural change is that it stops being an appendix.

Two pools, measured: 1001 fresh rows (410 of them <=244 B) plus 193 attempted-but-unresolved
negatives, of which 101 carry a NAMED mechanism and are overturnable by cookbook 182's rule,
and 92 carry no class at all and cannot be attempted until someone re-reads them. That is
what Goal B is for.

The plan states plainly that +148 is LARGER THAN ANY PHASE SO FAR (Phase 11 closed +118) and
that it cannot be reached from the fresh worklist alone — it depends on the negatives-overturn
programme working at scale, which is a measured route but only a ONE-ROW demonstration so far
(worker F's 0x8002622C). Two safeguards are built in rather than discovered late: the cycle-1
checkpoint must include at least 8 bodies from the negatives pool, so a low overturn rate is
measured in cycle 1 and not at the close; and the leading-indicator rule stands unchanged —
report the projection and ask, never redefine or self-certify the milestone.

Also recorded: the fresh-band size distribution, the negatives class distribution, and the
baseline revalidation table including the failed precondition that T0 repaired.
2026-09-24 16:41:11 -04:00
Christopher Williams e53086a59e phase12: T0 — regenerate the STALE maspsx patch and guard it with a test + sf3_free reads the CURRENT phase's ledger
Both defects were found by the Phase 12 open checklist, and both were then PROVEN by
direct test rather than by inspection.

*** 1. THE TRACKED PATCH WAS STALE, AND THE TOOLCHAIN WAS NOT REPRODUCIBLE ***

ea51ac9 ("the epilogue transform now handles BOTH shapes") corrected maspsx=epilogue in
the working tree but never regenerated the tracked patch, which still carried the
superseded shape-A-only transform from 05be974.

  patch -p1 < tools/patches/maspsx-phase10-r1r2.patch   (pristine 86ccd7d)
  -> exit 1, 4 of 10 hunks FAILED, .rej files for BOTH files

The superseded transform emits `nop / jr $31 / addiu sp,sp,32` where 0x800F44D0 has
`jr $31 / addiu sp,sp,32`, so every shape-B row comes out 4 bytes long. 15 registered
regions carry maspsx=epilogue, so a fresh clone could NOT have rebuilt the 611-region
green gate from tracked files. The working tree was right, so every gate was green; the
patch was wrong, so nothing failed. That is why it survived the rest of Phase 11.

Regenerated as pristine -> working tree for exactly the two files the patch touches, and
verified: applies with exit 0 and no rejects; reconstructs both files byte-identically
(cmp exit 0); reverse-applies cleanly, proving the tree IS pristine+patch; still carries
all four opt-in modes.

  4bd21823402d73659f76b3afbf60f8f93daaa3e8 (stale, 167 lines)
  0d7f643ba1c31d1ae28de257737c16c623a9eb00 (regenerated, 229 lines)

*** A TEST, NOT ANOTHER RULE ***

The standing §11 rule — "do not edit a git-ignored tool without carrying the change as a
tracked patch" — WAS followed in Phase 11: 05be974 regenerated the patch and explicitly
verified it. It was stale by the very next commit that touched the transform. The
generalisable defect is that the artifact which is verified is not the artifact that is
enforced: a verification performed once by hand decays the moment the OTHER side of the
diff changes, and the ignored side is the side no review sees. Cookbook 188.

So the remedy is executable. tools/tests/test_maspsx_patch.py reconstructs the vendored
tool from pristine + the tracked patch and requires a byte-identical match against the
working tree; it also requires the pinned commit, requires ONLY those two files to be
modified, requires all four opt-in mode flags to survive a regeneration, and exercises
BOTH epilogue shapes through the reconstructed script. It skips cleanly when the ignored
checkout is absent, like the existing local-toolchain skips.

PROVEN TO CATCH THE DEFECT: run against the superseded patch it fails 4 of its 7 tests
(2 failures, 2 errors); against the regenerated patch it is green. A stale patch now
fails `make test` instead of being found by a human reading a diff — which is exactly how
it was found this time.

*** 2. sf3_free HARD-CODED .run/p11/, AND WOULD HAVE READ A DEAD LEDGER ***

The tracked free-check hard-coded INFLIGHT = .run/p11/inflight.tsv. .run/ is git-ignored
and the ledger is session-scoped BY DESIGN: it coordinates the workers who are alive now.
In Phase 12 the tool would have read a 306-line p11 ledger and never the ledger its own
workers were writing to.

The direction of that failure is the opposite of cookbook 187's, and worse. A stale `wip`
row produces a false TAKEN — a worker is told a free row is held and wastes an
opportunity. A BLIND ledger produces a false FREE — a worker is told a row another worker
is actively holding is free. That is the 0x800320D8 incident: two workers matching the
same region independently, one overwriting the other's source file. Cookbook 189.

The fix encodes the semantics the ledger already had: one phase's ledger is dead to the
next. Resolution is --ledger, then --phase N, then the highest existing .run/pN/ — and a
previous phase is deliberately NOT a fallback, because a row held `wip` by a worker
retired at that phase's close must read FREE. The resolved path is printed to stderr, so
"which ledger did that read?" is always answerable while stdout stays parseable.

21 new tests (tools/tests/test_sf3_free.py) pin end-exclusive containment, last-row-wins,
the phase resolution order, and the no-fallback rule.

*** VERIFICATION ***

  make clean && make all   exit 0
  cmp                      exit 0
  SHA-1 (both files)       e173426c157384ebf1b6caf8c6fea18a85a14af9   (unchanged)
  make test                281 tests, OK   (from 253; the clean run captured 253 before
                                            these suites existed, independently
                                            reconfirming the Phase 11 baseline count)
  make extents-verify      regions=611 disagreements=0 result=AGREE
  make gate                c_regions=611 differing_bytes=0 result=MATCH

No src/, config/ or toolchain working-tree change, so the gate cannot be affected.
2026-09-24 16:41:01 -04:00
Christopher Williams 9f2543b46e phase11: close-record correction -- tracked file count 720 -> 722
The firewall line in all four close records said 720 tracked files. That figure was measured BEFORE the
two close records themselves were added, so the true post-close count is 722
(phase-ends/PhaseEnd_Phase11.md and docs/PHASE11_VERIFICATION.md). Corrected in the PhaseEnd, the digest
entry, the verification record and the ledger.

Same class of error as the two the close already records: a number measured at one moment, then quoted
in a document that itself changes the thing being measured. The firewall RESULT is unaffected --
0 tracked paths under any prohibited root either way.
2026-09-24 16:18:13 -04:00
Christopher Williams ec7e2c6242 phase11: PHASE CLOSE — 602 bodies / 611 regions, milestone MET and developer-confirmed
Developer confirmation of the 600-body milestone was requested and given before this record was
written, per the plan. The phase closes at 602 distinct matched bodies / 611 registered regions, from
the Phase 10 close state of 484 / 493 (+118 / +118).

Closing gate set, all green from a clean tree:

  make clean && make all   exit 0
  cmp                      exit 0
  SHA-1 (both files)       e173426c157384ebf1b6caf8c6fea18a85a14af9  (UNCHANGED from Phase 10)
  make test                253 tests, OK   (from 237)
  make extents-verify      regions=611 disagreements=0 result=AGREE
  make gate                c_regions=611 differing_bytes=0 result=MATCH

The SHA-1 being identical to the Phase 10 close is the point: all +118 bodies are additions to a
binary that still reproduces exactly.

Records written:
  phase-ends/PhaseEnd_Phase11.md        the close record
  docs/PHASE11_VERIFICATION.md          the verification record
  phase-ends/DIGEST.md                  Phase 11 section
  phase-ends/CURRENT_PHASE.md           rewritten: NO PHASE ACTIVE, roster-restart warning
  phase-ends/logs/Phase11.md            Cycle 3 close, corrections, ledger reconciliation
  docs/MATCHING_COOKBOOK.md             187
  docs/ORCHESTRATOR_WORKFLOW.md         section 4.0 roster rule, section 10 close steps, section 11

Three corrections made during the close, each to something already reported:

1. The negatives index was reported as "200 rows, 82 with a mechanism". Measured: 194 data rows
   (200 LINES, 6 of them header), 101 with a named class, 86 with a substantive note, 92 with no
   class at all. The 700-body route is LARGER than reported. wc -l on a file with header comments is
   not a row count -- and the same error was then made again with the symbol count (413 lines, 400
   rows) inside this very record.

2. One in-flight ledger row was STALE-TAKEN and invisible. 0x800298C0 (392 B) traced
   C wip -> C released -> A wip -> C wip, so last-row-wins read it TAKEN while NEITHER holder was
   working it -- both were out of context and would never append a release. It is a live worklist
   row and is NOT in the negatives index, so nothing else would have surfaced it. Released with
   coordinator as the worker field; tools/sf3_free now reports FREE. This is cookbook 179's failure
   mode in its OTHER half: the copied script blocked rows that HAD been released, while this row
   shows the ledger cannot express "the holder no longer exists" at all. Now a standing close step
   (cookbook 187).

3. Worker A's "32 first-attempt" claims are not reconcilable from its artefact -- only 27 of its 46
   report rows carry an explicit first-attempt note. 27 is recorded as the verifiable figure and 32
   is flagged unverified, because a first-attempt rate is a COST claim and cost claims drive
   dispatch.

Ledger reconciliation at close: 275 rows over 135 addresses; last row released for 113, claimed for
21, wip for 1. The 21 claimed rows are all already registered, so they need no action -- claimed is a
legitimate resting state.

The roster does NOT survive this close. All six worker sessions are retired and their herdr panes
closed, so Phase 12 MUST spawn a fresh roster; there is nothing to reconnect to. This is a change
from Phase 10, which left three retired sessions listed in `intercom list` -- and a roster that is
retired but still listed is indistinguishable from one that is live. Both halves are now standing
rules in ORCHESTRATOR_WORKFLOW section 4.0.

No changes to AGENTS.md.
2026-09-24 16:17:27 -04:00
Christopher Williams 4824b7af9a phase11: restore tools/sf3_diff (overwritten blind) + cookbook 186
b29963e promoted worker F's staging tools, and in the same commit I copied F's
staging `diff.py` over `tools/sf3_diff` WITHOUT READING IT FIRST -- a direct
violation of AGENTS.md rule 3, 'Never overwrite blind.'

`tools/sf3_diff` was a 364-line Phase 9 tool: two subcommands (diff, resolve), a
PS-X-EXE header parser that reads the text address rather than hardcoding it, a
symbol-registry loader, and a lui/addiu + gp-relative address resolver. It had
17 tests of its own. Replacing it with a 97-line staging script dropped
`make check` from 253 tests to 237 and failed it with exit 2.

Restored from b29963e^ and verified byte-identical to it (cmp exit 0).

  make check           exit 0, 253 tests OK
  extents-verify       regions=611 disagreements=0 result=AGREE
  gate                 rebuilt 1886208 B, differing_bytes=0, result=MATCH
                       sha1 e173426c157384ebf1b6caf8c6fea18a85a14af9

Documentation corrected, because the overwrite also left the record wrong:

* cookbook 185 documented the interface of F's STAGING script
  (`sf3_diff 0xSTART 0xEND <workdir>`), which is NOT the interface of the
  tracked tool and never was. Replaced with the real one, and the reason the
  Phase 9 tool is worth more is now stated: its `notes` column resolves
  lui/addiu pairs against the symbol registry and gp offsets against the gp
  base, so a residual row can name WHICH GLOBAL an address is.
* cookbook 186 records the defect. The question to ask before promoting into
  tools/ is not 'is the new one better?' but 'what does the old one already do
  that the new one does not?'
* ORCHESTRATOR_WORKFLOW.md section 11 gains both as standing prohibitions, with
  the diagnostic: a SHRINKING TEST COUNT means a tool that had tests no longer
  satisfies them, and it fires before the failure itself is explained.
* phase-ends/logs/Phase11.md: 'seven defects' -> nine, recording the free-check
  defect (cookbook 179) and this one.
2026-09-24 13:00:11 -04:00
Christopher Williams b29963eafb phase11: promote sf3_cc + sf3_diff to tools/ (worker F) + cookbook 184-185
Worker F's judgement, which I agree with: these two together are the highest-value tooling built
this phase, because the first removes the COST of a spelling and the second removes the GUESSWORK
about what is wrong.

  tools/sf3_cc <file.c>              one-file cpp -> cc1 -> maspsx, printing the assembly.
                                     A spelling costs ~0.1s, which turns 'try a few variants'
                                     into 'grid the whole space'.
  tools/sf3_diff 0xS 0xE <workdir>   opcode-level diff of the candidate object against the
                                     original, difflib-aligned, so you see the SHAPE of the
                                     residual rather than a byte count.

A differing-byte count tells you HOW WRONG a candidate is; it does not tell you WHICH DIMENSION
the error lives in. sf3_diff marks differing instructions with '<<', so a run of identical
mnemonics with differing register names is the allocator class -- a CLASSIFY signal, not a
spelling signal.

184: a load's BASIC BLOCK is not source-movable -- sched2 cannot cross blocks, so a load in a
different block than the candidate's is a SOURCE-ORDER fact.
2026-09-24 11:47:41 -04:00
Christopher Williams 0116fa5685 phase11: cookbook 183 — a two-instruction shortfall with a missing lw is a MISSING DEREFERENCE LEVEL
Worker E's 0x8007F9B0 was recorded as '8 bytes short' and the cause was a transcription error:
it had written TWO dereference levels where the original has THREE. The deficit is exactly the
missing lw plus its load-delay nop = 2 instructions = 8 bytes.

The diagnostic: a body exactly TWO instructions short, where the original has one more lw in a
pointer chain, is a MISSING DEREFERENCE LEVEL -- not a missing statement. Recompute the chain
from the displacements.

And the distinction matters: finding 149 is for bodies short by missing nops around INDEPENDENT
BLOCKS; this is short by a missing LOAD. Two different causes with the same 4-8 byte signature.
2026-09-24 11:43:54 -04:00
Christopher Williams 6141079f9b phase11: merge 61 — worker E's 0x8007F9B0 -> 602 bodies / 611 regions 2026-09-24 11:43:43 -04:00
Christopher Williams b0ed17d494 phase11: merge 60 + cookbook 181-182 — 601 bodies / 610 regions
Worker F's 0x8002622C (44 B) — A RECORDED NEGATIVE OVERTURNED, with a new class.

The old record said 'cc1 folds it, unreachable' and tried FOUR ALGEBRAIC re-spellings. All four
were doomed: the fold is at RTL combine, not in the front end, so no re-spelling can avoid it.
Only LIVENESS can. Same body with 'return 0' is 32 B LENGTH-MISMATCH; with 'return n' (the
difference live past the addition) it is 44/0/MATCH. Diagnostic that proves the pass: cc1 -da
shows the minus present in the .flow dump and gone in the .combine dump, while cse/cse2/jump/
loop/sched/sched2 all still contain it.

181: when an original keeps an arithmetically-cancelling pair (subu+addu, x-c+c), the intermediate
is LIVE PAST the second operation — find the later reader.

182: a negative with a NAMED mechanism is overturnable; one without is not. 'cc1 folds it' is not
a classification; 'RTL combine cancels it, and algebraic re-spelling cannot reach combine' is,
and it immediately implies the liveness lever.
2026-09-24 11:42:17 -04:00
Christopher Williams 3519afe441 phase11: tools/sf3_free + cookbook 179-180 — the EIGHTH rule defect, and the token table
179: a rule every worker must follow belongs in a TRACKED tool, not a copied script. The registry
free-check lived in a worker's staging dir and the orchestrator told everyone to copy it. It was
WRONG -- it reported TAKEN if ANY ledger row for the address was not 'released', so a row that was
wip and later released stayed blocked FOREVER. Worker E found it and measured 13 released rows
reading as taken, several of them the cheapest rows left, and the same stale pattern existed for
workers A and D too. Fixed and promoted to tools/sf3_free (tracked, docstring explains the bug).
A copied script cannot be fixed for the people who already copied it.

180: worker E's maspsx=epilogue token table, MEASURED not inferred -- 5 rows REQUIRED, 3 HARMFUL
or NO-OP, 2 HARMFUL-but-unmatched. Three of E's fourteen claims sit on that list and TWO would
have failed outright if the token had been applied by shape. The cheap read: '4 bytes SHORT with
the token on' means the token was unnecessary.
2026-09-24 11:39:45 -04:00
Christopher Williams 5c3e5d0e07 phase11: *** 600 DISTINCT MATCHED BODIES — TARGET REACHED ***
609 regions / 600 distinct bodies, from the 484 / 493 baseline at phase start (+116 regions,
+116 bodies). Worker E's 0x80077D04 (120 B) is the 600th.

VERIFIED, not asserted:
  full-binary gate   : 609 regions, differing_bytes=0, result=MATCH, exit 0
  make check         : 253 tests, OK, exit 0
  every source file  : present on disk and tracked by git
  working tree       : clean

Corpus maximum 1232 B; 28 regions exceed the 244 B ceiling the phase was planned around.

Delivered by six workers over the phase: A 46, D 21, B 17, C 13, E 14, F 3 = 114 claims,
every one gated before merge.

The phase's premise was falsified early and replaced with working tooling:
 - the 244 B ceiling was a DISPATCH ARTEFACT (5 of 427 rows above it had ever been attempted);
 - ASPSX does not fill delay slots at all, so the authorised post-pass shrank from a modelling
   project to one mnemonic (maspsx=moves) plus an epilogue swap (maspsx=epilogue);
 - cost is TIE-BREAK DENSITY, not size, measured independently by two workers from opposite
   directions;
 - the GTE class moved from BLOCKED to OPEN (worker D's 0x800F3E18 is the first GTE row matched
   in this project).
2026-09-24 11:37:10 -04:00
Christopher Williams bec90acd31 phase11: cookbook 175-178 — mask chains, CSE store-forwarding, OR reassociation, and 170's constant variant
175: mask chains must be SEPARATE STATEMENTS -- one expression folds to a single and; three
statements emit the original's lw/and/and/and/sw. cc1 does not fold constants across statements.

176: a MECHANISM, not a tip. Three separate RMW statements give one lw, three ands and ONE sw
(cse forwards each load from the previous store within a block); an intervening early return makes
that store land in the beq delay slot and the tail's store stays alive because its load is
forwarded from a store in the PREVIOUS block. A local chain loses the delay-slot store (100 B);
volatile keeps both stores but flips the entry branch (100 B).

177: fold REASSOCIATES | -- one statement becomes v | (C1 | X), a 17-byte residual; two statements
give the original (v | C1) | X.

178: finding 170 has a CONSTANT-clobber variant -- the same entry-block copies arise because mask
constants are materialised into the parameters' own homes, with no call involved. The rule
generalises: a redundant entry copy means the value is live when its home is clobbered, whether by
a call's argument setup OR by constant materialisation.
2026-09-24 11:34:23 -04:00
Christopher Williams efbcc2bd01 phase11: merge 59 — worker F's 0x800261C0 -> 599 bodies / 608 regions, ONE to the milestone 2026-09-24 11:33:44 -04:00
Christopher Williams 773cb88dcf phase11: cookbook 172-174 — a missing sign bias, a second swapped-subtraction, and when to decline a row
172: a MISSING sign bias is evidence of a shift, not a division -- the values are non-negative in
the source but cc1 cannot know that after a conditional, so the absence of the addiu is the
diagnostic. Finding 97 read backwards.

173: the absolute value is a SWAPPED SUBTRACTION (subu with operands exchanged), not negu -- the
natural -x is the trap. Second instance of finding 101.

174: the idiom-redundant-by-construction class, and the discipline of declining a row. Worker E
decoded 0x800FECF8 -- one of the highest-scoring fresh small rows -- and RELEASED it without
attempting it, because it is a magic-division row where every division has several equally
plausible spellings. A high redundancy score is not sufficient if the row is in a class known to
be spelling-ambiguous.
2026-09-24 11:31:30 -04:00
Christopher Williams eeaaf5b059 phase11: merge 58 — worker E's 0x80027D88 -> 598 bodies / 607 regions, TWO to the milestone 2026-09-24 11:30:55 -04:00
Christopher Williams 7a93946795 phase11: merge 57 + cookbook 170-171 — 597 bodies / 606 regions, THREE from the milestone
Worker E's 0x8005E17C and 0x8002FAB8; worker F's first two claims 0x800FBE84 (216 B, FIRST
SPELLING with worker A's derivation) and 0x80026274 (108 B).

170 generalises the argument-evidence levers (157/164) into a mechanism: a redundant ENTRY-BLOCK
copy of an argument means that value is still live at a call whose argument setup CLOBBERS that
same register. The copy is materialised in the entry block because the tie to a0's home is
illegal. The test that nailed it: the same body with a 2-arg call is 104 B LENGTH-MISMATCH; with
the 3-arg call it is 108/0. Two prior corpus instances had the copy AT the call; this is the
hoisted-to-entry variant.

171: worker F confirmed EXHAUSTIVELY that the constant-division divisor is unique per magic --
(n*M)>>(32+s) == n/D has exactly one D. So finding 67's identity is not an approximation.
2026-09-24 11:27:41 -04:00
Christopher Williams 696b7dfbfb phase11: cookbook 169 + sf3_family bug fix — 0.96-0.99 is idiom noise, CONFIRMED by raw-word diff
Worker A calibrated sf3_family by checking two 0.97 entries and finding neither shared its
sibling's body. I have now confirmed that on eight candidates by raw-word diff, which is the
decisive test: 0x8006EBA0 vs 0x80028CE0 differs in 60 of 61 words; 0x800FFFEC vs 0x8007E8B8 in
18 of 19; 0x8005E17C vs 0x800FB54C in 25 of 26. Contrast the genuine sibling 0x800F3DC0 vs
0x800F3E18: 1 of 22 words.

So a high cosine with ratio 1.00 is NOT evidence of a shared body -- at 0.96-0.99 the histogram
matches common IDIOMS. The useful band is ratio 1.000 AND a near-zero raw-word diff.

Tool bug fixed: sf3_family did not exclude already-claimed rows, so its top hit was a row
matching ITSELF. The registry is now the authority and claimed rows are skipped.
2026-09-24 11:24:48 -04:00
Christopher Williams eb64656c5d phase11: merge 56 — worker E's 0x800182F4 -> 593 bodies / 602 regions 2026-09-24 11:23:12 -04:00
Christopher Williams c091483083 phase11: merge 55 + cookbook 123 SOLVED + 167-168 — 592 bodies / 601 regions
Worker E's 0x8002311C (160 B) CLOSES COOKBOOK 123'S OPEN QUESTION. Finding 123 recorded the
branchless MAX0 (x & -(x > 0)) as unreached -- 'no ternary and no bitwise spelling reached it'.
Worker E solved it: the lever is NAMING THE BOOLEAN.

  return s & -(s > 0);        -> BRANCHES
  return s > 0 ? s : 0;       -> branches
  flag = s > 0; return s & -flag;  -> EXACT (slt / negu / and)

Mechanism: naming the comparison forces cc1 to materialise it as a VALUE (slt) rather than a
test feeding a branch. That is finding 44's 'name the boolean' lever applied to the MAX half --
finding 44 previously had only the cond-into-&& direction for this family.

167: a 4-byte store cc1 DELETES means the object's address is never taken -- fold the word into
the array whose address IS taken by a call.

168: s = f(); s += f(); s += f(); loses one instruction vs three named results summed.
2026-09-24 11:22:01 -04:00
Christopher Williams db6022c9f7 phase11: merge 54 + cookbook 166 — 590 bodies / 599 regions
0x800F3DC0 (88 B) — a ONE-WORD sibling of the matched 0x800F3E18, found by worker E via
sf3_family at ratio 1.000 and confirmed by raw-word diff: identical in all 22 words except the
COP2 command field (0x4B70000C vs 0x4B78000C). The route was one copy, two renames and one field
change; every __asm__ and register binding carried over untouched.

166 records it, and notes it is the MIRROR of finding 161: on 0x800F3E18 the field 0x178000c was
the WRONG answer (one byte off, 0x170000c correct); on 0x800F3DC0 0x178000c IS correct. A count
tells you a field is COMMON, not that it is right -- and a ratio-1.000 sibling is the cheapest
place to learn which one a row wants. When the family tool reports one, diff the raw words FIRST.
2026-09-24 11:18:58 -04:00
Christopher Williams 430f141a4f phase11: cycle-3 ledger at 589 bodies / 598 regions — 11 to the milestone
Records worker output (104 claims total), the GTE class moving from BLOCKED to OPEN (worker D's
0x800F3E18 is the first GTE row matched in the project), the epilogue post-pass and its two
corrections, seven defects in coordinator-written rules, the central finding restated with worker
D's seven-finder breakdown, and the ranker's known blind spot with both failed proxies.
2026-09-24 11:13:52 -04:00
Christopher Williams f4e14569bd phase11: merge 53 — worker E's 0x800B0E64 -> 589 bodies / 598 regions 2026-09-24 11:13:40 -04:00
Christopher Williams ecbe17ad1d phase11: merge 52 + cookbook 163-165 — 589 bodies / 598 regions
Worker E's 0x80107DE8 (128 B, maspsx=epilogue) and 0x8007D5FC (132 B, DEFAULT toolchain).

163: a recurring epilogue-list class WITH A SHAPE TELL -- correct length, identical instruction
multiset, and the residual is where cc1's reorg put the EPILOGUE LOADS relative to the last
gp-relative read-modify-write block. The original's epilogue loads FILL the global load's delay
slot; cc1 emits a #nop instead. Tell: the row's last statement is a gp-relative read-modify-write
immediately before the epilogue. NOT a spelling problem -- worker E probed the maspsx mode split
and the nop is a cc1 #nop, not maspsx, so no option changes it. Classify it: post-pass territory.

164: a register residual can be ARGUMENT EVIDENCE -- when the only residual is a value in an
argument register and there is no argument setup at the jal, try passing it as that argument.

165: finding 147 confirmed a second time -- 0x8007D5FC is on the epilogue list but cc1 fills its
own slot, so the token is a no-op and the row is claimed with '-'. The list was selected on the
ORIGINAL's tail; the token must be decided from the CANDIDATE's.
2026-09-24 11:12:17 -04:00
Christopher Williams 998325c72e phase11: merge 51 — worker E's 0x8007D5FC -> 588 bodies / 597 regions 2026-09-24 11:10:32 -04:00
Christopher Williams 22cfdc874d phase11: merge 50 — worker E's 0x80107DE8 -> 587 bodies / 596 regions 2026-09-24 11:06:56 -04:00
Christopher Williams a74bc32306 phase11: merge 49 + cookbook 162 — 586 bodies / 595 regions
Worker A's final row 0x8010A6C4 (132 B, first attempt, maspsx=epilogue) -- its ninth epilogue
row and its 46th claim.

162: a callee called with DIFFERENT argument counts needs a NON-PROTOTYPE declaration --
func_8010A444(1) / (2, x) / (3, s1, s0) is only expressible as 'void func_8010A444();', the C89
empty-parameter form, not '(void)'. Same constraint that cost worker A a compile on 0x8002DD14.

Worker A's final totals: 46 claims (33 first-attempt), 95 evidence rows, 39 levers, 3 deferred
rows with derivations, 1 blocked row, 9 rows carrying maspsx=epilogue.
2026-09-24 11:05:36 -04:00
Christopher Williams bec136559a phase11: merge 48 + gtemac lwc2/swc2 + cookbook 159-161 — 585 bodies / 594 regions
Worker D's 0x800F3E18 (88 B) -- THE FIRST GTE/COP2 ROW MATCHED IN THIS PROJECT.

159: lwc2/swc2 move a word straight between MEMORY and COP2, unlike mtc2/mfc2 which move
between a GPR and COP2. The row uses lwc2 $9/$10/$11 and swc2 $25/$26/$27, so a row can use
the IR/MAC registers WITHOUT the IR/MAC macros. Added gte_lwc2IR1/2/3 and gte_swc2MAC1/2/3.

160: register variables PIN the COP2 operand registers -- worker D's entire residual was that
cc1 chose its own cfc2/mfc2 destinations. The GTE analogue of the named-locals family: an
inline-asm row's residual is usually the operand REGISTERS, not the sequence.

161 IS A CORRECTION TO THE COORDINATOR'S OWN ADVICE. I broadcast the command-field values with
their occurrence counts as if they were a lookup table. They are a DISTRIBUTION, not a per-row
answer: worker D wrote 0x178000c (counted 51x) into the row and it came out ONE BYTE wrong; the
correct field is 0x170000c. The low bits carry the shift/matrix/vector selectors, so two commands
differing only there are different instructions. Read the field off the ORIGINAL WORD.
2026-09-24 11:03:04 -04:00