mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-28 23:00:29 -04:00
87b02b044fabdd892273dd768f23fbd436a436bb
1061 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
87b02b044f |
fix(phase-29): jtbl_carve — repair the SPLIT-TABLE undercount, gated on the function's own sltiu
THE BUG (real, found by a wave agent): jtbl_range() ends a carve at the next data dlabel, assuming every dlabel is an object boundary. spimdisasm can CUT ONE JUMP TABLE IN HALF and emit the tail under an invented D_ label — func_8012AAAC's 50-word table is jtbl_801D7FB0 (28) + D_801D8020 (22). The carve then reserves 112 B for an object supplying 200 B of .rodata, shifting every later symbol. §84-class: match_one is structurally blind; it surfaces only as a whole-binary DIFF. THE AGENT'S EVIDENCE WAS WRONG (R14): it reported D_801D8020 as having "ZERO xrefs anywhere in the tree" and proposed deleting the label. It has TWO (.word D_801D8020 and +0x2 in tail.data.s) — almost certainly spimdisasm mis-symbolizing packed halfword data, but "almost certainly" is not a gate, and the proposed remedy would have deleted a symbol two emitted words reference. I built the xref census first, watched it refuse, and only then found the references. THE GATE USED INSTEAD — the function's own `sltiu N` range check, which gcc emits right before the indexed load, so the PROGRAM declares its own table length (func_8012AAAC: sltiu 0x32 = 50). Absorb only when the next label is immediately adjacent, its words are all code addresses in the overlay's text, and absorbing lands on an EXACT sltiu bound (the SET, not max() — a multi-switch function has several and no way to say which owns this table). Three further corrections, each caught by testing rather than assumed: - the absorption fired and the trailing-pad trim immediately UNDID it (re-trimming against the first dlabel's 28 words); the trim now sees the whole absorbed table; - a continuation ends at ITS OWN last .word, not the next dlabel (D_801D8020 ends 0x801D8078; the next dlabel is 0x801D8158, 224 B on) — using the next dlabel is the assumption being repaired; - the shortfall warning now fires only on an unambiguous single-bound pairing (it fired ~90 times across 38 tables before the guard — a warning that fires on ambiguity is noise, not a signal). VERIFIED: the split table 28 -> 50 words (112 -> 200 B), matching the agent's 3 independent confirmations; and across 38 jtbls x 6 functions = 228 combinations, EXACTLY ONE range changes — that table, for its owning function only. |
||
|
|
181ba8c4e6 |
docs(phase-29): SESSION-21 wave 1 first half — 8/8 match_one MATCH, 6 of 8 blocked on ONE lever
- 8 of 24 agents completed before the session limit (16 errored on the limit, none technically); resumed from cache. 8 MATCH / 0 near / 0 fail, stake 210,726 templatable ins, 2.48M subagent tokens. - CANDIDATES not banks (§58) — but DIAGNOSED ones: the prompt required symcheck + a named blocker, so instead of 8 opaque MATCHes there are 8 with their banking prerequisite stated. - THE FINDING: 6 of 8 are blocked on the SAME jtbl/rodata carve class — one mechanical lever in front of ~153,596 templatable ins in this batch alone. Corroborated independently by tools/reloc_verify.py, which flagged the identical class on the drafts it could check (R34). - A REAL jtbl_carve BUG found by an agent, confirmed 3 ways: jtbl_range() ends the carve at the next data dlabel, but splat split ONE 50-word table across jtbl_801D7FB0 (28) + D_801D8020 (22, zero xrefs) -> 112B carve for a 200B .rodata. §84-class: match_one is blind; it surfaces only as a whole-binary DIFF. Fix queued. - Agents touched zero tracked files (write-set constraint held). |
||
|
|
d0322d473c |
feat(phase-29): promote tools/reloc_verify.py — resolve every relocation before paying a gate cycle
The SESSION-20 carry item ("promote it — it closes 3 of the 4 blindness classes"), generalized:
base vram DERIVED from the target .s (was hard-coded to one function, R33) and the parse
coverage-asserted (R32 — a target that parses to zero instructions refuses to report a verdict
rather than reading "ALL RESOLVED"). Resolves jal callees, %hi/%lo data addresses (recovering the
implicit REL addend objdump -r never prints — the §84 trap) and internal j destinations.
IT TOOK TWO OF ITS OWN BUGS TO TRUST IT — both found by cross-checking masked_diff (R34):
1. `objdump -dr` instead of `-drz`: without -z objdump ELIDES identical-instruction runs, so
func_801330E0 read 104 ins vs masked_diff's 110 (6 elided nops) and every later index compared
against the wrong instruction — 2 phantom mismatches on a clean draft. A comparison tool MUST
share its reference oracle's index space exactly.
2. the .s word field is little-endian HEX TEXT, not the instruction integer; masked_diff byte-swaps
it and this did not — reporting "word differs" on three byte-IDENTICAL sites.
Now classifies instead of alarming: JTBL (gcc emits its own switch table via a local label => nothing
to relocate; the §81 routing signal — bank via jtbl_family_bank, never plain harvest_verify) ·
BAKED-LITERAL (same constant materialized inline: byte-correct here, but if the symbol is
per-overlay the exemplar matches and every SIBLING breaks — the §84 shape) · real mismatch.
Recorded: measuring a live wave's drafts is itself the §87 staleness error — a draft rewritten 12s
before the check gave a different verdict. Draft QA happens after the wave returns.
(The wave's gate driver lives at .run/s21_gate.py — gitignored scratch, §55b orchestration law
built in: --no-propagate per TU group, commit before the fleet propagate, and BANKED derived from
the stub set rather than read from gate_stage's accumulating verified-file.)
|
||
|
|
ccbc65e9c3 |
fix(phase-29): progress.linked_subsegs fails closed (R32) + the main-EXE bucket re-measured
- progress.linked_subsegs() was FAIL-OPEN: gated on the module global BINARY that set_binary() assigns, it returned an EMPTY SET when imported as a library without that call — i.e. "no linked library subsegs", which for main is confidently wrong (there are 49) and silently reclassifies ~960 already-byte-identical PsyQ-linked stubs as outstanding game-code work. Now raises when unconfigured; the CLI path is untouched (set_binary assigns before calling). Caught by hitting it myself while measuring bucket #2. - ENDGAME-MAP CORRECTION (measured, zero-token): the map's "main EXE game code ~59,765 ins / ~1,048 stubs" conflates two populations. Correctly split: game code 1,042 stubs / 31,888 measurable ins; LINKED PsyQ library 960 stubs / 27,877 ins (already byte-identical). Bucket #2 is ~47% smaller than quoted. Caveat kept: 467 game-code stubs have NO sig row (the documented main second-oracle gap), so the true weight is above 31,888 and not currently measurable — re-price when the main second oracle lands, do not quote either number alone. - .run/s21_zerocrack.json: the 60-family zero-crack pool enumerated (45 plain / 15 jr) and honestly discounted — its top entries (0x8013c414 -O0 wall, 0x80144090 LENGTH-DRIFT, 0x80133ab0 pinned) are already-diagnosed refusals, so ~95k of the 208,499 is not available. |
||
|
|
2b38c68333 |
feat(phase-29): SESSION-21 T1-T3 — the frontier measured, the family-exemplar wave, 3 tool fixes
- T1 FRONTIER MEASURED (zero-token, R35: family map regenerated on fresh sigs first — it was stale by ~657 banked members): 36,020 stubs / 2,345,599 weighted ins remain, and only 9.0% are h_exact-FREE. PROPAGATION IS TAPPED (238 distinct classes / 3,245 instances); 22,498 distinct classes / 1,680,097 distinct ins is what is actually left. The mass is FLAT across all 139 binaries (~300-550 sub-500 stubs each) -> "pick the best overlay" is not a strategy. .run/s21_frontier.py + .run/s21_frontier.json - T2 THE AXIS IS THE FAMILY, NOT THE LOCATION: 1,342 substantial h_seq families / 1,298,135 templatable ins = 55% of ALL remaining weighted instructions. Routed by blocker: jr/§81 181 fams (33.6%) · DRAFT-with-cached-Ghidra-C 91 (28.6%) · DRAFT-modal 1,023 (27.5%) · zero-crack 45 (6.5%) · permanent walls 2 (3.9%). Live+cached+non-wall in ov_SC01_077 = 54 families / 589,502 ins, value steeply concentrated (top 24 = 96%). .run/s21_targets.py + .run/s21_targets.json + .run/s21_draft_pool.json - T3 WAVE 1 LAUNCHED: tools/workflows/family_core_wave.js (NEW) — 24 xHigh drafters, one per family exemplar, stake 575,488 templatable ins (24% of remaining). Supersedes worker_wave.js for family work: carries each target's family STAKE, encodes the four §58/§87 integration rules at source (splat D_<UPPERHEX> not Ghidra DAT_; never invent a symbol; canonical callee sigs; leave decl plumbing to the ladder), and requires symcheck.py on any claimed MATCH. - T3b LADDER HYGIENE, both SESSION-20 carry items fixed — one defect, two masks: a byte-NEUTRAL transform was left in the tree when it banked nothing. family_sweep's --normalize-self-decls backstop only fired on MISMATCH (left 123 files of dead diff on a 0/123 run); gate_stage's ARITY undo narrowed to src/shared/ and left ~40 TUs. Both now restore the full snapshot when NOTHING banked (no banks to preserve => the splice hazard cannot apply). §61 on the success path. - T3c BACKLOG addr DEFECT fixed (R32/R33): new addr_of() derives the address from `name`, assert_addr_coverage() fails loud on an unkeyable row, append_record fills both directions. Found a latent bug doing it: load_best() keyed on `addr or name`, splitting one function into two "best" records. Keyable rows 128/1,701 (7.5%) -> 1,701/1,701 (100%). |
||
|
|
2d2c01f046 | docs(phase-29): SESSION-20 FINAL CHECKPOINT — behemoths done, the measured endgame map, §84-§89, the new throughput sequence | ||
|
|
5c422e8426 |
feat(phase-29): tools/sweep_parallel.py + §89 — the parallel gate farm, reachable from the family path (step 2)
bulk_harvest's Phase B has been a ProcessPoolExecutor over DISTINCT binaries (per-binary flock, per-worker result files, compute_fleet=False) since Phase 23 — but welded to Phase A's LLM drafting. Family sweeps stage drafts differently (family_sweep --stage-only), so the farm was UNREACHABLE from that path, and SESSION-20 gated 389 + 268 + 104 members SERIALLY for no architectural reason (~8-16x throughput loss on a 32-thread box). This is a thin adapter: same gate_stage.run_gate, same per-binary lock, NO new gate logic. Also fixes the phantom-dir bug at source: a bare .run/sweep/*/ glob matches gate_stage's own intermediate ladder dirs (-cn/-cast/-rc/-s2in/-uni) and calls them as binaries — 24 phantom PARTIAL 0/1 lines that inflated one run's notbanked from 0 to 56. Requires config/splat.<bin>.yaml to exist (R33/R36: derive the binary set, never glob it). Smoke-tested: the phantom is skipped and named, real binaries kept. §89 records both throughput rules the project already had and was not following. |
||
|
|
6ce2e8963f |
chore(phase-29): preserve the behemoth close-out artifacts (R20)
39 files from the three behemoth agents: the matched drafts (s21_func_80183814_b2.c, s21_func_8017D2DC_b1.c, s21_func_8017DC1C_b1.c), their reports with do-not-re-buy tables AND BASES (§80), and the reusable harnesses — including s21_g21_reloc_verify.py, which resolves every relocation (incl. the implicit MIPS-REL addend objdump -r does not print) against the target and is the missing rung between match_one and the binary (§88f). ~735k agent tokens of work; .run/giants is the curated allowlist. |
||
|
|
1748eabe62 |
feat(phase-29): tools/blast_radius.py — MEASURE the write set, enforce the §63 tier (step 1)
§63 has defined T0/T1/T2 since Phase 26 and never enforced it. Two measured consequences in SESSION-20: ~13 full clean-fleet verifies (~15 min each) for batches that were provably T1 (over-verification), AND two cases where the write set was LARGER than the belief about it — the §85 widen believed contained to one overlay broke ov_SC01_077, and gate_stage's ARITY pre-pass silently rewrote 40 TUs (under-verification, the dangerous half). A tier is a CLAIM about the write set; this turns it into a MEASUREMENT. --expect t1 fails loud when the tree disagrees. Binary list DERIVED from config/splat.*.yaml (R33, never hardcoded — R36's incident was a hardcoded set missing 4 real binaries). Coverage ASSERTED (R32): an unclassified path exits 2 and names itself rather than being silently skipped. overlays.mk is attributed per-binary by parsing its diff, so a change confined to one binary's var-block stays T1. |
||
|
|
890dd08e6f | docs(phase-29): §88 — the behemoth close-out laws (cross_jump/call, slti literal-position + its equality false-positive, banking order, the reloc gate) | ||
|
|
d2a79deff2 |
feat(phase-29): ALL THREE BEHEMOTHS BANKED — func_80183814 (5,122), func_8017DC1C (1,518), func_8017D2DC (1,586)
Zero functions >1000 ins remain unmatched anywhere in the fleet. func_80183814 (5,122 ins — the LARGEST function in the game) — round 2 closed it: length 5127->5122 exact, structural residual 36->0, register-sensitive 1201->0, frame -256 -> -0xF8 exact, saves 10 -> .mask 0x807f0000 exact. Verified independently (R14): match_one MATCH (5122 ins). ROUND 1's DIAGNOSIS WAS WRONG and the agent refuted it properly: the +5 length was a SYMPTOM, not the lever, and the §83d max_reg/cse.c:8340 story does not hold — a 15-line reproducer reproduced the case-0/3 CSE exactly (so it cannot be max_reg-gated), max_qty only gates extension ACROSS blocks, and the target leaves $s7/$fp unused (no pressure story). Confirmed from a second direction: C01 has the identical two groups over the identical symbols with ZERO residual, because a `break` puts a CODE_LABEL between them. The two biggest levers were pure DECLARATION SCOPE (§45/§76), not pins. func_8017DC1C (1,518) — MATCH first round, pin-free, zero __asm__ dials. NOT a jr fn (0 mid-fn jr). func_8017D2DC (1,586) — MATCH first round (banked in the previous commit). BANKING ORDER MATTERS — a new failure mode found and worked around: banking func_8017DC1C BEFORE the carve chain broke the build. Its draft establishes the canon for 39 previously-undeclared externs; jr_isolate_all's re-partition (overlay_src_split) then DROPPED ALL 39 across the new split boundary (`D_801C1EB0 undeclared`), leaving them in NEITHER file. The §77 preamble-drop class, in a third tool. FIX = ordering, not patching: run the §81 carve chain FIRST on a clean tree (gated BYTE-IDENTICAL), then bank. Reverted, re-sequenced, both banked clean. R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4). Fleet: instr 81.6 -> 81.7% · distinct-code 69.1 -> 69.3% · fn-count 89.52%. |
||
|
|
09b1993059 |
feat(phase-29): T0.7 sweep (104 members) + BEHEMOTH func_8017D2DC banked (1,586 ins)
T0.7 — the §86 one-member probe applied to the remaining FREE families: 9 LIVE / 6 DEAD / 5 unstaged. The three highest-value families by raw size (18,084 / 11,234 / 10,880 ins) all probed DEAD — the probe skipped them instead of burning ~400 gate cycles rediscovering it. Swept the 9 live: 104 banked, 8 of 9 families fully cleared (func_8017BEF8 has 8 stragglers). BEHEMOTH 2 of 3: func_8017D2DC (1,586 ins, ov_SC01_001) MATCHED and BANKED — closed in ONE agent round, pin-free. Verified independently (R14): match_one MATCH (1586 ins). §81 carve chain: the agent predicted step 1 unnecessary; jtbl_carve REFUSED (the subseg already hosts a .rodata carve and the new table's start != span start). The refusal was RIGHT and is the instruction to run step 1 — jr_isolate_all --only (2 fns/1 object) -> BYTE-IDENTICAL, then jtbl_carve -> BYTE-IDENTICAL, then the ladder banked it. R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4). Fleet: instr 81.5 -> 81.6% · distinct-code 69.0 -> 69.1% · fn-count 89.49 -> 89.52%. |
||
|
|
1eb36504f6 |
docs(phase-29): §87 — match_one never LINKS, so the 315 'integration' entries are not bankable
T0.6 measured: the autopsy's integration bucket (315 match_one MATCHes, 'blocked only on plumbing')
banked 0/27 through the full gate_stage ladder. Two causes, neither plumbing:
(1) UNDEFINED DATA SYMBOLS — the gate fails at LINK on symbols defined in NO overlay's symbol file.
match_one compiles one TU and never links, so an extern resolving nowhere is structurally
invisible to it. (Refuted the obvious alternative: the drafts WERE authored for the right binary.)
(2) STALE DRAFTS — 'redefinition of struct S80172C50': the struct was since lifted into
engine_types.h, so the draft's own copy collides. A stored draft is scored against TODAY's tree.
=> FOUR match_one blindness classes now catalogued: §81 jump tables, §84 masked %lo, §87 link, §87
staleness. A match_one MATCH is 'this TU compiles to the right bytes with relocations masked' —
nothing about linking, nothing about the current tree. A stored MATCH is a CLAIM WITH A TIMESTAMP.
Consequence: with §83's 44%-misfiled finding, docs/backlog.md's headline count is NOT a work queue.
Re-gate a sample before planning against any stored-draft pool. Cheap discriminator added (grep each
D_ symbol against the binary's symbol files; any UNRESOLVABLE will fail at link regardless of ladder).
MY RECOMMENDATION WAS WRONG: I ranked this pool first on 'highest certainty of any pool we have'.
The certainty was an artifact of a tool that cannot see link errors. 0 banked, 0 tokens, tree clean.
|
||
|
|
454a0d2a02 | docs(phase-29): SESSION-20 closing checkpoint — ~1,070 members from five tooling root causes; 81.5/69.0/89.49 | ||
|
|
c2566010f2 |
feat(phase-29): --allow-pins sweep — 268 banked; pin templatability is PER-FAMILY (cookbook §86)
The §42e pin guard refuses any family whose exemplar carries `register __asm__` pins: 680 of the top
8 FREE families' 1,083 members (63%) were skipped BEFORE any gate ran. Re-run with --allow-pins,
letting the byte-gate arbitrate (G3/P9): 268 banked, and ZERO cc1 crashes across hundreds of pinned
compiles — confirming the SIGABRT the guard was written against was Phase 27's extract_unit
macro-drop, NOT a compiler limit. The guard is protecting against a bug that no longer exists.
THE LAW (§86): templatability is a PER-FAMILY property, not a per-member rate.
func_801749C8 137/137 = 100% func_80133AB0 4/136
func_8014C6F4 137/137 = 100% func_8014CF04 0/137
func_80143D28 0/136
Two families at 100%, three at ~1%. MY REPORTED "37%" WAS AN ARTEFACT: a 19-member sample that
straddled families reported their AVERAGE and hid the bimodality. Sample PER-FAMILY, never per-pool.
=> PROCEDURE, now the default: probe ONE member per pinned family; bank -> sweep the family; fail ->
skip entirely. The blanket sweep spent ~412 futile gate cycles (60% of the run) on three families
that were never going to bank; the 1-member probe reduces that to 5 probes + 2 sweeps.
Left explicitly UNDIAGNOSED (do not guess): why two families template and three do not. Likely axis
is caller-saved pins spanning a `jal` (§74's corrupting form) vs pins fixing only a local allocno.
Diagnose BEFORE extending --allow-pins fleet-wide — the byte-gate makes a wrong guess free, but a
wrong PROCEDURE costs a sweep.
R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 81.3 -> 81.5% · distinct-code 69.0% · fn-count 89.41 -> 89.49%.
|
||
|
|
52e6522f1b |
feat(phase-29): T0.5 — FREE-subset sweep, 3 families fully banked (389 members, +37,713 ins)
Re-derived the T0.1 decomposition post-harvest (it was stale by 395 banked members): zero-crack pool 76 fams / 347,892 ins -> 73 fams / 290,850 ins (the harvest came out of it) FREE (sweepable, non-jr, non-O0) -> 58 fams / 167,368 ins Swept the top FREE families through the gate_stage ladder (sample 8/8 first, then the rest): 389 banked; func_801463A0 / func_8017B490 / func_80156670 now stubbed in ZERO overlays. R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4). Fleet: instr 81.0 -> 81.3% (10,645,711 -> 10,683,424) · distinct-code 68.8 -> 69.0% · fn-count 89.30 -> 89.41%. THE BLOCKER HAS MOVED — it is now OUR OWN PIN GUARD, not gcc and not declarations. Of the 1,083 candidate members in the top 8 FREE families, 680 (63%) were refused by the §42e pinned-exemplar guard BEFORE any gate ran; only 403 reached staging. Two pieces of evidence say the guard may now be over-conservative: SESSION-19 banked func_8017A4AC x134 WITH pins once the byte-gate arbitrated, and Phase 27 dissolved the cc1 SIGABRT that motivated it (it was the extract_unit macro-drop, not a compiler limit). Next probe: --allow-pins on a sample of 8, byte-gated. MY OWN SCRIPT BUG, fixed + negative-controlled: the sweep loop globbed `.run/sweep/*/`, which also matches gate_stage's INTERMEDIATE ladder dirs (-cn, -cn-cast, -cn-cast-rc, -s2in, -s2in-uni). Those were called as if they were binaries -> 24 phantom "PARTIAL 0/1" lines inflating notbanked to 56 when the true failure count was ZERO (stub counts 0/0/0 are the ground truth). Fixed by requiring config/splat.<ov>.yaml to exist; negative control confirms phantoms are skipped and real binaries kept. |
||
|
|
57e63730dc | docs(phase-29): SESSION-20 final checkpoint — 395 members harvested from two tooling bugs, ~56,200 ins, zero tokens | ||
|
|
015ebff536 |
feat(phase-29): §84 family fully harvested — 123/123 members, 0 failed (+29,280 ins, +27,840 distinct)
The derived-offset recompute swept the whole func_8013D53C family: 119 banked / 0 failed on top of the 4 earlier; func_8013D53C is now stubbed in ZERO overlays. R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4). RECIPE (and it is NOT the return-axis recipe — sampling caught this): §84 derived-offset -> per-member literal recompute AND the gate_stage ladder. With the recompute alone the sample was 0/8; through the ladder it was 3/3, then 119/119. Had I reused the return-axis recipe (plain harvest_verify, which banked 272/272 there) I would have swept 123 members to zero banks and mis-concluded the fix was wrong. Probe-before-scale. METRIC FINDING worth carrying: this harvest moved instr +29,280 AND distinct-code +27,840, while the return-axis harvest moved instr +26,928 and distinct-code +0. §84-class members are byte-VARIANTS so each is a new unique function; propagation-class members were already counted once via their shared exemplar. => §84-class work moves the RE-COMPLETENESS number; propagation moves only the DISPLAY one. Session fleet: 80.6 -> 81.0% instr · 68.2 -> 68.8% distinct-code · 89.18 -> 89.30% fn-count. |
||
|
|
9d9cd58028 |
feat(phase-29): T0.4 harvest — 272 return-axis members banked (+26,928 ins) + the §84 recompute in family_remap
THE §85 WIDEN PAID OFF AS PREDICTED. It is a ONE-TIME fleet edit, so once committed the
`conflicting types` blocker was gone for EVERY member of both families at once:
- sample 8 first (probe-before-scale): 8/8 banked with PLAIN harvest_verify, no ladder needed
- full sweep: 264 banked / 0 failed across 132 overlays; 10 skipped as not-stub
- total 272 members ~= 27k ins, ZERO agent tokens
R22 clean-fleet 140/140 BYTE-IDENTICAL; dedup 1886/0; 0 NON_MATCHING (G4).
Fleet: instr 80.6 -> 80.8% (10,589,503 -> 10,616,431, +26,928) · fn-count 89.19 -> 89.26%.
distinct-code UNCHANGED at 68.3% — propagation moves the DISPLAY metric, not the RE-completeness
one (the SESSION-19 split, reconfirmed).
§84 RECOMPUTE now implemented in tools/family_remap.py (fix_derived_offsets), wired into all three
apply_remap call sites as a PRE-pass on the exemplar body (the literal is ambiguous as a substitution
token, so it cannot be a table entry):
correct_literal = mapped(aliased_sym) - mapped(base_sym)
Verified by negative control: the hand-solved case recomputes 0x20 -> 0x18 exactly, and a site whose
target endpoint is NOT a mapped symbol is left byte-for-byte alone AND REPORTED in info
["derived_offsets"] (R32 — a silent skip is a defect, and a silent skip is how this bug survived).
|
||
|
|
c6b17f3056 | docs(phase-29): SESSION-20 checkpoint — 4 causes diagnosed, 3 banked, the unlock identified but not yet harvested | ||
|
|
772b5c4e02 |
feat(phase-29): the RETURN-axis fleet widen — 2 more families unlocked (cookbook §85); 140/140
Continues the "see why and try again" chain. Diagnosed all 4 T0.2 failures to 4 DISTINCT causes: func_8013D53C 240x123 §84 derived-offset remap bug -> BANKED (previous commit) func_8012CC88 105x137 §73/§30#2 RETURN-axis conflict -> BANKED here func_8014D12C 93x137 §73/§30#2 RETURN-axis conflict -> BANKED here func_80144090 154x136 LENGTH-DRIFT (+13 B, ~3 ins long) -> genuine codegen, real work THE FAILURE THAT TAUGHT THE FIX: widening only src/shared/engine_core.h banked the member in the TARGET overlay and BROKE ov_SC01_077 (R22 139/140) — the source overlay carries its OWN local `extern void func_X(...)` decls, so a shared-header-only widen puts them in direct conflict. The per-binary gate passed while breaking a binary it never built (§63/§61: a T2 write set is only provable by R22). A half-done axis is a guaranteed break, not a smaller win. THE FIX: do the WHOLE axis — 3,668 `extern void` decl sites across 2,688 files widened to `s32`, 0 remaining (R32 completion assertion). Precondition verified first: 0 callers consume the return value, so the widen is byte-neutral by construction. R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4). MY OWN ERROR, recorded (§85 trap): I first spot-checked ov_SC01_077 with `make build | grep | head; echo rc=$?` and read rc=0 as success — that is the exit status of `head`, not make, and the output had no BYTE-IDENTICAL line. I reported a false BYTE-IDENTICAL in the interim. Assert on the SUCCESS STRING, never on $? after a pipe. Fleet: instr 80.6% (10,589,503) · distinct-code 68.3% (3,846,656) · fn-count 89.19%. |
||
|
|
b6bb04ecff |
feat(phase-29): CRACK THE PLUMBING FAILURE — the derived-offset remap bug (cookbook §84); func_8013D53C banked
Drew: "if it fails, see why and try again with new knowledge." It failed twice, then banked.
ROOT CAUSE, byte-proven: a family_remap member reached match_one MATCH (240 ins) and failed the
whole-binary gate by ONE BYTE. The exemplar carries a deliberate matching idiom — reach a symbol via
a DIFFERENT symbol plus a literal offset, so gcc cannot CSE the two %hi/%lo pairs:
(*(S9*)&D_801DAA78) = *(S9*)(&D_801DA998 + 0x20); /* same addr as &D_801DA9B8 */
family_remap substitutes the symbol NAMES correctly and leaves the literal 0x20 — but 0x20 is not a
constant of the algorithm, it is the DISTANCE BETWEEN TWO PER-OVERLAY SYMBOLS:
exemplar 0x801DA998 + 0x20 = 0x801DA9B8 OK
member 0x801A5778 + 0x20 = 0x801A5798 WRONG (real symbol 0x801A5790)
member 0x801A5778 + 0x18 = 0x801A5790 correct
match_one MASKS HI16/LO16 so it is STRUCTURALLY BLIND to this — the §81 blindness in its DATA form.
TWO FIXES WERE EACH INDIVIDUALLY INSUFFICIENT: the byte fix alone re-failed as PLUMBING; the ladder
alone re-failed as DIFF. Together -> BANKED, R22 clean-fleet 140/140.
TWO LADDER CORRECTIONS (my own T0.2 error): bare harvest_verify is the LAST RUNG, not the ladder —
gate_stage runs canon_resident_calls -> cast_call_sites -> reconcile_tu -> ARITY -> sig_unify ->
harvest_verify, so T0.2's "8/8 PLUMBING" measured the UN-RECOVERED rate. And reconcile_decls.py is
RETIRED (R33, superseded by reconcile_tu): asking "what does the FLEET call this symbol?" is wrong by
construction in a loosely-typed engine (548 of its answers conflicted, rewriting 60 of 196 drafts) —
so Drew's suggested tool would have made it worse.
SCOPE, MEASURED (not over-generalised, §80): the idiom appears at only 5 sites corpus-wide — BUT one
gates a 123-member family (133 staged drafts all carry the un-recomputed +0x20 with different
per-overlay bases), so the mechanical fix is worth ~240 ins x 123 ~= 29,520 ins. It does NOT explain
the pool generally: func_80144090 / func_8012CC88 / func_8014D12C have ZERO derived-offset sites and
fail for a different, still-undiagnosed cause.
THE FIX IS MECHANICAL: correct_literal = mapped(aliased_sym) - mapped(base_sym). The remap already
holds both mappings, and the exemplar's own comment names the aliased symbol.
Also observed: the ARITY pre-pass left 40 TUs of caller-decl edits after a 0-bank run (same hygiene
bug as --normalize-self-decls, twice in one session) — reverted, both binaries byte-identical.
|
||
|
|
1d44ce25f3 |
docs(phase-29): CORRECTION — the T0.1 "zero-crack pool" is NOT banked (4 of 1,073 members = 0.4%)
Drew asked whether the 347,892-ins pool was banked. It was not: 4 members (func_801463A0 x2, func_8017B490 x2) = +396 instr-weighted / +194 distinct-code, ~1 part in 900 of the prediction. FRAMING ERROR OWNED: I labelled the pool "FREE" and "the actionable shortcut". The h_seq classification establishes "no DRAFTING needed" (exemplar matched, members structurally identical); it does NOT establish "no WORK needed", which is how "FREE" reads. The probe located the work: - 2 of the 8 top families (270 members) refused BEFORE any gate by the §42e pin guard => "FREE" did not even imply sweepable - of the members that reached the gate, 67% hit declaration plumbing, 0% hit compiler walls - both plumbing keys tried FAILED (--fix-def-sig regressed; --normalize-self-decls 0/123) Pool status: real, structurally confirmed, not gcc-blocked, still LOCKED. To bank it: find the working key (reconcile_decls.py — the DATA-symbol analog, and one failure text WAS a DATA symbol — or canon_sig_reconcile v3.2, both untried), then re-sweep (mechanical, zero-token), and handle the pin-refused families via --allow-pins. Recorded rather than quietly superseded because this pool has been mis-called in BOTH directions (P26 dead-off-a-broken-tool, P28 same family 89%). A prediction stated as a bank is how that happens. |
||
|
|
adafeb13d6 |
feat(phase-29): T0.3b autopsy — 44% of the "near-miss backlog" are not near-misses; 315 are plumbing-blocked MATCHes
Recomputed every backlog residual from the bytes (1,699 rows, -j 12, zero agent tokens) through the validated match_one path, deriving asm-subdir + -O0 from corpus.py. R34 cross-check PASSED (closeness agreed with masked_diff.structured_diff on all 1,610 built rows, 0 classifier errors); 89 nobuild rows REPORTED not dropped (R32). BUCKETS: redraft 707 | structural 528 | integration 315 | permuter 57 | unknown 3. 1. HONESTY CORRECTION: 707 of 1,610 (44%) are class SIZE-MISMATCH — the stored best-draft is a PARTIAL, an incomplete attempt logged with a closeness score (the func_80183814 666-of-5,122 shape). docs/backlog.md has been overstating readiness by ~44%. These route to a FRESH CRACK, not to a wall and not to the permuter. 2. ACTIONABLE: 315 entries are match_one MATCH *right now*, blocked only on the reconcile ladder — recomputing beat trusting the stored label because the tree moved since they were logged. ~108,959 gain-ins; top func_80174CB0 (16,482), func_801463A0 (13,534). §52b still applies: ~half of close=0 drafts fail the whole-binary gate, so these are CANDIDATES not banks. 3. The permuter bucket is 57/1,610 = 3.5% (Task-13B measured 7.7% and called targeting the problem). Extending the mutation set is CONFIRMED not the big lever — small, real, now bounded. 4. R34 again: 3 of 4 comparable labels DISAGREE with measurement — func_80140D68 / func_8012A328 / func_801549F8 recorded "schedule" but measure ADDRESSING -> cse. The grinder was aimed wrong. CONVERGENCE: T0.2 (8/8 failures PLUMBING, 0 walls) and T0.3b (315 integration) independently point at the SAME lever — the declaration/integration reconcile ladder, worth the 224,410-ins FREE pool AND ~108,959 backlog gain-ins. Two keys eliminated today; untried: canon_sig_reconcile v3.2 and reconcile_decls.py (the DATA-symbol analog — one T0.2 failure text was a DATA symbol). |
||
|
|
bce8cf3248 |
feat(phase-29): T0.2b + T0.3 — two levers eliminated for ~0 tokens; the backlog is current but 92% unclassified
T0.2b --normalize-self-decls: CLEAN NEGATIVE 0/123 on func_8013D53C (the family whose failure text matches the flag's own documented fix). The FREE pool's blocker is a DIFFERENT declaration class than either tested flag — --fix-def-sig REGRESSED it, --normalize-self-decls no-ops on it. Untested next candidates: canon_sig_reconcile v3.2 and reconcile_decls (one failure text is a DATA symbol, "conflicting types for D_800A651C", which neither tested flag targets). TOOL HYGIENE DEFECT: the flag's transform is byte-neutral by construction, so the non-neutral backstop never fires and it LEFT ALL 123 EDITS IN PLACE after banking nothing (123 files / 246+ / 246-). Byte-safe but a "git add -A" trap. Reverted; spot-rebuilt ov_SC01_000 + ov_SC07_010 BYTE-IDENTICAL. Candidate fix: on a 0-bank group restore the snapshot regardless of neutrality (§61's law applied to the success path — "neutral" is not "wanted"). T0.3 triage: 1,622 live entries, P9 filter finds 0 stale (backlog.py's drop-now-matched works). LEDGER DEFECT (R32): the addr field is null for 1,501/1,622 (93%) — the address survives only inside the name field, so an addr-keyed consumer silently sees 7%. My own first pass fell into it (read 121, reported off a 7% sample); name-derivation resolves 100%. Fix flagged, not applied mid-session. Closeness: 333 at 0, 184 at 1-4, 589 at 5-20. 1,486/1,622 (92%) UNCLASSIFIED — matching residual_class.py's own docstring. Highest-value next: run residual_class over the unlabelled set so the close band routes to a lever instead of grinding undirected. Zero tokens. |
||
|
|
6676ed8406 |
chore(phase-29): preserve the func_80183814 round-1 deliverables (R20)
367k agent tokens of recon: the 99.3%-structural draft, the 15-row do-not-re-buy table WITH BASES
(§80), and the 6-file harness that regenerates the draft identically (edit the 72-ins template once
-> re-propagates to all 35 sites). .run/giants/*.{c,md,py} is the curated allowlist; round 2 starts
from these rather than re-deriving.
|
||
|
|
d3e6d6a702 |
feat(phase-29): T0.2 gate probe — the FREE pool is PLUMBING-blocked, not wall-blocked (4 banked)
MEASURED, not projected (R14): 12 gate attempts across ov_SC01_000 + ov_SC01_001, one draft per build for clean attribution. - 4 BANKED (func_8017B490 x2, func_801463A0 x2); 8 failed; **0 DIFF — zero compiler walls** - all 8 failures are the §75a/def-side declaration class: `conflicting types for 'D_800A651C'` (DATA sym) and `conflicting types for 'func_8013D53C'` (the member's OWN def-side decl) - => raw conversion 33%, but the ceiling is NOT 33%: the blocker is declaration plumbing, which this project has named tools for. Plumbing recovery has out-earned drafting in every phase that measured both (P19 fix_arity_callers, P28 dedup_extend 6,174 members / 95.6% from one new mode) TWO CORRECTIONS TO MY OWN T0.1 POOL MATH, both downward: - 2 of the 8 top "FREE" families were refused outright by the §42e pinned-exemplar guard (the 270 skips) => "FREE" does NOT imply sweepable; pins are a third blocker the decomposition missed. Recoverable (--allow-pins; SESSION-19 banked pinned families x134), but I mis-labelled them - n_templatable counts the matched exemplar, so every T0.1 family figure is ~1 member (~0.7%) high NEGATIVE RESULT (§80, scoped to this base): --fix-def-sig REGRESSES this class — 0 banked and 2 PLUMBING became CC1-FAIL despite targeting the same error text. Do not re-buy without re-testing. NAMED NEXT LEVER: family_sweep --normalize-self-decls, whose help text cites fixing "the conflicting types for func_X that blocked 133/137 of func_801670E4" — exactly this failure. Gate-phase transform, so it cannot run under --stage-only, and --limit caps FAMILIES not MEMBERS => needs a full ~123-member family run. Highest-value outstanding probe, zero agent tokens. R22 clean-fleet 140/140 BYTE-IDENTICAL; tools-health/dedup 1886/0; 0 NON_MATCHING (G4). Fleet: instr 80.6% (10,589,065) · distinct-code 68.3% (3,846,416) · fn-count 89.19%. |
||
|
|
1fe4850136 |
feat(phase-29): T1.1 func_80183814 round 1 — 99.3% structural, named lever; cookbook §83
- VERIFIED INDEPENDENTLY (R14): match_one reproduces DIFF 5127 vs 5122, LENGTH-DRIFT/+5. Agent did not over-claim; tree untouched. Difflib-aligned truth: 36/5122 structural (99.3%), 17/21 cases EXACT, args+locals BYTE-EXACT at 216B - §83a: on a LENGTH-DRIFT class match_one's mismatch count is NOT a progress signal — 4,622 and 36 describe the same draft (index-wise comparison smears every index after the delta) - §83b THE LEVER: the handoff's '35x repeated template' (which I passed on flagged UNVERIFIED) is TRUE and was the whole game — 2,625 of 5,122 ins (51%) from ONE parameterised 72-ins body. Three sub-levers: pointer walk (no strength-reduction under -G0), rand()%(u32) for divu, cast barrier vs combine - §83c TRAP: the inherited 'dead local' pad[32] is gcc's OWN SPILL AREA — removing it made the locals area byte-exact. §83e: two 'pure allocation' residuals were a copy-pointer walk -> zero (§80 again) - §83d THE STALL, cited: cse.c:8340 sizes the quantity table by WHOLE-FUNCTION pseudo count, so no per-case edit can move a function-global CSE fork. Next move = close the +5 (buys length parity AND perturbs max_reg), then re-run the do-not-re-buy table on the new base - no pins in the deliverable (diagnostic-only, table row 15) — agent self-reported unprompted - DECISION: round 2 QUEUED, not spent now — T0.2 (224,410-ins pool) outranks a ~0.04pp lever |
||
|
|
57ee715f3c |
feat(phase-29): T0.1 frontier survey re-run (138 ovs) — the family lever is ALIVE; a 224,410-ins zero-crack FREE pool
- verified the tool BEFORE trusting its scan (R35): load() correctly globs all 138 overlays, but the generated header hardcoded '134' -> fixed to derive from the same glob (a doc misreporting its own scope is the P28 img_path shape, one severity down) - stale(07-23,134ov) -> fresh(07-26,138ov): fleet 88.5/79.0/68.4 -> 89.4/80.9/69.0%; families 2721 -> 2688; substantial 558 -> 544; with-matched-sibling 74 -> 76. Structure STABLE => the P25 family reframe is NOT an artifact and P26's ~0% stays unsupported post-fix - FINDING: 3,419 instances banked but only 85 distinct CLASSES fell -> recent yield was propagation, not new classes (SESSION-19's split, now fleet-wide) - THE POOL: 76 zero-crack families (exemplar already matched) = 347,892 ins = 19.4% of remaining distinct code, decomposed by real blocker: FREE(PURE/non-jr/non-O0) 61 fams/224,410 ins = 12.5% of remaining; jr 13/57,311 (§81 chain); -O0 2/66,171 (known deferred build-infra, Arm A proved 9/9 bank) - STILL A PREDICTION (R14/G3): T0.2 re-targeted from this data to measure the GATE conversion rate on 8 members sampled across the FREE subset before any arithmetic scales |
||
|
|
4a8e8f7e36 | docs(phase-29): the Drew-approved measured-order plan (T0 discovery-first) + the roadmap-to-100 gaps | ||
|
|
5873bd9cc8 | docs(phase-29): record the absolute-include portability defect as a PhaseEnd carry item (Drew: handle later) | ||
|
|
661f5aa751 |
feat(phase-29): bank func_8017C730 x ov_SC03_013 (+1,061 ins) via the §81 carve chain
The SESSION-19 handoff's item 1, closed as specified — no drafting, no agent. - §77 MINIMAL CLOSURE (519 lines, not the 2,993-line whole-file carry): 18 gte_* macros + 5 externs + the bandsetup static-inline helper -> match_one MATCH (1061 ins) - §81 chain, each step byte-gated before the next: jr_isolate_all --only (2 fns/1 object) -> BYTE-IDENTICAL; jtbl_carve --func (single-table, 44-piece interleave) -> BYTE-IDENTICAL; harvest_verify --chunk 1 -> verified 1 / failed 0, 7042bc71 BYTE-IDENTICAL - R22 clean-fleet 140/140 from a genuinely clean tree; tools-health OK; dedup 1886/0; 0 NON_MATCHING (G4). FLEET distinct-code 3,845,161 -> 3,846,222 = 68.3% (+1,061, all distinct — a behemoth-class bank, not a propagation); instr-weighted 80.6% - No §75a class spoke: the exemplar's ApplyMatrixSV(void*,void*,void*) canon fix was already carried, so the declarations were clean and it banked first try - cookbook §77: the ladder CLOSED with all four rungs measured (-56 -> -34 -> MATCH-but-uncommittable -> MATCH+BANKED), plus a NEW subsection — the CANDIDATE gate and the REAL gate need DIFFERENT preambles (match_one compiles standalone, so a shared-type body's CC1-FAIL is a report about the PROBE, not the draft; the types header goes in a throwaway probe copy, never in the banked draft) - FINDING, flagged not acted on (P5d): that shortcut already leaked an ABSOLUTE include path into 21 git-tracked files / 23 lines. All 21 verified semantically no-op (guarded engine_types.h via engine_core.h at line 2) => removal is byte-neutral, but cpp must still find the literal path, so those TUs cannot preprocess on any clone not at /home/musashi/bfm-decomp. Invisible to every byte-gate (R34's null-oracle shape, aimed at portability). Proposed as the next task. |
||
|
|
3b3728b19d |
docs(phase-29): checkpoint — an explicit START HERE NEXT SESSION block
Drew asked for the next-session recommendation to be logged. Added a ranked "START HERE" block above the open-actions list: 1. func_8017C730 @ ov_SC03_013 FIRST (~30 min, +1,061 ins) -- the match ALREADY EXISTS; pure integration, no agent. Minimal preamble (bandsetup + 5 externs + 18 gte_* macros) then the §81 carve chain. Explicitly warns NOT to re-carry the whole region file (standalone MATCH that fails the real gate -- the §77 corollary, measured). 2. THEN func_80183814 (5,122, the biggest left) with one Opus 5 agent @ xHigh, and an HONEST expectation reset: the family bonanza is over. All six behemoths banked this session were one renderer family with matched relatives bracketing them -- that is why 5 of 9 levers were readable rather than discoverable. func_80183814 has 0 fingerprint overlap and 37 callees; it is a different subsystem. Budget TWO passes (the func_8017BF14 shape, not the func_8017C954 near-one-shot); a 99% round 1 is on-plan, not a stall. 3. Then func_8017D2DC (32 callees -- §71 IS usable) and func_8017DC1C (ZERO callees -- §71 CANNOT fire; use §79 DATA-symbol fingerprinting, shared syms only, and read a matched relative's fingerprint from its banked C since matched fns have no nonmatchings/*.s). A 0.00 from §71 on a leaf means "cannot answer", not "no relative" -- that error cost a whole agent brief today. Also notes that behemoths were the ONLY thing that moved distinct-code this session (+26,730 of +31,649), so they stay the lever if the queue holds. |
||
|
|
cf570ec75c |
docs(phase-29): checkpoint — reflect §77's 5th variant now folded into the cookbook
The checkpoint said §77 'gains its 4th variant' and listed 4; the static-helper variant is the 5th and was only in the phase log until commit:1027 folded it into the cookbook proper. Both mentions corrected so the checkpoint and the cookbook agree. |
||
|
|
2199c71192 |
docs(phase-29): §77 — add the 5th measured variant (static helper) + the minimal-closure corollary
Drew asked whether the cookbook was actually being updated per behemoth. It was (13 commits, each paired with its bank), but the check found a REAL GAP: the last probe's two lessons went into CURRENT_PHASE.md and a commit message and were never folded into §77 itself. So the cookbook PREDICTED the static-helper variant (its closing line named it) without recording that the prediction had since been CONFIRMED, and lacked the corollary entirely. - VARIANT 5: a `static inline` helper, dropped by family_remap -> LENGTH-DRIFT/-56 with NO compile error at all. The nastiest variant precisely because it produces no diagnostic: the draft compiles clean and is simply ~56 instructions short, which reads as a codegen residual rather than a missing construct. Rule added: a NEGATIVE length drift with no compile error on a mechanically-remapped sibling means look for an uncarried static/inline helper BEFORE touching a lever. - COROLLARY (measured, and it cost a bank): carry the MINIMAL TRANSITIVE CLOSURE of what the body references, not the whole file. Carrying the exemplar's entire 2,993-line region file produced a clean standalone match_one MATCH and then failed the whole-binary gate on PLUMBING -- over-carrying trades a match_one failure for an in-TU collision. Measured ladder: -56 (nothing) -> -34 (helper + externs) -> MATCH-but-uncommittable (whole file); the minimal set is the only bankable point. - Also recorded: the walk-back-to-previous-brace heuristic breaks on an ISOLATED REGION FILE (_jr_<addr>.c from jr_isolate_all), where the construct above the function IS the needed helper -- it returns a 1-line preamble. A preamble-carry tool needs a reference-closure rule, not a positional one. |
||
|
|
22798c2809 |
docs(phase-29): SESSION-19 FINAL CLOSING CHECKPOINT
Fresh session safe here. No background job running; tree clean; R22 clean-fleet 140/140 (12x); tools-health OK; 0 NON_MATCHING; dedup 1886/0. HEAD at 38 commits this session. FLEET 80.6% instr / distinct-code 3,845,161 = 68.2% / fn-count 89.18% (opened 80.0/67.7/89.02). +31,649 distinct-code ins: 26,730 from SIX behemoths + 4,919 from the h_norm-remap pool. Every propagation win contributed +0 to distinct-code -- the session's most actionable finding. Records: the banked table (11 entries incl. six behemoths and the largest match in the project, func_8017BF14 at 4,763 ins); ten cookbook entries §73-§82 all from measurement; the through-line (almost every cap was our own tooling or my own use of it, including four of my own claims that collapsed under checking); six ranked open actions with named causes; six method traps that silently return 0.00 or a false MATCH; and the measured behemoth economics (two passes at 4,700+ ins, second cheaper; reading a matched relative beat the clever lever five times). |
||
|
|
9d04c0d6df |
docs(phase-29): func_8017C730 @ ov_SC03_013 — standalone MATCH, not banked (§77 4th variant)
- family_remap alone: -56 LENGTH-DRIFT. §77's own text predicted the cause verbatim (a "static" helper is a preamble construct the extractor does not carry): the exemplar uses "static inline void bandsetup(...)" -- the §82-oracle-1 inlined helper -- and none was carried. helper + its 5 externs: -56 -> -34. Whole 2,993-line region file as preamble: MATCH (1061 ins). - BUT the full-file carry is wrong for BANKING (my error): right for a standalone match_one compile, collides wholesale in the real TU. Gate -> PLUMBING, reported as "conflicting types for memcpy" = the §58 red-herring; the real cause needs a hand-splice + real cc1 stderr. - NAMED NEXT STEP: minimal preamble = bandsetup + its 5 externs + the 18 gte_* macros from that region file, then the §81 carve chain (this sibling is ALSO a jr function). Draft preserved at .run/giants/s19_func_8017C730_SC03_013_nearmiss.c - §77 gains its 4th measured variant (static helper) + a NEW COROLLARY: the right carry is the MINIMAL CLOSURE of what the body references, not the whole file -- over-carrying trades a match_one failure for an in-TU collision. Also: s19_remap_tu.py's walk-back-to-previous-brace heuristic breaks on an isolated region file, where the preceding construct IS the needed helper. |
||
|
|
109ce6a2c3 |
feat(phase-29): BEHEMOTH #6 func_8017C730 BANKED (1,061 ins) + §82 two source-shape oracles
- CRACKED at xHigh and VERIFIED INDEPENDENTLY: match_one MATCH (1061 ins); agent re-matched 3x from clean runs (100% register-masked AND register-kept, all 10 regions, frame 0x270 exact). §81 carve chain clean first try: jr_isolate_all --only -> byte-identical cacaf7c2 -> jtbl_carve (43-piece set) -> byte-identical -> bank -> R22 clean-fleet 140/140, tools-health OK. instr 80.6%; distinct-code 3,844,100 -> 3,845,161. - WHAT IT IS: the matched base func_8017CA80 + camera height-band cull + distance-driven CLUT fade. func_8004974C (TransposeMatrix) sits in a 36-ins prologue deriving a Y band; the part-level `lim >= g.otz` cull is GONE; flat arms gain an `sz < lim` near-plane cull. The base+one-extra-callee fingerprint predicted this exactly. - §82 ORACLE 1 -- A DUPLICATED `addiu $aN,$sp,K` ACROSS A `jal` MEANS THE BLOCK WAS INLINED. `&X` on any non-first local always creates a pseudo and CSE always merges two of them (expr.c:6260 ADDR_EXPR -> force_operand(..., NULL); exception: virtual-stack-vars offset 0). So the same stack address re-materialised at two sites separated by a jal means CSE was PREVENTED from merging => not the same function body. 17 non-inline spellings failed; a `static inline` helper reproduced the prologue BYTE-FOR-BYTE first try. Reusable probe: scan the ~1,200 built objects for that signature in NON-INCLUDE_ASM functions. - §82 ORACLE 2 -- SCALAR vs AGGREGATE DECIDES *WHEN* A STACK SLOT IS ALLOCATED: lazily at first `&` for a scalar, AT DECLARATION for an aggregate. Six GTE result words had to be six separate longs, not a struct, or they don't land after the inlined helper's temps and the frame isn't 0x270. Second-order: it also flips MEM_IN_STRUCT_P (§30's /s) -- with one word a fixed-address scalar, ((PolyF3*)pkt)->rgbc stops aliasing it, so a store needed respelling to keep the target's nop. A scalar-vs-struct choice is simultaneously a frame-layout AND an aliasing decision. - BANKING FOOTNOTE (§75a class A): first bank rejected `conflicting types for ApplyMatrixSV` -- draft (MATRIX2*, SVECTOR2*, SVECTOR2*) vs the TU/fleet canon (void*, void*, void*), 2,286 of 2,835 sites. Conforming the decl is byte-neutral and banked first try. On a jr function expect BOTH gates to speak: the carve chain answers the jump table, §75a answers the declarations. - Also reproduced: §78 (reuse a busy variable), §80(i) (a lever went -8 -> exactly neutral as the base moved), §72 (a register pin made it worse). - AGENT'S OWN CAVEAT, recorded not hidden: one zero-byte __asm__ keeps a vestigial `mnc = hmid` alive that flow.c would delete (costing 10 ins + the 0x130 spill slot). Emits nothing, compile is 1061 exact, but it is a documented stand-in -- 12 natural spellings measured, all DCE'd. |
||
|
|
c41acdc473 |
docs(phase-29): record the func_8017C730 agent in the checkpoint
Opus 5 (xHigh) on func_8017C730 (1,061 ins, ov_SC03_010). Records the strongest starting signal yet (shared-symbol set is a strict SUPERSET of the matched base func_8017CA80 -- all 6 symbols plus exactly one extra callee func_8004974C, and 1,061 vs 952 ins => base + ~109 ins of one feature), the five matched family exemplars bracketing it, and the §81 warning: it IS a jr function, so match_one MATCH is not the end -- banking needs the carve chain, which is my step. |
||
|
|
0907a35cf5 |
docs(phase-29): reconcile checkpoint — 5 behemoths banked, +30,588 distinct-code
HEAD commit:1021, 33 commits, R22 140/140 (11x), tools-health OK. Fleet 80.6% instr; distinct-code 3,844,100 = 68.2% (+30,588 this session: 25,669 from five behemoths + 4,919 from the h_norm-remap pool; propagation contributed +0). func_8017C954 added to the banked table; func_8017C730 recorded as the approved next target. |
||
|
|
faf4547345 |
feat(phase-29): func_8017C954 BANKED — jr carve chain cleared; a shared type was PRESENT but INVISIBLE
- BANKED (1,194 ins, ×1 distinct-code). Chain cleared, each step byte-gated before the next was
built on it: one-line fix to jr_isolate_all._engine_types() -> jr_isolate_all --only
func_8017C954 (2 fns / 1 object, NOT the bare 47-fn / 21-object resegment) -> BYTE-IDENTICAL
b7b0d4ae -> jtbl_carve --func func_8017C954 (44-piece carve set + interleave order) ->
BYTE-IDENTICAL -> harvest_verify VERIFIED BYTE-IDENTICAL -> R22 clean-fleet 140/140,
tools-health OK. instr 80.5 -> 80.6%; distinct-code 3,842,906 -> 3,844,100.
- THE DEFECT (tools/jr_isolate_all.py): _engine_types() harvested shared type names with four
patterns -- `typedef ... X;`, `} X;`, forward-decl `struct X;`, fn-ptr typedef -- and a TAGGED
DEFINITION WITH A BODY matches NONE of them. So `struct PW8017E6D8 { int w; }
__attribute__((packed));` at engine_types.h:658 was present in the shared header yet invisible
to the carried-type check, and `extern struct PW8017E6D8 D_801E1EC4;` could not be placed.
MEASURED BLAST RADIUS: 77 such tags in engine_types.h were invisible. One added pattern fixes
all 77.
- WHY THIS COST 20 MINUTES INSTEAD OF A MYSTERY BYTE-DIFF THREE PHASES LATER: the Phase-26 audit
had already turned this predicate's SILENT DROP into a LOUD REFUSAL. The original bug dropped
4,040 col-0 decls, 683 of them function PROTOTYPES -- and a dropped prototype is a SILENT
BYTE-CHANGER (C89 implicit `int f()`; return type drives delay-slot fill in this codebase). The
refusal named the exact symbols and the exact remedy. A loud "I cannot place this" is worth far
more than a green build -- the audit paying for itself, live.
- §81: the 3-step jr-carve chain + why match_one CANNOT see the problem (it masks jal/HI16/LO16,
so a jump-table function reports MATCH while the whole-binary gate reports DIFF, correctly).
Detect with `grep -cE 'jr \$(v0|v1|a0|t[0-9])'` on the target .s + a jtbl_ in asm/<ov>/data/.
ALWAYS use --only: bare would have resegmented 47 jr-functions across 21 objects.
|
||
|
|
fa51d30d3e |
feat(phase-29): func_8017C954 MATCHED (1,194 ins) — banking blocked on a NAMED 3-deep infra chain
- CRACKED by an Opus 5 agent @ xHigh and VERIFIED INDEPENDENTLY: match_one -> MATCH (1194 ins),
100% every region, 1129 -> 1069 -> 37 -> 28 -> MATCH. It is the matched base func_8017CA80
(952) + two deltas: a 14-ins grey-colour prologue from D_801DCCA0, and a FIFTH switch arm
(case 2 / case 3 split, proved against the real jump table) emitting a POLY_FT4 plus a 7-word
subtractive overlay.
- NOT BANKED. The whole-binary gate said DIFF and it is RIGHT: this is a jr (jump-table) function
(jr $v0 at .s:409; table jtbl_801DB70C in asm/ov_SC06_029/data/tail21.data.s). Matching the C
makes gcc emit that jtbl into .rodata while the raw copy stays in the data tail -> duplicate +
wrong address. match_one masks jal/HI16/LO16 so it CANNOT see this -- the §53 carve law.
- THE CHAIN, each step failing LOUD with its own remedy (the tooling behaved well, R32/R35):
(1) harvest_verify -> DIFF, not PLUMBING.
(2) jtbl_carve --func func_8017C954 -> refuses: subseg ov_SC06_029_jr_8017AE2C would host
NON-CONTIGUOUS .rodata carves (0xb3468, 0xb35b4); one object can't leave a gap for the
unmatched jtbl between them. Remedy: isolate into its own code subseg first.
(3) jr_isolate_all --dry-run (47 jr / 21 objects) -> REFUSES: 2 file-scope decls
(extern struct PW8017E6D8 D_801E1EC4/EC8) could not be placed, and it will not emit a region
that silently omits them ("a dropped prototype is a SILENT BYTE-CHANGER" -- C89 implicit
int f(), and return type drives delay-slot fill here). NB struct PW8017E6D8 IS already in
engine_types.h:658, so this looks like a placement-logic gap, not a missing type -- that is
the precise next thing to check.
- => banking is a bounded BUILD-INFRA task (T2 config resegment => full R22), not more matching.
Deliberately not started this deep into the session. Match + harness preserved and tracked.
- AGENT FINDINGS: §80(i) confirmed twice more (x_e1swap measured exactly neutral then later paid
-2; the za lever measured worse and became necessary two levers on). NEW DIAGNOSTIC: when a
residual is "a whole block of registers renamed by ONE SLOT", read the .greg `;; N conflicts:`
AND `;; N preferences:` lines for the block's top allocno -- a missing hard-reg conflict plus a
new copy preference is the signature of a one-slot slide, one dial away not forty bugs
(c954_reg.py, the per-region scorer, is the reusable tool).
- HONEST CAVEAT (the agent's own): its lever 1 is a hand-placed byte-free __asm__ register-clobber
dial, not a construct the original author would have typed; 14 natural spellings were tried and
measured. Bytes unaffected, true source shape unfound; the report names the next probe.
|
||
|
|
9b4b027f0e |
docs(phase-29): record the func_8017C954 agent + the fingerprinting method trap
Opus 5 (xHigh) on func_8017C954 (1,194 ins, ov_SC06_029); func_8017C730 queued next per Drew's approval of successive single agents. Records why this target (1.00 SHARED-symbol fingerprint vs the matched renderer base; 4 matched exemplars bracket it) and — importantly — the two ways I got the fingerprint wrong before getting it right: per-overlay D_801????? names can never match across overlays (compare only shared syms < 0x80128158), and a MATCHED function has no nonmatchings/*.s so its fingerprint must be read from its banked C. Both mistakes silently return 0.00. |
||
|
|
da5b32ac4c |
docs(phase-29): reconcile checkpoint — 4 behemoths banked, +29,394 distinct-code
HEAD commit:1017, 29 commits, R22 140/140 (10x), tools-health OK. Fleet 80.5% instr; distinct-code 3,842,906 = 68.2% (+29,394 this session: 24,475 from four behemoths + 4,919 from the h_norm-remap pool; propagation contributed +0). func_8017BF14 added to the banked table as the largest single match in the project; its open action retired; 5 behemoths remain. |
||
|
|
8b828f1ea6 |
feat(phase-29): BEHEMOTH func_8017BF14 CLOSED — 45 -> 0 (4,763 ins, the largest match yet)
- 45 -> 37 -> 33 -> 21 -> 11 -> 3 -> 2 -> 0, reproduced 3x from independent work dirs. Verified independently before believing it (R14): match_one MATCH (4763 ins), then harvest_verify --binary ov_SC03_116 BYTE-IDENTICAL, then R22 clean-fleet 140 passed, 0 failed of 140. distinct-code 3,838,143 -> 3,842,906 = 68.1% -> 68.2%. instr 80.5%. Agent was interrupted by a weekly API limit and RESUMED FROM ITS TRANSCRIPT -- its round-2 harness survived, nothing was re-derived. - §80 THE PROCESS CORRECTION, worth more than the match: A DO-NOT-RE-BUY ENTRY IS SCOPED TO ITS BASE, NOT TO THE FUNCTION. Three of round 1's ~40 measured negatives INVERTED on round 2's base -- the same edit (qsingle23) measured 1,040 mismatched on the 45-base and 11 on the 21-base. Re-testing the round-1 negative list cost ~20s and produced THREE of the seven winning levers. Such a table records (edit, base) -> result, NOT edit -> useless; after any lever that moves the base materially, RE-RUN THE NEGATIVE LIST. This retroactively qualifies every do-not-re-buy table in the cookbook (§45, §60b, §75a, §76, §78, §79). Concrete: round 1 measured "removing the va->$t2 pin costs 4% elsewhere" => keep the pin; on a base with c0..c3 at function scope, removing those pins is worth 21->13. Same experiment, opposite conclusion. - MY FLAGGED "#1 MOVE" LOST, and the failure is the finding. I briefed variable REUSE (§45-A / RC-14) as the top lever because it took func_8017F510 from 97->10. Swept in full here: EVERY merge lost, 43-3294 across 8 merges. Reason: the TRI and QUAD grants did not differ by RANK but by IDENTITY -- two independent allocno sets, and re-ranking inside one set cannot fix a two-set problem. Diagnose ranking-vs-identity before reaching for a merge. The actual fix (c0..c3 at FUNCTION scope, 33->21) was read off the two matched relatives (b5:310, b4:338) and confirmed against the target -- the 4th time today that reading a matched relative beat the clever lever. - PIN'S HIDDEN COST, cited: combine_regs' hard-register branch (local-alloc.c:1795, reached from :1295 with already_dead==0) records the pinned reg in qty_phys_sugg UNCONDITIONALLY -- no death guard. A pin invites local-alloc to tie producer chains into it. New cure R7: a zero-byte __asm__ ref keeping the pinned value live past the temp so find_free_reg can't honour the suggestion -- closed the last 2 ins (c1->$a0 is uniquely load-bearing; every alternative pin lost 64 ins). - §78's attribution primitive RUN and REPRODUCED: under -fno-schedule-insns, -fno-schedule-insns2 and both, order unchanged => the rgb transposition was never a sched.c decision. - Cold-start economics complete: round 1 = decode + exact length + exact frame + 99.06%; round 2 = the last 45, and cheaper. Budget TWO passes at this size. 5th source copy-paste artefact found. |
||
|
|
bd768d8a4e |
docs(phase-29): record the func_8017BF14 round-2 agent in the checkpoint
Opus 5 (xHigh) closing the last 45/4763. Checkpoint records its deliverables, sandbox, the round-1 residual map (a)/(b)/(c) with the measured do-not-re-buy constraints, the #1 move (variable-REUSE sweep across c0..c3/a0v..a3v -- the one §76 lever class round 1 never swept, and the exact merge that took func_8017F510 from 97 to 10), and the cheapest unrun probe (the §76 attribution primitive on residual (c)). |
||
|
|
f5f8dec5ee |
docs(phase-29): the cold-start experiment — func_8017BF14 to 45/4763; §79; full R22 discharged
- COLD-START RESULT (verified independently): 4763/4763 ins, 45 mismatched = 99.06% byte / 99.94% structural, exact frame, exact opcode histogram. NOT a match; nothing banked (45 != 0, the byte-gate is the sole arbiter). The residual is 3 register-grant ties, 0 structural divergence. Named next move: variable REUSE across c0..c3/a0v..a3v, the one §76 lever class the pass never reached. - MY BRIEF'S PREMISE WAS WRONG BY CONSTRUCTION -> §79. I chose this target partly because §71's callee-set fingerprint returned 0.00 against every matched giant = "a genuine cold start". But the function makes ZERO jal calls, so its callee fingerprint is EMPTY and §71 CANNOT FIRE: 0.00 meant "cannot answer", not "no relative". Grepping the target's DATA symbol D_800A5E60 found the matched func_8017BEBC at once -- func_8017BF14 is the 4-light-box member of the same renderer family whose 3-box sibling func_8017D960 was matched hours earlier. RULE: when §71 returns an empty/zero-overlap callee set, fall back to DATA-symbol fingerprinting; an empty fingerprint must never become a cold-start brief. - NEW LEVER (§79): THE FRAME LAYOUT IS A DECLARATION-ORDER ORACLE. gcc-2.7.2 assigns stack slots to spilled pseudos in pseudo-number order, and pseudo numbers follow first use ~ declaration order -- so the target's frame map reads back its source's declaration order. Moving ONE line took 73% -> 84% structural and brought all 127 slots into exact correspondence. - §76 CONFIRMED AT SCALE: the entire -62 length residual was ONE allocno-class decision (c0..c3 declared inside the cull blocks -> 1-death local allocnos -> global.c:668-671 removes those regs from the global pool -> r1lo spills), 52% -> 93%. An __asm__ ref-dial reached the same spill and scored WORSE -- declaration scope beat the ref dial again. - PIN NUANCE: pins are safe on a 0-jal function (§74's hazard cannot arise), 4 pins took 94% -> 99%; but §72 held -- pins 5 and 6 made it worse. - EFFORT ANSWER, HONEST: xHigh from a genuine cold start on a 4,763-ins giant bought the decode, the exact length, the exact frame and 99.06%, and did NOT close. Budget a SECOND pass at this size: the first buys structure, the last ~1% is register grants. - FULL R22 DISCHARGED: make clean + extract-all + check-all -> 140 passed, 0 failed of 140 (run after the agent finished, per the deferral recorded in the pool commit). tools-health OK. |
||
|
|
a1f1a948af |
feat(phase-29): h_norm-remappable pool — +4,919 ins banked, no cracking, no agent
- BANKED (each whole-binary byte-gated; make check-all -> 140 passed, 0 failed of 140): func_80130D48 ×4 (1,064) · func_8018F3E4 (478) · func_8018B3D0 (478) · 13 × 223-ins siblings of func_8017E6D8 (2,899). distinct-code 3,833,224 -> 3,838,143 = 68.0% -> 68.1%. - TWO OF MY OWN COUNTS COLLAPSED UNDER SCRUTINY BEFORE I ACTED ON EITHER (R14/R35): "func_8017CA80's family = 102 unmatched" was really 13 -- my count tallied family members whose NAME appears as a stub anywhere in the fleet, not instances actually unmatched (a semantics error, not arithmetic). "56,267 ins remappable" was really 8,114 -- 86% was the known -O0 / deferred set (the func_80144B9C whale, the func_8013C414 cluster). I nearly recommended a target on the first number. - THE §77 CARRY GAP IS THE DOMINANT COST OF MECHANICAL REMAP: 23 of 27 first-pass CC1-FAILs. NEW .run/giants/s19_remap_tu.py sources the preamble from the exemplar's OVERLAY TU (the block between the previous top-level `}` and the def), applies family_remap's own substitution map, and adds the two includes match_one never adds -> 21 drafts went 0 MATCH -> 14 MATCH. The 13 223-ins siblings share ONE exemplar, so a single preamble fix cleared all 13. - USEFUL ASYMMETRY: func_8018F3E4/func_8018B3D0 FAILED match_one but BANKED in the whole-binary gate -- the real TU supplies decls the standalone compile lacks. A match_one CC1 FAIL is not a reason to skip the real gate on a remapped sibling. - RESIDUAL 3,195 ins, causes NAMED not guessed: func_8017D5C0 (952) matches standalone, gate reports `conflicting types for memcpy` = the §58 red-herring (a warning from an unrelated TU position; SESSION-14 hit the same label and the true cause needed a hand-splice + real cc1 stderr). func_80166994 ×3 + func_8016A290 ×4 still CC1-FAIL after the TU carry. - FULL R22 DEFERRED DELIBERATELY: make clean wipes asm/, which the concurrently-running BF14 agent reads on every probe. This batch changed only src/*.c (no config), so check-all is sound; the clean R22 must still run once the agent finishes. |
||
|
|
95684f9807 |
docs(phase-29): reconcile checkpoint — 3 behemoths + a 5-member family banked
HEAD commit:1012, 22 commits, R22 140/140 (8x). Fleet 80.5% instr; distinct-code 3,833,224 = 68.0% (+19,712 ins this session, ALL from the three behemoths; propagation contributed +0). Banked table updated with func_8017D960's 5-member family. Open actions re-ranked: 6 behemoths remain (func_8017E778/func_8017CD9C are DONE as part of behemoth #2's family). |
||
|
|
f9a2de3edd |
feat(phase-29): BEHEMOTH #2 func_8017D960 CRACKED (1806 -> 0) + its 5-member family = 16,690 ins
- CRACKED pin-free at xHigh (Opus 5 agent), then ALL FOUR family siblings banked via §40 remap, each MATCHING FIRST TRY: ov_SC03_090 (the crack) · ov_SC03_089 · ov_SC03_104 · func_8017E778 @ ov_SC03_091 · func_8017CD9C @ ov_SC03_102 (the last two cross-address). Verified independently before believing the report (R14): match_one MATCH (3338 ins), then harvest_verify BYTE-IDENTICAL on all five binaries, then R22 clean-fleet 140/140. - METRICS: distinct-code 3,816,534 -> 3,833,224 (+16,690) = 67.7% -> 68.0%, the first percentage-point movement in that metric all session. instr-weighted 80.3% -> 80.5%. Session distinct-code total +19,712 ins, ALL from the three behemoths; propagation gave +0. - MY BRIEF WAS WRONG IN AN INSTRUCTIVE WAY -> §78. I said a negative length drift means "missing instructions". The 4 absent instructions were 4 emit tails × 1 nop -- delay slots the target could NOT FILL because the register it wanted was still live. otp at function scope has 4 deaths -> fails local-alloc.c:472 -> global allocno in $a2 -> via global.c:668-671 pushes tp off $a1 -> the 0xFFFFFF mask is free early -> maspsx hoists it into the slot. Declaring otp PER EMIT ARM fixed the whole drift in one edit (3334->3338, 1806->333). SECOND TIME IN ONE SESSION a "structural"-looking residual was an allocno-class choice (the first: F510's "scheduling" transposition, §76). A nop present in the target but absent from the draft is usually a register-liveness fact, not missing code. - TWO MORE REUSABLE FINDINGS (§78): gcc-2.7.2 fold NEVER leaves a literal first in an `|` chain (7 parenthesisations, all reassociate) -- so `or acc, var, K` first in the target means K was a VARIABLE in the source, an asm->source read that retires a whole sweep family. And "make it a variable" has TWO separable effects (fold-opacity vs a new allocno): a fresh short-lived local fixes structure and wrecks allocation (690 mismatched, damage ~300 ins away); reuse a busy one. - ECONOMICS: 9 levers, each necessary by drop-one ablation, and 5 of the 9 were read straight off the MATCHED relatives func_8017F510 (cracked earlier today) and func_8017CA80. Crack the smaller family member first -- it is a lever library for the larger one. - NEW TOOL .run/giants/s19_remap_family.py: family_remap + the §77 preamble carry in one step (reproduces the exemplar's FULL file-scope preamble with the tool's own substitution map applied). Took the 4 siblings from "4 rounds of CC1 FAIL each" to MATCH first try, ×4. |