mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 21:36:06 -04:00
4aa7dbdfa435d4e8eabf358e8fc56da80dd8efdb
354 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6e0b1605c6 |
fix(phase-30 S40): cast_call_sites read a RETURN as a prototype and deleted it — 0/39 sweep becomes 18/39
THE BUG. tools/cast_call_sites.py classifies a declaration line with
^([ \t]*)(extern\s+)?([A-Za-z_][\w \t\*]*?)\b([A-Za-z_]\w*)\s*\(([^;{]*)\)\s*;
Feed it a return statement and `return` is a perfectly good identifier where a type is expected:
return func_8012CB64((s32)out, -0xC0, 0x40, -0x60, 0);
^^^^^^ captured as the return TYPE, func_8012CB64 as the DECLARED NAME
so the "rewrite this decl to canonical" path REPLACED the statement with
`extern s32 func_8012CB64(s32,s32,s32,s32,s32);`, DELETING the return. In C89 a declaration after a
statement is a parse error, so the damage surfaced as a bare syntax error in the DRAFT -- reading as
the draft's fault, not the tool's. 9 of 9 staged members of family 0x801848dc lost their return.
fix: a keyword guard (a declaration's type-specifier can never begin with a statement keyword)
family_sweep --hseq --band all over 5 families: 0/39 -> 18/39 banked (only the guard changed)
⚠️ AND THE TRAP INSIDE THE FIX: the obvious R33 move is "route it through cdecl". CHECKED, and it is
WRONG -- cdecl.parse() is a DECLARATOR-GRAMMAR parser that assumes it was handed a declaration; it
reports `return func_X(...);` as declaring func_X and `if (f(a));` as declaring `if`.
Statement-vs-declaration is a question cdecl does not answer. Routing there would have been a silent
non-fix that looked principled. §134's law still holds for line-SHAPE masking; this is a different
question.
BLAST RADIUS (measured, not assumed -- R14): cast_call_sites is in gate_stage's DEFAULT pipeline
(canon_resident_calls -> cast_call_sites -> sig_unify -> harvest_verify) and has been since Phase 20.
Of 44,833 stored drafts, 318 (0.7%) carry a `return f(...);` line this mis-reads, across 67 callees
(func_8014F468 x41, func_8014F6F4 x37, func_8014F74C x32, ratan2 x25). Every one, every time it
passed the gate pipeline, lost its return and failed as PLUMBING. Part of the historical plumbing
tail is this bug.
ALSO IN THIS COMMIT
- S5 CALIBRATION WAVE (8 agents, ultracode, 1.31M tokens). Pool VERIFIED FIRST (R14 -- Fable's whale
claim was 3/4 wrong): measured 1,677 clusters / 5,795 fns / 319,755 ins at a 3.68x multiplier vs
its claimed 1,689 / 5,956 / 326,261 at 2.7x -- its numbers hold, and the multiplier is BETTER.
Result: 8/8 match_one MATCH (close=0), and 5/8 banked whole-binary -- the §52b/§61 gap is
integration, not codegen. Banked: func_801822E0 func_8017EC98 func_801851A8 func_80189A34
func_80188E10 (693 ins x1 before propagation). Not banked: func_8018B238 (FAILED),
func_8017EF54 + func_801802EC (NEAR) -- drafts kept in .run/wave-s40/ for recovery.
- 18 member-banks from the re-run sweep (the cross-address free-h_exact pool: h_exact-identical at
DIFFERENT addresses, which dedup_propagate correctly refuses since it assumes position-locking --
family_sweep is the right lane).
- cookbook §143 (this bug + the cdecl trap + the blast radius); index regenerated.
VERIFIED: make clean && make extract-all && make check-all -> 140 passed, 0 failed of 140.
Fleet 12419169 -> 12420375 instr; distinct +1,526 / +5 uniq; fn-count +23. audit-digest OK.
0 NON_MATCHING (G4).
NEW IDIOM FROM THE WAVE, not yet folded into §31 (agent was told to write only its draft): a byte
counter must be spelled `cnt + 0xff`, NOT `cnt - 1`. Both are mod-256 identical and both compile to
one addiu, but gcc-2.7.2 picks the immediate encoding from the SOURCE SPELLING (0xFFFF vs 0x00FF).
Also flagged: .run/ghidra_c/func_8017EF54.c is a stale decompile of the WRONG function.
|
||
|
|
a0e499d8f1 |
feat(phase-30 S39): make audit-frontier — the reconciliation gate (Drew's MASTER_REMAINING, derived form)
Drew asked whether we should build a master list of all funcs, a banked list, and a MASTER_REMAINING = total - banked that we hand-edit on every bank. Assessment in docs/decision-log.md (2026-08-04): ADOPT THE GOAL, REJECT THE MECHANISM. The triple already exists and is DERIVED, not maintained: total = .run/sig.*.jsonl (sig_image over the ORIGINAL bytes, independent of splat) banked = sig - stubs (INCLUDE_ASM pastes the original asm => not-wrapped == byte-exact) remaining = corpus.stubs() (filesystem-derived, coverage-asserted) and "remove it when we bank it" already happens -- banking IS deleting the INCLUDE_ASM line. A hand-maintained file would drift SILENTLY and flatteringly, which is the exact failure R33 exists for (fuel_manifest recorded 130 live stubs when the truth was 30, hiding 91.6% of remaining gain). What was genuinely missing is CROSS-ASSERTION. Six artifacts answer "what's left" -- corpus.stubs, worklist, backlog, family_hseq, fuel_manifest, progress.fleet -- each individually derived, none ever compared to the others. That is what cost P30 T0 a hand-reconciliation (family_hseq 29,961 vs progress.py 28,296). R34: not a better assertion inside one oracle, but a second one that can argue. tools/audit_frontier.py takes corpus.stubs as the reference and checks every other view against it: rows/targets naming an already-banked function, and any view whose PUBLISHED count disagrees with a recount. On first run it immediately caught a real one: family_hseq publishes 11,456 unmatched instances; only 11,297 of its members are still open per the corpus (delta +159) -- the map predates tonight's 159 banks. Ranking work off it would have mis-scoped by that much. It also PRINTS ITS OWN SCOPE LIMIT, deliberately: agreement here does NOT mean the denominator is complete. Every view, and the byte-gate itself, is blind to never-onboarded code -- the 39 type-1 modules and main's missing independent boundary oracle stay open (R34/R36). DELIBERATELY NOT wired into tools-health (Drew said "dont do this now" about the master list; this is the additive half). --strict exits 1 for when he wants it binding; wiring is one line. |
||
|
|
e9c66a6720 |
fix(phase-30 S39): close the §134 class — the last two line-shape scanners route through cdecl._mask
§134 had been patched individually in six tools; the standing note said the fix is ONE masking
oracle, not a seventh regex (R33). The last two holdouts are migrated.
progress.py.strip_comments — a private 2-line regex, NOT string-aware, feeding three line-shape
decisions in classify(): the {-vs-; definition/declaration scan, the count('{')-count('}') body
walk, and the empty-vs-real body test. A brace inside a string literal therefore mis-buckets a
function in the FN-COUNT metric. Negative control:
void f(void) { puts("}"); x = 1; }
old regex -> body-depth -1 (the string's brace was counted)
cdecl._mask -> body-depth 0 (correct)
Metrics IDENTICAL before/after on today's corpus (341365/353717; REAL 339510, empty 896, stubs
12345) -- a latent defect, harmless until someone banks a function containing "{".
lint_symbol_refs.strip_comments_strings — correct, but a SECOND implementation of the same
masking. Deleted in favour of cdecl._mask. The one behavioural difference (_mask blanks the quote
DELIMITERS, the private scanner kept them) was CHECKED not assumed: irrelevant because every token
the linter hunts lives outside the quotes. Gated on the linter's OUTPUT being byte-identical across
the change (it is), not on the two masks being byte-identical -- the right gate is the tool's
answer, not its internals.
cookbook §141 + index regenerated. No src/ or config/ change; no bytes touched.
|
||
|
|
1576570271 |
fix(phase-30 S1e): the distinct-code "regression" was a STALE DIGEST — alias lever ungated
The S38 checkpoint gated the phase's best lever ("do NOT scale the alias lever") on
distinct-code falling 89.3 -> 89.2. It never fell.
PROOF (each commit's metric recomputed from its OWN committed tree, 0 unresolved):
commit:1426 TRUE : instr 12394533 distinct 5022306 (77895 uniq)
commit:1426 COMMITTED: instr 12402412 distinct 5029324 (78025 uniq) <- stale
HEAD TRUE == COMMITTED: instr 12405402 distinct 5025082 (77952 uniq)
=> true delta 843->HEAD: instr +10869, distinct +2776 ins / +57 uniq. ALL ROSE.
The 843 digest was generated from a working tree still holding work REVERTED before the
commit landed (+7,879 ins / +130 uniq overstated) and never regenerated, so the next
HONEST digest read as a fall. => THE ALIAS LEVER IS UNGATED (scale it, §61 small batches).
Both recorded leads were wrong (R14): progress.py:423's SIG regex feeds fn-count ONLY
(neither weighted metric sees a C identifier — both derive matched = sig - corpus.stubs),
and "the harvest reverted functions to INCLUDE_ASM" died on one grep (483 removed, 0 added).
The 3-grep proof: identical sigs + unchanged tools/ + zero +INCLUDE_ASM => HEAD's stub set
is a strict subset => both numerators are FORBIDDEN to fall.
THREE INSTRUMENT DEFECTS, all one class (a bare except around a fail-CLOSED oracle):
- progress.py stub_addrs wrapped corpus.stubs in `except Exception: return set()`. An empty
stub set means "could not answer", not "no stubs", so matched = sig - stubs credited EVERY
function. Byte-witnessed: instr 100.00% / distinct 100.00% in a tree with no asm/. Now
propagates.
- cast_call_sites.tu_for + reconcile_tu.tu_for had the identical swallow, falling back to the
default <ov>.c instead of the jr/-O0 split TU — silently reinstating the exact bug
cast_call_sites' own docstring says it exists to fix. A wrong-TU reconcile fails the gate,
and this phase's base rate is ~24k PLUMBING vs 4,917 DIFF, so it presents as a codegen wall.
Now propagate CorpusError; ValueError fallback for curated names preserved; derived-TU path
re-verified (a _jr_ split stub resolves correctly, both tools agree).
NEW GATE (R34 — the byte-gate is a null oracle for DOCUMENTS; check-all stays 140/140 over a
stale digest forever): tools/audit_digest.py + `make audit-digest`, wired into tools-health
after report. Recomputes the three headline metrics from the current tree and fails if the
committed digest disagrees. Compares INTEGERS, not percentages — the +7,879-instruction
staleness printed as "94.4%" on both sides. Negative-control-proven against the stale 843
digest (fails, exit 1) and green on HEAD.
Verified: make report exit 0 (dedup-check 1910 validated / 0 failed, C1 coverage
241216/241216); audit-digest OK; cookbook-index OK (398 sections); metrics unchanged by the
fix (94.40% / 89.18%). No src/ or config/ edits — no bytes touched, nothing banked.
cookbook §140 · decision-log 2026-08-04 · SETUP.md inventory (R21) · R14/R32/R34/R35.
|
||
|
|
3f7534c10a |
fix(tools): the alias scanner I added this morning had the §134 defect I documented this morning
asm_label_aliases scanned the RAW source with a greedy `[^;{}]*` that spans newlines. Byte-witnessed
on ov_SC07_006: a match STARTED inside a comment (`… -> MATCH (40 ins)`), ran through the `*/` and
two blank lines, and swallowed the real declaration below it — so the map recorded
`'MATCH': 'func_80146AFC'` while the actual alias `aF80146AFC` never appeared, and a
`register u8 *p __asm__("$6")` pin contributed `'void': '$2'`.
That is exactly the §134 multi-line-comment class whose project-wide answer is `cdecl._mask` (R33,
ONE masking oracle) — which I described in §139 this morning and then did not use. Sixth tool.
Two non-obvious things the fix had to get right:
- REJECTING a bad match after scanning the source does not work: finditer resumes at the END of the
match it yielded, so the greedy comment-spanning match CONSUMES the real declaration and
rejecting it loses that declaration entirely (verified: aF80146AFC stayed invisible with a reject
in place). The scan must run on the MASKED text so a match cannot start in a comment at all.
- `_mask` blanks string content AND its quotes, so the scan pattern cannot require them. Added
`_ALIAS_SCAN` (quotes optional) for the masked scan; the real symbol is read back from `src` at
the same offsets, which is legal precisely because the mask is length-preserving.
`$N` targets are excluded — a hard-register pin is not a symbol alias.
Found because the R32 partition guard added earlier today REFUSED to rewrite the file rather than
silently dropping the function — the guard working as intended, on its first real encounter.
|
||
|
|
df99a71732 |
fix(tools): jtbl_family_bank reported "gate-fail" with no reason — the third missing-payload defect today
`last_err` was only ever set when a STAGE failed to produce a candidate. A candidate that built to the wrong bytes — i.e. the actual gate rejection — recorded nothing, so every caller saw a bare "gate-fail" and the failure was unroutable (measured: wave 6's 13 sibling failures could not be classified at all). Now captures the build's hard diagnostics, keeping BOTH ends of the line so the symbol survives (never left-truncate — same fix as harvest_verify.classify_fail earlier today), and says "built, bytes differ (genuine DIFF)" when there is no diagnostic. Third instance of one defect class this session: a tool reporting an OUTCOME without the EVIDENCE that routes it (harvest_verify.classify_fail truncating the symbol away; .run/s6f_gate.py booking a crashed child as silence; this). All three were quietly converting recoverable plumbing into apparent walls. Also adds .run/jr_family_sweep.py — the S2 front end for the has_mid_jr matched-exemplar families that family_sweep --hseq refuses BY DESIGN (§53). Families are DERIVED from the map and looked up by EXEMPLAR (a just-banked head leaves the `members` array — that cost a round earlier today), members re-checked against corpus.stubs, --raw passed when a seed exists, and it commits per family because jtbl_family_bank requires a clean tree between families. |
||
|
|
2483fc902a |
fix(tools): jr_isolate_all was SILENTLY DELETING asm-label-alias definitions during a repartition
ROOT CAUSE (byte-witnessed, P30 S38 — the fifth tool with this same blindness).
A function banked under the §37/§73 DEFINITION-SIDE ASM-LABEL ALIAS form is spelled with a private
C identifier and bound to its real symbol by a GNU asm label:
void aF8018A860(s32, s16 *, u8 *, u8 *) __asm__("func_80183AF8"); <- decl, stays in preamble
void aF8018A860(s32, s16 *, u8 *, u8 *) { ... } <- THIS emits func_80183AF8
overlay_src_split.addr_of() resolves `func_<hex>` arithmetically and everything else through `syms`.
`aF8018A860` matches NEITHER, so it returned None — and partition() keeps only items with a
resolved address, so the definition was dropped from EVERY region. The file was then rewritten
without it and nothing said so. One carve of ov_SC02_028 deleted the definitions emitting BOTH
func_80183AF8 and func_80184268; the overlay stopped linking with `undefined reference`, and six
wave-6 drafts were written off against that as a plumbing/compiler wall.
TWO FIXES:
- CAUSE: overlay_src_split now builds an asm-label alias map from the source and resolves a
definition through its EMITTED SYMBOL rather than its C name (verified: aF8018A860 -> 0x80183AF8,
aF8018AFD0 -> 0x80184268 — exactly the two symbols the link was missing).
- SILENCE: partition() and jr_isolate_all._partition() now REFUSE to rewrite a file when any
construct's address does not resolve (R32), instead of discarding it. That guard alone would
have surfaced this the first time it happened.
RESULT: 3 of the 6 alias-class wave-6 drafts bank immediately, for ZERO agent tokens —
func_801884D8 (137 ins) · func_80180B04 (251) · func_801380E0 (438). R22 clean-fleet 140/140.
The other 3 (the three LARGEST: 557/513/710 ins) have a second, size-correlated cause — open.
NOTE FOR THE FLYWHEEL: family_remap._alias_decl_for ALREADY handled this exact form, and its
docstring records the identical lesson ("that blindness was the WHOLE of the h_seq sweep's 137 'no
matched unit' skips. The tool, not the compiler (R35)"). The fix was never propagated. The alias
form needs ONE shared oracle, the way §134 comment-masking ended up on cdecl._mask — five tools
have now independently rediscovered it.
|
||
|
|
6b21af6a14 |
fix(tools): classify_fail truncated its diagnostic from the LEFT, severing the symbol name
These diagnostics are "<long path>:<position>: <message>", and the MESSAGE names the symbol — which is the entire routing value of the PLUMBING/CC1-FAIL label. The old [:90] spent its budget on the path and cut the payload mid-token, so a real wave-6 failure was labelled: PLUMBING: src/ov_SC03_001/..._jr_8017AE2C.c:(.text+0xc090): undefined reference to `func_801 with the symbol severed at four hex digits — unroutable. New _squeeze() keeps a short head (the file is still identifiable) and the tail (the symbol), so the same line now ends "undefined reference to `func_80143C74'". Same family as the §58 red-herring guard directly below it and §136a: a label that cannot distinguish its inputs carries no information, and one that drops the payload is worse than none. |
||
|
|
73321fe2c2 |
fix(phase-30 S38): the gate was booking CRASHES as silence — 10 of wave 6's 16 drafts vanished
THE DEFECT CHAIN (byte-witnessed, both ends fixed):
1. harvest_verify._reload_corpus re-applied the `--src` filter AFTER a jtbl carve. Following a
carved stub to its NEW TU is that function's entire documented purpose, and it was deleting the
very stub it had just followed. Then:
_stubs loses fn -> render() raises KeyError -> UNCAUGHT -> _jtbl_restore(snap) never runs
-> the carve is STRANDED in config/ + src/ -> every LATER group in the same gate run then
built against a tree the earlier crashes had mutated.
line 136 already calls --src "an optional filter, not a location oracle"; this was the one place
treating it as one. Fix: the filter never drops a draft under verification, wherever it now
lives, + an R32 loud report if a working stub vanishes across a reload (which also repairs
_touched/baseline — a carved fn missing from _stubs left its new TU unbaselined, so the revert
path could not have restored it either).
2. .run/s6f_gate.py never checked the child's returncode — it grepped stdout for VERIFIED:/FAILED:
and booked "neither" as NOTHING, printing a clean-looking tally over 10 missing verdicts. This
is the §136a defect I logged against my own capture tool last session, in the gate itself.
Fix: 1:1 accounting assertion (banked+failed+no-verdict == drafts), the child's rc + output tail
on anything unaccounted, and exit 1 — a crashed child may have stranded a carve, so it must
never look like success.
PROOF THE FIX IS NOT COSMETIC: func_8017EA84 (579 ins) now carves and banks BYTE-IDENTICAL. The old
tool reported it as nothing at all.
BANKED 7 (R22 clean-fleet 140/140 from `make clean` + extract-all + check-all):
func_8017EA84 ov_SC02_000 (579) · func_8017FEE0 ov_SC02_026 (299) · func_80181CE4 ov_SC03_111 (491)
func_80183AE0 ov_SC03_112 (240) · func_80184C74 ov_SC06_018 (288) · func_80180F98 ov_SC03_097 (263)
func_801841C8 ov_SC02_035 (44)
MY OWN ERROR, RECORDED (R37/R14): after reverting the stranded carves I re-extracted ONE overlay,
not all 16 — the Phase-20 R22 corollary (a reverted CONFIG needs a `make extract`, not just a
revert) which I know and skipped. Run 2 therefore read WORSE than run 1: three genuinely-banked
functions failed against stale asm. Re-extracting the 16 touched binaries produced the honest run.
A gate result measured against stale asm is not a measurement (R35).
.run/w6_diag.py: run the REAL gate path for one (ov,fn) with the child's full output. s36_capture.py
splices without the carve, which is the wrong path for a table-bearing fn (§61b: the carve must
follow the splice) and produces a failure that is an artifact of the diagnosis.
|
||
|
|
e5380ff6fb |
fix(tools): family_hseq's stdout claimed "fleet" for an OVERLAYS-ONLY number
load() globs .run/sig.ov_*.jsonl, so main and the resident are absent from every denominator — the numbers run ~0.3-1.0pp above the authoritative `make report` fleet (measured today: 96.6/94.6/ 89.7 vs the digest's 96.28/94.1/88.7). docs/family-hseq.md has always carried the "(overlays)" qualifier; the stdout print did not. That asymmetry matters because stdout is the channel a session actually reads and transcribes into a checkpoint — a right number under a wrong label is how a wrong number propagates (R35: the measurement was fine, the instrument's LABEL was the defect; R14: a checkpoint that disagrees with the digest must lose, and it can only lose if the disagreement is visible). Print-only; the metric itself is unchanged and correct for its scope. |
||
|
|
b497ee1649 |
feat(phase-30 S33c): PROPAGATE head COMPLETE — func_801466F0 x137 took three fixes + a type-lift
Fleet 96.17 -> 96.21% fn-count / 93.8% instr / 88.0% distinct; dedup 1909 -> 1910
groups, 0 failed, C1 241216/241216. R22 clean-fleet: 140 passed, 0 failed of 140.
The head is now 5/5 classes, 18,545 templatable ins, all banked this session from
a standing start of 0.
func_801466F0 had sat since S6b behind THREE separate blockers, each of which
looked sufficient on its own to explain the failure:
1. Its definition is under a §37/§73 ASM-LABEL ALIAS (`aF801466F0` in C, bound to
the real symbol by `__asm__`), and dedup_propagate.find_site anchored its head
regex on the literal `func_<ADDR>` — structurally blind to the form, returning
None, which every caller reads as "not matched". Now reuses
family_remap._alias_decl_for rather than growing a second matcher (R33).
2. That matcher was itself blind to the WRAPPED (multi-line) declaration — the
§134 shape, third tool. Fixed by matching over the joined text and mapping the
offset back to the decl's FIRST line (extract_unit carries from there).
Regression control: the single-line form still resolves. Fleet census after:
2,768 of 2,768 alias sites resolve, 0 missed.
3. Its record type was a draft-local typedef, so the body failed
compiles_standalone. Lifted Rec801466F0 to src/shared/engine_types.h INSIDE
the include guard (the SESSION-19 double-include note) and switched both the
macro and the exemplar to it — byte-neutral, gate-proven.
Probed on ONE member before the fleet run: byte-identical 9052dc0e first try.
MEASURED, NOT INHERITED (R37): the S6b note frames the alias-regex gap as a CLASS
of missed work. It is ONE function — 91 distinct alias decls fleet-wide, the
per-line matcher resolved 90. Recording it so a future session does not scope a
phase against a class that does not exist.
cookbook §138 extended with the alias-form tool boundary and the three-blocker
story; index regenerated.
|
||
|
|
356373efab |
fix(phase-30 S33b): the PAIR rule — my 42-decl relax did NOT unblock the lane; the mirror form did
HONEST CORRECTION to commit:1382. That commit's message implies the 42 `(void)` relaxes unblocked the PROPAGATE remainder. They did NOT: the re-run banked 0/1 in all 134 overlays with the same error, because DEFINE_func_8016BA68 declares func_80146C3C `(u8*)` — the MIRROR of the EXTEND-lane pair — and my relax only touched the `(void)` direction. Root cause is the R37 shape a third time: I bucketed by SYMBOL and stopped. The lever is set by the (macro-shape, TU-shape) PAIR, and the same symbol conflicts in BOTH directions across this fleet. One awk over the macro I was ACTUALLY fixing — which I ran for the EXTEND macros and not for this one — shows the pair before a 134-build run. §138 amended with the PAIR rule; correction logged in CURRENT_PHASE.md rather than rewritten out of history. The 42-decl relax still stands: byte-neutral, R22 140/140, removes a real conflict class. It just did not do what I predicted. THIS commit relaxes the 2 remaining `(u8*)` decls (uses are cast; `()` is compatible with the (void)/()/(u8*) forms the fleet carries and no decl of this symbol has a default-promotion param). R22 clean-fleet: 140 passed, 0 failed. ALSO: tools/overlay_src_split.py `_split_macro_body` — the §134 sweep's one real target, fixed. It carried the identical single-line-only comment test, and it decides where a macro body's file-scope externs END, so a multi-line comment truncated the extern set. SIZED FIRST: 38 live lines in engine_core.h macro bodies hit it today. Now decides on cdecl._mask (one oracle, R33) with the length-preservation invariant asserted (R32). Proven both directions by a control: pre-fix it stopped at `/* multi` carrying 1 of 2 externs and treated the comment as the definition head; post-fix both externs carry and the def head is correct. Not in the gate path (only o0_subsplit + jr_isolate_all import it). |
||
|
|
62042f65ca |
fix(phase-30 S11): multi-line-comment blindness in dedup_propagate; func_8012A598 x138
Fleet 96.06 -> 96.10% fn-count / 93.7% instr / 88.0% distinct; dedup 1907 -> 1908 groups, 0 failed, C1 240807/240807. R22 clean-fleet: 140 passed, 0 failed of 140. func_8012A598 (3,288 templatable ins) was being written off as CARRY-FIXABLE. It took TWO fixes; either alone leaves it skipped. 1. TOOL (R33) — find_site's preamble backscan. The SESSION-18 fix handled blank, `//`, and SINGLE-LINE `/* … */` lines, but a MULTI-LINE block comment still halted the walk: its middle lines start with `*` and its last line ends `*/` without starting `/*`. So the three externs above the body were dropped and the body then failed compiles_standalone on now-undeclared data. This is the §134 multi-line-blindness class — S6b fixed the identical shape three times in family_remap (D1/D2/D5) and this copy was never reached. Fixed by deciding skippability on `cdecl._mask` — the project's ONE masking oracle — instead of on line syntax: it subsumes every comment form at once and cannot be fooled by a `/*` inside a string, with an R32 assertion on the length-preservation invariant it rests on. Strictly monotone (it can only carry MORE preamble), and dedup_propagate is a byte-gate feeder, so a bug here can fail to bank but never falsely bank. 2. EXEMPLAR — the body also declared a draft-local `struct BigCopy164` tag, which the tool refuses by design (two macros defining one tag would redefine it in a single TU). The shared `struct BigCopy` (engine_types.h L312) is the identical layout and is ALREADY used this exact way at engine_core.h:16158, so switching the exemplar to it is byte-neutral and drops the alias too. Probed on ONE member before scaling (R37/S29): byte-identical 9052dc0e first try; then 138 overlays byte-identical. PROPAGATE head accounting after this: 7,398 of 18,545 ins banked (func_80147364 4,110 + func_8012A598 3,288). Still open, each with a NAMED cause and none yet diagnosed against a build: func_8012f274 (3,973, dropped), func_8016ba68 (3,886, 4/138), func_801466f0 (3,288, the S6b D4 wrapped-alias gap). |
||
|
|
aa600c56ef |
fix(phase-30 S6e): family_sweep snapshotted TUs it never edited — the self-decl lever measures 0, honestly
- D6: hseq_sweep took the tu_snapshots snapshot UNCONDITIONALLY, one line before the `if nfix:` that decides whether to edit. A TU that normalize_self_decls merely INSPECTED was therefore registered, and the phase-2 MISMATCH backstop attributed ANY group failure to a "self-decl edit" that was never made -> revert + `0/N banked`. Measured: 909 of 909 groups took that branch while NSD actually fires on ~25% of members (3 of 12 probed). The §103 tu-scope path below has always snapshotted inside `if _rep["moved"]:`; NSD now matches it. - After the fix: NON-NEUTRAL 909 -> 303 (consistent with the fire rate) and STILL 0 banked — the 606 groups that now take the normal path bank nothing, so the lever's verdict is REAL, not an artifact: this residue is not self-decl-conflict-bound. Lever measured, closed, zero. - R14 on my own conclusion: I byte-measured a firing case instead of trusting the backstop — func_80162CCC/ov_SC01_000 builds to 9052dc0e... WITH and WITHOUT the NSD edit (byte-NEUTRAL), so the surviving 303 verdicts are wrong too (likely accumulated multi-member edits in one TU). Logged as a named open item, not chased: the lever yields 0 either way. - The tell, twice in one session (§134): a 100% rate is a property of the mechanism, not of 1,622 different functions. Three earlier sweeps over the same population reported 0 NON-NEUTRAL. |
||
|
|
39558b2991 |
fix(phase-30 S6b): MULTI-LINE BLINDNESS in family_remap — 4 faces, 3 fixed; +740 members (R22 140/140)
- ONE root cause, four faces (cookbook §134): extract_unit's preamble scanner reads C
one line at a time, so every construct that WRAPS was misread.
D1 the {-guard fired on a documentation comment mentioning a brace -> carry truncated
mid-comment -> `parse error before 'the'`.
D2 _def_head_at's "param list continues -> ANSI definition" fallback accepted a WRAPPED
DECLARATION as a definition head -> a 16-line fragment with no body, closed by a brace
pair inside a comment -> a silent 0/137 that reads exactly like a compiler wall.
D5 the backscan met a multi-line typedef's CLOSING line `} T;` first and stopped -> the
type never travelled -> `T undeclared` across 17 families / 24,332 templatable ins.
(The code comment claimed they "route through the engine_types.h lift"; measured, they
routed nowhere.)
D4 wrapped __asm__("func_...") alias invisible to a single-line regex — MEASURED (1 exemplar,
3,288 ins, second blocker behind it) and deliberately NOT fixed; it now returns None so the
sweep reports a VISIBLE skip instead of 137 silent failures (R32).
- Fixes: _def_head_at(ln, idx, more=()) lookahead (no-lookahead keeps the historical answer);
{-guard exempts comment-only lines + an R32 dangling-comment backstop; forward brace scan
counts over cdecl._mask (R33, one masking oracle); _typedef_block_start carries whole blocks.
- BLAST RADIUS (R14): extract_unit diffed vs the pre-fix tool over all 181 zero-crack exemplars
-> 157 byte-IDENTICAL, 24 changed, all in the intended direction.
- PAYOFF: D1+D2 +323 members from families that banked ZERO; D5 +417 incl. func_8012B77C 139/139
(8,062 ins) and func_80128C98 137/275. S6 total 1,582 members (pre-fix tool scored 842).
- R22 clean-fleet 140/140. Fleet 94.43->94.88% fn-count, 91.4->91.9% instr, 84.0->84.6% distinct.
- TELL worth keeping (§134): bimodal bank rates (57 all / 52 zero / 8 partial) are a TOOLING
signature, not codegen. Probe one member and read one compiler error before writing a family off.
|
||
|
|
10dc6d3635 |
feat(phase-30 S1): --span-rel unblocks the zero-crack head; func_8014032C ×1 probe banks (§132b)
S1's head family (0x8014032C, 183 ins ×137 = 25,071 templatable ins) gate-failed on its probe sibling. Diagnosis (the §132 ladder, one build): the object emits FOUR tables — BOTH functions in it are multi-switch (8+5 entries each) — while the carve derived THREE starts. The missing start belongs to the ALREADY-MATCHED owner func_8013FFD8, and neither oracle can see it: its stub .s is pruned by extract, and its second table abuts its first with NO pad (8 entries = 32 B = 0 mod 8, so `.align 3` emits nothing) — precisely the honest limit §132's payload zero-word recovery documents. Note the true pads [0,0,4,0] are exactly what natural alignment produces; the build breaks only because a SHORT spec gets written. Fix: thread the documented `--span-tables` escape through the sweep as `--span-rel` — offsets relative to the FIRST NEW table, which func_jtbls reads from the sibling's own .s. Byte-verified family-invariant before use (identical relative offsets on 3 sampled siblings; same code, same entry counts, only the base moves). Empty by default => every other family untouched. Also clears my own §132a guard of suspicion (R14): the --like transfer was inert here regardless (exemplar subseg `ov_SC01_077` vs sibling `_jr_8013FFD8` — roles never matched). |
||
|
|
73570a5089 |
feat(phase-30): func_8013B83C ov_SC07_010 + the --like over-transfer guard (§132a)
The one sibling both sweeps failed on. `--like <exemplar>` transfers the exemplar span's table
STRUCTURE, and jtbl_carve matches donor to recipient by the subseg's ROLE NAME. ov_SC07_010's
-O0 region is named `_o0` — the same role as ov_SC01_077's, and the ONLY other overlay so named
(the other 136 are `_o0c`, whose role never matched, which is the only reason the sweep worked).
The exemplar had just banked 2 more owners than the sibling has, so the transfer unioned its
rebased 4 offsets with the sibling's real 2 + the new table: SIX starts for THREE emitted tables
-> jtbl_rodata_pads refused ("consumed 3 rodata .align(s) but 6 pad spec(s)").
Guard (tools/jtbl_family_bank.py like_arg): suppress --like when the sibling already carries a
committed `tables=` for the target subseg — its own record is authoritative, and jtbl_carve's new
payload zero-word recovery covers the incomplete-record case that --like used to paper over.
Derived via jtbl_carve.func_subseg, the same derivation the carve itself uses (R33); never blocks
a bank (any failure falls back to the old behaviour). Inert for all 136 siblings already banked;
fixes exactly the broken one. Both fns now bank on ov_SC07_010 => 137/137 siblings each.
|
||
|
|
b9efe66f91 |
fix(phase-30): the JR-PAIR "wall" was TWO instrument defects — pair banked, class retired
S28 ledgered `JR-PAIR-IN-ONE-O0-OBJECT` (two jr fns matched in one -O0 object => a clean build that cannot link: `undefined reference to $L105` + `func_8013C938`) with §81 step 1 (isolate one into its own code subseg) as the untested escape. BOTH the class and the escape are REFUTED — no isolation, no compiler wall, both fns banked from a genuinely clean fleet. The 4th consecutive "structural wall" to resolve to our own tooling (§124/§125/§126/§131). - DEFECT 1 (tools/jtbl_carve.py): ov_SC01_077_o0's carve at 0xb01a4 predates the §8e `tables=` persistence and is a MERGED DOUBLE (func_8013C0F8 $L75 + func_8013C414 $L105); the 2nd owner is MATCHED so extract pruned the stub .s naming its table. The single-table- predecessor inference derived 3 starts where the object emits 4 tables -> JTBL_PADS 0,4,4 -> jtbl_rodata_pads refused mid-stream, correctly. FIX: R32 coverage assertion + payload recovery at the single choke point (spec_from_starts) — every zero word inside a span is an original `.align 3` pad (the tool's own axiom), so the word after it STARTS a table; recovered starts are logged. No-op where structure is known (the 134 sibling _o0c spans carry tables=+0x0,+0x70). Honest limit: tight (0-pad) boundaries stay unrecoverable but fail LOUD via the filter's count guard — never silent. - DEFECT 2 (Makefile): no .DELETE_ON_ERROR, so `as` (a pipeline consumer) left a TRUNCATED .o on disk — 12 of 16 T func_, undefined $L57/$L59/$L63/$L75/$L76 — newer than its .c, and the NEXT build linked the corpse. That IS the S28 link error, one build downstream of a loud, correct compile error. Negative-control-proven on a scratch invocation. - BANKED: func_8013B83C (272 ins) + func_8013BD74 (198 ins) in ov_SC01_077 (d19c9580). Byte proof: 4 tables 0x801D8254/828C/82FC/836C (13/27/27/27 entries, each zero-pad separated); span 0xb00fc..0xb0280 = 388 B = 52+4+108+4+108+4+108 exactly; spec 0,4,4,4. - R22 clean-fleet (make clean + extract-all + check-all): 140 passed, 0 failed of 140. The incremental result was NOT trusted (§130). Fleet 93.25% fn-count / 89.2% instr / 80.5% distinct; dedup 1905/0; 0 NON_MATCHING (G4). - cookbook §132 + index (356 sections): the mechanism, the fingerprint (an undefined $L<n> in a LINK error is a truncated object, never codegen), the 30-second standalone-TU ladder that named the 4th table owner before any build, and the transferable rule — a fail-loud guard is only as trustworthy as the artifact hygiene around it. |
||
|
|
0958120006 |
fix(phase-30): jtbl_carve OVER-SPAN clamp — the one real instrument failure, root-caused and fixed; behemoth func_80191C50 (710 ins) banked
The last surviving "compiler wall" of the session turned out to be an off-by-one-word
carve. Chain, all byte-grounded:
SYMPTOM jtbl_carve diverges on ov_SC06_018 after a BYTE-NEUTRAL jr_isolate_all.
SHAPE image -3 bytes; 853 differing bytes in 699 scattered runs; **812 at byte 0 of a
word** = the low byte of a 16-bit immediate, every sampled one changing by
exactly -4. Not a shift, not a pad: hundreds of `%lo` operands 4 bytes low.
CAUSE func_80191C50's own `sltiu $v0,$v1,0xC` names 12 entries; the emitted .rodata
holds 12 words; the carve reserved 13. The 13th word (0x3038200A) is ordinary
NON-ZERO data that spimdisasm ran into the dlabel span (the next dlabel sits
past it). Reserve 13 / supply 12 => the .rodata piece UNDER-FILLS by one word
=> every later symbol slides down 4 bytes.
WHY MISSED The trailing-trim's axiom is "0x00000000 cannot be a jump target", so it only
trims ZERO words. This surplus is non-zero, so nothing trimmed. The tool already
treats `sltiu` as ground truth - it EXTENDS a table spimdisasm cut in half, and
it WARNS when a span is too short - but had no clamp for a span too LONG.
FIX: an over-span clamp with the SAME authorization as the existing extension logic -
only when the `sltiu` bound is UNAMBIGUOUS (one bound; a multi-switch fn cannot say which
table owns which), and only when the surplus words are NOT plausible code addresses. If any
surplus word looks like a real entry it REFUSES LOUDLY rather than silently dropping one.
jtbl_carve: jtbl_801D3BA4: clamped 1 trailing NON-ZERO word(s) - func_80191C50's own
`sltiu 12` names 12 entries and the surplus is not code
Then: carve BYTE-IDENTICAL, draft spliced, **func_80191C50 (710 ins) BANKED**.
R22 CLEAN-FLEET: extract-all 139/139 (+main); check-all 140 passed, 0 failed of 140.
Retires the JTBL-CARVE-BREAKS-BYTES ledger class: the one instrument failure that survived
this morning's retraction round was REAL, and is now a named bug with a fix - not a wall.
|
||
|
|
05f6293067 |
fix(phase-30): 137 tracked C sources held a raw NUL that made grep SILENTLY SKIP them
FOUND BY ACCIDENT, WHICH IS THE POINT. `grep -rn func_8013C08C src/` returned NOTHING for a function that is defined right there. The file held a RAW NUL byte inside a character literal — the source read `== '<NUL>'` where it should read `== '\0'`. It COMPILES (the fleet was byte-identical), so no byte-gate ever objected. But file(1) classifies such a file as `data`, and **grep treats a file containing NUL as BINARY and reports nothing, silently**. The whole file therefore vanished from every grep-based audit and every hand search. I burned real time chasing a phantom missing function before `file` gave it away. SCOPE, measured: 137 files — every `_o0c`/`_o0e` region created in THIS session. The templated bodies carried the NUL fleet-wide, so I propagated the defect today. All fixed (`'<NUL>'` -> `'\0'`); R22 CLEAN-FLEET 140 passed, 0 failed of 140 => byte-neutral. NEW ORACLE: tools/audit_text_sources.py + `make audit-text-sources`, wired into tools-health, coverage-asserting over all 3,887 tracked .c/.h files (R32). This is the SAME silent-skip family as SS124 (a scanner that cannot see something reports it is not there) and SS126a (a bare except swallowing a coverage assertion) — but one layer LOWER, in the tool everyone reaches for first. The byte-gate is structurally blind to it (R34): the bytes are correct, so it has nothing to say. It needs its own oracle. MY OWN ERROR, RECORDED: proving the new guard fires, I injected a NUL into the REAL tracked file and restored it through nested shell escaping. The restore left `'\\0'` — an escaped backslash, i.e. a multi-character constant, NOT a NUL — a genuine semantic change. R22 caught it (139/140) in one cycle, before any commit; repaired to `'\0'` (10465 -> 10466 bytes) and re-verified 140/140. The lesson is not "be careful": a negative control must corrupt a SCRATCH COPY under .run/, never the tracked file it is testing. Testing a guard must not risk introducing the defect the guard exists to catch. |
||
|
|
feb32ed23e |
feat(phase-30): T4 — grinder ILS warm-restart wired in; SS119 posture audited + a stale doc hazard struck
(1) --fix-def-sig POSTURE: AUDITED CLEAN. `action="store_true"` (defaults False), one
consumer via getattr(a,"fix_def_sig",False), and NO caller anywhere passes it — checked
tools/, .run/ scripts, docs recipes and the Makefile. The flag help already carries the
SS119 warning.
BUT the audit surfaced a live hazard the earlier pass missed: docs/decision-log.md
still recommended "--fix-def-sig should likely be default-on for the h_seq path".
That was byte-REFUTED by T84/SS119 — the flag is a REPAIR, not a default; on 0x80161c98
it imposed a signedness-wrong `s32 a1` over the true `u32`, turned a byte-correct draft
into a 1-instruction DIFF (slti vs sltiu), and held 137 members at 0 until DROPPED.
Struck through in place with a superseding note rather than deleted, so the original
reasoning stays legible (R31) — but a forward-looking "should be default-on" sitting in
a doc a fresh session reads FOR DIRECTION is a hazard, not a historical note.
(2) GRINDER WARM-START: tools/permuter_ils.py has sat beside grinder.py since Phase 24 and
was never wired in, so every grind was a COLD search that burned its whole time box
re-descending ground the previous run had already covered. grinder.py now runs `--cycles`
(default 4) timeboxed permutes, each warm-restarted from the previous cycle's best byte
waypoint, stopping early on no gain. `--cycles 1` reproduces the old cold behaviour exactly,
so it is opt-out. --permute-secs is now documented as the PER-CYCLE box.
JUSTIFIED BY MEASUREMENT, not by the task list: the lane looked dead (Phase-22 audit: 7
all-time banks, all Phase 21, 0 since), so I checked for live fuel before building. The
backlog holds 665 open near-misses in the permuter-tractable band (close 1-20), 157 of them
close 1-4, including func_8016BA68 at close=1 with reach=134.
HONEST LIMIT: this is a WIRING change whose yield is UNPROVEN. The Phase-24 evidence for ILS
is one function (func_80148094, 72 -> 36 over ~8 restarts); I have not run it on this
backlog. A winner remains a CANDIDATE — the whole-binary byte-gate is still the sole arbiter
(G3/P9), and an intermediate waypoint is only ever re-seeded, never banked.
|
||
|
|
d2b48b7680 |
feat(phase-30): tools/o0_subsplit.py — the T2 carve-within-a-carve driver; +3 banked in ov_SC03_015
Promotes the proven probe (commit:1266) into a real tool, and validates it FIRST-TRY on a
fresh overlay.
tools/o0_subsplit.py <ov> --lo <vram> --hi <vram>:
- derives the range's contents from the SOURCE ANCHORS (overlay_src_split.parse_overlay_c:
`asm` = unmatched stub, `define`/`def`/`nonmatch` = already matched), NEVER from an asm
scan -- a matched fn emits no .s, which is exactly the blindness that made the range look
like a clean contiguous run (SS126 / SS124's shape);
- computes the -O0 bound as (address range MINUS already-matched bodies) and emits ONE
sub-region per maximal run of unmatched anchors (K matched islands => K+1 regions);
- names each `<ov>_o0<letter>` picking free suffixes, so the widened Makefile glob selects
them; refuses loudly if it runs out or if the range spans >1 object or is already -O0;
- honours the one-carve-per-region law (forces a cut at every already-banked jr in the
object) and reuses jr_isolate_all's plan/build_new_config/ascending-unique validation
verbatim, so carve-repoint + source-repartition stay on the proven path;
- warns (does not refuse) when a stub in an -O0 run lacks the frame-pointer prologue --
the byte-gate is the arbiter, not the heuristic.
VALIDATION on ov_SC03_015 (untouched by the manual probe): the tool independently derived the
SAME structure found by hand on ov_SC03_014 -- 2 matched -O2 islands (func_80184440,
func_801848E4), 2 -O0 regions (8 + 7 fns), same 5 cuts. Sub-split -> BYTE-IDENTICAL. Then 3
drafts, each global DERIVED FROM THAT OVERLAY'S OWN ASM (%hi operand) rather than copied:
3/3 match_one --o0 MATCH (22 ins), 3/3 through the whole-binary gate.
BANKED this commit: func_801846E4 / func_8018473C / func_80184794 in ov_SC03_015 (6 across
the two overlays now). The other 24 stubs in the region are undrafted -- the route makes them
DRAFTABLE (they were un-bankable at any effort before); drafting them is crack-wave work.
R22 CLEAN-FLEET: extract-all 139/139 (+main); check-all 140 passed, 0 failed of 140.
cookbook SS126 (the address-range-is-not-an-optimization-region law + the probe ladder).
|
||
|
|
e99c823cb4 |
fix(phase-30): extract_unit was blind to the asm-label ALIAS definition form (the 137 "no matched unit")
The h_seq sweep's 137 "no matched unit for func" skips were ONE exemplar x 137
same-address members, not 137 distinct failures: func_8016191C @ ov_SC01_077,
band=mid, non-jr, 24 ins, ALL 137 members still INCLUDE_ASM stubs => 3,288 ins
left on the floor by a tool lookup miss (R35 again).
ROOT CAUSE (and it refutes the SESSION-27 checkpoint's own diagnosis, R14): the
exemplar is NOT "banked in a different binary". It is banked in ov_SC01_077 under
the SS37/SS73 asm-label alias — the byte-true body conflicts with the fleet-canonical
decl on BOTH SS73 axes (return void vs s32 AND params void*/s32 vs int/unsigned),
so it was banked zero-touch as
int aF8016191C(int, unsigned int) __asm__("func_8016191C");
extract_unit only ever matched a definition head literally NAMED func_<ADDR>, so
it returned None, and every caller reads None as "not matched".
FIX (tool-only, T0 blast radius):
- _alias_decl_for(): resolve `<ident>(...) __asm__("func_<ADDR>");` -> <ident>.
- extract_unit(): accept the alias identifier as the definition head, re-derived
PER FILE so one file's alias can never leak into the next.
- carry the alias DECLARATION into the unit (without it the sibling emits the
symbol aF8016191C and the body never lands at func_<ADDR>); the backscan now
walks PAST the alias line so the fn's own preceding externs are carried exactly
as for a plain definition, with a start<=alias_ln<=end guard against double-emit.
- R32: an alias decl with no findable definition now REFUSES LOUDLY instead of
falling through to _macro_unit and reporting "not matched" — the silent-skip
class this fix exists to delete.
Rejected the alternative the banking agent suggested (widen engine_core.h's decl
void->s32, drop the alias): it addresses only SS73's RETURN axis while decl and body
also disagree on PARAMS, and it is a T2 fleet-shared edit where the alias is T0.
VERIFIED: unit extracts with externs + exactly one asm label; remap_hseq produces a
sibling draft; rtu_match 3/3 MATCH (24 ins) in the real TU (ov_SC01_000/001/004).
make tools-health RC=0. The whole-binary byte-gate remains the sole arbiter (G3/P9).
|
||
|
|
d79d8f2356 |
feat(phase-30): cookbook-index — 9 curated symptom lines from wave-4 agent feedback (10/10 MATCH, 9/10 index hits)
Every line is a symptom an agent HIT and had to re-derive from gcc internals because title-keyword search structurally cannot surface it: - SIZE-MISMATCH/short + frame-pointer prologue => the target is -O0, pass --o0 (the flag was documented nowhere an agent would look; a 4th -O0 region also exists beyond the 3 known ones) - rotated instruction window => sched1 order; brute-force all N! statement orders (24 runs, 2 min) - if/else result in $v1 vs target's $v0, and load-hoisted-above-store => §76 variable reuse (§76's title reads behemoth-only, so nobody finds it for a 48-ins function) - ori 0xffd8 vs addiu -0x28 => negative const in an UNSIGNED narrow local; signed keeps the lhu - LENGTH-DRIFT -1 as a missing jal-delay copy => narrow ANSI prototyped param (not just K&R §43) - lwl/lwr+swl/swr is a delay-slot SPONGE (the inverse of the §5a fence case) - a vanished param copy => cse.c make_regs_eqv live-range rule - a ghidra_c seed may be a DIFFERENT function (overlays share VAs) |
||
|
|
f3ec6ef588 |
fix(phase-30): treelock.sh — an flock MUTEX for tree-writing campaigns (incident 2: a poll is not a mutex)
I gated 8 binaries in parallel while wave-2's propagation loop was still running, then ran 'make clean' on top. check-all 77/140; the corpus denominator moved, so the apparent 91.4% instr was a half-written tree, not a gain. Reverted to commit:1245 (last R22-verified) — 140/140 restored, all 58 drafts survived because agents only ever write .run/. ROOT CAUSE, and it was structural not unlucky: my guard was while pgrep -f dedup_propagate; do sleep; done A CAMPAIGN is a LOOP of short-lived processes (15 sequential invocations), so it has gaps where no process matches. The poll sampled a gap and started. Presence-of-a-process cannot express 'a campaign owns the tree'. treelock.sh holds one flock for the WHOLE campaign, released by the kernel on exit OR kill, with --status; both drivers refuse to run unlocked. LAW: guard the CAMPAIGN, not the process. Corollary (twice today): a killed process performs no undo — a fleet-tier write needs a lock ABOVE it, not cleanup inside it. |
||
|
|
ed2b0f8c2a |
fix(phase-30): gate_stage CLI exposes --verified-out/--failed-out, defaulting PER-BINARY (parallel-gate safety)
run_gate() always took per-worker result paths; the CLI never exposed them, so every CLI gate used
the shared .run/harvest_{verified,failed}.txt. Concurrent per-binary gates — safe on every other
axis, since the byte-gate IS per-binary — would have read each other's results and mis-attributed
banks (the §55b trap-4 shared-scratch defect that bit match_one in P28, one level up). Default is
now .run/harvest_verified.<binary>.txt: safe by construction, not by remembering a flag.
|
||
|
|
c14f15a09f |
fix(phase-30): cookbook-index — 226 -> 324 sections (the unnumbered idiom headers) + curated symptom hints from wave-2 agent feedback
WAVE-2 MEASURED THE INDEX: 15/19 index hits and the bank rate went 57% (wave 1, no index) -> 79% (wave 2, index-first) on the same gate. Agents also NAMED its gaps, which is the flywheel working. Two real defects found and fixed: 1. COVERAGE. The parser required a '§' prefix, so 111 h2-h4 headers were invisible — including '### T4 — Branch polarity', the fix match_one names by class (BRANCH-POLARITY) and which two agents re-derived by hand, and the §1/§2 idiom-catalog entries (I1-I4, T1-T4). 2. THE ASSERTION ITSELF. My R32 check compared §-headers-parsed against §-header-CANDIDATES — a tautology over a set I had already narrowed. R32 says the candidate set must OVER-approximate; it now counts EVERY header and accounts for each as indexed-or-explicitly-skipped. The tool written to stop silent skips had the silent-skip defect. 3. Keyword matching over titles cannot surface an idiom whose title omits the symptom, so the index now opens with a hand-curated SYMPTOM -> section list, seeded from what agents actually hit (branch polarity, (void)-canon conflicting types, asm-label alias, one-base-register reuse, folded andi, slti/sltiu, sibling-first, delay-slot theft, void->s32). |
||
|
|
4d5bee0be8 |
fix(phase-30): T4 — 2 'Phase-22 grinder bugs' verified STALE (already fixed); real fixes: asm_subdir_for derives from corpus (R33), --fix-def-sig help carries the §119 warning
Verified against code rather than the ledger: the split-blind lookup globs */ correctly, and the churn was fixed by T5's input-signature gating. Both struck. asm_subdir_for was still a parallel oracle (silent g[0] on multi-match) -> now corpus.asm_path. --fix-def-sig defaults off correctly but advertised 'Byte-neutral; gate arbitrates' — the claim T84 refuted (signedness-wrong header decl over a byte-correct draft; 137 members held at 0 until the flag was dropped). A defect ledger nobody re-verifies decays into busywork — verify before scheduling. |
||
|
|
4570a5854b |
feat(phase-30): cookbook-index — a SYMPTOM-keyed index (226 sections, derived + coverage-asserted, in tools-health)
Wave-1 measured the tax: three agents each reported a 'NEW idiom' that was ALREADY documented — the asm-label alias (line ~2516, same 'address-of perturbs regalloc' mechanism) and the void->s32 non-neutrality (§41d, Phase 26; the agents cited the very entry §41d corrects). They consulted the cookbook as instructed and could not FIND them. 716 KB / 226 sections with no index = a discoverability failure, and every wave re-paying for prior waves' findings is the inverse of R16. docs/cookbook-index.md maps SYMPTOM (what you see in the diff) -> sections, 14 buckets, a section listed under every symptom it addresses. Derived by tools/cookbook_index.py (R33 — cannot drift), --check wired into tools-health. R32 on my own tool: the first regex required an em-dash separator and silently dropped 50 sections — including §1 (idiom catalog), §2, §5a (cross-jump, cited by an agent today). An index missing its most-cited entries turns 'I could not find it' into 'it is not there'. Now asserts extracted == candidate '§' headers and hard-exits on a gap. |
||
|
|
71133942e4 |
fix(phase-30): T3 — gate_stage propagation timeout scales with bank count AND is caught (wave-1 incident: 3600s x8 banks killed the driver mid-fleet-write, 124/140, reverted clean)
A killed process performs no undo, so a fixed timeout on a fleet-tier write is a tree-corruption mechanism, not just a delay. Now: timeout = min(6h, 1800+1800*banks); on expiry the driver reports TREE DIRTY, REVERT REQUIRED and returns cleanly. Standing practice for multi-bank waves: re-gate --no-propagate, then propagate PER FUNCTION (dedup_propagate --addr) — bounded and resumable. Wave-1 result recorded: 14/14 match_one MATCH -> 8/14 banked (the §52b law); 3 new idioms owed. |
||
|
|
ce38cfbcda | fix(phase-30): T3 pre-work — gate_stage commit stages NEW jr-carve files + overlays.mk + the binary's yaml (T1a found the -u gap); bounded failed-prep hazard documented | ||
|
|
fe8095a24d |
feat(phase-30): T2 — rollout_o0.py (generalized o0b driver) + x1 probe verdict: append route REFUTED for the remaining cluster (R14 correction)
o0b-bearing != o0b-adjacent: T85's 0x801457A4 banked by append only because it abuts the o0b object's END; the 0x8013Bxxx-0x8013Cxxx families mis-place by construction (probe 1/1 gate-reject) and per-fn isolation IS the Arm-A re-carve. Frontier report corrected; T2 pivots to the Arm-A +0x20 defect itself (symbol-pin hypothesis first). Driver stands as the post-fix sweep harness. |
||
|
|
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.
|