494 bodies / 503 regions. Three bodies above the old 244 B ceiling from two workers
(248/248/264 B). Cookbook 59 extended with the unreferenced-array-local mechanism
(cc1 gives unreferenced scalars no home but does allocate for unreferenced arrays),
the amended three-direction rule, the per-band cost table, the sf3_merge fail-fast
fixes, and the rank-instability broadcast.
Worker D's 0x800308C4 (264 B) MATCHES, verified independently and gated on the whole
binary before promotion. Three bodies now sit above the old 244 B ceiling
(248/248/264 B) from two independent workers, so the dispatch-artefact verdict is
settled by result.
COOKBOOK 59 EXTENDED WITH A NEW MECHANISM. D's first attempt was 8 words from a match
and all eight were the FRAME; 56 of 64 words were already byte-identical including
every call site, delay slot and register. The missing 8 bytes are an UNREFERENCED
ARRAY LOCAL, and the mechanism is measured across five spellings: cc1 allocates stack
space for an unreferenced ARRAY local but NOT for an unreferenced SCALAR local
(`int pad0, pad1;` gives no home; `int pad[2]` and `short pad[4]` both match). The 8
bytes are size-load-bearing and content-free -- a limit of the reconstruction, not a
recovered fact, recorded as such in the file header.
AMENDED RULE: when the residual IS the frame, vary the local aggregate's declaration
in THREE directions -- element type, row stride, and total size. Instance 1
(0x8009F6A0) needed the row stride; instance 2 (0x800308C4) needed the total size.
+8 bodies: worker A claims 1-4 (0x8005E820, 0x80012DE8, 0x8001644C, 0x800A6880)
and worker C claims 1-4 (0x80048180, 0x800B107C, 0x80082868, 0x80036134).
Candidate gate MATCH before promotion; all 8 md5s matched on disk.
FOUR gp symbol rows added for 0x80017C6C (D_80121A2C/34/3C/44), coordinator-verified
against the payload: the original materialises them with addiu $2,gp,244/252/260/268.
C's claim 4 (0x80036134, 248 B) is the SECOND body above the old ceiling, matched on
lever-c-large row 1 — so two independent workers have now matched above 244 B, and the
dispatch-artefact verdict is confirmed by result rather than by inference.
sf3_merge format fixes, both triggered by real worker files:
- a bare header row is now rejected with "looks like a column HEADER" instead of a
confusing "not a hex address: 'start'"
- a lone `-` in the override column means "no overrides", matching the absent-value
convention the other tracked tables use
Suite 246 tests OK; make check green: regions=502 AGREE, differing_bytes=0 MATCH.
Finding 58 AMENDS finding 41, which was Phase 10's headline result and drove every
dispatch decision. The "1-in-12 for 200-800 B" comparison was taken on a queue that
had never been attempted (5 of 427 rows above 244 B ever tried, 1.2%; three in an
excluded class; both non-excluded attempts near-matched). The first row attempted
above the ceiling matched. The band's measured yield is a function of the lever set at
the time of measurement, and the lever set grows -- RE-MEASURE a band before
concluding it is exhausted, and never treat a band as closed at ~1% attempt coverage.
Findings 59-62: the local-aggregate row-stride/element-size lever that broke the
ceiling (int t[3][4] not int t[9]; frame 48 vs 40; residual concentrated on the frame
adjustment); the division_check trapped class; the localisation of the maspsx/GNU-as
mutual exclusion with the developer's ASPSX-as-oracle-only rule; and the fail-fast
validation of region override keys in sf3_merge.
Worker D's claim 0x8009F6A0..0x8009F798 (248 B) MATCHES. Verified independently by the
coordinator on a fresh work dir (candidate_bytes=248 differing_bytes=0 MATCH) and
gated on the whole binary before promotion. This is the FIRST body above 244 B ever
matched, and it sets a new corpus maximum (previous max 244 B at 0x80099078).
THE LEVER (worker D, 4 spellings): the local working buffer must be a 3x4 word array
(`int t[3][4]`, only columns 0..2 used), NOT `int t[9]`. The 4-WORD ROW STRIDE IS
BYTE-LOAD-BEARING: it moves the 2nd and 3rd triples to 0x10 and 0x20, makes the frame
48 B instead of 40 B, and leaves the unused 0x0C/0x1C slots the original shows. New
instance of cookbook 54 (a 2-D array's row stride is byte-load-bearing). The element
type is the other half: `short` locals let cc1 drop the sign extension (lhu/subu, no
frame); `int` locals keep it (lh/negu).
Diagnostic broadcast: correct length + right instruction multiset and order + residual
concentrated on the FRAME ADJUSTMENT and every sp-relative offset => suspect a local
aggregate's row stride / element size, not the control flow. D's variant (c) was a
textbook case: 19 differing bytes, all of them the frame size and the address shift
that follows from it, closed by one array-shape change.
Also in this commit — a fail-fast fix to sf3_merge. Worker D placed the source md5 in
the claim row's 4th column, which sf3_merge passed through as a region override, so the
row MERGED and only `sf3_match gate` failed later with "unknown override key 'md5'".
sf3_merge now validates override keys at merge time and rejects the row with a message
naming the valid keys and pointing at report.tsv for per-claim metadata. 4 new tests,
suite 242 -> 246, OK. The candidate gate caught it; the tracked registry was untouched.
Records: the "244-byte ceiling" is a DISPATCH ARTEFACT not a measured wall (5 of 427
rows ever attempted, 1.2%; three in an excluded class; both non-excluded attempts
near-matched; 84% unclassified ordinary code), so worker C is re-assigned above the
ceiling onto a 619-row dispatch file with 258 known-callee rows.
The new division_check exclusion class (break and div always co-occur; 0 of 493
registered regions contains either; worklist 1193 -> 1118).
Worker B's localisation of the maspsx/GNU-as mutual exclusion (ASPSX does both the
move->addu conversion and the fill; maspsx the first only, as the second only, and they
cannot be combined) with a 42-row/14% census in its partition, and the developer's
authorization of a tracked post-pass modelled on ASPSX's behaviour via oracle
characterisation -- ASPSX itself is a diagnostic oracle only, never a build stage,
because it is proprietary and git-ignored and a build depending on it could not be
reproduced.
Worker D's Goal B census found a second trapping-class-style blocker: `break` NEVER
appears without `div` and `div` NEVER appears without `break` -- 75 worklist rows,
0 exceptions. The shape is GCC's divmodsi4 with MASK_CHECK_ZERO_DIV|MASK_CHECK_RANGE_DIV
(div / bnez+break 7 zero check / li at,-1 + bne + lui at,0x8000 + bne quotient,at +
break 6 range check / mflo). `break` cannot be produced from C.
COORDINATOR-VERIFIED before implementing, as the project requires:
0 of 493 registered regions contains a div, a rem or a break (disjoint from corpus)
75 worklist rows have div+break; 0 div-only; 0 break-only (perfect correlation)
69 of the 75 are >244 B
Compiler matrix (worker D): every available cc1 either emits a BARE div with no check
(open 2.5.7-2.91.66-psx, and Sony CC1PSX 4.0-4.5, which reject -mcheck-zero-division),
or emits a check with a DIFFERENT shape (open 2.95.2-psx and CC1PSX 4.6: mflo before
the check, scratch $3/$4, comparing the DIVISOR against 0x80000000; the original
checks before mflo, uses $at, and compares the QUOTIENT). Not reproducible with this
toolchain -- the route is a cc1 build with those masks in TARGET_DEFAULT, which is a
developer-owned toolchain decision, not a source lever.
Implemented as `division_check` with a counted reason; excluded_division_check=81;
worklist 1193 -> 1118. 5 new tests, suite 237 -> 242, OK. make check green:
regions=493 AGREE, differing_bytes=0 MATCH.
Written at the developer's request after the control model changed from "the developer
spawns sessions" to "the orchestrator spawns, monitors and retires its own workers",
with pi-context-tools installed globally.
Covers: authority and what the orchestrator decides versus escalates; the herdr control
model with exact commands, the verified 2x2 topology, and the known failure modes
(including the transient agent_pane_not_found on a just-split pane, and the
mutual-ask intercom deadlock); the context model and the compaction-first policy with
the requirement it creates (state must live in files); roster sizing and the Goal A /
Goal B assignment split; the session lifecycle including the updated capability probe
with context_info; the hardened merge flow with the md5 drift guard; the verification
ladder; the full charter template; all nineteen process rules WITH THEIR PROVENANCE
(each earned by a real incident); and the phase open and close checklists.
Supersedes the session-management sections of PHASE8_PROTOCOL.md and PHASE9_PROTOCOL.md;
the merge/verification contract in those documents still stands.
Baseline revalidated at the Phase 10 close state: tracked maspsx patch applied,
make check exit 0, extents regions=493 AGREEE, gate differing_bytes=0 MATCH, 237
tests OK, 484 bodies / 493 regions, worklist 1193 with excluded_already_registered=493.
Roster spawned by the orchestrator via herdr (tab w1:t4, 2x2 at ~115x31); all four
probed clean and all four confirmed context_info + compact_context, which is the
basis for the compaction-first policy. Worker D is assigned GOAL B (the 244-byte
ceiling) with a bounded four-step investigation and a running GOALB.md deliverable;
A, B and C are on Goal A with 12-claim cycle targets.
Partitions: 4-way rank-interleaved, 299/298/298/298, disjoint, union == worklist,
near-identical tier/size mixes. Lever files rank size-band-first (cookbook 41):
P2 (<=200B, no lever) at 359 rows is the main target across the four partitions.
Charters carry the Phase 10 process rules as hard requirements: md5 per claim,
verify the staged path, cleanup audit before reporting, read ranges from the worklist
row, one attempt on a named lever then classify, fold region options into the claim row.
Plan approved by the developer with all four recommended decisions: milestone 600
(stretch 700), Goal B (the bounded 244-byte-ceiling investigation) in scope, a
4-worker roster spawned by the orchestrator with discretion to scale or retire, and
a compaction-first context policy.
Roster spawned by the orchestrator via herdr in a verified 2x2 grid (tab w1:t4,
~115x31 per pane):
w1:p5 worker-a 01a0d36a-be1f
w1:p6 worker-b 01a0d36b-1dc6
w1:p7 worker-c 01a0d36b-f346
w1:p8 worker-d 01a0d36c-02f4
All four probed; worker A confirmed context_info works (1.4% at spawn) and that
compact_context is available, which is the mechanical basis for the compaction-first
policy.
phase-ends/CURRENT_PHASE.md rewritten for Phase 11 with the planning finding (the
matched corpus median is 52 B, p90 108 B, max 244 B, and nothing larger has ever
matched), the roster table with herdr pane and intercom ids, the context policy, and
the carried machinery.
Prepared immediately after Phase 10 closure (484 bodies / 493 regions).
THE PLANNING FINDING THAT DEFINES THE PHASE: the matched corpus is n=493 with a
median of 52 bytes, p90 108 bytes and MAX 244 bytes -- NOT ONE BODY LARGER THAN 244
BYTES HAS EVER MATCHED. Against a remaining worklist of 1193 rows:
<=120 B 115 rows proven-matchable band
121-200 B 284 rows proven-matchable band
201-400 B 367 rows partially proven (up to 244 B)
401-800 B 245 rows UNPROVEN -- nothing has ever matched here
>800 B 182 rows UNPROVEN
So the ~400-450 rows at <=244 B are the finite proven band, and +116 bodies means
matching a quarter to a third of it. The 244-byte ceiling is therefore the real
subject, and the plan states TWO goals: A consume the proven band (the milestone
path), B break the 244-byte ceiling (a bounded investigation with a measured
deliverable, where a result that adds zero bodies is still a met goal).
Structural change: the orchestrator spawns and retires its own workers via herdr
(4 agent panes per tab, 2x2 verified at 115x31 each); the developer spawns only the
orchestrator. Context management changes too: pi-context-tools is installed
globally, so every session has context_info and compact_context -- measurement is
exact and self-service, and COMPACTION replaces rotation as the first response to a
full context, with rotation second. That is only safe because the state lives in
files, so keeping the ledger current becomes a hard requirement.
Four decisions requested: the milestone number; whether Goal B is in scope this
phase; the 4-worker default with orchestrator discretion to add a fifth; and
confirmation of the compaction-first policy.
MILESTONE MET AND EXCEEDED: 484 distinct matched bodies / 493 registered regions
(target 475, from the 400 baseline) — +84 bodies. Developer confirmation of the
milestone was requested and given before any close record was written.
Closing checklist all green from clean:
make clean && make all exit 0
cmp exit 0
SHA-1 both files e173426c157384ebf1b6caf8c6fea18a85a14af9
make test 237 tests, OK
make extents-verify regions=493 disagreements=0 AGREE
make gate c_regions=493 differing_bytes=0 MATCH
registry audit 493 rows, 0 overlaps, 0 unsorted, 0 bad extents,
0 missing sources, 484 distinct sources
worklist listed=1193, excluded_already_registered=493
negatives index 194 rows, address-ordered, 0 registered
git status --short src/ empty (0 untracked files)
firewall 0 prohibited-root paths (591 tracked files)
New records:
phase-ends/PhaseEnd_Phase10.md the phase record
docs/PHASE10_VERIFICATION.md the verification record
docs/MATCHING_COOKBOOK.md findings 41-57 (57 total)
phase-ends/CURRENT_PHASE.md CLOSED, with the checklist itemised
phase-ends/DIGEST.md the Phase 10 digest entry
The headline finding is methodological (finding 41, THE SIZE-BAND LAW): the matched
corpus median is 48 bytes with 454/459 at <=200 B while the remaining levered rows
had a median of 456 B, and two independent measurements — one controlled — put the
small band at 1-2 attempts per row against 1-in-12 for 200-800 B.
The phase's character: five of the coordinator's own generalisations were bounded by
workers (rare-epilogue class, register-field diagnostic, polarity lever, goto
trigger, load-delay consumer form). The rules that survived are the ones that were
bounded.
Five incidents recorded rather than smoothed over; the candidate gate rejected three
batches and the tracked registry was never corrupted. Scope held: the blocked classes
stay excluded, no scheduler-changing flag was granted, and inline asm was extended
only to shapes C provably cannot express.
STOPPING HERE. Phase 11 does not begin in this session.
+2 bodies (worker C2 claims 3-4). Candidate gate MATCH before promotion.
NEGATIVES EXTRACTION (the closing-checklist step that protects worker findings
from ignored staging being lost): imported 28 new negatives from all five workers'
staging into the tracked index, and dropped 28 rows that had since been REGISTERED
(the reconcile step working as designed). Index 166 -> 194 rows, address-ordered,
0 duplicates, 0 registered. Worklist 1193 rows; excluded_recorded_negative=170;
0 unregistered negatives survive into the worklist.
make check green: regions=493 AGREE, differing_bytes=0 MATCH, 237 tests OK.
Worker C2's cross-cutting finding recorded: three of its four negatives are the -O2
SCHEDULER, not source shape. For 0x800A6C34 (16 differing) and 0x80016F80 (45
differing), both at correct length, the identical source with
`cc1 -quiet -O2 -G0 -fno-schedule-insns` produces the original's instruction ORDER
byte-for-byte -- and both then show the same two-sided signature: sched OFF gives the
original's order but the wrong allocation (or the wrong delay slot), sched ON gives
the right allocation and the wrong order. Neither alone matches. C2 correctly did NOT
take the scheduler override (out of scope); both stay recorded as negatives with the
lever named. Diagnostic broadcast: correct length + a residual that is a permutation
of a few instructions + the unscheduled build matching the original order => the
residual is sched.
Also recorded: the commutative-operand lever has a COUPLED ALLOCATION side-effect
(0x80027744 at 4 bytes, 0x80050674 at 1 byte) -- every spelling giving the original's
addu operand order also flips which value takes v0 vs v1.
+2 bodies (worker C2 claims 1-2: 0x80051864 88 B, 0x8006AA10 120 B). Candidate
gate MATCH before promotion. make check green: regions=491 AGREE,
differing_bytes=0 MATCH, 237 tests OK.
C2 also confirmed the paired named-boolean rule exactly as broadcast (one shared
li a3,1 across four acceptance paths; nested ifs give two blocks and 124 B
LENGTH-MISMATCH; the single combined condition is byte-identical), and caught the
mask 0x00400000 vs 0x40000000 by computing the lui high half.
CROSS-CUTTING FINDING (worker C2, recorded for the cookbook): THREE of C2's four
negatives are the -O2 SCHEDULER, not source shape. Diagnostic: when the length is
exactly right and the residual is a permutation of a few instructions whose
-fno-schedule-insns build matches the original, stop hunting for a source shape.
Trap: on 0x80016F80 sched OFF gives the original's ORDER but the wrong allocation
while sched ON gives the right allocation and the wrong order, so neither alone
matches. C2 correctly did NOT take the scheduler override; all three stay recorded
as negatives with the lever named.
Also: the commutative-operand lever has a COUPLED ALLOCATION side-effect
(0x80027744, 0x80050674) -- every spelling giving the original's addu operand order
also flips which value takes v0 vs v1, because operand order changes pseudo creation
order. Finding 22's re-spell advice is necessary but not sufficient.
cc1 emits an explicit `#nop` marker when it wants a load-delay nop. maspsx used to
RE-DERIVE the need and could overrule cc1 for a BARE-SYMBOL STORE consumer:
uses_at('sw\t$2,D_801221C4') -> True (macro store, expands via $at)
uses_at('sw\t$2,0($4)') -> False (register+offset, no macro)
nop_at_expansion is False for ASPSX >= 2.30
so neither test in _handle_nop_before_next_instruction fired, nop_required stayed
False, and an instruction cc1 had explicitly asked for was dropped (0x80107C5C at
108 vs 112; worker B's 0x8003A9C8). The fix honours the marker instead of
overruling it; only that path changes.
Worker B2 found the gap but mis-diagnosed it: its proposed fix was to extend the
`line_loads_from_reg` predicate, which ALREADY returns True for a store source, so
that patch would have been a no-op. The coordinator traced the actual call and found
the real mechanism in the uses_at / nop_at_expansion interaction. B2 then appended a
CORRECTION row to its own staged report superseding its paragraph — the right
response, and it records the general lesson: a named mechanism is a hypothesis until
it is traced, even when the observation is solid and reproducible.
REGRESSION VERIFICATION (the whole point of gating this):
make check exit 0
regions=489 disagreements=0 AGREE
c_regions=489 differing_bytes=0 MATCH
237 tests OK
All 489 previously-matched regions are byte-identical with the fix in place.
Carried as a tracked patch (tools/maspsx/ is git-ignored, so an in-place edit would
not survive a fresh clone); patch verified to reproduce both modified files exactly
from the pristine pinned checkout. docs/SETUP.md records the fix and its provenance.
0x80107C5C NOW MATCHES (112 B, 0 differing, verified against worker B2's variant
X3.c) — but that is a BARE variant with no header, and the project convention requires
a documented source stating the observed instructions and limits. So the row is
UNBLOCKED and one documented source away, not claimed. Recorded as a carry-forward.
+6 bodies (worker A claims 23-26, worker B2 claims 8-9). Candidate gate MATCH
before every promotion.
FULL CLEAN AUDIT GREEN at the milestone:
make clean && make all exit 0
cmp exit 0
SHA-1 both files e173426c157384ebf1b6caf8c6fea18a85a14af9
registry 489 rows, 0 overlap, 0 unsorted, 0 bad extents,
0 missing sources, 480 distinct sources
firewall 0 tracked paths under any prohibited root (583 files)
suite 237 tests OK
The phase goal (475 from the 400 baseline) is met with 5 bodies to spare.
Per the plan, phase close requires the developer's explicit confirmation of the
milestone; this commit records the state, not the close.
SIZE-BAND FINDING now confirmed a third time, within-worker: worker B2's nine
matches cost 1, 4, 6, 4, 1, 2, 2, 1, 1 attempts -- the three <=120 B frameless leaves
all cost exactly ONE attempt, while the two >200 B P1 rows it opened with cost 6 and
4 attempts and produced ZERO matches.
Two more levers recorded from the closing rows:
- RECORD IDENTITY FROM WIDTHS (0x80041610): the two arms read three shorts at
+264/+266/+268 versus three ints at +20/+24/+28 through one extra indirection,
so they are TWO record types; declaring one shared type would have been wrong.
- a1[1] = -a1[1] RELOADS FROM MEMORY and its source order matters: the negation
reloads 4(a1) because the intervening store to 8(a1) may alias it.
- A 2-D ARRAY'S ROW STRIDE IS BYTE-LOAD-BEARING (0x800AC7A0): D[a3][a4] emits
sll a3,4 + sll a4,2 + add (correct length); D[a3*4 + a4] folds the outer *4 into
a second sll and comes out 4 bytes SHORT. When a scaled index is one sll short,
the source is a 2-D array, not a flattened index -- a LENGTH-class lever.
+3 bodies (worker B2 claims 5-7). Candidate gate MATCH before promotion; md5 drift
check clean on all three (second use of the guard). make check green: regions=483
AGREE, differing_bytes=0 MATCH.
Region option granted: gp=-D_80121BFC on 0x80048128. This one carries PER-ACCESS
evidence in a single merge: D_80121BFC is read gp-RELATIVELY in worker B2's claim 7
row (lw v1,708(gp)) and ABSOLUTELY in claim 5's row (lui v1,0x8012 + lw v1,7164(v1)).
So the same symbol needs the override in one region and not in the other -- direct
confirmation of worker B's lever 12 that the access form is per-SITE, and an argument
that the override is a region property rather than a symbol property.
THREE NEW LEVERS (worker B2), each with a control:
1. A LOCAL SHARED BY TWO GUARD BLOCKS GETS COALESCED; TWO BRACE-SCOPED LOCALS DO NOT.
0x8008BA80: the original's first guard loads the state byte into a0 (the
parameter's own dead register) while the second guard loads *a2 into a FRESH v1.
One function-scope local makes cc1 coalesce the live ranges into a0 and DIFFs;
brace-scoping each reproduces it. A SCOPING lever, distinct from the
named-locals family.
2. THE EVALUATION ORDER OF TWO SCALED TERMS IS BYTE-REQUIRED. 0x800504E4:
base + a0*384 + a1*3072 scales a0 first; base + a1*3072 + a0*384 scales a1 first,
which is the original. Worker B's claim-1 lever extended from the operands of one
'+' to the ORDER OF TWO INDEX COMPUTATIONS.
3. AN UNSIGNED LOOP COUNTER SHOWS AS sltiu vs slti -- ONE BYTE (opcode 0x0b vs 0x0a).
0x800504E4's i < 16 is sltiu, so the counter is unsigned int. The loop-test form of
the signedness trap.
Also independently reproduced: worker C's lever 6, (unsigned)(c - 58) < 2 giving ONE
addiu+sltiu pair where c >= 58 && c <= 59 gives two tests (on 0x80048128).
+3 bodies (worker B2 claims 2-4; claim 1 was already merged). Candidate gate MATCH
before promotion. make check green: regions=480 AGREE, differing_bytes=0 MATCH.
FIRST MERGE USING THE MD5 DRIFT GUARD (adopted after the 0x800A9C24 collision).
Worker B2's claim rows carry the source md5 and all three matched on disk at merge
time, so no re-verification was needed. B2 folded its gp=-D_80121B88 region option
into the claim row itself, which is the standing rule.
Region option granted: gp=-D_80121B88 on 0x800A6998. Byte-required and verified both
ways (with the override 128 B / 0 differing; without it 120 B LENGTH-MISMATCH).
Third instance of the per-site gp form. Worth recording that on THIS row BOTH the
symbol spelling and the literal spelling are wrong for two DIFFERENT reasons: the
symbol gives the gp-relative encoding the original does not use, and the literal
makes cc1 cache the address in s0 so the frame grows 24 -> 32 bytes with an extra
saved register.
TWO NEW LEVERS (worker B2):
1. A SHARED CALL PAIR HAS TO BE A NAMED goto LABEL. 0x800A6998 is an infinite loop
whose two func_800A6FAC(0) calls appear EXACTLY ONCE in the original, reached
both by fall-through and by the w != 0 branch. Only a source naming that block
reproduces it; while/for spellings either invert the loop (156 B) or make cc1
CROSS-JUMP the two ==0 tests so the x-load goes dead (124 B). Same goto
statement-form family as the 0x800A9C24 guard, but the lever here is SHARING ONE
BLOCK rather than branch polarity.
2. && KEEPS A BRANCH THAT EARLY RETURNS FOLD AWAY -- the exact INVERSE of the
named-boolean lever broadcast earlier. Three separate if (...) return 0;
statements make cc1 fold the third guard into a branchless boolean
(xor/sltiu, 124 B); ONE && chain with a single trailing return 0 keeps the branch
(byte-identical). A goto fail; ... fail: return 0; spelling is also identical, so
the distinguishing fact is "one combined condition with one trailing return".
PAIRED RULE: when the original has a BRANCH on a boolean expression, combine the
conditions into one && chain; when it has the BRANCHLESS form, name the boolean.
Also recorded: 0x800B67B8 is the CALLER of the registered func_80082750, and the
callee's source gave the D_80121C00 spelling directly (it holds a base POINTER, not
the table address), so the argument map transferred with zero guessing -- the
family lever working as advertised.
Records the 0x800A9C24 collision (coordinator double-assignment: the row was
redistributed to B2 and then chartered to C2), B2's diagnosis that "verify the file
at the path you claim" is necessary but not sufficient because the gap is between
verify and merge, the adopted md5-in-claim-row guard, and worker A's 4 first-attempt
bodies including the second byte-exact stack-switch row that isolates the variable
to an argument move at the call site.
+4 bodies (worker A claims 19-22, all first-attempt). Candidate gate MATCH before
promotion. make check green: regions=477 AGREE, differing_bytes=0 MATCH.
THE STACK-SWITCH IDIOM IS NOW PROVEN TWICE, BYTE-EXACT, WITH NO OVERRIDE.
0x8006B9E0 matches on the first attempt with no maspsx override, no clobbers and no
operand declarations -- the three-macro form reproduces it exactly, including the
bare filler nop between the second restore and the epilogue's lw ra. Contrasting it
with A's 0x800BC658 near-match (same macros, off by 2 nops) isolates the variable:
0x8006B9E0's two calls have EMPTY delay slots while 0x800BC658's non-fast-path calls
set up an argument cc1 schedules into the slot. So the asm is not the variable -- an
ARGUMENT MOVE AT THE CALL SITE is. That narrows the 0x800BC658 remainder.
TWO NAMED-LOCAL FINDINGS, BOTH DIRECTIONS NOW OBSERVED (the family has seven
instances across the team):
- 0x8005E538: the HANDLE must be a named local loaded before the first call.
Inline, cc1 keeps only the object pointer in a callee-saved register and
RELOADS the handle after the first call (76 vs 88 bytes). Generalisation: a value
that must survive an intervening call has to be a named local.
- 0x800F66B8 is the MIRROR CASE and a useful NEGATIVE: d[0] and d[1] are each
loaded twice with neither held in a register, and the two loads of d[0] go to
DIFFERENT registers. Naming a local would have been WRONG. So naming a value can
be byte-required and NOT naming it can be byte-required; the diagnostic is the
load count, not a rule about locals.
Inline asm in src/func_8006B9E0.c is documented per the Phase 10 convention
(header states the observed sequence, the reason and the limits).
Records: worker A's 8 first-attempt bodies and two general levers (two-arm polarity
byte-required; named boolean local forces the branchless compare); worker B2's
limitation of the coordinator's polarity broadcast (both ordinary spellings give the
mirrored branch, so the guard must be a goto); the staging slip the candidate gate
caught (verified finding, wrong file staged) and the standing rule it produces
(verify the file you stage, not a scratch variant); and the dead-store-elimination
class from 0x80016F80.
Third coordinator over-generalisation a worker has caught this phase.
+8 bodies (worker A claims 11-18, all first-attempt; 16 of A's 18 claims needed
no override at all). Candidate gate MATCH before promotion.
TWO NEW LEVERS (worker A):
1. A two-arm selection's POLARITY is byte-required. For a1 = (x<2) ? a3 : saved,
the natural order and the ternary both emit beq v0,zero with the arms swapped
(right length, 5 differing bytes). Writing the larger-than arm first
(if (x >= 2) a1 = saved; else a1 = a3;) makes cc1 emit bne v0,zero with
a1 = a3 in the BRANCH DELAY SLOT, so that assignment runs on both paths and is
overwritten on the >=2 path -- which is the original exactly. Same family as the
mirrored comparison load order; second polarity case in A's partition.
2. A NAMED BOOLEAN LOCAL forces the branchless compare. rec[6] = ((a3 & 0xff) != 0) << 1
is branchy (92 vs 88); only naming the boolean first,
int flag = (a3 & 0xff) != 0; then rec[6] = flag << 1;
reproduces the original's andi / sltu / sll. Six spellings measured. cc1 will
un-do a boolean you inline when the VALUE (not the branch) is what you need.
make check green: regions=473 AGREE, differing_bytes=0 MATCH, 237 tests OK.
Worklist 1241 rows; excluded_already_registered=473.
Worker C's precision note: the draft uses a LITERAL address (deliberately, to avoid
the name-keyed gp trap), and the harness's gp rewrite only acts on SYMBOL accesses.
So --no-gp D_80121B88 had nothing to rewrite and identical output was the EXPECTED
result, not evidence about the encoding. The conclusion is unchanged (the encoding
is already correct, so the hoist is the whole residual), but the recorded REASON was
wrong and would have misled a future worker into thinking the name-keyed gp trap is
irrelevant at 0x80121B88 when it is in fact why the draft looks the way it does.
Corrected: the entry now says the override is inert by construction here, and keeps
the untried lever (a source order that stops the hoist).
+1 body (worker B2 claim 1, 0x8008A758, first attempt via the registered callee
func_80073F78). Candidate gate MATCH before promotion.
Worker C's cleanup-audit catch recorded: 0x800A6C34 (88B) is an OPEN near-match at
the correct length (16 differing bytes at 0x800A6C54) that fell out of C's staging
after a DIFF in its first batch. Added to the tracked negatives index. Coordinator
verified the residual exactly as C reported AND tested C's named lever (a) — the
gp=-D_80121B88 override does NOT close it (identical 16 bytes) — so the index
records the untried lever (a source order that stops the hoist) rather than the
disproven one. C's process note is recorded: run the cleanup audit BEFORE the final
report, not after.
make check green: regions=465 AGREE, differing_bytes=0 MATCH, 237 tests OK.
Worklist 1249 rows; index 166 rows, ordered, 0 registered.
+2 bodies (worker C claims 21-22; 19-20 were in the previous batch).
SIZE-BAND MEASUREMENT CONFIRMED (worker C, strongest evidence yet for the
size-first re-rank): after switching to the <=200B band, four consecutive rows cost
1, 1, 1 and 2 attempts, against 1-in-12 on the 200B-800B band. Same worker, same
levers, same day -- the only variable is the size band. The <=200B P2 band is where
the remaining bodies are.
make check green: regions=464 AGREE, differing_bytes=0 MATCH, 237 tests OK.
+2 bodies (worker C claims 19-20) and 3 gp symbol rows (D_80121F90,
D_8012196E, D_80121964). Candidate gate MATCH before promotion.
NEW EXCLUSION CLASS `restores_unsaved` (worker A, coordinator-verified). A body
that restores a callee-saved register it never saves cannot be a whole function:
the register it restores was established by an enclosing prologue that the derived
extent cut off. These are jal targets INSIDE a real function, so the walk began
mid-body — distinct from bad_extent_start, which flags starts that are not
function entries at all.
Verified disjoint from the matched corpus before acting, as the project requires:
0 of 462 registered regions trip the rule; 11 worklist rows do.
excluded_restores_unsaved=11; worklist 1265 -> 1253 rows. 5 new tests; suite
229 -> 237, OK. make check green: regions=462 AGREE, differing_bytes=0 MATCH.
Independent corroboration worth recording: the new rule re-derives 0x8010080C,
the false extent start worker C reported earlier via a completely different signal
(the first instruction reads a register the range never defines). Two independent
detections of one defect class. bad_extent_start is False for that row, confirming
C's observation that the older rule missed it. Its Makefile --exclude entry is
retained only as the provenance record for that defect.
+1 body (0x800C1424, worker C claim 18, closed with two register-allocation
levers: guard-on-the-expression so CSE keeps one load whose destination is the
guard's operand, and a counter initialisation as a statement so the counter
takes a2).
FORECAST FINDING (the important part of this commit): measured the matched
corpus against the remaining pool and re-ranked the lever files.
matched: 459 regions, median 48 bytes, 454 of 459 at <=200 bytes
remaining levered rows: median 456 bytes
Size is therefore the strongest predictor left, so .run/p10/lever*.tsv now ranks
size band FIRST and lever second:
P1 = <=200B and levered (47 rows across partitions)
P2 = <=200B, no lever (412) <- the unexploited band that actually matches
P3 = levered but >200B (503)
P4 = rest (303)
Worker C had proposed continuing on fresh P1 rows (old meaning: known-callee),
which under the new ranking are mostly P3 -- the 200-800B band where the
allocator/optimiser tie-breaks live. Redirected to P2 from the top.
Worker C's measured re-flag accepted (last 12 attempts produced 1 match, 8 of 9
sub-8-byte negatives being cc1 scheduling/allocation with no spelling lever) and
answered with a band change rather than a stop, since C is at 47% context.
Developer decision (2026-09-24), extending Phase 8's coprocessor/kernel allowance.
Trigger conditions recorded: plain C attempted and mechanism identified; the
inexpressibility is structural rather than a scheduling/allocation tie-break;
integer logic stays in C and scheduling still comes from cc1/maspsx; per-file
header documents the sequence, the reason and the limit.
First accepted instance: the PSX scratchpad stack switch (lui at,0x1f80 /
sw sp,1020(at) / lui sp,0x1f80 / ori sp,sp,0x3dc ... / lw sp,1020(sp)), found
independently by workers A (0x800BC658) and B (0x800BC6EC). A stack-pointer READ
was already covered by the register-name binding and needs no exemption; only the
switch does.
Explicitly does NOT reopen trapping-arithmetic, rare-epilogue or primitive-init.
+3 bodies from 447. Candidate gate MATCH before promotion.
Negatives reconciliation (the protocol step that protects worker findings from
being lost with ignored staging): imported 26 new negatives from all three
workers' staging into the tracked index. Index 140 -> 166 rows, address-ordered,
0 duplicates, 0 registered. excluded_recorded_negative 116 -> 142; worklist
1268 rows; 0 unregistered negatives survive into the worklist.
make check green: regions=459 disagreements=0 AGREE, c_regions=459
differing_bytes=0 MATCH, SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9.
+7 bodies from 440. Candidate gate MATCH before promotion on every batch.
SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9 stable; make check green
(regions=456 disagreements=0 AGREE, c_regions=456 differing_bytes=0 MATCH).
Note: my claims.with-options.tsv for worker B went stale (it predated claims
17-20) and the first attempt silently added 0 regions; regenerated from the
current claims.tsv and re-gated. Standing rule: regenerate the folded-options
claims file from the live claims.tsv immediately before every merge.
Worklist 1294 rows, excluded_already_registered=456 (= registry size).
Partitions refiltered: a=439 b=428 c=427, disjoint, union==worklist.
+8 bodies from 432 (440 total, from 400). All gated on the candidate whole-binary
before promotion. SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9 stable.
Registry option granted: gp=-D_801226F4 on 0x8002D364 (worker B). The first
candidate gate for this batch DIFFed (differing_bytes=1117197) because worker B
staged the option in a separate request file rather than in the claim row, so the
region merged without it; the tracked registry was never touched and the gate
caught it. Folded the option into the claim and re-gated to MATCH. Recorded as a
protocol event: a worker's region-option request must be folded into the claim
row before merging.
Evidence model: 0x8010080C is a FALSE EXTENT START (worker C, coordinator-verified
against the payload). Its first instruction is beq s0,zero and s0 is never written
in the range, so it is mid-body. The walk split one real 252-byte function
(0x801007E0..0x801008DC) at a spurious boundary because 0x8010080C is a jal target
from 0x8011419C. The row was unclaimable and is now a named exclusion
(excluded_named_exclusion 8 -> 9) with the reason in the Makefile and the
extents-defect record in the ledger. Fixing sf3_extents itself is left as a
bounded candidate, not attempted here.
Worklist 1304 rows, excluded_already_registered=446 (= registry size).
Partitions refiltered against it: a=441 b=432 c=431, disjoint, union==worklist.
32 new bodies from 400, all verified on the candidate whole-binary gate before
promotion. SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9 stable.
Registry requests granted (each byte-verified with a failing control):
cc1=-G8 on 0x800A6BEC; gp=-D_80121B88 on 0x80015D50
symbols D_80122700, D_80122704, D_80121AD4 (gp)
Harness: per-region maspsx modes wired through sf3_match (maspsx=noreordernop,
maspsx=regread) plus --no-jump-slot-nop/--nop-on-reg-read for range. Both are
opt-in and default-off; make check green at 441 with them off, suite 229 -> 232
tests. Carried as a TRACKED patch (tools/patches/maspsx-phase10-r1r2.patch)
because tools/maspsx/ is git-ignored, so an in-place edit would not survive a
fresh clone; patch verified to reproduce the working tree byte-identically.
R1/R2 are recorded as a MEASURED NEGATIVE: neither closes a region (cookbook
finding 40 has the mechanism and the remaining developer-owned route).
Docs: cookbook finding 40 (rare-epilogue mechanism + why the obvious maspsx fix
fails); SETUP.md maspsx patch provenance and apply step.
Negatives: 0x8010AA28 imported; index sorted by address (140 rows, 0 registered).
Full clean audit green: make clean && make all exit 0, cmp exit 0, both SHA-1
match, registry 441/0 overlaps/0 bad extents/0 missing sources, 0 firewall.
- Phase10_PLAN.md status DRAFT -> APPROVED (Goal A, 475 bodies, 70-80% ctx cap)
- phase-ends/CURRENT_PHASE.md: Phase 10 control record
- config/near_match_negatives.tsv: drop 6 rows that were already registered
(index header says every row is UNREGISTERED); index 145 -> 139 rows, 0 registered.
Worklist regenerates byte-identically after the fix.
- Baseline revalidated at 25bf4a3: make check exit 0, regions=409 disagreements=0
AGREE, c_regions=409 differing_bytes=0 MATCH, 229 tests OK, SHA-1 stable.
- Worklist regen: listed=1343, excluded_already_registered=409 (= registry size).
- 3-way partitions in .run/p10 (448/448/447, pairwise intersection 0, union == worklist).
- Roster deviation recorded and developer-approved: 3 workers, not the plan's 2.
Phase 9 closed at 400 distinct bodies / 409 regions (from 149/158), all
gates green from clean: CMP_OK, SHA-1
e173426c157384ebf1b6caf8c6fea18a85a14af9, 229 tests, make gate
c_regions=409 MATCH, extents-verify AGREE, worklist listed=1343 with
excluded_already_registered=409, firewall 0, src/ 0 untracked.
Milestone partially met (400 of 500): the blocker is recorded with per-class
evidence (tier-1 tie-break domination, class disjointness, measured rate
decay) per the plan's Or branch. PhaseEnd_Phase9.md, digest entry, and the
ledger archive (logs/Phase9.md) written. docs/PHASE9_PROTOCOL.md (scale-run
retrospective: queue fix, tier-2 pivot, family-set lever, rotation
discipline, leading-indicator rule, measured budget), docs/PHASE9_VERIFICATION.md
(all gates, incidents, class exclusions), cookbook findings 29-39,
conventions additions, README updated to 400/409.
Phase10_PLAN.md drafted (Goals A/B/C: tail squeeze vs library-boundary
investigation, developer decisions enumerated) — requires explicit approval
before any Phase 10 task; no Phase 10 work begins in this session.
The 400-body target (P9-T6 checkpoint, 251 new bodies from 149) is crossed
on the coordinator's own whole-binary gate: c_regions=409, differing_bytes=0,
SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9.
Final batch: B's last 2 family members (0x800578EC, 0x800577E8, both 100B),
11 consecutive first-attempt family matches closing the run. Cycle-4 total:
47 new bodies from worker B following the milestone-close directive
(reliable-rows over exploration). The family-as-a-set lever (scan for rows
calling a matched target; stable positions selector->arg2 sentinel->arg3
a1->arg9, per-row elsewhere) drove 11 first-attempt closes.
Workers: B 60 claims (rotated cleanly, handoff block written with 104
report entries and the corrected cookbook items), C 70 claims (earlier
rotation). Coordinator holds merge/gate/ledger at 86% protecting the
essential role.
B 9-for-9 across the family batches. 0x80057B30 has TWO incoming stack
params; the family-set lever's scope limit sharpened: stable positions
(selector->arg2, sentinel->arg3, a1->arg9, extra->arg10) + per-row
positions elsewhere — the map proves once for the STANDARD form only, and
B corrected its own earlier 'map proves once' framing (belongs in the
cookbook with the lever). Two more 100-byte callers (0x800578EC,
0x800577E8) in flight for margin. Gate MATCH whole-binary SHA-1
e173426c157384ebf1b6caf8c6fea18a85a14af9.