mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 22:45:39 -04:00
85fb289db582d842fc41dc059fa187bb992e76ea
422 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e02fd38358 |
feat(phase-30): T0.5 — DefineFunctions completion pass wired into the batch (define missing stubs from splat truth, re-decompile)
Probe: main +476/477, fresh-import ov_SC03_001 +238/238, ov_SC02_011 +227/228. Raw-blob auto-analysis finds only the reachable subset (Phase-10 finding, now automated per program). |
||
|
|
bd76f2373c | fix(phase-30): T0.5 — import trigger matches Ghidra's actual 'not found' phrase (probe caught +0/371 silent no-fire); probe findings logged | ||
|
|
19766a6dc3 |
feat(phase-30): T0.5 — prefetch_fleet.py, the fleet Ghidra-C batch orchestrator (+ SETUP row, R21; fuel manifest ride-along)
One representative per remaining h_seq distinct class + all main/resident stubs -> .run/ghidra_c/. Resumable (skips cached); serial on the exclusive project lock; auto-stops a serving MCP (R23); imports missing overlay programs on demand via ghidra_import_raw.sh (blob derived via family_remap.img_path, vram from the splat yaml — R33, never guessed); R32 per-program outcome report, continues past failures. Dry-run: 126 programs / 7,966 uncached representatives. |
||
|
|
e03494dc1f |
feat(phase-30): T0d — backlog _open_stubs derives from corpus.stubs (STUB_RE deleted, R33); ledger verified already-clean
- Filter + prune existed since 07-24; jsonl already compacted (the tree's uncommitted edit was S25's un-committed prune output; prune today 1350->1350/0 dropped). Stale rows were worklist.md's. - Oracle agreement: 0 divergence across 131 ledger binaries (my first probe compared names vs int addrs — R35 on my own instrument). Hex-case canonicalized in the membership test (R32). - Parity proven post-change: load_best 1350 == 1350. §83 doctrinal caveat stands. |
||
|
|
de7b347f6f |
feat(phase-30): T0c — the family_hseq/progress 'gap' was a cross-date+scope misread; digests now self-stamp (scope+HEAD+oracle)
Same-tree regen: family_hseq(overlays) 27,248 == progress 28,296-1,034(main)-14(resident) EXACT. The 07-29 map was SESSION-25's open snapshot; 29,961-27,248 = 2,713 = the session's banked total. Zero definitional gap — both tools already derive from corpus.stubs (Phase 26-A). family-hseq.md header now stamps 'OVERLAYS only' + generation HEAD + compare-at-same-HEAD. Roadmap D-bucket corrected. Fresh readings: 478 substantial fam / 678,404 templ ins / 28 zero-crack (S25 ate 33). |
||
|
|
53b30962ee |
feat(phase-30): T0b — rtu_match FAIL verdicts surface the real cc1 error (filtered, first-15) instead of a warning tail
The SESSION-25 recipe (grep -v warnings from --stderr-out) applied in-tool across all 4 stages; gcc-2.7.2 hard errors have no 'error:' prefix so the old tail-truncation drowned them (cost 3 probes). Raw-tail fallback if the filter empties. Verified on a real deliberate CC1 failure. |
||
|
|
b5a28edae8 |
feat(phase-30): T0a — gate_stage stage-0 raw gate + fix_arity_callers per-edit journal undo (§122)
- STAGE 0: gate the RAW drafts before any transform (GATE_NO_STAGE0 escape) — the carried 'ladder destroys good drafts' defect (SESSION-22 reproduction: _o0 pair + func_80138C60, ladder-FAILED/bare-VERIFIED) is impossible by construction; ladder+arity now touch only stage-0 failures. TU-blind-transform root-cause hypothesis recorded in-code, open. - fix_arity_callers --journal/--undo-journal --keep: exact per-edit undo in the WRITER, shared by ladder AND bare workflows (the 17-TU residue class); replaces the two-special-case file snapshot; undo moved after stage 2 (closes the stage-2 arity parity gap); stale-journal guard. Negative-control: apply->undo byte-identical; --keep exact. - Flow test .run/t0a_flowtest/driver.py 7/7 PASS. Cookbook §122. CURRENT_PHASE T0(a) logged. |
||
|
|
7e32da8f64 |
feat(phase-29): T95/T96 — func_80142B2C 136/136 (§121); all 3 byte-identical stragglers closed
- The draft calls ((void(*)(void))func_80142C84)() but nothing declares that symbol above the splice: it is DEFINED by DEFINE_func_80142C84() in engine_core.h, so gather_externs has no extern line to harvest, and the member TU instantiates the macro BELOW our function. - The wrong guess was the useful step: a no-prototype `extern s32 func_80142C84();` turned `undeclared` into `conflicting types` — a DIFFERENT error, proving the diagnosis right and the type wrong. Synthesised from the macro's own definition head -> MATCH (34 ins) -> 136/136. - NEW macro_def_sig_map() (1,878 signatures): the complement of header_sig_map(), which reads the externs a macro emits FOR ITS CALLEES; this reads the signature a macro DEFINES. Cookbook §121. - ALL THREE byte-identical stragglers carried since SESSION-24 are now closed: func_80146750 137/137 (T84), func_801759D8 137/137 (T93), func_80142B2C 136/136 (T95) = 410 members, and not one was a compiler wall (a signedness-wrong header decl, a type-name collision, a missing extern). - Blast radius 0 (74 further families re-swept). FOUR data points now: only §117 (wrong LOGIC) generalised at 1,209 members; §118/§120/§121 are path-reachability gaps worth ~one family each. - GATES: R22 clean-fleet 140/140; dedup 1886/0; 0 NON_MATCHING (G4). - METRICS: fn-count 91.88 -> 91.96% (+273, exact) · instr 87.3 -> 87.4% (+12,296) · distinct +0 (both byte-identical families — §111 predicted exactly that). |
||
|
|
9a1507462f |
feat(phase-29): T93/T94 — func_801759D8 137/137 via type-uniquify (§120) + two T92 corrections
- CORRECTION 1 (R14/P9): T92's "strip-if-ambient" recipe was WRONG. Stripping the draft's duplicate
typedef breaks the extern that USES it (the TU's own copy sits below the spliced function), so the
"second stacked blocker" T92 recorded (D_800AF634 used prior to declaration) was my own fix
misfiring, not a real blocker. RENAME, don't remove: rtu_match CC1 FAIL -> MATCH (56 ins).
- CORRECTION 2: T91's wiring never RAN. family_sweep has THREE staging sites sharing the identical
two lines (edit-remap / hseq / plain h_norm); I patched by rindex twice, which lands on the PLAIN
site, so --hseq staged the draft unchanged and the lever looked ineffective. Re-anchored on the
hseq site's unique write (func_{to_addr:08X}.c) and the draft came out renamed. T91's revert was
right discipline on a false premise.
- RESULT: _uniquify_draft_types wired into the hseq path (byte-neutral — C type names never reach
codegen). func_801759D8, one of the three long-standing byte-identical stragglers: 0 -> 137/137,
0 failed. Blast radius 0 (74 further families re-swept, none moved) => TARGETED lever, like §118
and unlike §117.
- Cookbook §120, incl. the law: before concluding a lever does not work, prove it RAN — diff the
staged artifact for the change it is supposed to make.
- GATES: R22 clean-fleet 140/140; dedup 1886/0; 0 NON_MATCHING (G4).
- METRICS: fn-count 91.88 -> 91.92% (+137, exact) · instr 87.3 -> 87.4% (+7,672) · distinct +0
(byte-identical family — §111 predicted exactly that).
|
||
|
|
bf71232d0b |
feat(phase-29): T87/T88 — ordinal immediate resolution (§118): 158 banked
- The T86 asm-ambiguous refusal was CORRECT (a by-value swap would corrupt the non-differing occurrence); the safety TEST was too strict. It compared the C literal's occurrences against EVERY asm use of that value, but gcc synthesises uses no C token names — e.g. D_80187044[*(u16 *)((s32)a0 + 0x2)]() has one C literal 0x2 and TWO asm uses of 2 (the per-member offset + a fixed sll ..,2 for the 4-byte stride). Unsatisfiable by construction. - FIX (_ordinal_edits, §118): pair C occurrences to asm positions IN ORDER, accepting either len(spans)==len(asm_pos) (every use named) or len(spans)==len(diff_pos) (extras are implicit). Rewrite only occurrences whose instruction is in diff_idx. Order is a heuristic, so the whole-binary byte-gate stays the sole arbiter — a wrong pairing is rejected, never banked. - T87: func_801599A4 0 -> 137 drafts, 137 banked; +12 singletons = 149 (family 0x80131eec). - T88 blast radius: only 9 of the other 144 immediate-refusals converted (refusals 67 -> 34). A TARGETED lever, not a second §117 — recorded so it is not over-projected. - GATES: R22 clean-fleet 140/140; dedup 1886/0; 0 NON_MATCHING (G4). - METRICS: fn-count 91.79 -> 91.84% (+158, exact) · distinct-code 69,450 -> 69,593 (+143). |
||
|
|
dcbeebaf49 |
feat(phase-29): T84/T85 — 0x80161c98 137/138 + func_801457A4 133/133 (+270 members)
- T84 (item 1): the top still-zero family's whole diff was ONE instruction — slti (signed) vs the target's sltiu. --fix-def-sig was conforming a byte-correct draft to engine_core.h's signedness-wrong decl (extern void func_80161D20(s32,s32)) while the exemplar's own def is (int, u32). Re-swept the 92 still-zero families WITHOUT the flag: 137 banked (all of 0x80161c98), other 91 unmoved => family-specific, NOT a second §117. Recorded as such. - T85 (item 2): rewrote tools/rollout_801457a4_o0.py as the two-file ATOMIC driver §116 called for (remapped body -> <ov>_o0b.c AND drop the INCLUDE_ASM from <ov>_after.c in one edit; build vs config/check.<ov>.sha; restore BOTH files on mismatch, §61). Validated on 3, then 130/130. No splat change — the Arm-A re-carve wall never touched. - Item 4 PRICED AND DROPPED: STRUCT residue = 34 families / 166 members / 0.02pp. - R14: my new_distinct estimator over-projects ~2x (priced 259, measured 125) — it counts classes unmatched at run time, so concurrent sweeps double-count. Ranks correctly, overstates absolutely. - GATES: R22 clean-fleet 140/140; dedup 1886/0; 0 NON_MATCHING (G4). - METRICS: fn-count 91.72 -> 91.79% (+270, exact) · instr 87.2 -> 87.3% (+16,946) · distinct-code 69,325 -> 69,450 (+125). |
||
|
|
28dc3785f5 |
feat(phase-29): T82 — symbol-KIND fix in symbol_map: func_80174784 2/255 -> 251/251 (§117)
- CAUSE: family_remap.symbol_map zips exemplar/sibling reloc slots positionally and spelled the SIBLING's symbol from the EXEMPLAR's kind. Same-address families always agree, so it was invisible for 20+ phases; cross-address families need not agree — func_80174784's callback slot is the FUNCTION func_801747CC while member func_8017CFD4's same slot is the DATA symbol D_80182688. The map emitted func_80182688, the body materialized a name for an address that is not a function, and the fleet gate refused all 251 members. - FIX: spell the target by what the target address IS in the SIBLING's overlay (func_ iff in that overlay's sig set — the same boundary oracle nins_of trusts, R33; memoized). Phase 26-A had already established this rule and applied it only to the exemplar side. - WHY IT HID: rtu_match/match_one MASK HI16/LO16, so a wrong %hi/%lo symbol still reports a clean MATCH (measured: "MATCH (10 ins)" on a member the fleet gate rejected). masked-MATCH + whole-binary DIFF is the exact signature of a compiler wall. Cookbook §117 carries the law. - Also refuted en route (cheaply): --normalize-self-decls was NOT the cause — re-swept without it, still 0/251. - GATES: R22 clean-fleet 140/140; dedup 1886/0; 0 NON_MATCHING (G4). - METRICS: fn-count 91.41 -> 91.48% (+251, exact) · distinct-code 68,782 -> 69,024 unique fns (+242, projected 246) · instr +2,510. - BLAST RADIUS UNMEASURED: symbol_map serves every family sweep; 229 eligible non-jr families / 2,575 members have never been swept with a correct target spelling, incl. the byte-identical families T76 measured at 0/682 (same failure shape). |
||
|
|
90a644fb80 |
docs(phase-29): T80/T81 — two 0/N diagnoses + the SESSION-25 checkpoint
- T80: the §116 rollout prescription was WRONG and the build refuted it in 56s across 133 overlays. "Byte-neutral by construction" was a claim about the LINKER; splat keys asm/ generation to the SEGMENT, so deleting func_801457A4's INCLUDE_ASM from <ov>_after.c stops func_801457A4.s being emitted and <ov>_o0b.c cannot assemble. Reverted, nothing committed. Cookbook §116 corrected IN PLACE with the refutation + the corollary (build it before you call it neutral). Real route: a two-file atomic driver (body -> _o0b.c AND drop the stub from _after.c in one edit). 129 distinct still on the table, now costed. tools/rollout_801457a4_o0.py kept as the inventory pass ONLY — do not --apply. - T81: 0x80131eec 0/288, and the two halves have DIFFERENT blockers — func_80151944 (138) staged and gate-failed on the T71 decl conflict; func_801599A4 (137) + 13 singletons were REFUSED AT REMAP for unresolved immediates and never reached a compiler. My prediction that the correct-decl half would bank was the T76 error shape (reason from one property, ignore the disqualifying diff_class: IMM) — recorded, not buried. CORRECTION IT BUYS: T71's "the immediate engine is not the bottleneck" holds for T70's families and is FALSE here (150 of 288). T2a immediate resolution is now a named, sized lever. - Refuted from source before spending a probe: the reloc tracker DOES see a function address materialized as an argument (LO_OPS includes addiu), so 0x80174784's 2/255 is not that. - SESSION-25 checkpoint: fleet 86.9% instr / 77.4% distinct / 91.41% fn-count; 641 banked this session; ranked next-list with all six items measured. Nothing running, tree clean. |
||
|
|
611622c9e7 |
feat(phase-29): T78 — PsyQ-symbol widening: func_8012F40C 0/137 -> 137/137 (three places, not one)
I called this "a one-line predicate widening". It was THREE, and fixing the first two changed nothing
— the sweep still reported 0/547 (cookbook §115):
1. canonical_map : re.fullmatch(r'func_[0-9A-Fa-f]{8}') + keyed by parsed ADDRESS
2. DECL_LINE_RE : (func_[0-9A-Fa-f]+) as the name group
3. split_sig_string : \bfunc_[0-9A-Fa-f]+\s*\(
Each is a SILENT SKIP indistinguishable from "no conflict found". With 1+2 done the symbol reached 3
and died there; only tracing transform's internals (`callees cast: 0` while the canonical map plainly
held `s32 RotTransPers(s32, s32, s32*, s32*)`) located it. THE TRAP WORTH REMEMBERING: a partial fix
to a name-form assumption produces the exact symptom of no fix at all, so a correct hypothesis looks
refuted. Curated naming increases as RE quality improves, so any func_-only predicate is
rot-by-design — the same shape as stub_map's (Phase 26-A).
RESULT: func_8012F40C 0/137 -> 137/137. The other three families (801759D8, 80146750, 80142B2C) still
fail on different causes.
GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK; dedup 1886/0; 0 NON_MATCHING.
METRICS: instr 86.7% (+4,932 ins); fn-count 91.19% -> 91.23% (+137); distinct +0 (byte-identical).
NEXT: the byte-VARIANT tier is worth re-sweeping — T70 banked 1/10 BEFORE the callee axis existed, and
26 families remain unswept by the two levers added since.
|
||
|
|
970559423d |
feat(phase-29): T77 — wire the callee-decl lever into family_sweep; func_80173A60 0/135 -> 135/135
Item 1. The T76 diagnosis was right and the fix was a lever we already owned. cast_call_sites
(§17a-1/§20) handles the callee-conflict class and lived ONLY in gate_stage, which the family sweep
deliberately does not use — the THIRD instance this session of a lever unreachable from the path that
needs it (T56 data-decl unreachable, T57 function-decl off-by-default, now T77 callee).
the 5 byte-identical families : 0/682 -> 135/682
func_80173A60 specifically : 0/135 -> 135/135
Wired after scope_data_fix (orthogonal axes: data vs callee), default ON with --no-cast-callees. Two
details that matter: the canonical map is built from the TARGET sibling's TU via cpp
(canonical_map(ov, src_file=tu) -> cdecl.tu_scope) so it sees MACRO-INJECTED declarations — a
raw-text scan returns nothing for exactly the callees that conflict (§51g LAW 7) — and it is read
AFTER any tu-scope edit is on disk.
THE OTHER FOUR STILL FAIL, different causes. And the next finding is already visible:
func_8012F40C's blocker is RotTransPers, a PsyQ LIBRARY symbol — a callee conflict the cast should
have handled. It did not, because cast_call_sites' canonical map keys on
re.fullmatch(r'func_[0-9A-Fa-f]{8}'), so NAMED PsyQ callees are structurally invisible to it. That is
a one-line predicate widening with ~270 members behind it (RotTransPers + ApplyMatrixSV families).
GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK; dedup 1886/0; 0 NON_MATCHING.
METRICS: instr 86.6% -> 86.7% (+7,965 ins); fn-count 91.15% -> 91.19% (+135); distinct +0
(byte-identical — §111 predicted it).
cookbook §114 — the three decl axes, and "conflicting types for X: READ X".
|
||
|
|
862e31df10 |
fix(phase-29): T75 — reconcile_def_sig no-prototype regression; func_80147364 is the narrow-param wall
Item 3 closes with 0 banks and a real answer: both routes priced, both refused.
conform_decls (4,021 sites) : ⚠ SCALAR-NARROWING (s32->u16), NOT caller-neutral — argument
promotion changes at every call site (byte-proven on func_80175DA8).
Trades a plumbing failure for a byte failure.
§99 no-prototype (9 sites) : gated 140/140 byte-neutral, but the sweep fails with
`conflicting types ... An argument type that has a default promotion`
The second is the PHASE-15 DEAD-END reproduced: gcc-2.7.2 refuses to match a `()` no-prototype decl
against a definition with a default-promotion parameter (s8/s16/u8/u16/float). func_80147364 takes
(u16, u16). The remaining route is §43 — convert the DEFINITION to K&R so its params promote to int —
which is def-side and needs the exemplar re-matched, not a header edit.
A REGRESSION I CAUSED AND FIXED IN THE SAME TASK: the §99 header change broke reconcile_def_sig —
with the canonical now `void func_80147364()`, _merge_sig saw zero canonical params and returned the
canonical verbatim, DELETING the definition's parameters so the body referenced `param_1' undeclared
x137. A no-prototype decl constrains nothing, so it now REFUSES rather than conforms, distinguishing
`()` from `(void)` on the raw text. Verified the def keeps (u16 param_1, u16 param_2).
A §61 JUDGMENT CALL, FLAGGED: the func_80147364 header edit bought 0 banks and §61's undo law says an
edit that bought nothing gets undone. I KEPT it — `()` asserts no wrong type where `(u16, s32)` did,
it is gated byte-neutral, and it is a prerequisite for the §43 route; reverting costs another full
R22 gate for no functional gain. This is a judgment call against a documented law, Drew's to overrule.
|
||
|
|
3e6c364cd8 |
feat(phase-29): T73 — items 1+2: ARITY class resolved, audit learns §113; DECLS is the last value
ITEM 1 (call-vs-address re-check). My first detector counted the DECLARATIONS as calls, so every function looked "called". Stripping `extern ...;` first gives the real split: func_80144B14 is ADDRESS-TAKEN only (full retype — done in T72, 137/137); func_8013BD34 / func_8014358C / func_8017D808 are genuinely CALLED and need §99. §99 applied to all three -> R22 clean-fleet 140 passed, 0 failed of 140, byte-neutral. SWEEP YIELD: ZERO, and recorded as such. func_8013BD34's family swept 0/136 — exactly as predicted when I switched T72's probe off it (its def lives in ov_SC07_010_o0.c and _o0 families sweep ~1/137). func_8014358C has no family as exemplar; func_8017D808's family is 1 member with an unbanked exemplar. The §99 fixes are correct and byte-neutral but unblock nothing today. ITEM 2: called_in_headers() strips declarations, treats `fn(` as a call and `&fn` as not; arity_ok is now "arity matches OR the macro never calls it" (§113). Verified against all four. THE AUDIT AFTER BOTH — 28 findings (from 61): DECLS 9 fns 141 stubbed binaries <- the only class with value left SAFE 13 fns 15 ARITY 3 fns 0 <- §99 cleared the stub-bearing ones §85 3 fns 0 func_80147364 is 137 of those 141, and is item 3. |
||
|
|
a6e5abfd39 |
fix(phase-29): T69 — audit preconditions computed, not discovered; validated against known outcomes
Item 1. audit_header_sigs.py now COMPUTES the safe subset instead of leaving it to a failed gate,
and the two new preconditions took two wrong models to get right (cookbook §112).
PRECONDITION 1 — ARITY: correcting a `(void)` header decl for a 1-param definition breaks the macro's
OWN call site ("too few arguments"). Measured before the batch.
PRECONDITION 2 — VISIBLE COLLISION, and the two wrong models on the way:
(a) "any disagreeing decl in src/ blocks it" — compares type SPELLINGS, so s32-vs-int and
u32-vs-unsigned-int count as disagreements. Fixed by comparing type IDENTITY via
cdecl.compatible. Finding count 61 -> 32 once that noise is gone.
(b) "any INCOMPATIBLE decl in src/ blocks it" — STILL WRONG. It blocked ALL SIX corrections that
had just gated 140/140 and banked 685 members. func_80161774 has 1,063 TUs carrying the old
spelling and correcting it was byte-clean.
The right model: a macro-body decl is only visible where the MACRO IS INSTANTIATED, so a collision
needs a TU that BOTH instantiates the macro AND carries an incompatible decl. Measure the
INTERSECTION, not the population (macro_owners() + per-TU macro-use set).
VALIDATED AGAINST KNOWN OUTCOMES (the control this needed): the six that gated clean -> 0 colliding
TUs each; the one that failed the gate (func_80147364) -> 272. Perfect discrimination.
HONEST RESULT: 32 findings, 13 SAFE — but the safe subset is worth only 15 stubbed binaries. The
high-value targets (func_80147364 at 137, the arity trio at ~410) are all BLOCKED and need
conform_decls or §99 first. The cheap header lever is spent.
No src/ or config/ change: no bank, no metric move.
|
||
|
|
0c5df25faf |
feat(phase-29): T67 — audit_header_sigs.py; 61 header decls contradict byte truth, 6 corrected
THE TOOL (tools/audit_header_sigs.py, cookbook §112). A DEFINE_func_*() macro forward-declares the
functions its body calls, and that decl is visible in EVERY overlay instantiating the macro — so when
it disagrees with the byte-true definition the whole family becomes untemplatable and the failure
wears a compiler wall's clothes. Three such were found ONE AT A TIME earlier this phase
(func_80156044, func_8016163C, func_8014D610), each worth ~137 members, each costing a
diagnose/fix/re-sweep cycle. This audits all of them in one pass: parse every `extern func_X(...)` in
src/shared/*.h, find every DEFINITION in src/**/*.c (via §110's _def_head_at, not "ends in ;"),
compare with cdecl, and report only where NO definition agrees — one overlay disagreeing is loose
typing (§16/T49), all of them disagreeing means the header is the outlier.
RESULT: 3,043 decls across 1,023 functions; 265 have definitions; 61 contradict every one. The top 10
are full-fleet families (137/136/134 live stubs, 1,366 total), all with an unambiguous byte truth.
APPLIED: 6 functions / 11 decl sites, R22 clean-fleet 140 passed, 0 failed of 140 —
func_80138DE0, func_80146750, func_80161374, func_80161774, func_80161888, func_801778A8.
TWO PRECONDITIONS THE AUDIT DOES NOT YET CHECK, both found by gating rather than by reasoning:
1. ARITY. func_80144B14 / func_8013BD34 / func_8014358C declare (void) but are DEFINED with one
parameter. Correcting the header would break the macro's OWN call site (too few arguments), so
they need the §99 no-prototype treatment instead. Excluded before the batch, by measurement.
2. OTHER IN-SCOPE DECLS. The first batch of 7 FAILED the gate 2/140 with `conflicting types for
func_80147364` — the overlays' own TUs declare it the old way (9 header sites rewritten, but
src/ov_*/…:347 disagrees). A header correction is only safe when no other in-scope declaration
disagrees; that one additionally needs a conform_decls pass. Excluded; the other 6 then gated
140/140 clean.
The gate caught the bad batch immediately and the culprit was found by reading one object's real cc1
output rather than by a 7-way bisect (7 fleet gates = ~2.5h; one serial compile = seconds).
|
||
|
|
ec34c31b68 |
feat(phase-29): T65 — extract_unit definition-detection fixed; func_80156044 137/137 (+10,138 ins)
Item 3, and it banked the third family. extract_unit located a definition with "the line matches
<type> func_<addr>( and does not end in `;`" — wrong whenever ONE LINE holds both a declaration and a
definition, which the handwritten inline-asm wrappers do:
extern void func_80156044(int, int); int func_80155FF8(int, int) { __asm__ … }
The line does not end in `;`, so func_80156044 — appearing there only in the DECLARATION — was taken
as a definition head. extract_unit lifted the neighbouring WRAPPER instead of the real definition
seven lines below; every sibling already defines that wrapper via its shared DEFINE_ macro, so all
137 failed with `redefinition of func_80155FF8` and it read as a compiler wall.
FIX: ask what follows the PARAMETER LIST, not what ends the line (`_def_head_at`) — `;` is a
declaration, `{` or end-of-line is a definition. Plus the R32 assertion: a unit that defines a
function other than its target cannot template, so refuse LOUDLY (`_foreign_defs`).
TWO TRAPS HIT WHILE WRITING THAT ASSERTION, both caught by regression-checking against families known
to bank: (1) _def_head_at ALONE over-fires — a call whose args wrap has nothing after the `(` on its
line, which "end of line => definition" reads as a definition; it refused THREE families that had
just banked 137/137. (2) The type-prefix test ALONE under-fires — it is what missed the wrapper
originally. The predicate needs both: split the prefix on its last `;`, require the remainder to look
like a return type, then check what follows the parameter list. All five known-banking families
extract byte-identically before and after.
RESULT: func_80156044 0/137 -> 137/137, 0 failed.
GATES: R22 clean-fleet 140 passed, 0 failed of 140; tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, dedup 1886/0); 0 NON_MATCHING (G4).
METRICS: instr 86.2% -> 86.3% (11328601 -> 11338739 = +10,138 ins); fn-count 90.84% -> 90.88%
(321335 -> 321472 = +137); distinct-code 76.7% -> 76.9% (67937 -> 68066 = +129).
cookbook §110.
|
||
|
|
610a13c21c |
fix(phase-29): T61/T62 — items 1-4; all three families converge on one root cause (the shared header)
0 banked. Four tool fixes, one byte-neutral header correction (committed separately), and the three
families resolve to a SINGLE root problem — plus a defect I introduced and caught by measuring.
FOUR TOOL FIXES, each verified by a verdict MOVING rather than by assertion:
1. gather_externs prefers FILE-scope decls. Its contract says "file-scope extern decls" but
`^[ \t]*extern` also matches an INDENTED one — a block-scope decl inside some OTHER function, not
even in scope at the exemplar's own definition. Carried to file scope in the sibling,
`extern void func_80155FF8(void *, u8);` (ov_SC01_077 L1213) landed above that sibling's
DEFINE_func_80155FF8() macro and collided. ORDERED, not filtered — an indented decl stays the
fallback it always was, so a symbol declared only block-scope is unaffected.
2. reconcile_def_sig keeps the BODY's param names (the T60 fix, cookbook §109).
3. §85 return-axis precondition — refuses when callers consume the return (reuses
conform_decls.consumers, R33).
4. Param-use guard — refuses to retype a parameter the body indexes/dereferences (func_8014D610's
header says `void *a2` where the byte truth is `u16 *param_3` and the body does param_3[0]).
A DEFECT I INTRODUCED, CAUGHT BY MEASURING: func_8016163C read as a clean DIFF after T60 and I
reported it as "genuine codegen". It is not. match_one says SIZE-MISMATCH: draft 58 ins vs target 78
(delta -20, ratio 0.74, bucket redraft). Both overlays are 78 ins and extract_unit is fine —
--fix-def-sig demoted the return s32 -> void and gcc deleted the computation feeding it as dead. My
§85 check only asked whether CALLERS consume the return, never whether the BODY returns a value. The
tool manufactured a different-sized function and the verdict blamed the draft. Guard added. A "clean
DIFF" appearing right after a transform is a suspect, not a result.
THE CONVERGENCE: engine_core.h declares all three with types that contradict the byte truth —
func_80156044 int vs void (FIXED, byte-neutral, R22 140/140), func_8016163C void vs s32,
func_8014D610 void(s32,void*,void*) vs s32(s32,s32,u16*). Both remaining flips measure 0 §85
consumers. The fix is to correct the HEADER, not to bend the drafts.
AND ONE MORE LAYER: with its header fixed, func_80156044's verdict moved to `redefinition of
func_80155FF8` — extract_unit lifted a unit spanning TWO definitions and the sibling already defines
the wrapper via the shared macro. A unit-boundary defect, a fourth distinct cause. Three fixes peeled
three layers off one family.
|
||
|
|
50a108b5a8 |
fix(phase-29): T60 — reconcile_def_sig name bug fixed (verdicts moved); 0 banked, three causes separated
Tool fix + a sharper diagnosis. NO BANKS — the three "header-conflict" families share a SYMPTOM, not
a cause.
THE FIX (cookbook §109): reconcile_def_sig now conforms the canonical TYPES and keeps the BODY's
parameter names, parsed with cdecl (base/params/pnames, R33 — not a regex). Two re-render traps
handled: `void*` + `a1` -> `void *a1` (cdecl glues stars to the type), and an EMPTY parameter list is
handed back verbatim because `(void)` and `()` both parse to params==[] and are DIFFERENT
declarations (§99 no-prototype). Unit-tested across 6 shapes incl. both void forms and an arity
mismatch; falls back to the wholesale canonical string for fn-ptr/array params.
THE FIX IS REAL, AND THE PROOF IS THAT THE VERDICTS MOVED:
func_8016163C `param_1 undeclared` -> DIFF (plumbing CLEARED; codegen left)
func_8014D610 `param_1 undeclared` -> `void value not ignored` (the HEADER is wrong)
func_80156044 unchanged -> `conflicting types for func_80155FF8` (WRONG LEVER — callee conflict)
TWO FINDINGS UNDER THAT:
1. The §85 return-axis precondition applies to reconcile_def_sig and NOTHING CHECKS IT. Conforming a
def's return to the canonical `void` is only safe when no caller consumes the return.
func_8014D610's callers do, so engine_core.h's `void` contradicts the byte truth and conforming
yields `void value not ignored`. The HEADER is the wrong artifact; correcting it is fleet-shared
blast radius (§61/§63), not a sweep-time fix.
2. func_80156044 was never the def-signature class — its conflict is on the CALLEE func_80155FF8
(decl 2 lines above the splice). That is cast_call_sites / canon_sig_reconcile territory.
HONEST ACCOUNTING: re-swept all three with the fix -> 0/411, tree clean throughout. The lever is now
correct (it no longer manufactures a false compile failure) but it was ONE of three causes, not the
cause. My T59 write-up grouped them as a single ~30,000-instruction block; that grouping was WRONG,
and what disproved it was re-reading each verdict after the fix rather than re-running the batch and
reporting the total.
No src/ or config/ change: no bank, no metric move.
|
||
|
|
56e2d808ab |
feat(phase-29): T56 — wire the tu-scope lever into family_sweep; func_80144090 0/136 -> 136/136
T55's two-part next step as one job. +20,944 instructions banked. 1. THE LEVER WAS UNREACHABLE FROM THE PATH MOST FAMILIES USE (cookbook §107) §103 was wired into jtbl_family_bank only (T53), and that tool runs for has_mid_jr families. Everything else sweeps through family_sweep, which gates via PLAIN harvest_verify by design — so the lever existed, was byte-proven, and most families could not reach it. The symptom was indistinguishable from a compiler wall: func_80144090 swept 0/136 with `conflicting types for D_800A651C`. Why it does not violate the plain-harvest_verify rule: that rule exists because gate_stage's transforms PERTURB A CORRECT DRAFT (§19/T3). The tu-scope never touches the draft — it moves a DECLARATION IN THE TARGET TU. The test is not "is it a transform" but "does it change the draft?" Reused the existing undo instead of inventing one: family_sweep already snapshots TUs it edits at staging time (--normalize-self-decls) and reverts on a final MISMATCH (not byte-neutral) AND on a zero-bank group (§61 undo law — no dead diff). The tu-scope shares that dict and inherits both backstops; renamed nsd_snapshots -> tu_snapshots. Default ON with --no-tu-scope to A/B it (the T24 --allow-pins precedent): byte-neutral by construction, a no-op when nothing collides, auto-reverted when it buys nothing. 2. THE DUPLICATE-DECL REFUSAL RELAXED — AND IT DID NOT MATTER scope_tu_externs refused N>1 file-scope decls as "ambiguous"; duplicate-IDENTICAL externs are legal C, so N identical decls are one decl written N times. Now compares whitespace-collapsed forms and refuses only on genuine disagreement. MEASURED, and my hypothesis was WRONG: D_800B9A02 is 3 decls in 2 DIFFERENT forms, so it was correctly refused all along — the family banked 136/136 without it. RESULT: func_80144090 0/136 -> 136/136, 0 failed, with NO change to any draft. GATES: R22 clean-fleet 140 passed, 0 failed of 140. tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED, cdecl, audit-binaries, report/lint/dedup 1886/0). 0 NON_MATCHING (G4). METRICS (reconciled against make report): instr-weighted 85.7% -> 85.8% 11258063 -> 11279007 = +20,944 ins fn-count 90.65% -> 90.69% 320656 -> 320792 = +136 distinct-code 76.4% -> 76.4% +0 (67812 unique, UNCHANGED) FLAGGING the third row rather than explaining it away: 136 banked functions moved distinct-code by ZERO, where T52's 132 moved it by +125, and both families are classed PURE. I do not have a verified cause and will not invent one — either a real property of this family or a gap in the metric. Worth one probe before that number is quoted again. |
||
|
|
bcd44badc9 |
fix(phase-29): T55 — frontier re-mapped; 2 families swept, 0 banked, both blockers diagnosed to the line
Honest result: NO YIELD. What it produced is a re-measured frontier, a real fix to my own T53 work,
and both failures diagnosed rather than left as "0/N".
FRONTIER RE-MAPPED (T52's +132 moved it): family_hseq -> 2,647 target families, 513 substantial,
64 with a banked exemplar AND live stubs. Caveat recorded: the top two by byte-weight (0x8013c414
180KB, 0x8013c0f8 84KB) are -O0, and _o0 families are already measured at ~1/137 — do not be drawn
by their weight.
FAMILY 1 func_8014032C (183 ins x 136 ~ 25,000 ins): sample 0/8, last_err empty. Read one sibling's
real gate result (§53/§59) past the -j16 interleave and the §58 memcpy red herring — TWO causes:
(1) conflicting types for D_80115128 — the T48/T51 class, which tu-scoped should have caught;
(2) jtbl_rodata_pads "more rodata .align than pad specs — table-count drift vs the carve", a
DISTINCT class jtbl_family_bank's own comment documents as NOT isolate-fixable (§91 --like
role trap).
After fixing (1): still 0/8. Cause 2 is the live blocker — carve work, not decl work. NOT ground
further; it is a documented wall.
THE T53 DEFECT, FOUND AND FIXED: contested() scanned only the draft's BLOCK-scope externs, because
T51's motivating family had them hand-written in the body. But gather_externs carries decls in at
FILE scope, and those are exactly the ones scope_data_externs.fix DROPS when the TU already declares
the symbol — its give-up branch, the fatal case the lever exists for. Measured: scope_data_fix
dropped 3 symbols while contested() returned []. So the stage never fired on its own class. Now
scope-independent; regression-checked against T51's case using the pre-T51 TU from git (old ==
new, added []), and it now finds D_80115128 on the T55 target.
FAMILY 2 func_80144090 (154 ins x 136 ~ 21,000 ins), chosen because has_mid_jr=False avoids the
carve: 0/136. Diagnosed: conflicting types for D_800A651C (2210 vs 379) — the SAME class.
family_sweep gates via PLAIN harvest_verify by design, so it never sees the tu-scoped lever, which
lives only in jtbl_family_bank. Probed: the lever would move D_800A651C + D_800AF648 (deletion-only)
and REFUSES D_800B9A02 as "3 file-scope decls above (ambiguous)" — an over-conservative refusal,
since duplicate-IDENTICAL externs are legal C.
THE FINDING: the same decl-scope collision class gates the frontier's mechanical families — it cost
T52's family 133 of 137 siblings, and it blocks both families probed here. The lever exists and is
byte-proven; it is not reachable from the sweep path most families use.
No src/ or config/ change: no bank, no metric move. Tree verified clean after every probe.
|
||
|
|
f72e2344a6 |
fix(phase-29): T54 — correct the ADDRESSING route, and fix the reason changing it was inert
Item 1 off T53's list. Two changes: the route, and the design flaw underneath it.
THE DEFECT UNDER THE DEFECT (cookbook §106). residual_class answers two questions in one pass:
`klass` is a MEASUREMENT (expensive, from comparing instruction streams); `(profile, bucket)` is a
POLICY (a table lookup over it). autopsy persisted BOTH and verdicts() read BOTH back — so editing
_ROUTE changed nothing until someone re-ran the whole collect, and a weeks-old row could silently
out-vote the live table with no oracle to report it. The corpus on disk is dated Jul 21 and does not
even contain the SESSION-23 targets the recommendation cited.
Fixed by re-deriving at read time: residual_class.route_for(klass, detail), called from
autopsy.verdicts(). R33 — persist the measurement, derive the decision. Subtlety: LENGTH-DRIFT's
route is MAGNITUDE-dependent (permuter only when |delta|<=2 AND explains=="tail", §60b), so a naive
re-derivation from klass alone would have silently demoted 9 rows; both inputs are already in
`detail`, so the override reproduces exactly — VERIFIED 1610/1610 against the stored corpus with the
table UNCHANGED, before touching it.
THE ROUTE CHANGE: ADDRESSING ("cse","permuter") -> ("cse","structural"). It contradicted this file's
own bucket definition ("structural — local mutation CANNOT introduce it ... it wants a C-level
idiom"): the §10/§20 hoist-vs-remat shape is a multi-instruction change with a documented recipe
(gcc-2.7.2-map/cse_expr.md §2, byte-proven on func_80149374/func_801493D0). Measured (T31): both
admitted ADDRESSING targets plateaued under a §31-directed permuter, and the class was 32% of the
admission pool. After: pool 56 -> 38, exactly 18 rows changed, ALL ADDRESSING, nothing else moved;
grinder admits 45, structural skips 512 -> 530.
THE BOUND (R14), kept in the _ROUTE comment: I read the T31 record instead of the summary line, and
the summary was looser than the evidence. T31 finding 4 byte-tested the §2 recipe on func_80132F40
across six variants and it never closed (best 40 mismatches). `structural` does NOT promise a free
fix — it means "a search over local mutations is the wrong tool, try the documented idiom", exactly
what WIDTH / BRANCH-POLARITY / IMM-OFFSET already mean. Also corrected: the checkpoint cited
func_80176734 as the flat-for-32-min evidence, but that function is not in the corpus at all.
Tooling-only: no src/ or config/ change, no bank, no metric move.
|
||
|
|
f285de46e8 |
feat(phase-29): T53 — fold the T51 lever into the gate; kill gather_externs' false positive; close a revert gap
Items 1-2 off T52's list, plus a third defect found by T53's own testing. TOOLING ONLY — banks
nothing; metrics unchanged by design (85.7% instr / 76.4% distinct / 90.65% fn-count).
1. THE T51 PRE-PASS IS A jtbl_family_bank STAGE (cookbook §103, AUTOMATED)
Order: raw -> scoped -> tu-scoped -> recovered -> reconciled. After the non-invasive stages (it
edits the TU outside the spliced body); BEFORE the recovery stages deliberately — those bend the
DRAFT and T48 measured both at +3 ins for this class, so they cannot succeed here. The stage
re-runs scope_data_fix against the SCOPED TU rather than reusing the raw body: composition-correct,
since the contested symbols no longer have a file-scope decl to be dropped against.
COUNTERFACTUAL, byte-gated on a reproduced blocker (ov_SC01_000 restored to its pre-T51 TU):
raw -> compile error (conflicting types)
scoped -> compiles, FAILS the byte check (§8d drops the decl -> the u8 CSE costs +3)
tu-scoped -> BANKED
That is the evidence the stage does the work — not T52's sweep, which ran on TUs T51 had already
scoped by hand.
Refactor note: a stage editing outside the spliced body must RE-FIND the splice point (the stub
offset indexes the ORIGINAL TU). Each stage now carries its base; every pre-existing stage passes
`orig`, where the re-search returns the identical span — same operation as before, by construction.
2. gather_externs' COMMENT-SCANNING FALSE POSITIVE — FIXED (cookbook §104)
It scanned RAW text, so a symbol named only in the draft's PROSE counted as referenced: the
func_80135D20 warning that fired on 137/137 and was right 0 times. Fix is a two-text discipline —
MATCH on cdecl._mask'ed text, EMIT by span from the ORIGINAL (a masked decl is all blanks, so
"just mask it" would splice whitespace). Same change closes a second, unobserved defect of the
class: a COMMENTED-OUT extern could be selected as the carried decl and spliced in as live code.
MEASURED as a no-op on output (R14): 20 (exemplar, sibling) draft pairs across 4 families, old vs
new -> 20 identical / 0 differing. Only the false warning changed.
3. UNPLANNED — A REVERT THAT DID NOT SURVIVE AN EXCEPTION (cookbook §105)
A wrong exemplar made remap_hseq raise AFTER the carve rewrote config/ and jr_isolate created a
region file; the exception propagated out of bank(), the revert never ran, and the tree kept a
rewritten carve config plus an UNTRACKED region file (git checkout -- src/ does not remove it).
In a 132-member sweep that residue rides into the next member's build. bank() is now a
revert-guaranteed wrapper around _bank(). Negative-control proven: the crashing invocation now
reports {'exception': 2} and leaves git status -- config/ src/ at 0.
"Revert on failure" != "revert on every exit"; the exits are success, gate-fail, refusal, and the throw.
GATES: R22 clean-fleet 140 passed, 0 failed of 140. tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, report/lint/dedup 1886/0). 0 NON_MATCHING (G4).
HONEST COVERAGE GAP: no live end-to-end BANK through the refactored loop — all three big families are
137/137 and the only family with live stubs (0x80191c50) has no banked exemplar, so it refuses. The
counterfactual byte-gated the exact splice on all three candidates and the 2-member run exercised
construction/refusal/revert/tally; the next real family sweep is the true end-to-end validation.
MY ERRORS: invoked the sweep with a wrong exemplar+address for a cross-address family (an unmeasured
guess about a members file I had not read — it is what surfaced defect 3); and deleted last_err's
initializer while refactoring, which would have raised NameError on the first clean gate-fail.
cookbook §103 (AUTOMATED) + §104 + §105; SETUP inventory row updated (R21).
|
||
|
|
a524142eb5 |
feat(phase-29): T51 — the fleet-wide decl-scoping tool; func_80135260's 132 siblings unblocked
Item 1 off the SESSION-23 list. T48 proved the lever by hand on the exemplar, T50 located the same
blocker in every sibling; this builds the tool, measures the population, applies it fleet-wide, and
gates it. It BANKS NOTHING — it removes the blocker. The sweep is the next task.
MEASURED BEFORE BUILDING (R35). The blocker census over all 132 still-stubbed siblings is perfectly
uniform: 132/132 carry it, 3 contested symbols each, EXACTLY ONE file-scope decl statement per
(TU, sym), ZERO file-scope references below the decl (so the deletion is always safe), 660
block-scope re-declarations needed.
THE TOOL: tools/scope_tu_externs.py — the TU-side complement of scope_data_externs.py (§8d). §8d
fixes the incoming DRAFT and has a give-up branch that DROPS the draft's own decl when the TU already
declares the symbol at file scope. Right when the types agree; fatal when the byte-true draft needs a
different one — which is exactly how 132 byte-true siblings gate-failed wearing a codegen wall's
costume. This moves the TU's OWN file-scope decl down into every later function that references the
symbol and lacks its own block-scope decl, then deletes the file-scope line.
FILE(u8 D_x) ... then BLOCK(u16 *D_x) below it -> conflicting types (the 132 failures)
(no file-scope decl) ... BLOCK(u8) ... BLOCK(u16 *) -> builds; each fn owns its own view
- contested set DERIVED, never hand-listed (R33): the remapped draft's block-scope D_ externs
intersected with the TU's file-scope decls above the splice point; --family does it per sibling
- built on cdecl.split_statements/_mask (R33), not a 7th regex: depth-0 spans (a fn definition
flushes at its closing '}' — a column-0 test is NOT a file-scope test, m2c emits goto labels at
column 0 inside bodies) + length-preserving comment/string masking. That masking is what kills the
comment-scanning false-positive class still open as item 3.
- REFUSES LOUDLY, never skips silently (R32): >1 file-scope decl above the splice point; a
file-scope statement below the decl referencing the symbol; an unlocatable body brace
- coverage asserted as a DELTA (R32): file-scope -1, block-scope +len(consumers). An absolute
"a block-scope decl exists" check would have passed VACUOUSLY — these TUs already carry ~18
legitimate block-scope decls of the same symbols
VERIFIED IN TWO STEPS (T48's structure — why a 132-file edit was safe to make):
1. the move ALONE on ov_SC01_000 -> make build -> 9052dc0e BYTE-IDENTICAL, then reverted
2. fleet-wide -> R22 clean-fleet (make clean && extract-all && check-all) -> 140 passed, 0 failed
of 140; make tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED, cdecl, audit-binaries,
report/lint/dedup 1886/0). Metrics UNCHANGED at 85.5% instr / 76.1% distinct / 90.62% fn-count
— the correct result for a declaration-only change.
The diff is uniform to the line: all 132 files +11/-3. A second --family run reports 132
nothing-to-do, 0 refused (idempotent).
Deliberately NOT done: wiring this as an automatic jtbl_family_bank stage. That waits until the
sweep measures the payoff — folding an unproven pre-pass into the gate is the same unmeasured
premise this phase keeps catching.
Also preserves .run/near6/g5260_a.c (the T48 raw crack body, allowlisted) — the sweep may need it
for --raw.
cookbook §103 + SETUP tool-inventory row (R21).
|
||
|
|
ce7780b91f |
feat(phase-29): T46 — reg_renumber-swap oracle built + validated; it REFUTES the func_80176734 framing
Item 1's remaining half. Oracle mechanized and reusable at tools/oracle/reg_renumber_swap.sh: break at reload entry (cc1 unstripped: reg_renumber @0x82d4330, reload @0x815d4d7), swap two hard regs across reg_renumber, finish the compile, re-score with masked_diff REUSED not reimplemented (R33). NEGATIVE CONTROL: a no-op swap (31<->31) reproduces exactly the baseline 13 mismatches, so the harness faithfully reproduces the pinned compile. RESULT: both contested swaps are far WORSE — <-> (17 pseudos) = 345 mismatches +1 insn; <-> (31 pseudos) = 97. Baseline 13. WHY, AND IT REFUTES THE FRAMING: reading .greg for cluster B's own insn shows (set (reg/v:SI 6 a2) (plus:SI (reg/v:SI 5 a1) (const_int 60))) — the destination is a HARD register, not a pseudo. reg_renumber only maps pseudos (>= FIRST_PSEUDO_REGISTER = 68), so that value is structurally unreachable by this oracle. The draft has NO register __asm__ pins (header says so, grep confirms), so is hard because it is an incoming PARAMETER register that local-alloc reused as a destination. VERDICT for func_80176734 (51,198 ins): the residual is NOT global-allocation 2-colouring. It is the LOCAL-alloc hard-reg reuse / tying class — combine_regs (2.7.2 local-alloc.c:1722) + qty_phys_copy_sugg, i.e. regalloc.md K8/RC-4, whose lever is C-level LIFETIME SHAPING, not the permuter and not reg_renumber. That also explains the flat permuter: it was mutating a dial that does not control this residual. MAP REFINEMENT OWED: §H presents the swap oracle as THE way to discriminate RC-6 from S3 in one gdb run. It has an unstated PRECONDITION — the contested registers must be held by PSEUDOS. Check .greg first; if they appear as (reg/v:SI N ...) with N < 68 they are already hard, and a coarse swap returns a large meaningless number (345 here) that looks like a verdict and is not one. Tree clean; nothing banked, nothing broken. |
||
|
|
86caa7dfdb |
fix(phase-29): T36/T37 — my checker had a FORM-FEED bug; the "12 fabricated" were mine, not the agents'
T36 CORRECTION (the important half). Building the cookbook sweep tool surfaced a defect in
tools/verify_map_findings.py, which I had already used to validate BOTH map audits:
- GNU C sources use FORM FEED (\f) page separators — loop.c 47, cse.c 36, reload1.c 27,
local-alloc.c 21. Python's splitlines() splits on \f; grep/sed do not. Every line number computed
after the first \f was shifted (up to 47 in loop.c), which is LARGER than the checker's own +/-40
window — precisely how a real quote gets reported FABRICATED.
- Re-run after the fix: T34 regalloc 27 OK/153 NEAR/0 FAB -> 180 OK/0/0. T35 four-file
47 OK/240 NEAR/12 FAB -> 299 OK/0/0. THE AGENTS' LINE NUMBERS WERE EXACT ALL ALONG. I had even
written the false "off by +2..+19" claim into the T35 agent prompt.
- MY DIAGNOSIS OF THE 12 WAS ALSO WRONG. I said agents pasted map prose into source_quote and
"verified" it by grepping — but I grepped claim_excerpt (which IS map prose) instead of
source_quote. The real source_quote was ` record_jump_equiv (insn, 0);` at cse.c:7511, a
correctly-located C line. Two stacked errors: a broken tool, then a check of the wrong field that
appeared to confirm it.
- Fixed: both tools use split("\n"); verify_map_findings.py documents the trap so it cannot return;
loop.md's "12 unverified" note is WITHDRAWN in place. Nothing was deleted on this basis (all 12
were CONFIRMED-status, none underpinned a refutation), and the T34/T35 upheld/overturned splits are
unaffected — those came from adversarial agents, not the checker.
T37 THE COOKBOOK SWEEP (what was asked for). New tools/sweep_citations.py puts the mechanical half of
a citation audit into zero-token tooling (offline-tooling-first): symbol-form cites are compared to
the real 2.7.2 definition line; file-form cites are localised to their enclosing function.
- matching-cookbook.md: 57 resolvable citations, all localisable. MIXED provenance but mostly sound —
materially better than the map files. loop.c:5556, local-alloc.c:1765/1795/1825, global.c:906/917/
924/1000, local-alloc.c:1021/1064, global.c:588/594, sched.c:820, expmed.c:556, jump.c:2131 all
land where the prose says. GENUINE MISS: expr.c:5535 is MIN/MAX optab code; the /s grant sites are
4577 and 4904.
- STATED LIMITATION: "lands in the right function" is weak for giants (expand_expr 4026->~6300,
jump_optimize 139->~2200). This is a CITATION sweep, not a claim audit — proportionate because the
cookbook's idioms are byte-proven and its cites are explanation. No idiom re-litigated.
Docs+tools only: no src/ or config/ touched; R22 not re-run and not claimed.
|
||
|
|
7acd0c4e2a |
feat(phase-29): tools/verify_map_findings.py — the codegen-map audit fabrication check (R3)
Moved out of .run/ (where the blanket ignore would have lost it) into tools/ per R3, and generalised
from regalloc to any docs/gcc-2.7.2-map/* audit.
The check an LLM audit of a source-derived document cannot do for itself: agents return
{real_file, real_line, source_quote}; this re-opens each file at each line and compares the verbatim
quote to what is actually there. Whitespace-normalised, +/-40-line search window, and it reports
NEAR (quote real, line wrong) SEPARATELY from FABRICATED (text appears nowhere) — because with the
2.8.1->2.7.2 drift reaching +611 lines in reload1.c, a wrong lookup lands INSIDE A DIFFERENT FUNCTION
and every sentence built on it still reads plausibly.
Used on the T34 regalloc.md audit: 184 findings, 0 FABRICATED.
|
||
|
|
599a33c056 |
feat(phase-29): wave22 — 5 exemplars banked from an 18-target Ultracode wave (R22 140/140)
THE WAVE: 18 h_seq family exemplars (~255k templated instructions), one agent each, drafting from
cached Ghidra-C + the target .s with canonical callee/data decls resolved from the real TU scope.
Result 12 MATCH / 6 NEAR / 0 FAIL (2.59M subagent tokens). No agent touched the tree — the
draft-only constraint held (verified: git status clean across src/config/tools/include).
BANKED 5: func_80148E54, func_80171B4C, func_8014A738, func_8012A328, func_80163534.
R22 clean-fleet 140 passed / 0 failed of 140.
A BUG I INTRODUCED EARLIER TODAY, FOUND BY WORKING THE 12->5 GAP. My block-scope descent in
reconcile_tu fed ordinary STATEMENTS to cdecl.parse; some parse without raising into a declarator
with an EMPTY base type and the statement's symbol as its name. That fake row overwrote the genuine
plan entry for the same symbol, so the span rewrite landed on a statement instead of the declaration
— and my own R32 completion assertion still PASSED, because the conformed text appeared somewhere.
Byte-witnessed on D_80126B5C: planned twice ("draft 's32'" and "draft ''"), output unchanged, gate
PLUMBING. Now block-scope rows are accepted only from a real `extern` with a non-empty base type.
TWO BANKS CAME FROM TODAY'S OWN FINDINGS:
- func_8012E014's single 0-arg call site took --cast-zero-arg-calls (built this morning for
func_801789AC's 138 sites).
- §99 HELD A THIRD TIME: K&R conversion dissolved func_80163534's s32->u16 narrowing across 1,072
declarations, leaving only a caller-neutral pointer change on the last param.
STILL UNBANKED (measured blockers, not guesses): func_8013B6A0 + func_8013B598 CC1-FAIL in the _o0
split; func_80133298 + func_80135260 + func_8012E014 genuine DIFF (match_one MATCH did not hold
whole-binary = TU-context); func_80138C60 parse-order (an extern referencing a body-local typedef
declared after it); func_80177DA8 prototype-vs-K&R mismatch.
|
||
|
|
8479c4491e |
fix(phase-29): scope_data_externs DROPS a data extern the TU already declares (SC07 type-identity blocker)
THE BLOCKER: the SC07 quartet (ov_SC07_006/007/010/011) refused two families with
`conflicting types for D_800AF634`. Both sides read `S_AF634 []` — the SAME type STRING — so it is a
type-IDENTITY collision: the templated body carries its OWN block-scope `typedef struct {…} S_AF634;`
while the TU's declaration of D_800AF634 comes from a MACRO-INJECTED one (§8c), making two DISTINCT
types with one name. cdecl.compatible cannot see this and correctly answers "compatible".
THE FIX: if the TU already declares the symbol above the insertion point, DROP ours instead of
keeping it. A redeclaration we do not emit cannot collide — with anything, identity or type — and the
TU's own declaration is in scope and authoritative. Strictly better than the previous behaviour,
which was a hard compile error; the whole-binary byte-gate still arbitrates if the TU's type implies
a different access.
AND IT HAD TO REACH BLOCK SCOPE, not just column 0. §8d demotes these externs on the way in, so by
the time a sibling draft is STAGED they are already indented — a col-0-only scan (my first cut) saw
nothing to do on exactly the drafts that needed it. C requires a block-scope `extern` to agree with a
file-scope declaration in scope, so a redundant redeclaration conflicts at ANY scope.
VERIFIED on the failing member: D_800AF634's declaration is removed from the staged draft, and the
re-sweep's error moved past it. HONEST STATUS: the quartet is NOT yet banked — the next conflict is a
FUNCTION decl (`conflicting types for func_80024054`), a different axis (§58c / cast_call_sites)
which the sweep's member staging does not currently run. Not peeled further here: §95's law says
splice once and dump EVERY cc1 error rather than one per gate cycle. Tree clean, nothing banked.
|
||
|
|
55cd894024 |
perf(phase-29): family_sweep gates groups in PARALLEL by default (the SESSION-20 adapter, finally wired)
SESSION-20 measured serial family-sweep gating as "roughly an 8-16x throughput loss on a 32-thread box" and BUILT tools/sweep_parallel.py for it — but only reachable via a manual `--stage-only` two-step, so this path stayed serial and three sweeps in SESSION-22 (133 + 273 + 137 members) ran serially for no reason. §101, the stale-default class. SHAPE OF THE CHANGE — deliberately minimal after two failed attempts earlier today. A parallel PRE-PASS (phase 2a) runs only the per-group `harvest_verify` subprocess; phase 2b then consumes the results IN THE ORIGINAL SERIAL ORDER, so every line of post-processing (the MISMATCH backstop, the zero-bank restore, the counters, the prints) is untouched and output stays deterministic. No closure restructuring — that is exactly what broke it twice before. SAFETY, not a new claim: the Makefile already builds binaries concurrently (check-all/extract-all use `xargs -P$(JOBS)`, JOBS=16) and bulk_harvest's farm does the same with a per-binary lock. The §28 hazard is two makes racing on the SAME artifacts, prevented by the per-overlay lock (two splits of one overlay build the same binary and therefore serialise). NEGATIVE-CONTROLLED BOTH WAYS: `--stage-only` stages identically under -j1 and -j12 (4 groups each); a full gate returns IDENTICAL tallies (0 banked / 4 failed) parallel vs serial; tree clean after both. HONEST MEASUREMENT: on the only sample available (4 groups, and they fail FAST on a compile error rather than running full builds) parallel was 4s vs serial 6s — ~1.5x, NOT the 8-16x. That figure needs a large family (137 groups of full builds) to show, and every such family was already banked today. The wiring is proven correct here; the throughput claim remains SESSION-20's measurement, not mine. `-j 1` restores the old behaviour. |
||
|
|
ab065b1646 |
fix(phase-29): sweep pinned exemplars BY DEFAULT — the §42e guard's cause was removed in Phase 27
The guard skipped any exemplar carrying a `register __asm__("$N")` pin because templating it
cc1-CRASHED the sibling TUs (§42e). Phase 27 BYTE-PROVED that SIGABRT was `extract_unit` dropping the
body's file-scope macros — OUR bug — and fixed it (_carry_macros); its own roadmap delta then put the
PINS class "back on the mechanical-harvest table". The cause was removed and the default never
changed, so the guard kept skipping real work.
MEASURED THIS SESSION on one family: func_80175AB8 reported `skipped {'pinned-exemplar': 137}` and
then banked 133/137 the moment it was bypassed (R22 140/140). A protection whose cause is gone is not
free — it is a silent skip (R32) wearing a safety label, and the whole-binary byte-gate was always the
real arbiter here.
--allow-pins kept as an accepted no-op so existing recipes/docs keep working; --no-pins restores the
old behaviour. Negative control: --no-pins still reports `pinned-exemplar: 4`; the default stages them.
This is the THIRD stale default found today, after sweep_parallel being opt-in (8-16x throughput left
on the table) and conform_decls refusing a remedy it could perform. Same shape each time: correct when
written, cause since removed, still the default, opt-out only if you remember the flag.
|
||
|
|
5f1fc5b5eb |
feat(phase-29): caller pair banked (57,822 templ ins) via K&R defs — §92's remedy corrected (§99)
func_80175AB8 + func_80175DA8 both banked. R22 clean-fleet 140 passed / 0 failed of 140.
§92 SAID these need "the §17a-1 caller pair, NOT a bare conform" — the diagnosis was right (conforming
a narrow param changes argument promotion at every call site, measured PLUMBING -> DIFF) but the
remedy was the expensive one. The actual fix touches NO declaration: convert the DEFINITION to K&R,
where a narrow param PROMOTES to int (C89 6.3.2.2) and is therefore already compatible with the
fleet's existing `s32` prototype, while still emitting narrow-param codegen. §43 applied to the def
side. T0 draft-only, ZERO blast radius, versus a 524-site fleet conform.
THREE reconcile_tu BUGS SURFACED, ONE OF THEM MINE:
(a) BLIND TO BLOCK SCOPE. split_statements is depth-0 BY DESIGN, and §8d deliberately demotes data
externs into the function body — so the tool saw one statement and no declarations, printing
"reconciled: 0 draft(s), 0 data symbol(s); coverage defects: 0" for a draft cc1 rejected with
`conflicting types for D_8011F7BC`. A silent skip (R32). Fixed: descend one level.
(b) MY BUG, introduced by (a): descending into ANY `{` also enters struct/union/enum definitions, so
MEMBERS parse as declarations and get conformed — `u32 code;` became the TU's
`typedef void (*code)(unsigned short*);` INSIDE the struct, and `p->code` became
`p->(*(u32 *)&code)`. Caught by DIFFING THE TOOL'S OUTPUT AGAINST ITS INPUT before trusting it;
the byte-gate would have said PLUMBING and explained nothing. Guard: function bodies only.
(c) LATENT since the tool was written: _cast_sub matched bare identifiers and rewrote MEMBER ACCESSES
as globals. Unreachable until (a) existed. Guard: (?<![.\w])(?<!->).
cookbook §99.
|
||
|
|
445b149279 |
perf(phase-29): -j16 on jtbl_family_bank's make calls — measured 16s -> 14s per sibling (12%)
MEASURED, not assumed. Baseline ~18s/sibling (92 siblings in 27:37). Profiling a realistic cold cycle: `make extract` 3s + `make build` 2s = ~5s of the 16s, so MAKE IS NOT THE BOTTLENECK and -j cannot be the 8-16x lever. Confirmed end-to-end on one sibling: 16s -> 14s. Where the rest goes: the per-sibling loop tries up to FOUR stages (raw -> scoped -> recovered -> reconciled) and EACH runs its own `make build`, plus jtbl_carve and remap/canon_sig_reconcile. WHY THE REAL LEVER IS NOT DONE HERE. Cross-sibling parallelism is worth ~8-16x on this 32-core box (each sibling is an independent binary, and the Makefile already proves per-binary parallel builds safe: check-all/extract-all run `xargs -P$(JOBS)` at JOBS=16). It is blocked on a specific hazard, not on effort: `revert()` restores config/ from git, and `config/overlays.mk` is SHARED, so a concurrent revert would clobber peers' carve entries — the same "revert-from-HEAD eats another worker's state" failure this tool's own precondition check warns about. Safe parallelisation needs line-scoped + locked + atomic edits to overlays.mk and a revert that never wholesale-restores shared paths. Designed, not built. ALSO REVERTED THIS SESSION: an attempt to make parallel gating the default in family_sweep. The adapter for it already exists (tools/sweep_parallel.py, built SESSION-20 after measuring the same 8-16x loss) but is only reachable via the manual `--stage-only` two-step, so the default path stayed serial — and three sweeps today (133 + 273 + 137 members) ran serially for no reason. Wiring it is right, but my patch broke the tool twice (missed import, then a closure-scope error) and family_sweep banked 543 members today. Restructuring a proven tool with blind string replaces at the end of a long session is how a working thing gets broken; reverted and left as a specified next-session task. |
||
|
|
2f1aa8659d |
feat(phase-29): func_801789AC banked — conform_decls can now FIX the arity precondition it diagnoses
STUCK SINCE SESSION-21, and conform_decls was RIGHT to refuse it: the byte-true signature takes a parameter while 138 zero-arg CALL SITES exist across 138 files, so conforming the declarations alone would turn every one into `too few arguments` — a fleet-wide compile break the per-binary gate cannot see. The tool printed the exact remedy in its refusal message and could not perform it, so the function sat blocked for two sessions. NEW --cast-zero-arg-calls: cast every 0-arg call site to ((s32 (*)(void))func_801789AC)() — gcc folds the cast of a known symbol to a direct jal, so caller bytes are unchanged — then re-run the conform normally. PLAN -> VALIDATE -> WRITE like the decl axis, because a partial cast set is itself a fleet-wide compile break. Not a macro this time (unlike func_8015B950's single shared site): 138 genuine per-overlay call sites, one each. VERIFIED IN STAGES, not all at once: the 138 casts ALONE are byte-neutral (d19c9580); then the 660-site declaration conform (axis complete, 0 remaining); then the gate -> verified 1 / failed 0; then R22 clean-fleet 140 passed / 0 failed of 140. Reach 138 x 91 ins = 12,558 templated instructions unlocked for the sweep. |
||
|
|
ee55bd5cae |
feat(phase-29): func_801330E0 banked — conform_decls taught to read K&R definitions
THE GAP: conform_decls could not parse a K&R definition at all — it exited "no DEFINITION found, refusing to guess". Honest, but §43 (a narrow param declared K&R-style, producing the in-place `sll $a2,$a2,16` tell) is a documented, load-bearing idiom here for exactly the narrow-param class. So the tool was silently refusing the drafts that most need it: a whole idiom family read as "nothing to conform" (R32 coverage). THE SUBTLE PART IS PROMOTION (C89 6.3.2.2). A K&R definition promotes each narrow parameter, so a prototype in scope must declare the PROMOTED type or gcc rejects the pair with `argument 'x' doesn't match prototype`. That is why the fleet prototype reads `s32 a2` for a parameter the definition declares `s16` — and why emitting the declared (unpromoted) type would RE-CREATE the narrow-param conflict this tool exists to remove. The parser now promotes s8/u8/char/s16/u16/short -> s32 and float -> f64, pointers untouched, and reports (R32) any K&R param with no declaration. RESULT: byte-true signature read as `void func_801330E0(void *, s16 *, s32)`; the only real change vs the fleet's 973 declarations was param_1 `s16 *` -> `void *` (a pointer shape, caller-neutral). 973 sites / 973 files rewritten, axis complete. Gate: verified 1 / failed 0, d19c9580 BYTE-IDENTICAL. R22 clean-fleet 140 passed / 0 failed of 140. Reach 138 x 110 ins = 15,180 templated instructions unlocked for the family sweep. |
||
|
|
ad57c16e61 |
feat(phase-29): func_8014CF04 + func_8015D1B8 banked; conform_decls had 3 defects R22 caught (§98)
THE BANK: the T14 PLUMBING census showed func_8014CF04 blocking THREE drafts at once. Conforming its
decl axis banked func_8014CF04 + func_8015D1B8 (func_80135260 is a genuine DIFF, agreeing with its
independent SESSION-21 diagnosis). R22 clean-fleet 140/140; report fail-closed green (dedup 1886/0,
0 NON_MATCHING). fn-count 317,896 -> 317,898; distinct 66,110 -> 66,111.
BUT THE AXIS WAS A 1,748-FILE T2 WRITE SET (the --check per-form counts read "1"), and R22 came back
139/140 -- TWICE -- on a change the per-binary gate called BYTE-IDENTICAL. Three defects (§98):
1. THE REGEX CROSSED NEWLINES. `[^;]*` matches '\n', so a match starting at a DEFINITION line ran
past the `{` to the first `;`, swallowing `s32 func_8014CF04(...) {` PLUS the register pin on the
next line and replacing both with a prototype -> undefined reference. Fixed to `[^;{\n]*`: a
definition is now unmatchable by construction.
2. IT REWROTE INSIDE COMMENTS (H5, 3 lines). Now scans cdecl._mask() and rewrites by SPAN (R33 --
that length-preserving primitive already existed for exactly this).
3. THE REAL CAUSE -- IT ASSUMED ONE SIGNATURE FITS THE FLEET. ov_SC07_006 carries its own banked
definition with a DIFFERENT byte-true signature ((s32,s32,void*) vs (s32,void*,void*)), under a
decl marked "per-overlay-local decl (byte-true sig); do NOT re-macroize". That is the Phase-16
loose-typing wall inside a tool that structurally assumes it away. NEW RULE: a TU that DEFINES the
function owns its own declarations; a fleet axis is meaningful only for CONSUMING TUs. This grows
more common as banking proceeds -- every overlay that banks a function becomes an exception.
Then the R32 completion assertion cried wolf on its own by-design skip ("HALF-AXIS -- DO NOT BUILD"
for a complete rewrite): an assertion must be exact about its DOMAIN, not just its condition. Scoped
to consuming TUs -> 1,747 sites, 1 excluded by design. Also hardened to PLAN -> VALIDATE -> WRITE;
the refusal path had aborted mid-write while claiming nothing was modified, creating the very
half-axis §85 calls a guaranteed break.
META (R22's premise, re-earned): after fixing defect 1 I EXPECTED R22 to pass; it failed again for an
unrelated reason, and an individual `make build` of the failing binary SUCCEEDED by reusing objects
the clean run rebuilds. An incremental pass does not refute a clean-tree failure.
|
||
|
|
97cd2739b5 |
fix(phase-29): the gate manufactured 3 false CC1-FAIL verdicts — carve-refusal, tree hygiene, R32 (§97)
A 15-draft harvest_verify batch reported CC1-FAIL=4 and `final SHA None`. Three of the four were the HARNESS, not the compiler. Checked the tree FIRST (the MISMATCH is a tree alarm, not a result), reverted to the committed baseline rather than reasoning about a half-applied state, rebuilt -> d19c9580 BYTE-IDENTICAL. No banked result was ever at risk: the byte-gate cannot manufacture a match, but it CAN manufacture a verdict — and verdicts are what the backlog and roadmap are built from. ORDERING PROVED THE CASCADE (R14): items 1-11 are real (9 PLUMBING, 2 DIFF), all before item 12 — jtbl_carve REFUSING func_8013B83C (§59(3) non-contiguous same-subseg table). Items 13-16 are four CC1-FAILs on the SAME ov_SC01_077_o0.o = one refused carve counted four times. THREE DEFECTS FIXED: 1. `_ok` was computed and IGNORED — a refused carve was spliced and built anyway into a guaranteed Error 33, filed as CC1-FAIL. Now a named CARVE-REFUSED class, skipped (one build cheaper). 2. attempt() never restored on failure, so the tree was dirty BETWEEN drafts — and _jtbl_snapshot() snapshots the tree AS IT FINDS IT, so a later carve captured an earlier FAILED draft's splice and its undo faithfully RE-APPLIED it, after the final _write(baseline). That is the entire `final SHA None` mechanism. Invariant restored: the tree is at baseline except while a draft is under test (atomic AND bisect branches). 3. The recovery's own `make extract` rc was unchecked (_sh does not raise — §93's sibling). Now loud. Plus an R32 assertion on the cleanup: at 0 verified a non-empty git status is residue, not a result; it names the files and the recovery command. It fired correctly on its first real run. MEASURED RECOVERY (same drafts, clean tree): func_8013B83C -> CARVE-REFUSED; func_801789AC -> PLUMBING (actionable); func_8017C974 -> DIFF (corroborates its agent's global_alloc spill diagnosis); func_80140958 -> CC1-FAIL (genuinely its own). final SHA None -> d19c9580; tracked diff empty. BLAST RADIUS OF §96, HONESTLY: the reconcile_tu span fix unblocked func_80176218 (banked, swept 133/137) and no other draft in the batch. 7 of the 9 PLUMBING are `conflicting types for <the function itself>` = the DEF-side self-decl axis conform_decls owns — the next lever, now a measured target list rather than a guess. cookbook §97. |
||
|
|
8c36ede849 |
feat(phase-29): func_80176218 banked (45,126 templ ins) + reconcile_tu span/R32 fix
THE DRAFT was failing in a CHAIN, one "next conflict" per gate cycle. Applied §95's own diagnostic law instead — splice once, dump EVERY cc1 error — and the whole set named the cause immediately: three errors on TWO axes (one data decl, two callee decls), not three problems. THE DATA ERROR WAS reconcile_tu AGAIN, ONE SHAPE DOWN (§96). split_statements returns comment- STRIPPED text WITH SPANS; the rewrite re-found each planned statement by comparing that text to a raw LINE, so `extern u8 D_80078E78; /* cur base ($s5) */` never matched. The decl was left unconformed WHILE THE USE-CAST PASS STILL FIRED -> a draft whose uses are cast for the TU's storage against the draft's own declaration -> cc1 reports `conflicting types` AT THE VERY DECL THE TOOL JUST CLAIMED TO FIX, exit 0, "reconciled: 3 symbols". FIX: rewrite by SPAN (the primitive existed — its docstring says spans are preserved *because drafts get rewritten*). Plus the R32 assertion the old code was missing: it had a dropped_check counter incremented in two places and NEVER COMPARED — "a loud failure nobody counts is exactly as invisible as a silent one" in miniature. Now declarators-in vs -out AND a per-symbol check that each planned tu.declaration() actually landed, both as `!!` notes so --strict exits non-zero. MEASURED: 3 -> 4 data symbols reconciled on the same draft; trailing comments preserved (H5). THE TWO CALLEE CONFLICTS were the other axis (reconcile_tu skips kind=='func' by construction): cast_call_sites (§20) conformed func_80177AD4 (TU `void (int, unsigned int)`) and func_80178298 (TU `(u32*, u8*, short, short)`) and cast each call site to the draft's intended widths. GATE: verified 1 / failed 0, d19c9580 BYTE-IDENTICAL. Write set is one overlay-local TU = T1 per the §63/§85 blast-radius taxonomy, so the per-binary gate is sufficient; the ×137 sweep is the T2 case and takes a full R22. |
||
|
|
db620d4b8d |
fix(phase-29): reconcile_tu dropped the sibling declarators of a multi-symbol extern line
THE DEFECT (on the banking path — gate_stage runs reconcile_tu): its rewrite replaced the draft's declaration LINE with the TU's declaration of the ONE conflicting symbol. A statement can declare several: 'extern u16 D_80078EB2, D_8011F82A, D_8011F82C, D_80078EB4, D_8011F8C4;' where only EB4 conflicts became 'extern s16 D_80078EB4;' — four symbols silently gone. WHY IT HID: the draft does not fail at the declaration. It fails later with 'D_8011F82A undeclared' at a USE, several conflicts down a peeling chain, nowhere near the cause. I peeled four separate 'next conflicts' out of func_80176218 before dumping ALL cc1 errors in ONE build and seeing three undeclared symbols that the tool itself had removed. FIX: group the plan by STATEMENT rather than by symbol; re-emit EVERY declarator (TU's version for the conflicting ones, the draft's own for the rest); note multi-declarator statements; and when a statement cannot be re-parsed, say so loudly instead of emitting only the planned symbols. VERIFIED: all 5 declarators survive, and the same draft now reconciles 3 symbols instead of 2 — the dropped ones had been hiding a further conflict. cookbook §95. The law (R32 again): a transform that REPLACES a syntactic unit must account for everything that unit contained — the STATEMENT, not the line, is the unit of a C declaration. Diagnostic: when a draft fails in a chain, stop peeling one error per gate cycle; splice once and dump every cc1 error, because the shape of the whole set names the cause. |
||
|
|
7d9dd6deca |
feat(phase-29): func_8014D820 banked (41,952 ins) + conform_decls scalar-narrowing guard
WAVE 2 (9 never-drafted exemplars, ultracode): 9/9 returned, 5 MATCH / 4 near, 2.25M tokens. BANKED: func_8014D820 (304 ins ×138) — and its agent ROOT-CAUSED the failure I left undiagnosed. It was never an assembler problem: cc1 exit 33, `conflicting types for 'Ent'` vs engine_types.h:434, surfaced by the recipe's `set -o pipefail` and MISATTRIBUTED to `as` because `as` is the last stage in the pipe (Makefile:560). Fixed by moving V4/Desc/Ent to BLOCK scope — byte-neutral and collision-proof across all 138 member TUs. Vindicates flagging it to the agent as UNVERIFIED rather than passing my own guess forward as fact (§88e). R22 clean-fleet 140/140. NEW GUARD — SCALAR NARROWING IS NOT CALLER-NEUTRAL (byte-proven, and it cost 3 gate cycles): conform_decls treated all decl type changes alike. A POINTER change is caller-neutral (func_80179B74 conformed 1,600 sites s16*/short* -> u16* and stayed byte-identical fleet-wide). A SCALAR WIDTH change is NOT: narrowing `s32 a0` -> `u16 param_1` changes argument promotion at every call site. MEASURED on func_80175DA8: decls reverted -> gate says PLUMBING; conform applied -> gate says DIFF. The conform did not fix the draft, it changed the CALLERS. Now warned explicitly (not refused — the draft's sig is still byte-truth for the callee and the gate arbitrates), with the instruction that a DIFF after this conform means examine the callers (§17a-1 pair), not the body. Verified the guard discriminates: fires on func_80175DA8 (s32->u16), silent on func_80179B74. STILL OPEN from wave 2: func_80176218 + func_80175AB8 (DATA-symbol conflicts, D_80078EB4 / D_8011F7BC -> reconcile_decls) · func_80175DA8 + func_80135EB0 (need the §17a-1 caller pair, not a bare conform) · 4 near-misses with precise residuals recorded (func_80176734 129 length-drift, func_80140958 49 inverted-hoist, func_80177B5C 19 sched tie, func_8017C974 83 -> permuter). |
||
|
|
e721452526 |
feat(phase-29): 3 more exemplars banked + 3 family sweeps; conform_decls extern-optional fix
BANKED: func_8015B950 (271 ins) · func_8016AE5C (85) · func_80179B74 (111).
SWEPT: func_8015B950 137/137 · func_8016AE5C 136/137 (ov_SC03_108 refused, left a stub rather than
forced). Three full family sweeps this session, all unblocked by the --like role guard.
FLEET 82.4% instr · 70.4% distinct-code (crossed 70%) · 89.72% fn-count. R22 140/140 throughout.
conform_decls has now been right in BOTH directions: it REFUSED func_8015B950 (which by hand broke
138 binaries) and CLEARED func_80179B74 (1,600 sites / 523 files, three decl forms, pointer-type
only). Then it found its OWN coverage gap: it required a leading `extern`, so it reported "no
declaration found" for a TU declaring the function on line 23 without one — a silent miss that reads
exactly like "nothing to do" (R32). `extern` is now optional and PRESERVED where present.
⚠️ INSTRUMENT FAILURE, recorded: mid-session `grep <pat> <file> | head` began printing NOTHING while
exiting rc=0 (i.e. matching). Read showed the line plainly; re-done in Python the file has 3
occurrences including a CALL at line 68. It cost one wrong intermediate claim ("no extern anywhere"),
which conform_decls immediately contradicted. NO banked result is affected — every bank passed the
whole-binary byte-gate and a clean-tree R22, neither of which reads shell output. A broken diagnostic
wastes time; it cannot manufacture a match. Diagnostics moved to Python. §90a, aimed at the shell.
func_8013BD74 is NOT a wall: its byte-true def takes a draft-LOCAL struct `A`, conforming the
prototype fails `parse error before '*'` (A undeclared that early), and the prototype cannot be
deleted because a call at line 68 precedes the definition at 71. Needs the §20/§64 type-lift.
|
||
|
|
7c1640888f |
feat(phase-29): func_8016AE5C banked + tools/conform_decls.py; a 138-binary break R22 caught
BANKED: func_8016AE5C (85 ins ×138). R22 clean-fleet 140/140.
⚠️ I BROKE 138 OF 140 BINARIES AND R22 CAUGHT IT — the per-binary gate could not.
Conforming func_8015B950's decl from `(void)` to its byte-true `(s32 arg0)` across 926 sites gated
BYTE-IDENTICAL on ov_SC01_077 and broke 138 other binaries with Error 33. §63/§85 exactly: a T2
write set is provable only by R22, and the binary the gate authorises is not the binary that breaks.
MECHANISM: conforming a decl to a signature that TAKES parameters makes every existing 0-ARG CALL
SITE a hard `too few arguments` error once a prototype is in scope. Not a declaration-only change.
PROCESS NOTE (mine): a first R22 reported 138 failures, an individual rebuild of a "failing" binary
said BYTE-IDENTICAL, and I nearly filed it as a flake. The second clean R22 reproduced it exactly —
the individual build passed only by reusing objects the clean run rebuilds. An incremental pass does
not refute a clean-tree failure; that is R22's whole premise, pointed at me. Reverted to a known-good
baseline (a stray jr_isolate region file was also in the tree) and redid the one good bank cleanly.
NEW tools/conform_decls.py — because applying this axis by hand three times in one session is how a
half-axis happens. Derives the byte-true signature from the DRAFT's definition (§58b), rewrites EVERY
site, asserts completion (R32). Encodes both preconditions: the §85 return axis (refuse if any caller
consumes the return) and a NEW arity precondition (refuse if 0-arg call sites exist, naming the cost).
The guard immediately gave a better diagnosis than my hand-fix had: func_8015B950's 0-arg call is in
ONE place — src/shared/engine_core.h, a DEFINE macro body — expanded into all 926 TUs. That fix is a
SINGLE cast, not 926 edits. Named as the next step rather than run on tired context.
Also lands cookbook §91 (the --like role trap) from the previous step.
|
||
|
|
a5e5ea45ef |
fix(phase-29): jtbl_carve — guard the --like role-transfer; the ×137 sweep goes 0/3 -> 3/3
ROOT CAUSE of the 0/3 (found by reading the tool's ACTUAL invocation, not by guessing): jtbl_family_bank calls `jtbl_carve <sibling> --func <fn> --like <exemplar_ov>`, and the role-transfer keys on the SUBSEG ROLE (`ov_SC01_077_a` -> `_a`). Its premise — "same family => same span structure" — silently breaks when the exemplar and the sibling host the function in subsegs with DIFFERENT roles, which happens whenever the exemplar has a split the sibling does not. MEASURED: func_8012AAAC lives in `ov_SC01_077_a` (role `_a`) in the exemplar but in the MAIN subseg (role ``) in every sibling. The transfer therefore looked up `ov_SC01_077` — an unrelated SEVEN-table span belonging to entirely different functions — and stamped those starts onto a sibling span holding one table. jtbl_rodata_pads then refused with `consumed 1 rodata .align(s) but 2 pad spec(s) given — table-count drift`, and jtbl_family_bank deliberately does NOT treat that error as isolate-fixable, so all 137 siblings returned a bare `gate-fail` with the cause discarded. THE FIX: transfer only when the exemplar's subseg for THIS FUNCTION has the sibling's role; otherwise derive the span from the sibling's own carve (which was already computing it correctly). Fail-open is not acceptable here — a wrong table set corrupts the image, so the guard defaults to local derivation. MEASURED RESULT: the 3-member probe goes 0/3 -> 3/3 BANKED. R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed. Two earlier hypotheses were tested and are recorded honestly in CURRENT_PHASE.md: the sibling call-site casts (real conflict, fixed, byte-neutral — but NOT the blocker) and my own carve-alone test (which fails by construction for this shape, because a stub object does not emit the table its 2-entry spec describes — the tool splices the body BEFORE building, so its path is the valid one). |
||
|
|
2cc49d0310 |
feat(phase-29): SESSION-21 — func_8012AAAC banked via the §81 carve chain (R22 140/140)
The first jtbl-routed bank of the session, and it validates the whole chain end-to-end:
1. jtbl_carve SPLIT-TABLE repair (this session): jtbl_801D7FB0 28 -> 50 words (112 -> 200 B),
authorized by func_8012AAAC's own `sltiu 0x32`.
2. NEW FIX — SINGLE-TABLE PREDECESSOR: adding a second table to a subseg whose existing carve was
single-table lost the FIRST table's start entirely (new_offs has only the new one;
overlay_jtbl_addrs cannot see the old one because its owner is banked and extract PRUNED the
stub .s; and single-table carves persist no tables= to rebase). The span then failed its own
validator with "first must equal the span start" — the invariant naming the missing entry.
A single-table carve spans exactly its one table, so ITS SPAN START *IS* THAT TABLE'S START:
inference, not persistence, so it also works for spans carved before tables= existed. This is
the RECOVERABLE half of the documented func_8013F350 lesson (that one was a pre-§8e merged
DOUBLE — two tables, no record, genuinely unrecoverable).
Result: ov_SC01_077_a JTBL_PADS := 0,0 tables=+0x0,+0x14. Carve alone byte-gated BYTE-IDENTICAL
BEFORE the bank was attempted (§81 step 2).
3. ARITY axis, all-or-nothing: 1,244 decl sites / 1,240 files `(void)` -> `()` + an R32 completion
assertion (old-form remaining: 0).
4. ONE call-site cast: the definition lands at line 811 and a 0-arg call sits at 822, so gcc sees
the prototype and rejects it — `((void (*)(void))func_8012AAAC)()` (§17a-1; gcc folds the cast
of a known symbol to a direct jal). Only 1 of the 1,386 fleet-wide 0-arg call sites needed it:
the others see only the `extern ()` decl, which permits a 0-arg call.
DIAGNOSIS NOTE: the failure read CC1-FAIL with only a warning visible under make. Running the
pipeline stage-by-stage (cpp | cc1 | maspsx | jtbl_rodata_pads | as) put it on cc1 rc=33, and cc1's
own stderr named it exactly: "too few arguments to function func_8012AAAC" at line 994. Isolating
the stage was what turned an opaque Error 33 into a one-line fix.
R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed.
family_sweep correctly REFUSED this exemplar (§53: a jr-family must route through
jtbl_family_bank.py; "a 0% from this path would be a TOOL artifact, not a wall") — the ×137 member
sweep is the next step and needs a clean tree, which this commit provides.
|
||
|
|
5be4c21480 |
feat(phase-29): SESSION-21 — the ×138 member sweep: 274 members from 3 exemplar cracks (R22 140/140)
- family_sweep --hseq over the 3 newly-banked exemplars: BANKED 274 member-matches / 137 failed across 137 overlays, for ~0 agent tokens. Session total: 3 exemplars + 274 members = 277 fns. - THE STALE-MAP STEP, hit and handled: the first sweep returned "0 matched-exemplar families" because .run/family_hseq.json still listed the fresh cracks as draft-ov077. Regenerated (matched-sib families 60 -> 63) and the sweep found them — the documented bank-x1 -> regen -> sweep path (memory crack-wave-sweep-map-regen). - §86 REPRODUCED CLEANLY: 2 of 3 families templated ~137/137; the third failed ~137/137. Not a rate — a BIMODALITY. One probe per family, then sweep or skip; never a blended pool average. - R22 clean-fleet: extract-all 139/139, check-all 140 passed / 0 failed (second clean-tree verification this session). dedup 1886/0, C1 coverage 239,604/239,604, 0 NON_MATCHING (G4). - FLEET 81.7 -> 81.9% instr · 89.52 -> 89.60% fn-count · distinct-code 69.3% UNCHANGED — correct and expected: these are h_seq PURE propagation-class families, and SESSION-20's routing rule says propagation moves only the DISPLAY metric (members were already counted once via their exemplar). To move RE-completeness, target byte-VARIANT families. Stated plainly so the next session picks targets by the metric it means to move. - drive-by: family_sweep --help crashed (argparse %-expands help text; a literal "0%" needed "0%%"). |
||
|
|
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. |
||
|
|
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.)
|