Commit Graph

4842 Commits

Author SHA1 Message Date
Drew T bfff202b14 chore(integration): sync func_8017F35C's return-type decl (void -> s32) before gating 2026-09-01 13:32:29 -06:00
Drew T 85f064333b feat(decomp): parallel gate — 1 fns across 1 binaries (1 workers)
ov_SC07_000    func_8017E1BC
2026-09-01 13:30:25 -06:00
Drew T 75e4b305cd feat(decomp): parallel gate — 1 fns across 1 binaries (2 workers)
ov_SC04_002    func_80183A88
2026-09-01 13:22:00 -06:00
Drew T 34249a4696 docs(cookbook): §392-§394 — harvest the 7 sonnet overlay waves (105 agents, 57 MATCHes)
The hard gate caught me: m1/m2 (§379-§383) and the fable escalation (§385-§388)
were harvested, but o1-o4 and p1-p3 were not — 57 MATCH notes sat unbanked while
I was about to draw new waves.

§392 — seven byte-proven spelling levers, each of which closed a match on its own:
  (a) a same-address dual-sign read is fixed by ORDER (emit the unsigned
      store-source read first); cse merges lh/lhu for every cast spelling tried
  (b) a narrow temp picks the narrow load — s16 vs s32 decides lh vs lhu, and a
      signed decrement temp yields "sll 16" where unsigned yields "andi 0xFFFF"
  (c) tbl[idx-2] folds -8 into the lw offset; hoisting the subtract forces addiu
  (d) identical switch arms must be SEPARATE case blocks — the target duplicates
      arg setup per case and cross-jump-merges only the shared tail
  (e) distinct pseudos per repeated inline copy — one shared pair biases sched1's
      tie-break for the first copy only
  (f) split the widen into two statements to move the sll off a pinned register
  (g) the RETURN TYPE alone closed a 7-ins schedule residual (s32 -> void)

§393 — the BIRTHING BOOST: a single-set local gets max scheduling priority and
sched2's backward pass pushes it LATE; a zero-byte re-tie gives it a second set
and kills the boost. The scheduler-side sibling of §380 — same trick, different
pass, opposite symptom.

§394 — two align-1 accesses in one function reserve a phantom 8-byte stack slot;
a frame 8 bytes too large with no spill to account for it is the tell.
2026-09-01 13:14:55 -06:00
Drew T f11bf13b4f feat(seed_ref): the CONTAINED tier + docs for the twin ladder (§390/§391, accelerator #18)
tools/seed_ref.py gains --contained/--contained-control: an open stub that is a
banked body plus or minus WHOLE BLOCKS — the class edit distance ranks badly.
Branch-offset masking was required (unmasked offsets veto exactly the target
pairs) and a min-side-25 floor (89% of raw hits were prologue/epilogue vacuity).
Ranks by (substitutions+regions, cover), not by d. Controls: planted-deletion
positive 60/60, random-pair base rate 0/397, R32 population 346/346, and a
post-refactor --near regression reproducing the stored slice exactly.

Banked on first use: ov_SC01_077/func_80184D50 = banked ov_SC03_007/func_8018283C
minus its trailing `&= 0x7FFFFFFF;` — MATCH, closeness 0, 98/98.

* cookbook §390: minimum distance is not minimum work (rank by effort; a deletion
  is free, a substitution is thought), the lookalike filter r = d/min(nins) ~ 0.3
  (17 of 30 "cousins" were boilerplate coincidence), and the three fleet-wide
  nulls that close the scanner question — 0 new / 9 / 2. Spend integration
  effort, not scanner effort.
* cookbook §391: a byte-aligned struct copies in FOUR instructions (lwl/lwr/swl/
  swr), a word-aligned one in TWO. Never invent an aggregate type to make a draft
  compile — an invented word-aligned Blk8 lost exactly 8 ins across two copies and
  read as a believable "near, closeness 70" codegen residual.
* accelerators #18: a claim derived from BYTES is not a claim verified by a
  COMPILER. Every similarity/correctness claim must name the tier it reached
  (stream containment / compiled standalone / whole-binary gate / clean fleet);
  a report that says "verified" without one invites the strongest reading.
  Non-reproduction is a finding — say so rather than assuming your own setup.
* playbook §2a-2: the twin ladder (exact -> RELOC-ONLY -> CONTAINED -> cousin ->
  cold), take the cheapest tier available, widen only when the tier above is empty.
* SETUP inventory row; generic-decomp-package: rank by work, and stop building
  scanners once the well is dry.
2026-09-01 13:06:27 -06:00
Drew T eb6c2305c9 feat(decomp): parallel gate — 1 fns across 1 binaries (1 workers)
ov_SC01_077    func_80184D50
2026-09-01 12:50:55 -06:00
Drew T 881dd5a37c docs: the NEAR-TWIN BAND — §389, accelerator #17, SETUP, playbook §2a, generic-decomp-package
The exact-hash twin tier found 22 of 352 reachable open stubs (6%). The
edit-distance band added by `seed_ref --near` finds 75 of 352 (21%) — 3.4x — on a
corpus we believed fully mined. 31 of the new rows were PURE reloc-only twins of
already-banked bodies; 8 banked the same day at ~0 agent tokens, one 94-ins
exemplar serving five open copies.

* cookbook §389: the h_norm hole (norm_stream drops its pending lui-hi on an
  intervening R-type, so indexed-global reloc twins hash differently and vanish
  from seed_ref/twin_sweep/dedup/family-maps at once). Do NOT fix h_norm — every
  stored calibration keys on it; the near tier reads through it.
* accelerators #17: the generalisable law. A similarity hash built for DEDUP
  under-matches by design, which is correct for dedup and silently lossy as a
  FRONTIER join — the two questions want opposite error directions, and the
  frontier failure looks exactly like "this function is unique".
* generic-decomp-package §2b: build the near band at the same time as the exact
  tier, with the three verifications. It pays from the first bank for a new
  project, where we paid a session to recover the debt.
* SETUP inventory row + playbook §2a (run it before believing any "no twin"
  verdict; never send a RELOC-ONLY row to a drafting agent).
2026-09-01 12:20:59 -06:00
Drew T 56289ea18c feat(decomp): parallel gate — 8 fns across 8 binaries (10 workers)
ov_SC05_002    func_8017F348
  ov_SC06_018    func_801874E4
  ov_SC03_108    func_8017D110
  ov_SC06_020    func_80181654
  ov_SC03_124    func_8018A788
  ov_SC06_032    func_80182288
  ov_SC06_022    func_80185578
  ov_SC06_024    func_80187A50
2026-09-01 12:11:50 -06:00
Drew T 70134d06bf feat(decomp): parallel gate — 13 fns across 13 binaries (12 workers)
ov_SC03_094    func_8017E254
  ov_SC03_031    func_8017F728
  ov_SC03_028    func_8018568C
  md_SC07_004    func_801A3D60
  ov_SC02_031    func_801831B4
  ov_SC02_017    func_8017FCFC
  ov_SC01_080    func_801815D4
  ov_SC03_030    func_8017FB04
  ov_SC04_004    func_80182014
  ov_SC04_015    func_8017ED28
  ov_SC03_102    func_801815D4
  ov_SC05_005    func_80181828
  ov_SC03_119    func_801832E0
2026-09-01 11:19:01 -06:00
Drew T cee69c1bb5 feat(draw): NO SONNET — opus <=150 ins, fable >150 (Drew, 2026-09-01)
Measured over 129 drafting agents in one session, per MATCHED instruction (the
only cost that matters, since a failed agent is billed in full):

    sonnet  105 agents, 57 MATCH   4,289 tok/matched-ins  (flat ~47% above 30 ins)
    opus     24 agents, 11 MATCH   2,083                  (m1 191-347: 1,291, 67%)
    opus at 347-670:     1/9       7,158   <- the cliff, 2.92M tokens for ONE bank
    fable escalation:    3/4 closed at ~1/3 the cost of the attempt it rescued

Sonnet's per-agent price was never the cost that mattered; cost per BANK is, and
it lost on that by 2.1x. The m2 wave should have been fable from the start.

Escalating SOONER is the standing finding — higher models crack harder functions
in fewer tokens. Tested twice now (S68 A/B, S69 measurement); do not re-derive a
cheap-tier argument from per-agent price a third time.
2026-09-01 11:18:23 -06:00
Drew T ebb0d3a2e3 feat(decomp): main in-tree gate — 3 fn(s)
main  func_80020598
  main  func_80030F80
  main  func_80038A58
2026-09-01 11:10:12 -06:00
Drew T 9d7b26523c docs: cookbook §384 — a carve-config bank is red until you re-extract; correct the S69 checkpoint
The 'false bank' in the S69 checkpoint was not one. Both instances verify
byte-identical after 'make extract BINARY=<b>'. §384 states the law (verification
must regenerate whatever the gate changed the inputs to), the trap inside it (a
src-only revert of a carve commit produces 'table-count drift vs the carve', which
reads like progress), and the give-away I ignored — the commit diffstat showed
config/overlays.mk and a splat yaml sitting next to the .c.
2026-09-01 11:09:31 -06:00
Drew T 5450f18181 revert(revert): restore ov_SC04_011/func_80182DCC — it was NEVER a false bank
Reverts commit:3475. The bank is byte-identical; MY VERIFICATION WAS BROKEN.

A jtbl bank changes CARVE CONFIG (JTBL_PADS in config/overlays.mk + the splat
yaml). Those are splat INPUTS: asm/ and the linker script are regenerated FROM
them. I checked the binary with `make build` alone, so the build linked
newly-carved C against STALE extracted state and produced a mismatched SHA. That
is the R22 corollary ("a reverted config needs a make extract, not just a make
check") pointed the other way — a LANDED config change needs one too.

Proof, run on both binaries:
    make extract BINARY=ov_SC06_025 && make build  -> BYTE-IDENTICAL
    make extract BINARY=ov_SC04_011 && make build  -> BYTE-IDENTICAL

So: R40 against myself. I attributed the failure to the subject (the bank) when
the instrument (a build over stale extract state) was at fault — after writing
"it may not even be false" into the checkpoint and reverting without testing it.
The first revert also cost real work: it discarded a legitimate 96-line match.

STANDING FIX: a per-binary verify after any gate that touched config/ MUST be
`make extract BINARY=<b> && make build BINARY=<b>`. Build-only is a valid check
ONLY when the gate changed nothing under config/.
2026-09-01 11:08:55 -06:00
Drew T 9fc27960e9 feat(integration): teach the rest of the toolkit about §378 (self-caller cast)
The lever existed but nothing downstream applied it. Proof it mattered: a wave
agent this session diagnosed its own blocker as "§378 THE SELF-CALLER CAST, a
TU-level fix (cast_self_callers.py) that requires editing src/, which I'm not
permitted to touch" — the knowledge propagated, the automation did not.

* recover_integration.py: NEW "self-cast" stage (tier=binary), so the driver can
  run the whole chain as --stages arity,self-cast. The docstring states WHY the
  order is not arbitrary: self-cast answers the error that "arity" CREATES.
* residual_rules_b.py: both decl-conflict tiers now prescribe the full chain
  instead of "route to integration / budget for banking", and
  NOCOMPILE-UNDECLARED-FIXED now says outright NOT to gate the autodecl arm (it
  is a second conflicting declaration in the real TU).
* wave-playbook §4b: replaced the stale two-step recipe with the three-step
  chain, the one-driver form, the callee variant, and the MANDATORY
  --undo-journal.
* SETUP.md: full inventory row (R21) — it had zero mentions.

Not wired, deliberately: gate_stage's ladder rewrites DRAFTS via _xform, while
this edits the TU; a src-side edit inside the automatic gate needs
revert-on-failure, which recover_integration already owns.

Still open: a draft_prechecks rule to catch the self-decl conflict statically,
before a build is spent. The new stage's plumbing is verified (CLI + candidate
selection); its functional end-to-end run is NOT — gate12 held the tree.
2026-09-01 11:03:06 -06:00
Drew T d349bc053a feat(decomp): parallel gate — 9 fns across 8 binaries (12 workers)
ov_SC03_089    func_80181F9C
  ov_SC05_001    func_801809B8
  ov_SC05_008    func_80180E84
  ov_SC03_094    func_801814F4
  ov_SC03_118    func_801832E0
  ov_SC06_025    func_80182170
  ov_SC03_105    func_8017FAE8 func_80185680
  ov_SC07_002    func_80185FB0
2026-09-01 10:47:13 -06:00
Drew T f95b895909 docs(phase-31): S69 FINAL checkpoint — 44 MATCH of 84 agents, the §378 lever, four self-inflicted defects measured 2026-09-01 10:45:07 -06:00
Drew T c076f9016d feat(decomp): parallel gate — 10 fns across 8 binaries (12 workers)
md_MAIN_045    func_800CCB28
  md_MAIN_033    func_800CB4EC
  ov_SC05_000    func_8017E1E8
  ov_SC03_099    func_8017D2AC
  ov_SC02_005    func_8017ECAC
  ov_SC02_011    func_80182564 func_80182FF0
  md_MAIN_003    func_800D0100 func_800D0A7C
  ov_SC06_025    func_8017E48C
2026-09-01 10:39:02 -06:00
Drew T 4949acdf31 revert(integration): drop the §378 casts for the 25 chain candidates that did not bank
cast_self_callers casts a function's call sites in PREPARATION for banking it.
When the draft then fails, the cast must come back out — the tool journals every
edit for exactly that, and I did not run the undo.

The cost was concrete: the leftover cast on func_8017F8B8 made ov_SC07_000 fail
to COMPILE at HEAD, so every subsequent gate verdict on that binary was measuring
a broken baseline rather than the draft. Two drafting agents reported it as
BASELINE-RED before I noticed.

24 casts reverted across 11 files in 7 binaries; all 7 rebuild green. This is the
discipline recover_integration already documents ('REVERTS the caller edits for
anything that doesn't bank') applied to the new tool.
2026-09-01 10:35:29 -06:00
Drew T d8d4f8ac6f revert(decomp): ov_SC04_011/func_80182DCC — a FALSE BANK committed under --no-r22
Reverts commit:3472. The binary was RED at HEAD: sha1 9c94d36a vs expected
8bc09c42. The gate that banked it ran with --r22 disabled because 24 drafting
agents were live (R22 does make clean, which deletes asm/ under them), so the
one check that would have caught it was the one I had turned off.

The revert must carry the CARVE STATE, not just the C: the bank moved
JTBL_PADS 0,0,4,4 -> 0,0,4,4,4 plus the splat yaml, and a src-only revert left
4 tables against 5 pad specs ('table-count drift vs the carve'). Reverting the
whole commit restores BYTE-IDENTICAL.

Found only because two drafting agents independently reported their target's
binary as BASELINE-RED and I checked their claim against the bytes.
2026-09-01 10:35:11 -06:00
Drew T 6a96a62f27 feat(decomp): main in-tree gate — 2 fn(s)
main  func_8001DA34
  main  func_80021D38
2026-09-01 04:53:15 -06:00
Drew T c794091725 feat(decomp): main in-tree gate — 8 fn(s)
main  func_80020A28
  main  func_80021284
  main  func_800221A8
  main  func_8002374C
  main  func_80026514
  main  func_8002D904
  main  func_800377D8
  main  func_8003DC90
2026-09-01 04:51:47 -06:00
Drew T 02d0461d46 feat(decomp): parallel gate — 1 fns across 1 binaries (12 workers)
ov_SC04_011    func_80182DCC
2026-09-01 04:02:41 -06:00
Drew T cb7a28551c chore(integration): §378 chain applied to 25 fresh closeness-0 candidates (pre-gate) 2026-09-01 03:57:19 -06:00
Drew T 05195783b9 docs: mark the triage-ladder spec BUILT with its three corrections; accelerator #16
next-session-triage-ladder.md was still written as a to-build spec. It now leads
with the shipped status, the acceptance numbers, and the three things the spec got
wrong (the '32 free banks' were 0/28; the autodecl arm is worse in-tree than the
raw draft; PRE and POST cannot be the same pass because residual_rules_b needs a
draft), plus the one found by building it — never classify on a moving tree.

accelerators #16: a 'verified, just bank it' claim must name the compilation it
survived. Day-one kit material for a new decomp: any per-function oracle compiles
in isolation, every real bank compiles in context.
2026-09-01 03:49:21 -06:00
Drew T 6bc674ae45 docs(phase-31): S69 checkpoint — 8 banked from the '32 free banks' class, the §378 lever, the triage ladder acceptance-green 2026-09-01 00:11:20 -06:00
Drew T ae7c68a89a fix(gate): carry the per-function verdicts out of the worktree, and gate the in-tree retry on them
The first version of this parsed 'failed by class:' from the worker's stdout and
was INERT: the worker is gate_stage, which never prints that line (harvest_verify
does, one level down). classes came back empty for all 17 binaries of a batch and
the retry gate that consumed it fired ZERO times — a field that is always empty
makes its consumer a silent no-op (R54). Verified the claim only after re-reading
the log; correcting it here.

Now parallel_gate copies harvest_verify's <stem>.classified.txt out of the
worktree before teardown (it lives in the worktree's own .run/, which is not
symlinked and dies with it) and derives the class summary from those rows. That
also PRESERVES the verdict layer, which until now survived only as a side effect
of gater_lane re-running the whole binary in-tree afterwards (R47).

gater_lane judges the retry on the rows: a class with no per-function diagnostic
is the blind-worktree signature; anything cc1 named is a real compile error and
the serial rebuild would only reproduce it.

Verified live on ov_SC07_000: 'NOT retrying in-tree' fired, and the verdict row
landed at .run/gate_lane/ov_SC07_000.pgate.classified.txt.
2026-09-01 00:05:25 -06:00
Drew T b2d73108e2 feat(decomp): parallel gate — 1 fns across 1 binaries (16 workers)
ov_SC03_029    func_801847FC
2026-09-01 00:01:54 -06:00
Drew T ef504ffc32 chore(integration): decl-sync for the 21 remaining §376 candidates (pre-gate) 2026-08-31 23:54:17 -06:00
Drew T 1dd15eda32 feat(triage): the triage ladder — built, wired, and acceptance-green
tools/triage_ladder.py — the zero-token pre-agent pass, split PRE (target-side:
BANKED/WALL-332/PARKED, no build) from POST (residual_rules_b, needs a draft).
--escalate refuses a walled or banked target; --acceptance is the R39/R32 harness.
Refuses on a non-quiescent tree: a merging gate makes the stub oracle wrong in
both directions (measured, ov_SC01_004:func_8017EB30).

Acceptance, on the whole corpus: false-skip 0/1367 open stubs, recall 426/426
matched, wall tier fires on exactly the 10 enumerated walls (0 extra, 0 missing).
The first wall control asked for evidence that CANNOT exist — it scanned banked
functions' .s, which splat never writes — and printed '0 scanned / 0 tripped',
indistinguishable from a pass. The R32 empty-denominator assertion caught it on
its first run; replaced with a two-sided sweep over all open stubs.

tools/cast_self_callers.py — the §378 lever + --sync-decls for the narrow-param
case C89 forbids no-proto from reaching (§378a).

Wiring: wave_args drops walled/parked targets at draw time via pre_classify (one
implementation, R33); escalate_fable.js refuses any target without triage:'DRAFT'.

Tool fixes found by measurement:
* fix_arity_callers was blind to main entirely (globbed src/main/main*.c; main is
  src/*.c) — reported success over an empty file set through three gates. Now
  refuses when --binary selects no files.
* parallel_gate records each worker's 'failed by class' line (was truncated out of
  the 200-char tail); gater_lane retries in-tree ONLY on the diagnostic-free
  blind-worktree signature — S69 ran 22 serial retries against real cc1 errors.

docs: cookbook §376/§377/§378 (index 1033), SETUP.md, wave-playbook §4b.
2026-08-31 23:53:50 -06:00
Drew T c6f6f668ba feat(decomp): main/func_80036D58 — the narrow-param decl-sync completes the §378 chain
A no-proto decl is ILLEGAL against a definition whose parameter is affected by
the default argument promotions (s16 here): C89 requires the parameter types be
promotion-stable when one declaration has no prototype. So fix_arity_callers'
--any-proto cannot reach this case (it skips it as 'narrow-param').

With the call sites already cast (§378) the decls emit no code, so syncing them
to the draft's exact signature is byte-neutral: 3 decls in src/800.c rewritten to
extern void func_80036D58(s16). Byte-identical, main.
2026-08-31 23:51:19 -06:00
Drew T 779f3e2a19 feat(decomp): parallel gate — 5 fns across 5 binaries (16 workers)
ov_SC04_019    func_8017E7CC
  ov_SC01_080    func_8017D72C
  ov_SC01_077    func_80185E68
  ov_SC05_011    func_8017D818
  ov_SC07_000    func_8017F098
2026-08-31 23:49:37 -06:00
Drew T 72d3f61dcd chore(integration): self-caller casts for the 27 remaining §376 candidates (pre-gate) 2026-08-31 23:36:59 -06:00
Drew T 53fad0d031 feat(decomp): ov_SC04_010/func_8017D6CC — the self-caller cast lever, byte-verified
The §376 class's real blocker: after fix_arity_callers no-protos the conflicting
forward decl, the draft's definition becomes the prototype in scope and the TU's
own call site fails with 'too few arguments'. Casting THAT call site to a 0-arg
function pointer is byte-neutral (gcc-2.7.2 folds a cast of a known symbol back
to a direct jal, §20) and banks the function.
2026-08-31 23:18:43 -06:00
Drew T 6cca1e3ea0 feat(decomp): main in-tree gate — 0 fn(s) 2026-08-31 23:00:25 -06:00
Drew T a47981a494 docs(phase-31): S68 FINAL-2 checkpoint — 39 closed (453 -> 414), fleet 213/213, harvest drained 2026-08-31 22:33:02 -06:00
Drew T a22d5646f6 docs(cookbook): §374/§375 — two more bounds on the register-pin lever
§374 a register __asm__($30) reservation is NOT honoured by move_movables under
pressure, and the corruption is SILENT (the build succeeds) -- audit the raw
objdump register uses before trusting a pinned build that compiles.
§375 an $a0-$a3 pin used LATE relocates an EARLIER outgoing-call use of that same
register ~26 slots early, identically across three structural variants; argument
pins are not local the way $s pins are.

With §368 and §373 these now form a usable four-way rule for when a pin helps,
when it fights the allocator, when it is ignored entirely, and when it acts at a
distance.
2026-08-31 22:26:02 -06:00
Drew T 5c317dbe5f docs: wire the S68 tools into SETUP.md (R21) and the playbook (when to use them)
A tool nobody knows about is invisible work. Audit found neighbor_ref (built an
hour ago), residual_rules, lane_inflight and r22_verify in NEITHER doc, and
wall_sweep in the playbook but not the inventory.

SETUP.md gains a tooling-inventory row for all five with what each is FOR.

wave-playbook gains §2b: run neighbor_ref for EVERY card, placed right after the
seed_ref step because it answers the weaker and far more common question ('which
matched function should this agent READ?') that seed_ref structurally cannot. It
carries the measurement that justifies it -- a ~20x token swing on that single
variable -- and the failure it prevents: func_8017BEBC's card said 'no banked twin'
while a matched 755-instruction near-twin sat 3,700 lines up IN ITS OWN FILE.

Also states the two honest limits: an opt-level mismatch is PENALISED not merely
ranked low (§116 -- an -O2 example misleads an -O0 target), and a neighbour is a
worked example to READ, never a body to copy (§168 law 1, cousin-remap 0/26).
2026-08-31 22:25:43 -06:00
Drew T 02592300df feat(triage): the residual-classifier head-to-head — both implementations, kept
residual_rules.py (mine) and residual_rules_b.py (an independent Fable build,
forbidden from reading mine). Committed because the EXPERIMENT is the artifact:

                          mine    b
  classified            85/113  113/113
  errored                   28        0
  any rule fired           18%      88%
  certain/high              1%      63%
  pure residual-SHAPE      ~1%     1.8%

The last row is the finding. Two independent implementations CONVERGED at ~1-2% on
pure cookbook-shape rules, so that tier's ceiling is the POPULATION, not the code:
surgical single-mechanism residuals live at the END of escalations, not in
first-pass wave output. The shape tier belongs in escalation loops; the ladder's
value is everything above it (banked / wall / compile / autodecl / integration).

b also diagnosed my 28 errors exactly: they are functions banked DURING S68 after
the eval set was drawn, so corpus.stubs() no longer contains them and my resolver
raised IndexError on every one. It detects the same condition via corpus.matched()
and calls it ALREADY-BANKED — stale card, spend zero tokens.

Two things b did better that are worth copying: it never parsed disassembly TEXT
(every decision decodes the raw 32-bit word, so the two-disassembler formatting
disagreement that cost me two bugs never touched it), and it REMOVED three of its
own false-positive mechanisms found on held-out cases, all score-reducing, and
disclosed them.

Spec for finishing the ladder: docs/next-session-triage-ladder.md
2026-08-31 22:24:56 -06:00
Drew T 2daacec346 feat(decomp): ov_SC03_121 in-tree retry after a blind worktree gate 2026-08-31 22:22:45 -06:00
Drew T e32fdd47dd feat(decomp): parallel gate — 2 fns across 2 binaries (4 workers)
md_MAIN_011    func_800D1254
  ov_SC05_017    func_80189240
2026-08-31 22:22:43 -06:00
Drew T da32350873 feat(decomp): main in-tree gate — 1 fn(s)
main  func_80013B64
2026-08-31 22:18:35 -06:00
Drew T ba394da75a docs: spec the TRIAGE LADDER for next session, with its 32 free banks listed
The companion to neighbor_ref.py (built). Written so a session with none of S68's
context can finish it: what it is, where it sits in the pipeline, that
tools/residual_rules_b.py is already ~70% of it with its held-out scores, the
asymmetric failure mode that makes the R39 acceptance test mandatory (a false
'skip - already banked' silently drops a bankable function), and the falsifiable
predictions.

Includes the immediate payoff, verified by me rather than taken on trust: 10 drafts
are byte-MATCHES once an extern derived from the target's own .s is added (10 of 10
confirmed with match_one, closeness 0, patched drafts at .run/rules_b/*/autodecl.c),
plus 22 more that already match standalone and were misfiled as failures.

Also records the experiment's most important number: two INDEPENDENT
implementations converged at ~1-2% on pure cookbook-shape rules, so that tier's
ceiling is the POPULATION (surgical residuals live at the end of escalations, not
in first-pass wave output) and it belongs in escalation loops, not wave triage.
2026-08-31 22:09:11 -06:00
Drew T e936556ff0 feat(cards): neighbor_ref.py — retrieve MATCHED functions as worked examples, ranked
seed_ref answers 'is there a byte-identical twin?'. This answers the weaker but far
more common question: 'which matched function should I READ before drafting this?'

S68 measured a ~20x swing on that variable. Every cheapest large match came from an
agent finding a matched neighbour (func_800D1254 555 ins/72k; func_800D12D0 657
ins/122k FIRST COMPILE; func_8018AD9C 397 ins/87k; func_8017BEBC 753 ins/177k),
while main functions with no neighbour ran 200-350k for ~80 instructions.

THE FAILURE THAT MOTIVATED IT: func_8017BEBC's card asserted 'no banked twin' while
a MATCHED 755-instruction near-twin sat 3,700 lines up IN ITS OWN FILE, its header
comment documenting the four levers the target needed. seed_ref joins on signature
hashes and the two bodies are not hash-identical, so it was structurally invisible.
Three other S68 agents found their unlock the same way, unprompted.

Ranks on what actually worked, not intuition: SAME TU first (solved against the same
decl environment, and its header records the levers), then same binary, then shape
(li-normalised skeleton / call-sequence hash / reloc-kind sequence / CFG counts /
opcode-histogram cosine, all precomputed in .run/feat.*.jsonl), then instruction-
count proximity, with a HARD PENALTY for opt-level mismatch (§116 — an -O2 example
actively misleads an -O0 target). It surfaces the neighbour's HEADER COMMENT, which
is the payload agents actually consumed.

Explicitly NOT a remap claim: §168 law 1 measured cousins at 0/26. A neighbour is a
worked example to READ; seed_ref remains the tool for the byte-identical case.

Validated against ground truth: for main/func_80024054 (265k tokens, ended NEAR 32)
the top three neighbours are func_8003A0E4, func_800242D0 and func_800241C0 -- all
three MATCHED THIS SESSION, same TU, same call sequence, same reloc-kind sequence.
src/800.c holds 657 matched functions and the card offered none of them.

Bug fixed en route, and it is a repeat: the atlas writes addresses as hex STRINGS
while corpus.Stub.addr is an int. T4's verifier already lost rows to exactly this
string-vs-int mismatch (the R32 silent-no-op class). Normalised in _addr().
2026-08-31 22:08:34 -06:00
Drew T 7392fcf7c3 docs: accelerators #15 (the differential-oracle harness) + the generic decomp package thesis
#15 — the tool worth building FIRST in any decomp, because it works at 0% and
compounds: run every question down TWO independent paths on a schedule and fail on
disagreement. Ten-plus S68 blockers had one shape — a tool computing a TRUE number
about a NARROWER world than we believed it covered — and EVERY one was caught by
two measurements disagreeing, never by review. R32/R34/R40 already say this and
were not enough: they are rules applied by whoever writes the tool, and in S68 I
wrote R34's warning into one docstring and rebuilt the exact defect it warns about
an hour later in another file.

Includes Drew's scheduling half, which this project only ever did by accident: the
widening is PERIODIC. Tooling is not wrong when written, it goes STALE as new
idioms reveal populations it cannot see. At every phase close ask 'which scanner's
denominator just got wider?' — that question converts new knowledge into free
banks. The §332 sweep is the worked example: one review, 10 fns / 1,027 ins
reclassified, one in-flight escalation stopped mid-spend.

generic-decomp-package.md — what a NEW decomp inherits on day one and does BEFORE
cracking: mine the COMPILER SOURCE and sibling projects for idioms (this project's
best late idioms came from reading gcc-2.7.2's own passes and needed no matched
function at all — week-1 work done in month N), port the families/twins/dedup/carve
layer first, then the oracle harness, and only then crack. With the honest caveat
that tooling-first makes the cheap half free and does NOT shrink the hard tail.
2026-08-31 22:06:20 -06:00
Drew T d8b7fb4e49 docs(playbook): §1b — the §332 walls are now ENUMERATED, wire the sweep into the draw
tools/wall_sweep.py --emit-exclude feeds draw_waves --exclude directly. 10
functions / 1,027 instructions over 1,378 open-stub .s files, against §332's
'6 fleet-wide' with two named.

Recorded what it caught immediately: main/func_8005D734 was already escalated to
Fable at closeness 8 when the sweep listed it, and its site is exactly the residual
that agent described -- stopped. Filtering the live queue dropped two more before
they were drafted (func_8005D9C4 133 ins, func_8005F450 159 ins).

The §188 epilogue half is still NOT built and is now named as such rather than left
implied: its detector exists inside oracle_reorder.py and has never been run as a
sweep.
2026-08-31 20:02:56 -06:00
Drew T cd03c67652 feat(walls): wall_sweep.py — ENUMERATE the §332 delay-slot macro walls, 10 fns / 1,027 ins
§332 states the class is "6 FUNCTIONS FLEET-WIDE, NONE BANKABLE FROM C" and names
TWO of them. §332a then says, correctly, "Filter before drafting" -- but a filter
needs the LIST, and the rest were never written down, so the draw kept handing them
to agents. A COUNT WITHOUT AN ENUMERATION CANNOT DRIVE A FILTER.

Measured cost of that gap today: main/func_80061FA8 -- a fable agent produced C
that oracle_reorder proves BYTE-CORRECT (0 diffs / 103 ins) and that the pinned
triple still cannot emit. 92,684 tokens to rediscover a documented class. Plus
main/func_8005F0C8 at 289k tokens, the same story via §188.

The sweep is now the list: 10 functions, 1,027 instructions, derived from 1,378
open-stub .s files with 0 unreadable.

TWO DEFECTS IN MY OWN DETECTOR, both caught by demanding it reproduce members I
already knew -- the same rule I have been applying to every other tool today:
  * It returned a confident 0 across all 1,378 files. The .s lines carry a
    slash-star offset/addr/bytes star-slash comment prefix, and my regex anchored
    the mnemonic at start-of-line, so it matched NOTHING. A sweep returning 0 must
    prove it CAN return non-zero before the 0 means anything.
  * Widened, it found 6 but MISSED func_8005DBD8, which §332a names. Its delay slot
    holds a store through %lo -- the tail of a lui-%hi / store-%lo MACRO, not a la.
    Same mechanism, different mnemonic: ANY %lo in a delay slot is the second half
    of an assembler macro that gcc emits as one atomic insn, so C can never put it
    there.

IT PAID FOR ITSELF WITHIN MINUTES: main/func_8005D734 is in the list, and I had
escalated it to Fable at closeness 8 twenty minutes earlier. The sweep's site for
it is EXACTLY the residual that agent described. That escalation could never
succeed and has been stopped.

Ledger: .run/S68_walls_332.txt (--emit-exclude form, ready for draw_waves).
2026-08-31 19:59:27 -06:00
Drew T 0e50fbc84f docs(playbook): §1b — the walls ledger is always incomplete, and each gap costs an agent run
A wall nobody has met yet is invisible to the draw filter, so new ones are found by
PAYING an agent to hit one. Twice in S68 on main: func_8005E228 (a full run, then
banked the §265 verbatim-asm way) and func_8005F0C8 (289k tokens to reach closeness
36 with the residual confirmed as §188's epilogue by oracle_reorder.py).

Neither is a model failure. An agent handed a wall returns a NEAR with an
unexplainable tail, which looks exactly like a hard function -- and an escalation
cannot beat the toolchain, so escalating one is guaranteed waste.

The fix is named rather than left as folklore: run the §188 epilogue-shape detector
over every open stub AT DRAW TIME. It already exists inside oracle_reorder.py and
has never been run as a sweep. Until then, treat 'NEAR with an epilogue-shaped
tail' as a walls candidate and check it with the oracle BEFORE escalating.
2026-08-31 19:54:36 -06:00
Drew T e2f64a7c62 feat(o0): md_MAIN_003 second carve — func_800D12D0 (657 ins) banked as real -O0 C
MY HYPOTHESIS WAS WRONG AND THE AGENT SAID SO. I predicted the ownership oracle
was blind to verbatim-asm owners. It is not. 0x800cedf8 is the §154-A LEADING
RODATA ISLAND (the module-id header + jtbl/ptr table at segment offset 0), which
rodata_carves already exempts via 'off == 0 and sub == ov'. The S68 first carve
legitimately renamed that subseg to md_MAIN_003_jr_800D12D0 (§371: spimdisasm
rodata migration is same-subseg-only), so the 'sub == ov' conjunct stopped firing
and offset 0 leaked in as a 'carve'. The island has NO single owner BY DESIGN --
which is why the exemption exists -- so widening owner kinds could never have
restored 1:1.

The fix drops one conjunct: offset 0 alone is the honest structural key, because a
carve is a table LIFTED OUT OF THE DATA TAIL and can never sit at the segment's own
offset 0. Verified across all 213 configs: every offset-0 .rodata piece is an md_*
leading island; ov_*/main have none. The R32 hard abort is UNTOUCHED -- this widens
the recognised-island set, it does not soften the refusal.

NEGATIVE CONTROL (R39) over all 184 binaries with .rodata pieces: OK 182 -> 183,
ABORT 2 -> 1, and exactly ONE verdict moved (md_MAIN_003). The remaining us.exe
abort (UNOWNED 0x80073238, the LZSS jtbl carve whose owner LzssDecodeSector does
not live under src/us.exe/*.c) is byte-identical before and after -- PRE-EXISTING,
not newly hidden, and logged rather than silently absorbed.

Carve byte-neutral and bank byte-identical, both re-verified by my own rebuild:
sha1 dd1b32ecf1103c6f7cf1943d25546a3046e17b14 == config/check.md_MAIN_003.sha.
md_MAIN_003 12 -> 11 stubs.

THREE o0_subsplit GAPS surfaced and hand-finished, and they must be fixed before
the remaining 7 -O0 stubs here are carved: build_new_config drops a cut at the
object start so region 0 kept the -O2 name while the tool PRINTED the _o0 name;
parse_overlay_c folds pre-anchor text into the FOLLOWING anchor, so a verbatim body
inside region 0 attached to region 1; and the island .rodata piece needs repointing
to whichever TU ends up holding its emitters.
2026-08-31 19:46:10 -06:00
Drew T 3f9430e569 feat(gater): retry IN-TREE when the worktree gate FAILS every draft and banks none
The worktree gate is silently unable to build some binaries and reports it as
'failed', which is indistinguishable from bad drafts. Measured twice this session:
main (its psyq_integrate link inputs are not staged) and ov_SC06_010 (root cause
still unknown) each reported 'banked 0' while the SAME drafts banked byte-identical
through harvest_verify in the main tree. In the ov_SC06_010 case that was 1,191
instructions I re-gated twice and nearly wrote off as bad drafts.

Now any binary whose worker failed EVERY draft and banked none gets one in-tree
retry. A genuinely bad draft fails there too and costs one build; a harness-blind
binary banks. A real NEAR is left alone -- only all-FAILED is treated as suspicious.
The whole-binary SHA remains the sole arbiter (G3/P9), so this cannot launder a
wrong draft into the tree; it only stops the harness misattributing its own
blindness to the model.
2026-08-31 19:28:36 -06:00
Drew T 3897886acb feat(decomp): ov_SC06_010 func_8017E764 (438) + func_8017BEBC (753) — 1,191 ins
Both banked in the MAIN TREE after parallel_gate's worktree reported 'banked 0'
TWICE. The drafts were never the problem:
  * baseline ov_SC06_010 builds byte-identical (05c2d8c4 == check.sha) -- so the
    binary was not red the way main was;
  * both drafts probe MATCH in their REAL TU via rtu_match/blocker_probe;
  * harvest_verify in the main tree: verified 2 / failed 0, final SHA
    05c2d8c46363483a1dce434ee745957b76d0530e BYTE-IDENTICAL.

So the worktree gate has a second binary it cannot handle, and it reports that as
'banked 0' -- indistinguishable from a wave of bad drafts, and the reason I re-ran
this gate twice before doubting the harness instead of the model. Same shape as the
main worktree defect: the failure is SILENT and its symptom points at the wrong
suspect. Root cause not yet identified for ov_SC06_010 specifically; recorded here
rather than left as folklore.

func_8017BEBC's unlock is worth keeping: its card said 'no banked twin', but a
MATCHED 755-instruction near-twin sat in the DESTINATION FILE ITSELF 3,700 lines up
(func_8017CAD4), and its header comment documented the four levers the target
needed. seed_ref joins on signature hashes, so a structurally-similar
non-hash-identical neighbour is invisible to it -- and a same-TU neighbour is
exactly where the richest context lives.
2026-08-31 19:27:37 -06:00