Restoring config/ from git does NOT rewind asm/, so a reverted overlay was left
with stale extraction — and `git status` is structurally blind to it (the R22
corollary, here inside a tool's own undo path). Within a sweep each sibling is a
DIFFERENT overlay, so the next sibling's own extract never repairs the previous
one: the damage persists to whatever runs next.
Byte-witnessed on the 0x8013C414 probe: after 3 gate-fails `git status` was EMPTY
and ov_SC01_004 failed to link (`undefined reference to jtbl_8018DAC8`).
- revert() now RE-EXTRACTS (extract=True default); the clean-slate call at the top
of bank_one passes extract=False since it extracts immediately afterwards anyway
- revert() only `git checkout`s a TRACKED path: an isolation creates region .c
files that were never added, and checkout on those emitted `error: pathspec ...
did not match any file(s) known to git` while doing nothing. The keep_regions
cleanup removes them instead.
NEGATIVE CONTROL: the same probe now gate-fails 3/3 as before, and all three
overlays build BYTE-IDENTICAL afterwards with a clean git status.
THE BUG (tools/gate_stage.py main(), introduced commit:0181, 2026-06-21, Phase 21 T3):
good_sha=a.good_sha or DEF_SHA, # DEF_SHA = ov_SC01_077's locked hash
DEF_SHA is TRUTHY, so it beat run_gate's per-binary lookup
(`good_sha or _check_sha(binary) or DEF_SHA`) and made that lookup DEAD CODE on
every CLI invocation. gate_stage therefore BUILT one binary and compared it to a
DIFFERENT binary's hash: it can never match, every draft reports as "near", and
NOTHING COULD EVER BANK outside ov_SC01_077 from the CLI. A "near" is
indistinguishable from a genuine codegen residual, so the failure looked like a
compiler wall for a month.
WHY IT HID: the programmatic callers take a different path and were all correct —
grinder/idiom_hunt pass good_sha=None (per-binary lookup), lora_grind/bulk_harvest
pass an explicit per-binary sha, orchestrator is 077-only where DEF_SHA is right.
That is exactly why the grinder banked func_80181F78 in ov_SC03_014 (Task 13B)
while my CLI ladder banked 0/10 on the same tree. Two paths disagreed for a month
and nothing compared them (R34's lesson, from the inside).
BLAST RADIUS, MEASURED (not assumed): 0 of 6,708 backlog records come from the
affected path — by source: worker 2724 / bulk-harvest 2275 / lora-grind 766 /
grinder 522, all correct. THE BACKLOG NEEDS NO RE-RUN. The void verdicts are the
manual CLI gates on non-077 binaries, i.e. exactly the Task-5 wave's "the gate
banked ZERO" on 12 preserved cracks — never a codegen finding at all.
FIXED: pass a.good_sha through; run_gate reads config/check.<bin>.sha (R33).
VALIDATED end-to-end: the raw draft that produced {banked:0, near:1} now gives
{banked:1}.
RE-RUN RESULT — 7 of the 12 preserved t5wave cracks are now banked:
func_8018F694 (478) · func_8019059C (673) · func_80135A4C (181) ·
func_80135888 (113) · func_80135D20 (100) · func_801749C8 (105) ·
func_801299C8 (158, was filed "PLUMBING: prototype declaration")
STILL BLOCKED (5, believed genuine): func_8012AAAC, func_80135260, func_801365B8,
func_80165CA0, func_80191C50.
⚠️ SUPERSEDES the earlier retraction: the "0/10 ladder" was not vague interference
— it was the gate comparing against the wrong binary's hash. Task 14 stages 2-3
were priced against a number that could only ever have been zero.
- R22 clean-fleet 140/140 BYTE-IDENTICAL; all 6 verified stub-free
THE FIX (harvest_verify._jtbl_prep_one): isolate WITH THE BODY STILL SPLICED.
jr_isolate_all accumulates each object's file-scope decls as the new region's
`ambient` set, so partitioning around an INCLUDE_ASM stub hands the region a
DIFFERENT decl context than the draft's body needs — and the gate then produced a
byte-DIFF rather than a compile error, which is why a batch of these read as
"9 compile / 0 bank" and looked like a codegen wall. §61b's law extends one step:
THE CARVE MUST FOLLOW THE SPLICE — AND SO MUST THE ISOLATION.
The un-splice existed only because the tool is stub-centric (a spliced fn is no
longer in corpus.stubs). New _unsplice_body() handles that properly: it finds the
file that NOW holds the body (isolation may have MOVED it into a fresh region file)
and writes back the stub line for THAT subseg, so the gate re-splices identical text.
BANKED via the corrected path (each whole-binary byte-gated, one per invocation):
func_80135D20 (100 ins, reach 138)
func_801749C8 (105 ins, reach 136)
func_8019059C (673 ins, reach 3 — a giant)
Together with func_80135888 and func_8018F694 earlier: 5 of the 11 preserved
t5wave cracks are now banked.
THE REMAINING 6, honestly classed: 2 genuine DIFF (func_80135260, func_80191C50),
2 CC1-FAIL (func_801365B8, func_80165CA0), 2 §57 self-decl plumbing
(func_801299C8 `prototype declaration`, func_8012AAAC own-name conflict).
⚠️ RETRACTION: the earlier "the ladder converts 0/10, which prices Task 14 stages
2-3" is WRONG and is withdrawn. Three of those ten bank through harvest_verify
alone with the SAME transformed drafts that gate_stage rejected — so that 0/10 was
measuring gate_stage's own interference, not the residuals. Prime suspect is the
arity pre-pass (fix_arity_callers --apply --any-proto edits engine_core.h before
the gate; the §19 regression mode). GATE_NO_ARITY=1 is the ready A/B. Task 14
stages 2-3 remain UNPRICED until that runs.
- R22 clean-fleet 140/140 BYTE-IDENTICAL; tree clean before and after
THE DIAGNOSIS (the recommended next task) did not find an image-level difference:
performed manually, there is no difference. The recipe
splice draft -> jtbl_carve (NON-CONTIGUOUS) -> jr_isolate_all --only <fn>
WITH THE BODY STILL SPLICED -> make extract -> jtbl_carve -> make extract -> build
yields BYTE-IDENTICAL (R22 clean-fleet 140/140). The draft was never wrong.
THE DIFFERENCE from the ladder path is one line: harvest_verify._jtbl_prep_one
UN-SPLICES before isolating, so jr_isolate_all partitions a TU in which the
function is still INCLUDE_ASM. jr_isolate_all accumulates each object's file-scope
decls as the new region's `ambient` set, so partitioning around a stub gives the
region a DIFFERENT decl context than the one the draft's body needs — and the gate
then builds a byte-DIFF rather than a compile error, which is why it read as
"9/10 compile, 0 bank" and looked like a codegen wall.
So §61b's law — THE CARVE MUST FOLLOW THE SPLICE — extends one step further:
THE ISOLATION MUST FOLLOW THE SPLICE TOO. The un-splice exists because after
isolating with the body in, the function is a real C def and corpus.stubs no longer
lists it (the tool is stub-centric). That is a fixable plumbing problem, not a
reason to isolate around a stub.
- func_80135888: 113 ins x reach 138 = 15,594 instruction-instances (~0.12pp) once
swept; banked x1 here, sweep is its own batch (§55b)
- R22 clean-fleet 140/140 BYTE-IDENTICAL; tree verified clean before and after
- the other 9 residuals are now expected to be the same class — to be re-run through
the corrected sequence, one per invocation (§61c fault 2 stands)
gate_stage._jtbl_prepare carried the SAME config-only undo as harvest_verify's did,
and ate the tree again on the first ladder run: 5 orphan region files, truncated
TUs, `undefined reference to func_80192F64`. That INVALIDATED the run's 0/10, so it
was re-measured rather than reported (R35 — a probe from a broken tool is not
evidence). Tree restored from HEAD and re-verified byte-identical first.
DELETED, not patched (R33 — the best outcome is a deleted stage). It was wrong on
two independent axes:
1. §61b already byte-proved THE CARVE MUST FOLLOW THE SPLICE. A batch pre-pass
carving unspliced functions reports "prepared" and yields a spec that fails
once the body lands — which is why it banked nothing.
2. Its undo snapshotted only config/, while jr_isolate_all rewrites region 0 back
over the ORIGINAL src/<ov>/<nm>.c truncated.
harvest_verify's per-draft prep is the correct mechanism, snapshots the full source
set, and undoes per function. Two implementations of one capability, the outer one
ineffective AND destructive.
THE HONEST RE-MEASUREMENT (clean tree; tree verified clean after):
- 0/10 bank, but 9/10 now COMPILE and land as whole-binary byte-DIFF; 1/10 plumbing.
- match_one close=0 on several (the function's own bytes exact) and rtu_match says
MATCH-in-real-TU for func_80135888 — while func_801299C8's transformed draft does
not compile in its real TU at all. The residual is MIXED, not uniform; at least one
is an IMAGE-level effect rather than the draft or its TU decl context (prime
suspect: jtbl/rodata carve placement). NOT generalized from one data point.
- This PRICES Task 14 stages 2-3 by measurement: the existing ladder converts 0 of
10, so they are not "wire in normalize_self_decls + the type-lift and collect ten
banks" — the projection error §57a already caught once this phase.
- R22 clean-fleet 140/140 BYTE-IDENTICAL with the giant func_8018F694 banked and the
func_80135A4C family swept 138/138
- cookbook §61d (the tree-eating undo in two tools; the constant-label defect; the
re-probe + ladder measurements; the general rule: an undo whose scope is narrower
than its write scope destroys work no byte-gate can see)
- decision-log + CURRENT_PHASE updated (R30/R31)
THE DEFECT (byte-witnessed, and it is the §61c mechanism). `jr_isolate_all`
repartitions a code object by writing region 0 back over the ORIGINAL
src/<ov>/<nm>.c — TRUNCATED to just that region — and emitting the rest as new
_jr_<lo>.c files. `_jtbl_restore` undid only config/ + the new region files, so
every gate-REJECTED draft left the original TU permanently truncated and its
stubs gone. Nothing regenerates them (splat does not rewrite a committed overlay
.c). Measured across an 11-draft re-probe: live stubs 419 -> 414 -> 406 -> 395,
ending in `undefined reference to func_80191C50`.
It is INVISIBLE to the gate that causes it: the incremental build keeps linking
stale objects (§42b) so `make build` stays green while a CLEAN rebuild fails.
That is exactly the "139/140, twice" signature that became the §61c blocker —
the tree was being eaten by the undo meant to protect it. The §61c attribution
(batch _jtbl_prep residue) is now a demonstrated defect, not an inference.
FIXES (both negative-control-validated on a draft that fails):
- _jtbl_snapshot captures every src/<binary>/*.c; _jtbl_restore restores them and
removes exactly the files the attempt created (derived from the snapshot's file
set, not re-guessed from the _jr_* name shape, R33). Undo by SNAPSHOT-RESTORE,
never an inverse transform — §61's law one level deeper. Same failing draft that
previously broke the tree now leaves it byte-identical, git status clean.
- classify_fail ignores `warning:` lines. The benign `conflicting types for
built-in function 'memcpy'` warning was winning the match on 8 of 8 failures
across four different real causes — a label identical for every input, which the
cookbook had to work around by hand ("the gate label is useless here, splice
individually and read real cc1 stderr"). Now reports the real error, and falls
through to CC1-FAIL:<last error line> rather than guessing.
THE RE-PROBE THIS ENABLED (11 preserved t5wave cracks, one invocation each):
- BANKED: func_8018F694 (478 ins) — one of the wave's three giants, previously
recorded as part of "the gate banked ZERO".
- The other 10 now carry TEN DISTINCT diagnoses: 4 data-decl conflicts (D_80193B64
x2, D_8011D030, D_80126B5C), 3 callee-decl (func_80135480 x2, func_8012F14C),
3 self-decl/own-sig (§57). ZERO jtbl-drift, ZERO local-type redefinition, ZERO
codegen DIFF.
- So §61a's "§8e-2 jtbl table-count drift blocks 10 of 12" does NOT survive the
carve-follows-splice prep: the carve now succeeds and what is left is ordinary
decl plumbing the existing ladder already handles (cast_call_sites / reconcile_tu
/ normalize_self_decls / fix_arity_callers) = Task 14 stages 2-3, no new tooling.
- ov_SC06_018 BYTE-IDENTICAL cbbc4f44 with the giant banked; nothing committed broken
at any point (tree was restored from HEAD and re-verified before these fixes)
- cookbook: the `git add -u` complementary hole (an isolation's NEW region file is
untracked, so a carve/isolation bank needs `git add -A src/ config/`)
The session-7 checkpoint gated the entire jtbl track behind one finding: the
carve+isolation path yields a bank that is incrementally valid and clean-invalid
(139/140, [FAIL] ov_SC06_018, "twice, identically"). The prescribed diagnosis
(diff the incremental vs clean object set) never ran, because the failure does
not reproduce.
MEASURED, with the bank applied through the single-function automated path
(harvest_verify --chunk 1 -> [jtbl] carved -> + chunk(1) -> BYTE-IDENTICAL):
per-binary clean (rm asm+build; extract; build) -> BYTE-IDENTICAL cbbc4f44
make clean && extract-all && check-all (run 1) -> 140 passed, 0 failed of 140
make clean && extract-all && check-all (run 2) -> 140 passed, 0 failed of 140
ATTRIBUTION (best-supported; the failing tree is gone): the 139/140 runs were
taken on the tree left by the BATCH _jtbl_prep (6 table-bearing -> 1 carved,
4 isolate-FAILED, 1 stale-asm carve fail) — five failed preps' residue of
stranded carves + half-applied isolations. The per-function snapshot-restore
that removes exactly that residue landed AFTER those runs, in commit:0803, the
same commit that named the blocker.
THE LESSON (R35 on ourselves, -> decision-log): "twice, identically" was not a
replication — two reads of the SAME contaminated state is one observation. A
replication must RE-CREATE the state, not re-run the check. Standing guard:
re-apply a fault from a known-clean tree before writing it down as a property
of the mechanism. Sixth "structural wall" to resolve to our own tree/tooling.
- BANKED: func_80135A4C (181 ins) x1 in ov_SC06_018 — isolated into its own
code subseg + .rodata carve (single-table, no JTBL_PADS; tail3..tail18 renumber)
- §61c faults 1-2 STAND: a stranded carve poisons the overlay; per-function undo
is unsound in a batch -> ONE jtbl draft per harvest_verify invocation.
jr_inventory's 1:1 ownership assertion was right and is unchanged.
- UNFROZEN: this family = 138 members / PURE / 24,978 ins ~ +0.19pp (jtbl_family_bank,
§53 carve law); the 9 preserved t5wave cracks (Task 14 stages 2-3, §57 plumbing)
- R22 clean-fleet 140/140 x2; tools-health OK (dedup 1848/0, C1 234481/234481,
cdecl 53189/53189, audit-binaries 140); 0 NON_MATCHING (G4)
- fleet 78.0% instr / 66.5% distinct / 87.95% fn-count
- also: preserve the 4 untracked wave-4 .o0 drafts (R20); killed an orphaned cc1
from the Jul-21 session burning a full core for 13h23m
THE BLOCKING FINDING (§61c): the jtbl carve+isolation path yields a state that is
INCREMENTALLY valid and CLEAN-INVALID. func_80135A4C banks every time through the automated
path (BYTE-IDENTICAL at harvest_verify's gate) and fails `make clean && extract-all &&
check-all` twice, identically (139/140, [FAIL] ov_SC06_018). The bank is therefore NOT
reproducible from committed config + source, and the gate that authorises it cannot see the
defect because the gate IS the incremental build (§42b, most expensive form).
=> NO jtbl core can be banked until that divergence is diagnosed. Next step is to diff the
incremental vs clean build/ov_SC06_018/** object set + generated .ld/asm for the carved
subseg — NOT to bank more. All 12 wave cracks stay preserved at .run/giants/t5wave_*.
TWO REAL DESIGN FAULTS FOUND AND FIXED IN harvest_verify:
1. A stranded carve poisons the overlay. _jtbl_prep carved a draft the gate then REJECTED;
the carve remained with NO owner (the fn is still INCLUDE_ASM), and jr_inventory's 1:1
ownership assertion then refused every later isolation in that overlay
([('UNOWNED','0x801d288c')] = func_801299C8's table). THE ASSERTION WAS RIGHT AND CAUGHT IT
— R32/R33 working as designed; the defect was mine for leaving the carve behind. Fixed:
per-function carve + exact snapshot-restore (config text + only this attempt's region files)
on gate rejection.
2. Per-function undo is UNSOUND IN A BATCH: isolation REPARTITIONS shared source, so restoring
one draft's snapshot deletes region files now hosting OTHER pending drafts, and their stubs
vanish (KeyError in render). jtbl drafts must run one per harvest_verify invocation, or the
undo must be region-aware.
MEASURED so it is not re-derived: of 11 preserved cracks exactly ONE (func_80135A4C) reaches
byte-identical through the carve path; the other 4 table-bearing ones fail one-at-a-time too,
on PLUMBING (§57 self-decl et al), not the carve.
Tree reverted; clean-fleet 140/140; nothing banked. cookbook §61c.
THE LAW IMPLEMENTED: the carve must follow the splice. harvest_verify._jtbl_prep() splices each
table-bearing draft TEMPORARILY, asks jtbl_carve, isolates on the §8b walls, un-splices, re-extracts,
and RE-DERIVES the stub map + baseline (isolation MOVES a stub's TU, so both are keyed on stale
paths otherwise). gate_stage's batch pre-pass could not work: while a fn is still INCLUDE_ASM the
non-contiguity is undetectable, so jtbl_carve reports success and yields a spec that fails when the
body lands.
BYTE-PROVEN AUTOMATED: func_80135A4C (181 ins, 138 members) ->
[jtbl] carved 1/1 table-bearing draft(s): func_80135A4C
+ chunk(1): func_80135A4C verified 1 / failed 0 BYTE-IDENTICAL
NEW BLOCKER, PRECISELY NAMED — banking a jtbl core makes its own carve UNOWNED to jr_inventory,
which then refuses every subsequent isolation in that overlay:
"committed .rodata carve ownership is not 1:1 (R32/R33) — a stranded/duplicated carve
(§8b func_801734BC class): [('UNOWNED', '0x801d288c')]"
Byte-proven both ways: on the COMMITTED tree `jr_isolate_all --only func_80135260 --dry-run`
succeeds; with func_80135A4C banked it fails the assertion. The assertion is RIGHT (a banked fn's
stub .s is pruned, so the owner lookup finds nobody) but its conclusion is wrong — the carve IS
owned, by C rather than a stub. SO TODAY JTBL CORES BANK ONE PER OVERLAY. 10-draft batch: 6
table-bearing -> 1 carved, 4 isolate-FAILED on this assertion, 1 stale-asm carve failure.
NEXT INCREMENT (precise): resolve jr_inventory's carve owners from corpus.matched U stubs, not
stubs alone (R33 — the same derive-don't-reparse move that fixed the corpus oracle).
BANK NOT KEPT: R22 clean-fleet showed 139/140 (ov_SC06_018 FAILS from a clean tree) even though the
INCREMENTAL build read BYTE-IDENTICAL — the stale-incremental false pass R22 exists to catch (§42b).
Reverted; clean-fleet re-verified 140/140, tools-health OK (dedup 1848/0). All 12 wave cracks remain
preserved at .run/giants/t5wave_*. cookbook §61b updated.
DIAGNOSIS CORRECTED: the wave's 10/12 blocker is NOT "§8e-2 table-count drift" (the symptom
the filter reports) but a NON-CONTIGUOUS .rodata carve — the new function's table is separated
from the TU's existing carve by an UNMATCHED function's table, and one object cannot straddle
that gap. jtbl_carve names its own remedy in the refusal message.
RECIPE BYTE-PROVEN (func_80135A4C, 181 ins / 138 members):
jr_isolate_all --only <fn> ; make extract ; <splice> ; jtbl_carve --func <fn> ;
make extract ; make build -> BYTE-IDENTICAL
(isolation verified byte-neutral on its own first; a config change needs extract, not just build.)
BUILT: gate_stage._jtbl_prepare — per-draft carve + auto-isolate on the §8b walls, logic LIFTED
from jtbl_family_bank (R33: one implementation, two callers — their divergence IS this bug),
snapshot-restore undo, GATE_NO_ARITY A/B guard. Ladder: canon -> cast -> reconcile_tu -> jtbl
-> arity -> gate -> sig_unify -> gate.
IT DOES NOT YET BANK, and that is the finding: THE CARVE MUST FOLLOW THE SPLICE. The
non-contiguity is only DETECTABLE once the body is in the object; while the fn is still
INCLUDE_ASM, jtbl_carve reports SUCCESS and produces a spec that fails when the body lands.
Byte-witnessed both ways (spliced -> NON-CONTIGUOUS 0xaa810/0xaa920; unspliced -> "prepared 1/1"
then byte-DIFF). Innocent suspects A/B'd out: the draft is IDENTICAL through canon/cast/
reconcile_tu, and GATE_NO_ARITY=1 changes nothing. FIX = per-draft prep inside harvest_verify's
splice loop (it owns the splice), not a batch pre-pass in gate_stage.
SUB-FINDINGS: (a) a wholesale `git checkout -- config/` undo is WRONG in a batch gate — it
discarded a previously-banked-but-UNCOMMITTED carve, leaving that bank's source with no subseg
(undefined reference to func_80136C90 at link). Now snapshot-restore + drop only this run's
region files (§61's constraint, which I had written and then not applied here). (b) being in a
_jr_* TU != having a table: only 4 of 8 wave drafts reference a jtbl_.
Tree restored byte-identical; nothing banked. cookbook §61a corrected + §61b.
Ultracode wave, 12 agents (~2M tokens), over freshly-prefetched ov_SC06_018 exemplars.
11 MATCH / 1 near, INCLUDING ALL THREE GIANTS (710/673/478 ins). Whole-binary gate: ZERO.
Splicing each failure individually (the gate's own label is §58's memcpy red-herring) gave
THREE DISTINCT blockers, none of which the ladder clears:
(1) §8e-2 jtbl table-count drift -- 10 of 12. "more rodata .align directives than pad specs".
STRUCTURAL FINDING: fresh crack fuel in a well-matched overlay CONCENTRATES in jtbl-carved
TUs (the non-carved ones were harvested first), so §8e-2 GATES the next tranche of
substantial cracking rather than being a straggler.
(2) §57 self-decl conflict -- the 2 plain-TU drafts ("argument 'arg2' doesn't match prototype").
normalize_self_decls exists, is wired into family_sweep, and is NOT in gate_stage -- the
same gap the arity pre-pass had.
(3) local-type redefinition (from the Task-14 diagnosis set) -- wants the type-lift.
So gate_stage needs THREE stages; only the arity pre-pass landed today.
All 12 drafts PRESERVED at .run/giants/t5wave_* (R20): genuine cracks with per-function lever
notes (cross-jump barrier placement, MEM_IN_STRUCT_P store/load ordering, §43 K&R s16 params,
$s-pins, CSE-break barriers). Do NOT re-draft -- they bank the moment the stages exist.
METHOD NOTE: `make build | grep -i error` missed the real failure TWICE (the jtbl_rodata_pads
line contains no "error" token; and the build failed at a later stage than the warnings I read).
Check rc, read the tail unfiltered -- a filtered build log is a selection tool, and every
selection tool here has eventually lied (R32/R35).
Tree reverted clean; nothing banked. cookbook §61a.
SCOPING (it corrected my own claim twice — the durable part of this task):
Remaining frontier = 2,873,658 stub ins = 22.0pp (resident+138 overlays; main excluded).
Substantial 80-1000 ins = 10,934 fns / 12.84pp across 1,398 distinct h_seq families.
I claimed "Task 5 gates 12.84pp". It does not:
340 families / 7.72pp exemplar ALREADY ATTEMPTED or WALLED — the top-12 by value are our
known set (func_801412A8 permanent wall, func_8013C414 -O0-blocked,
func_8014D820 close-11, func_8015B950/func_8013B83C cracked-but-
deferred, func_8013D53C 14/137, func_8013BD74 §8e-2). These need the
DEFERRED TOOLING fixes, not fresh drafting.
1058 families / 5.40pp exemplar NEVER attempted — the true fresh fuel, of which
48 fams / 0.62pp already have a cached member (draftable now, no MCP)
877 fams / 3.10pp have none — median 1 MEMBER, i.e. overlay-UNIQUE code (every big
138-member family lives in ov_SC01_077 and was cached+drained by
waves 1-4). So the prefetch is PER-OVERLAY, never fleet-wide.
Greedy overlay cover: ONE import (ov_SC06_018) unlocks 54 fresh families = 1.59pp; imports
2-8 add only +0.59pp combined, leaving a 632-family long tail. Ranked list:
.run/autopsy/t5_ready.json.
EXECUTED (R23 lock dance): MCP stopped (save succeeded) -> ghidra_import_raw.sh
extracted/retail/SC06.CD.dir/FILE_018.dir/0.4.dec @0x80128158 -> DefineFunctions
(created=72 existed=29 failed=0) -> DecompileFunctions over the 101 uncached substantial
stubs -> 101 ok / 0 fail. Ghidra-C cache 882 -> 983; ov_SC06_018 now 149 substantial stubs
cached. MCP restarted (41 tools, serving).
Overlay Ghidra program NOT committed (Phase-13/15 precedent: script-reproducible from
ghidra_import_raw.sh + DefineFunctions, no manual RE on it; avoids ~14MB/overlay .git bloat).
CAVEAT on the ready-list: attempted-detection matches backlog entries + preserved-draft
filenames, so it carries false-fresh entries (func_8014032C is jtbl-table-count-drift blocked;
func_8017A4AC is already banked). The 48 figure is an UPPER BOUND — verify per family.
DIAGNOSED, not assumed. The 12-draft integration probe banked 1/12 and reported the SAME
label for 10 of the 11 failures: `conflicting types for built-in function 'memcpy'` — the
§58 red-herring (a WARNING, from an unrelated TU position). Splicing three top-reach
failures individually and reading real cc1 stderr gave the actual causes:
conflicting types for `func_XXXX' 3/3 <- loose-typing ARITY conflict
redefinition of `struct V8' <- a SECOND class (type-lift), stage 2
A banked shared caller macro in engine_core.h declares the function with FEWER params than
its byte-true definition takes (the original calls K&R-style with fewer args than the callee
reads); a C89 prototype makes that a hard error. tools/fix_arity_callers.py --any-proto
already fixes it and was simply NEVER WIRED into gate_stage's ladder (only family_sweep
carried §57). Now wired as a TU-side pre-pass.
MEASURED: 2 of 7 top integration candidates banked (func_8016EFC8, func_80164418, both
reach-138) vs the 1/12 old-ladder baseline. R22 140/140; tools-health OK (dedup 1848/0).
INCIDENT — this stage BROKE 138/140 AND R22 CAUGHT IT (nothing was ever committed):
pairing `--apply --any-proto` with `--revert` for the unbanked drafts corrupted declarations
fleet-wide. `--revert` rewrites ()->(void), which inverts a PLAIN apply but NOT --any-proto,
so an unbanked fn whose real decl was `extern void func_801708B0(void *a0)` came back as
`(void)` — in engine_core.h (included by all 138 overlays) and 6 sites in ov_SC01_077's own
sources. harvest_verify --binary ov_SC01_077 reported BYTE-IDENTICAL and was RIGHT about that
binary; the other 137 were structurally invisible to it. Repaired to the exact lines.
ROOT CAUSE FIXED: the ladder now snapshots every file the pre-pass touches and undoes by
RESTORE + re-apply-for-the-banked-set-only — exact by construction, cannot invent a signature.
NEW HARD CONSTRAINT (cookbook §61): any ladder stage mutating SHARED state must be undone by
snapshot restore, never an inverse transform, and validated FLEET-WIDE (R22) rather than by
the per-binary gate that authorised it. §55b's propagation law, one level down. The planned
type-lift stage edits engine_types.h and inherits it by default.
ALSO FIXED: the first wiring passed only --drafts (the narrow-param FILTER) without the
required --funcs, so the stage exited `no funcs given` as a SILENT NO-OP and the gate reported
0/6 as though diagnosed. sh() does not raise on non-zero exit -> explicit rc check added.
The hindsight-study §7 taxonomy predicts plateaus decompose into missing-transform (the
"highest-value bucket and the whole point"), seed-structural, and genuine-wall. Run against
real plateaus this class produced NO missing-transforms, and the answer needed no LLM.
MEASURED: `length` probe, 20 targets, 1 win. tail 1/6; partial 0/12.
AUTOPSY (read directly from the bytes, 3 partial plateaus):
- func_8017F0C0 / func_801806C8: target has `sltiu $v0,$v0,1` = gcc's codegen for `!x`/`x==0`;
the drafts wrote `(u32)(D_x ^ 1)` which emits `xori`. No local mutation crosses that.
- func_8017FF90: draft stores to arg0+8, target stores to a GLOBAL. Different function.
=> these are WRONG DRAFTS wearing a small closeness, i.e. seed-structural, not a mutation gap.
THE FIX IS THE OPPOSITE OF "ADD TRANSFORMS" — a tighter ADMISSION rule:
- _drift_route: permuter only when |d|<=2 AND explains=="tail" (the shape that measurably
converts). length pool 339 -> 34; permuter bucket 389 -> 84.
- SIZE-MISMATCH: added a PROPORTIONAL test (|d| >= 0.5*nt). max(2,0.15*nt) is far too
permissive on a tiny target — a 2-ins draft vs a 4-ins target read as a near-miss.
permuter_weights needs NO extension for this class.
Transferable (cookbook §60b): raising a search-closer's yield is at least as often about
refusing it unreachable work as widening its mutation set. Same knife as Task-13A's
targeting fix, one cut finer. Drafter idiom recorded: `sltiu rd,rs,1` => `!x`, never `x^1`.
17 unit tests green; corpus re-collected (1654 rows, closeness cross-check clean).
PROPAGATION (§55b, its own targeted batch): dedup_propagate --addr 0x80141B90 --recover
-> "138 overlays byte-identical after propagation"; 117 remaining stubs -> 0; 1 new
dedup group. This was the ONLY one of the 21 directed-run banks worth propagating.
THE REPRICING (R14 — measure a bucket's VALUE, not just its conversion rate):
the directed run converted 27% (21/77) but moved the fleet ~0.03pp, because h_exact
reach of the 21 is: func_80141B90=138, TEN at reach-1 (nothing to propagate), rest 2-10.
Instruction-weighted, the ENTIRE permuter bucket is worth ~0.36pp at 100% conversion.
The mechanism is validated; the fuel was small. Priced frontier (ins-weighted / 13.08M):
LENGTH-DRIFT |d|<=2 472,178 ~3.6pp (339 fns) <- the real permuter-adjacent lever
integration 419,162 ~3.2pp (305 fns) <- Task 14's ladder
WIDTH 71,593 ~0.55pp (45)
permuter (current) 46,571 ~0.36pp (74)
BRANCH-POLARITY 9,462 ~0.07pp (22)
So WIDTH/BRANCH-POLARITY are NOT worth prioritizing; my earlier "~200 candidates"
framing undersold LENGTH-DRIFT 10x and oversold WIDTH.
NEW: permuter_weights._LENGTH profile (perm_temp_for_expr/perm_expand_expr are the only
passes that change instruction COUNT; the reorder/decl-order levers that dominate the
regalloc+schedule profiles cannot, so they are down-weighted here) + residual_class
._drift_route (|d|<=2 -> permuter/`length`, larger stays structural — same class,
opposite tool) + classify() accepts a PROFILE NAME directly (the measured profile beats
re-parsing a free-text label). 17 unit tests green.
grinder: --profile filter (probe ONE residual class's conversion) + a PERSISTENT attempt
ledger. `tried` was in-process only, so every fresh --once run re-permuted the previous
run's losers — the permuter is deterministic given (base.c, target.o), so that CPU can
never produce a new win. Measured: a 20-target probe drew 19 already-tried targets.
Keyed by draft_sig so an improved draft legitimately re-opens the function.