mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 22:45:39 -04:00
85fb289db582d842fc41dc059fa187bb992e76ea
422 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fa7b9d4c71 |
feat(phase-30 S45 L3): the emulator tour — all 28 script modules + MAIN/3 onboarded; fleet 212, R22 212/212
- THE TOUR (Drew driving the retail debug menu; mode-7 hammer over the Redux web API): all 28 script modules captured live at four byte-verified per-chapter slots (SC03/73-79 @0x801EF468 ch2-period, SC03/132-138 @0x801E25E8 ch3, SC04/24-30 @0x801E7B28, SC05/23-29 @0x801ED988); the routing law: debug-menu AREA selects the chapter, each CITY interior streams its own module (member k <-> interior k). md_MAIN_011/DISELECT byte-proven 24,236/24,240 in RAM; slots A/B/boot R34-verified live. - MAIN/3 DISCOVERED: the main-menu module (id 0x39, 121,884 B), mis-bucketed as data by BOTH audit oracles; live byte-proven @0x800CEDF8 (42,632-B exact prefix); onboarded. - 29 onboardings BYTE-IDENTICAL on first build -> fleet 212; R22 212/212 after three md_MAIN_003 catches: the A4 DsMix leak; an extract-order-sensitive splat boundary (bytes: a 1-word data sentinel in .text + fn at +4 -> pinned in symbols file); corpus.stubs now treats D_*/jtbl_* INCLUDE_ASM as blob includes (mirrors progress.py) - module-id census (offline, disc-wide): 77 id-law code payloads, 0 further misses; SC03/55 = confirmed DATA. audit-disc: UNCLAIMED 34 -> 6, residue 0 — the 6 carry byte-checked negative evidence; next tier = the CD-read tracer - docs: memory-map §S45 (slots + routing + debug-menu ops), disc-completeness S45 addendum, decision-log R31 entry, docs/debug-menu-list.txt (Drew's transcription) - .run/s45 evidence allowlisted (tour logs/scripts/rosters); 104 ram dumps LOCAL-ONLY - new baseline: 93.8% instr / 95.68% fn / 87.2% distinct over 212 |
||
|
|
a0f07d629e |
chore(phase-30 S45 II.2): retirements (R33) + SETUP module recipe
- DELETED: disc_code_sweep.py (superseded by disc_audit/make audit-disc), reconcile_decls.py (superseded by reconcile_tu; incumbent row removed from cdecl audit_differential — the differential existed to prove this deletion safe), rollout_801457a4_o0/rollout_whale_o0/ rollout_o0_cluster one-shots (rollout_o0.py is the live generic), ImportOverlay.java + VerifyOverlay.java (ghidra_import_raw.sh is the live path) - reference check first (R14): the plan's 'zero build refs' was wrong for 3 — comment refs annotated, the one LIVE import (cdecl) reworked; audit-cdecl + tools-health re-proven green - SETUP §6.7: module-class recipe (TEXT_LO derivation, paired-.rodata hdr carve, A4 symbol- window law, ELF-seeded sig-modules) + new_binary.sh inventory row + 3 RETIRED rows (R21); disc-completeness Reproduce marked retired |
||
|
|
4cadac4e11 |
feat(phase-30 S45 II.1c): module batch dedup-banked + verified — 408 banks, R22 183/183, audit-disc 75->34 (parked-only)
- dedup measure (R37 probe): 69/1,113 module fns h_exact-match matched corpus (~6%, LOW as
planned — modules are novel frontier); dedup_extend inapplicable (same-vram group model) ->
family_sweep --hseq --band all over the 57 matched-exemplar families: 408 member-matches
banked (182 into modules, 226 into the big 3 — families Part I's --only scoping missed),
169 failed + 77 STRUCT = genuine per-member frontier
- R22 clean-fleet 183/183 BYTE-IDENTICAL; audit-disc UNCLAIMED 75->34 residue 0 (34 = 31
parked-for-L3 + SC03/53,54,56 — 3 rows Discovery-3 never tiered, now parked with evidence)
- three instrument fixes, each negative-control-proven:
- family_sweep --hseq stub map derives ov_*+md_*+resident (was sig.ov_* glob -> module
members silently 'not-stub', R32 class) [committed earlier as commit:1506]
- sig-modules seeds from the built ELF's func_* symbols (bootstrap GLUES adjacent fns
around jtbl dispatch -> 24 false TRUNCATED; perturbed-sig control still bites)
- corpus.audit counts CODE lines only (module .s carries its header jtbl as .word lines);
progress.py buckets INCLUDE_RODATA symbols as blobs (unbucketed R32 hole)
- NEW HONEST BASELINE (183 binaries): 94.0% instr / 95.96% fn-count / 87.6% distinct;
tools-health OK, audit-digest OK
|
||
|
|
b1dbfcb572 |
fix(phase-30 S45): family_sweep --hseq stub map derives ov_*+md_*+resident (R32)
- the .run/sig.ov_*.jsonl glob had no md_ entries, so every module member fell into an empty stubs.get() and booked a silent 'not-stub' skip — the I.1d glob-widening class, missed because family_sweep sat on the audit's 'auto-OK' list - negative control: --only 0x80165b28 --stage-only staged 0 md members before, 32/32 after |
||
|
|
b15dae1077 |
feat(phase-30 S45 II.1b): SC07 module pair onboarded byte-identical at 0x801A00D8
- md_SC07_003 (345,132 B, text-lo 0xFC, 100 fns) + md_SC07_004 (365,152 B, text-lo 0x158, 315 fns) — both BYTE-IDENTICAL on first build at the header-derived own slot - independent first-prologue scan reproduced the plan's documented TLOs exactly |
||
|
|
85b526cddd |
feat(phase-30 S45 II.1a): all 38 MAIN modules onboarded byte-identical at the §S44 static addresses
- slot A 29/29 (md_MAIN_013..041 @ 0x800CAE08), slot B 6/6 (md_MAIN_042..047 @ 0x800CCB1C), boot trio 3/3 (md_MAIN_001 [=MAIN/0 twin], md_MAIN_008, md_MAIN_011 @ 0x800CEDF8) — every one BYTE-IDENTICAL on its FIRST build (byte-corroborating the §S44 loader table for slots A/B/boot) - TLO roster derived from the §154 id-word law (.run/s45/derive_tlo.py): 0x4 default; 011=0x7C, 025=0xC, 034=0x80, 039=0xC (first-prologue scan) - new_binary.sh: module hdr carve is now a dot-typed .rodata PAIRED with the c segment — a header can hold a function's jump table (md_MAIN_034), and standalone rodata emits .L locals that don't cross objects; bin links in the data block (both refuted by bytes) - A4 law: symbols.resident.txt dropped from the boot trio's stacks (windows inside the resident region; DsMix @0x800D1BD8 had minted a phantom fn boundary in md_MAIN_011) — re-extracted clean, all three byte-identical, phantom gone |
||
|
|
f6bbe7272a |
feat(phase-30 S44 I.2a): the big 3 onboarded BYTE-IDENTICAL — ov_MAIN_012, ov_SC02_037, ov_SC03_107
- Three uncompressed (PAC type-1) overlays at the standard 0x80128158 slot, onboarded via the new
tools/new_binary.sh, each byte-identical at 100% INCLUDE_ASM on the FIRST build:
ov_MAIN_012 d6b3e8b9 (383,783 B, 2,324 fns)
ov_SC02_037 b0c5394a (661,903 B, 2,434 fns)
ov_SC03_107 87d02b57 (474,087 B, 2,414 fns)
This also BYTE-PROVES the statically derived base (the §S44 loader table + the 500:1 h_exact
vote): a wrong vram could not have produced byte-identical images once symbols resolve.
- Fleet: 140 -> 143 binaries. audit-binaries currently FAILS on all three by design (no
engine_core.h include yet — the SC07-blindness check working as built); dedup_extend is the fix
and the next commit.
- Registered by the script: overlays.mk blocks, check.sha, symbols seeds, the 3 BINARIES dicts.
family map regenerated (3,577 target families / 279 with a matched sib — the new binaries'
members now visible).
|
||
|
|
f5ea0fdd49 |
feat(phase-30 S44 I.1e): tools/new_binary.sh — one onboarder for every flat-blob class
- Generalized from new_overlay.sh: ALIAS + PAYLOAD + VRAM + optional TEXT_LO (file offset of code). Class registry chosen by alias prefix (ov_* -> overlays.mk, md_* -> modules.mk; modules.mk created with its contract header on first use). Fixes the two places new_overlay.sh re-hardcoded the slot literal instead of $VRAM (:39 TEXTHI, :44 CODEEND). - TEXT_LO wires the §154 module-id law end-to-end: sig bootstrap gets --text-lo (else 0 functions on 75/78 payloads), and the splat yaml gets [0x0, rodata, hdr] + shifted code start (the resident's leading-word trick — ONE shared template, no second file, R33). _TEXT_LO lands in the mk block as a VRAM for make sig-modules. - The sentinel-anchored 3-dict registrar + check.sha/symbols/extract/build steps reused verbatim. - new_overlay.sh is now a 30-line WRAPPER (same CLI, docs preserved, H5); exec's new_binary.sh with the overlay defaults. |
||
|
|
369dd14f4f |
fix(phase-30 S44 I.1d): the module class reaches every enumerating consumer
- family_hseq: widened from src/ov_*+sig.ov_* to every non-main binary (resident + md_*); the map now carries 139 binaries incl. resident (was overlays-only — which is exactly why the R36 gate's CHECK 4 could never see them). Self-count uses the SAME widened globs (cannot drift). - progress --weighted :647 + audit_frontier :57: + sig.md_* globs. - corpus.sig_is_independent: md_* sigs are sig_image-signed => independent (R34 trust). - backlog alias regex + prefetch_fleet (md_* derived from splat configs) + dedup_propagate (reads modules.mk alongside overlays.mk — excluding modules would re-create the SC07 invisible-work bug one class over). - VERIFIED: family map regenerated with resident (139 binaries); audit-binaries OK over 140; all six tools parse. |
||
|
|
1826033fb0 |
fix(phase-30 S44 I.1c): the R36 citizenship gate is no longer blind to non-ov_ binary classes
- onboarded() derived from EVERY config/splat.<alias>.yaml (R33; templates + us.exe normalized) —
was splat.ov_*.yaml + a hand-set {main,resident}: the gate that exists to catch unwired binaries
was itself structurally blind to any class it did not know about.
- CHECK 4 widened: resident + modules must appear in the family map too (only main is exempt —
structurally barren, checked twice in S39). New honest warn: resident missing from the current
map (family_hseq widening lands next commit).
- NEGATIVE CONTROL: a planted config/splat.md_TEST.yaml FAILS check 1 ('MISSING md_TEST — invisible
to every derived tool'); removing it restores OK over the 140.
|
||
|
|
9ae1c9a743 |
fix(phase-30 S44 I.1a): family_remap derives vram per-alias from the splat config (R33)
- VRAM was a module constant (0x80128158) used in ALL offset math (reloc_targets :98, stream_words :160) — correct for the 138 shared-slot overlays, silently WRONG for every other class (resident 0x800CEDF8, the new md_* module slots): addr-VRAM would read garbage bytes without erroring, the R32 silent-skip shape in the tool the whole family engine stands on. - NEW vram_of(alias): read once from config/splat.<alias>.yaml (the jtbl_carve pattern; cwd-relative like img_path). Unregistered alias raises LOUD (R32, no default). - REGRESSION: the 0xECC-family remaps (ov_SC03_003/ov_SC04_021/ov_SC05_019) are byte-identical to the S43 banked drafts. Negative controls: resident derives 0x800CEDF8, overlays 0x80128158, unregistered alias raises. |
||
|
|
7473640838 |
fix(phase-30 S43): audit-disc listed only L1's code — the real backlog is 78 payloads, not 39
- MY BUG, found by reconciling against the old sweep (R14): when I introduced the two-oracle UNION I updated the BUCKET accounting but left the ledger's row-listing condition on L1 alone. So the byte total was already right (3,564,021) while the LIST under-reported — 34 rows instead of 78. Same "two code paths, one updated" shape as the day's other defects. Fixed: rows use the same union. - THE COMPLETION CONTRACT'S "39 type-1 modules" IS SUPERSEDED: the real backlog is **78 unclaimed code payloads / 3.56 MB** — MAIN.CD 42, SC03 18, SC05 7, SC04 7, SC07 2, SC02 2. The 39 came from disc_code_sweep, which reads only the RAW layer through a 4,096-WORD WINDOW and has no notion of a claim. Reconciled decisively: all 39 hash-checked against config/check.*.sha -> 0 of 39 claimed, so the new set strictly CONTAINS the old one. docs/disc-completeness.md updated, old text kept for provenance. - WORKED EXAMPLE of why the window mattered: SC07.CD FILE_003/1.1 is 345,132 B whose HEAD is code — the old window saw valid=100%, the whole-payload average is valid=0.571 (L1 says data), and L2 carves 3 real functions. Only the union gets it right, which is the entire argument for R34. - Partition still holds: residue 0 over 416,021,760 B, 1,291 payloads examined. |
||
|
|
964afdba4e |
feat(phase-30 S43): L2 second oracle — it found TWO L1 defects; unclaimed code 1.70 -> 3.56 MB
L2 (R34) is sig_image boundary carving: walk the payload cutting each function at the first `jr $ra`
at/after every forward branch target. Structurally different question from L1's statistical test
(valid>=0.90 AND jr>=0.01), so the two can ARGUE — and they did, 80 times, all one shape.
- L1 DEFECT 1 — A CLAIM OUTRANKED BY A HEURISTIC. A payload whose SHA1 equals a committed
config/check.<bin>.sha IS that onboarded binary (the build gates on that hash daily), but I let the
statistical verdict file it as classified-data. Onboarded bucket understated by 14.5 MB.
- L1 DEFECT 2 — WHOLE-PAYLOAD AVERAGING DILUTES CODE. A real location overlay is code followed by a
large data tail, so its whole-payload valid-ratio is ~0.87, under the 0.90 gate — while L2 carves
real functions from its head. The "classify the whole payload" fix for the old 4,096-word window had
traded a head-only bias for an averaging bias. 80 disagreements, every one this shape.
- RESOLUTION (bucket_of): a claim wins outright; otherwise take the UNION of both oracles. Union is
the conservative direction for this audit's question — over-reporting code yields a review queue,
under-reporting HIDES code, the exact failure that produced three "more code all along" surprises.
- RESULT: partition still holds, residue 0 over 416,021,760 B / 1,291 payloads.
onboarded-code 47,066,812 · UNCLAIMED-CODE 3,564,021 (34 payloads) · classified-data 134,265,572
· audio-video 184,338,000 · filesystem-metadata 46,787,355
Largest unclaimed: MAIN.CD sub-file 12 entry 1 type 1, 383,783 B; the rest small type-1, mostly MAIN.CD.
- The ledger now carries an explicit L2 REVIEW QUEUE section; L1=data/L2=code is flagged as the
DANGEROUS direction (missed code).
|
||
|
|
03794d91cd |
feat(phase-30 S43): make audit-disc — the disc PARTITION holds at residue 0; 34 UNCLAIMED code payloads
L1 of Drew's definitive disc audit ("we really need a full audit that definitively lists ALL code
that we need to decomp"). THE INVARIANT (R32): every byte on the disc belongs to exactly ONE bucket,
the buckets SUM TO THE DISC, and residue is a DEFECT — a partition with an asserted residue of zero
is a completeness proof; a longer list is only a longer list.
- WALKS THE DISC IMAGE, NOT OUR CONFIGS, classifies WHOLE payloads (no window), and decodes BOTH the
raw and LZSS layers — the three shapes that produced the three "more code all along" surprises
(the 0.4.dec glob missing 4 SC07 overlays; disc_code_sweep blind to COMPRESSED code, its type-4
row vacuous for 138 known binaries; a 4,096-word window reading only payload heads).
- CLAIMED-BY IS DERIVED (R33): config/check.<bin>.sha IS the SHA1 of that binary's disc payload, so
payload->binary is a hash lookup against the build's own byte-identity gate. It cannot drift.
- RESULT, 416,021,760 bytes, 1,291 payloads, RESIDUE 0:
onboarded-code 32,564,876 (7.83%) · UNCLAIMED-CODE 1,700,049 (0.41%) ·
classified-data 150,631,480 · audio-video 184,338,000 · filesystem-metadata 46,787,355
34 UNCLAIMED code payloads — largest a 383,783 B type-1 in MAIN.CD, the rest small type-1 entries.
These are the "there was more code all along" surprises, now ENUMERATED instead of stumbled into.
- MY OWN FIRST RUN FAILED THE PARTITION by -49,709,520 B, and the fail-closed exit is what caught it:
.DA entries' LBAs point PAST track 1 into the CD-DA tracks (double-counted against the whole-track
audio total), and .STR/.XA are MODE2 FORM2 (2324 user bytes/sector, not 2048). Both fixed.
- NOT wired into tools-health: it needs disks/, which a fresh clone does not have (H1).
- KNOWN GAP, stated not hidden: LIST.CD fails the TOC walk (it IS the TOC cache, not a container)
and is booked as data — correct today, worth a real classifier when L2 lands.
|
||
|
|
41a3de342d |
fix(phase-30 S43): family_remap now carries TYPEDEFS (transitive, brace-aware) — the §146/§152 gap closed
- ROOT CAUSE PINNED, and my first hypothesis was WRONG (R14, corrected in the log): I wrote that a gate transform ate a `/*` opener and turned comment prose into code. REFUTED — cast_call_sites, sig_unify and reconcile_tu each run with the gate's real --src-file all preserve it. The real cause is family_remap's preamble backscan, whose accept-set (blank/extern/comment/typedef) HALTS AT THE FIRST `#define` and never reaches typedefs above the macro block. `_carry_macros` then re-attaches the macros, which HIDES the truncation — the unit looks complete and is not. "parse error before 'unsigned'" was that failure surfacing at the next token (the following `extern unsigned char` line): a misleading label, not a second defect. - FIX: _carry_typedefs() — additive, TRANSITIVE (a carried typedef may name another; measured: carrying Vec8_80182FD4 alone then failed on SVECTOR_8016E7C8), and BRACE-AWARE (a `;`-terminated scan stops INSIDE the struct at its first member line, emitting a truncated unclosed typedef). Emits dependency-first for C89. Only types the unit actually names and does not already carry. - VERIFIED: the 0x80182FD4 unit now carries its Prim_8016E7C8 block complete; the 0xECC family's remaps now carry the four typedefs I had prepended BY HAND before gating them — i.e. the fix automates the exact workaround that banked those three siblings. Residual isolated-compile failure on SVECTOR_8016E7C8 is a match_one artifact: that type lives in src/shared/engine_types.h, which the real TU includes — the typedef gap is fatal ONLY when the target TU lacks the type, which is why this class failed loudly for some families and silently succeeded for others. |
||
|
|
50c6f61056 |
fix(phase-30 S43): the CC1-FAIL label named MAKE, not the compiler (~3,000 content-free labels)
- ROOT CAUSE: classify_fail took errs[-1], and make prints its own summary `make: *** [Makefile:N: build/src/<ov>/<tu>.o] Error N` LAST, always — so the wrapper won every time and the label carried only the TU name the record already stores. Each CC1-FAIL therefore cost a manual splice-and-rebuild to learn what cc1 actually said (3x in S42 alone). - MEASURED (R37, over the committed .classified.txt corpus): ~3,000 of ~4,000 CC1-FAIL labels are that wrapper; a further 1,019 are bare CC1-FAIL with no message at all. - FIX: _MAKE_WRAP guard excludes make's summary lines; the FIRST real diagnostic wins (cc1 cascades — error #1 is the root cause); nothing-but-wrapper is now labelled CC1-FAIL(no-diagnostic) rather than disguised (R32). Same family as the §58 warning red-herring guard directly above it: a label identical for every input carries no information. - VERIFIED on the exact branch (exec'd the real source, see hazard below): "CC1-FAIL: make: *** [...] Error 33" -> "CC1-FAIL: src/…/tu.c:2240: error: too few arguments to function `gte_ldv3'". DIFF/SKIP/PLUMBING paths unchanged. - HAZARD DOCUMENTED (mine): harvest_verify.py has NO `if __name__ == '__main__'` guard — the whole gate is module-level, so `import harvest_verify` PARSES argv, RUNS A BUILD and overwrites .run/harvest_*.txt. Tripped it unit-testing classify_fail (resident stayed 8e17e02f, 0 banked, tree clean, no damage). Nothing imports it today, so it is flagged in the file header rather than fixed by a risky 500-line refactor of our most load-bearing gate. |
||
|
|
fa122cf62f |
fix(phase-30 S43): permuter takes func_8017C6F4 63->42; the "rumour row" was an ADDRESS COLLISION
- THE FLOOR MOVED: permuter_ils on the S42 draft -> masked 65->44 (cycle 1, flat over 5 warm restarts); re-measured in match_one terms 63 -> 42 mismatched, 947/947 ins. First movement after ~40 hand probes, and it came from repairing an instrument (S43-1), not from new C. Draft preserved + allowlisted: .run/s43/func_8017C6F4.ils44.c; logged at closeness 42. - THE S42 "rumour" CLAIM WAS WRONG (R14): the 2026-07-01 row HAS an artifact, it IS on disk, and it reproduces exactly (14 mismatched of 15 target ins, SIZE-MISMATCH/redraft). It is a near-worthless draft on a DIFFERENT BODY: 0x8017C6F4 is 15 ins in ov_SC03_010/011/013 and 948 ins in ov_SC03_126/003 + ov_SC04_021 + ov_SC05_019 (§148-E, ledger side). - THREE ledger defects fixed: (1) load_best keyed on ADDRESS ALONE -> the two bodies merged and the lower ABSOLUTE closeness won, so 14-of-15-wrong (7% correct) masked 63-of-947 (93%); now sub-keyed by known nins, legacy rows unchanged. (2) binary=null defaulted to ov_SC01_077, where the fn does not exist AT ALL, and "not an open stub" was read as "banked" -> today's result was invisible to render/grinder/target-selection (absent != done, R32/R34); now derive binary from the draft path + only drop when closed everywhere it exists. (3) `log` had NO --binary flag -- the root cause of every null; added + derived in append_record. - IMPACT DERIVED, NOT ASSERTED (R37): replaying the pre-fix selection = 836 -> 837, 1 appeared (func_8017C6F4 nins=947), 0 vanished. One row today; the mechanism would eat every future one. - PROBED AND NOT BUILT: relative-closeness ranking (only 24/836 rows carry closeness+nins, and the two orderings agree 14/15 on those). Documented in the log instead. |
||
|
|
63d029b563 |
fix(phase-30 S43): the §148 "GTE macro wall" is a SILENT CPP FALLBACK — permuter lane was dead on 63 drafts
- ROOT CAUSE (reproduced): make_base_c ran cpp_expand_macros BEFORE #include lines were dropped, so `cpp -P -nostdinc -` died on `#include "common.h"` (rc=1, empty stdout) and the `return c` fallback handed back the UNEXPANDED draft. hide_asm then ate the gte_* #define block + the function itself -> "Function not found in base.c" -> decomp-permuter no-opped in 0s, indistinguishable at the call site from "searched, found nothing". - FIX: strip #include inside cpp_expand_macros (byte-neutral) + RAISE on cpp failure (R32/R35, no silent fallback); NEW defines_fn() assertion in setup() guards the OUTPUT so it catches every swallow cause (this, the §G comment class, future macro shapes); main() catches per-fn so a bad draft is loud+counted but cannot abort a batch. - VERIFIED: func_8017C6F4 base.c keeps the def, 0 gte_ macros left, 35 asm b64-carriers; proxy validated over 388 stored drafts = 0 false alarms, 0 cpp raises (macro-free untouched); permuter now loads at base score 65 and iterates (was a 0s no-op). - BLAST RADIUS (14,899 drafts scanned): 63 carry `#define … __asm__` + `#include`, incl. the behemoth renderer drafts — the permuter was silently dead on the highest-byte-weight targets. - CONSEQUENCE (R14): §147/§148's ~40-probe floors were measured with the permuter UNAVAILABLE; "the permuter also plateaus" was never actually tested on those functions. §148 note corrected. |
||
|
|
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. |