Commit Graph

91 Commits

Author SHA1 Message Date
Drew T dff33f66d9 docs(setup): WSL memory cap lowered 48GB -> 32GB (Drew, 2026-09-02); applies on next wsl --shutdown 2026-09-02 01:49:46 -06:00
Drew T 8f87334399 docs(setup): WSL2 memory reclaim — autoMemoryReclaim=gradual + the interim cache dropper
Task Manager showed WSL holding 30 GB while Linux was using 4 GB and ~22 GB was
reclaimable page cache (a gate wave reads the 450 MB asm/ tree plus every build
object). .wslconfig had a memory cap but no reclaim policy, so WSL2 never handed
freed pages back to Windows. Added autoMemoryReclaim=gradual (needs wsl --shutdown),
recorded the no-restart manual reclaim, and .run/memkeeper.sh as the interim
automatic form. R21.
2026-09-02 01:17:25 -06:00
Drew T 20f80fd933 docs: §400 + SETUP + carve-state memory for the new-file adoption fix; correct a stale docstring
§400 — a baseline check that conflates "absent everywhere" with "changed under
us" silently drops new files. The general law: when a comparison uses two
different sentinels for "nothing" ("" from a failed command, None from a missing
file), it reports a difference that does not exist — and in a GUARD, a phantom
difference becomes a refusal, which looks exactly like the guard working.

Corollary recorded in both §400 and the carve-state memory: "never blanket-add"
covers SHARED carve state (overlays.mk, splat yamls). It does NOT cover a carve's
own new per-binary source file, which is named by a committed yaml and whose 31
siblings are tracked — that one must be adopted with the bank that created it.

Docstring correction: parallel_gate does NOT use `git add -u src/` (that is
gate_stage's form); it adds exactly the adopted paths. My first diagnosis of this
bug blamed `-u` on the strength of that stale line and was WRONG — the cause was
the baseline comparison. Noted in the docstring so the next reader is not
misdirected the same way.
2026-09-01 15:33:04 -06:00
Drew T 8f171c6ce6 feat(tools): verify_binary + twin_rescan — put S69's two habits in the tooling, not in prose
Both rules were already written down (§384, §397) and both were violated anyway,
which is the argument for a tool: a habit you must remember at the moment you are
impatient is not a control.

tools/verify_binary.py — ALWAYS re-extracts before building, because a carve
rewrites splat inputs and a build over stale extract state produces a meaningless
SHA. S69 read three binaries as red on build-only checks; all three were
BYTE-IDENTICAL after extract+build, and two false reds cost legitimate work that
had to be restored (a 96-line match, and 23 declaration edits). --all-touched
sweeps everything with uncommitted src/ or config/ changes.

tools/twin_rescan.py — the twin oracle answers "is there a BANKED body like
this?", so an OPEN-OPEN cluster correctly reports "no banked twin" for every
member and that verdict is stale the instant one banks. Diffs the scan against
the previous snapshot so it reports what JUST became free, not the whole board,
with the ready-to-run family_remap command per row. Baseline: 318 open stubs, 37
already carry a banked twin at d<=5.

Memories added: rescan-twins-after-every-bank, check-against-a-known-true-case.
2026-09-01 14:50:26 -06:00
Drew T 860f866f6e docs: §322b/§332b/§378c + accelerator #19 + SETUP — the Fable-3 blocked-pile audit
§322b — the carve class is COMPLETABLE, and every worktree CARVE-REFUSED was an
instrument verdict (.run/sig.<b>.jsonl is gitignored, absent from worktrees, so
jr_inventory read every carve as UNOWNED). build_carve's refusal is EXACT, not
conservative — one object emits one contiguous .rodata — and the real fix
(isolate into its own subseg) already exists and harvest_verify already runs it.
Live census: 71 non-contiguous of 123 jtbl stubs; 21 of those are twins of
already-banked bodies (3,852 ins) free at ~25s each. End-to-end byte-proven in
23 seconds. Remaining blockers are 18 overlay_src_split plumbing defects (<=30
lines each) plus a jr_isolate_all port for main.

§332b — the §332 "walls" are a per-OBJECT assembler mode, not a C limit. A 3-line
maspsx reorder-passthrough + as -O2 is byte-INERT across the whole 800c3/800c2
objects and yields 0 diffs for SIX walls whose drafts already exist. That turns
"permanently unbankable" into a per-object Makefile switch and retires
oracle_reorder.py. Only 13 of the 15 listed walls are even reachable.

§378c — a FIFTH decl-blocker variant: the DRAFT redeclares a type/data/callee the
TU or a header already owns. Fix the draft to the TU's spelling (§367), never the
reverse. Two "integration-blocked" rows were phantoms, one of them my own
--any-proto pre-pass breaking a sibling TU (variant 4, second bite).

accelerators #19 — a verdict recorded inside an isolated environment describes the
ENVIRONMENT. Isolation exists so the worker sees less; every gitignored input is a
difference it cannot distinguish from a genuine rejection, and it writes that
difference down once per function. Negative-control the environment with a
known-good item; assert the worker's inputs; report a missing input as MISSING,
never as a verdict.
2026-09-01 14:12:59 -06:00
Drew T f11bf13b4f feat(seed_ref): the CONTAINED tier + docs for the twin ladder (§390/§391, accelerator #18)
tools/seed_ref.py gains --contained/--contained-control: an open stub that is a
banked body plus or minus WHOLE BLOCKS — the class edit distance ranks badly.
Branch-offset masking was required (unmasked offsets veto exactly the target
pairs) and a min-side-25 floor (89% of raw hits were prologue/epilogue vacuity).
Ranks by (substitutions+regions, cover), not by d. Controls: planted-deletion
positive 60/60, random-pair base rate 0/397, R32 population 346/346, and a
post-refactor --near regression reproducing the stored slice exactly.

Banked on first use: ov_SC01_077/func_80184D50 = banked ov_SC03_007/func_8018283C
minus its trailing `&= 0x7FFFFFFF;` — MATCH, closeness 0, 98/98.

* cookbook §390: minimum distance is not minimum work (rank by effort; a deletion
  is free, a substitution is thought), the lookalike filter r = d/min(nins) ~ 0.3
  (17 of 30 "cousins" were boilerplate coincidence), and the three fleet-wide
  nulls that close the scanner question — 0 new / 9 / 2. Spend integration
  effort, not scanner effort.
* cookbook §391: a byte-aligned struct copies in FOUR instructions (lwl/lwr/swl/
  swr), a word-aligned one in TWO. Never invent an aggregate type to make a draft
  compile — an invented word-aligned Blk8 lost exactly 8 ins across two copies and
  read as a believable "near, closeness 70" codegen residual.
* accelerators #18: a claim derived from BYTES is not a claim verified by a
  COMPILER. Every similarity/correctness claim must name the tier it reached
  (stream containment / compiled standalone / whole-binary gate / clean fleet);
  a report that says "verified" without one invites the strongest reading.
  Non-reproduction is a finding — say so rather than assuming your own setup.
* playbook §2a-2: the twin ladder (exact -> RELOC-ONLY -> CONTAINED -> cousin ->
  cold), take the cheapest tier available, widen only when the tier above is empty.
* SETUP inventory row; generic-decomp-package: rank by work, and stop building
  scanners once the well is dry.
2026-09-01 13:06:27 -06:00
Drew T 881dd5a37c docs: the NEAR-TWIN BAND — §389, accelerator #17, SETUP, playbook §2a, generic-decomp-package
The exact-hash twin tier found 22 of 352 reachable open stubs (6%). The
edit-distance band added by `seed_ref --near` finds 75 of 352 (21%) — 3.4x — on a
corpus we believed fully mined. 31 of the new rows were PURE reloc-only twins of
already-banked bodies; 8 banked the same day at ~0 agent tokens, one 94-ins
exemplar serving five open copies.

* cookbook §389: the h_norm hole (norm_stream drops its pending lui-hi on an
  intervening R-type, so indexed-global reloc twins hash differently and vanish
  from seed_ref/twin_sweep/dedup/family-maps at once). Do NOT fix h_norm — every
  stored calibration keys on it; the near tier reads through it.
* accelerators #17: the generalisable law. A similarity hash built for DEDUP
  under-matches by design, which is correct for dedup and silently lossy as a
  FRONTIER join — the two questions want opposite error directions, and the
  frontier failure looks exactly like "this function is unique".
* generic-decomp-package §2b: build the near band at the same time as the exact
  tier, with the three verifications. It pays from the first bank for a new
  project, where we paid a session to recover the debt.
* SETUP inventory row + playbook §2a (run it before believing any "no twin"
  verdict; never send a RELOC-ONLY row to a drafting agent).
2026-09-01 12:20:59 -06:00
Drew T 9fc27960e9 feat(integration): teach the rest of the toolkit about §378 (self-caller cast)
The lever existed but nothing downstream applied it. Proof it mattered: a wave
agent this session diagnosed its own blocker as "§378 THE SELF-CALLER CAST, a
TU-level fix (cast_self_callers.py) that requires editing src/, which I'm not
permitted to touch" — the knowledge propagated, the automation did not.

* recover_integration.py: NEW "self-cast" stage (tier=binary), so the driver can
  run the whole chain as --stages arity,self-cast. The docstring states WHY the
  order is not arbitrary: self-cast answers the error that "arity" CREATES.
* residual_rules_b.py: both decl-conflict tiers now prescribe the full chain
  instead of "route to integration / budget for banking", and
  NOCOMPILE-UNDECLARED-FIXED now says outright NOT to gate the autodecl arm (it
  is a second conflicting declaration in the real TU).
* wave-playbook §4b: replaced the stale two-step recipe with the three-step
  chain, the one-driver form, the callee variant, and the MANDATORY
  --undo-journal.
* SETUP.md: full inventory row (R21) — it had zero mentions.

Not wired, deliberately: gate_stage's ladder rewrites DRAFTS via _xform, while
this edits the TU; a src-side edit inside the automatic gate needs
revert-on-failure, which recover_integration already owns.

Still open: a draft_prechecks rule to catch the self-decl conflict statically,
before a build is spent. The new stage's plumbing is verified (CLI + candidate
selection); its functional end-to-end run is NOT — gate12 held the tree.
2026-09-01 11:03:06 -06:00
Drew T 1dd15eda32 feat(triage): the triage ladder — built, wired, and acceptance-green
tools/triage_ladder.py — the zero-token pre-agent pass, split PRE (target-side:
BANKED/WALL-332/PARKED, no build) from POST (residual_rules_b, needs a draft).
--escalate refuses a walled or banked target; --acceptance is the R39/R32 harness.
Refuses on a non-quiescent tree: a merging gate makes the stub oracle wrong in
both directions (measured, ov_SC01_004:func_8017EB30).

Acceptance, on the whole corpus: false-skip 0/1367 open stubs, recall 426/426
matched, wall tier fires on exactly the 10 enumerated walls (0 extra, 0 missing).
The first wall control asked for evidence that CANNOT exist — it scanned banked
functions' .s, which splat never writes — and printed '0 scanned / 0 tripped',
indistinguishable from a pass. The R32 empty-denominator assertion caught it on
its first run; replaced with a two-sided sweep over all open stubs.

tools/cast_self_callers.py — the §378 lever + --sync-decls for the narrow-param
case C89 forbids no-proto from reaching (§378a).

Wiring: wave_args drops walled/parked targets at draw time via pre_classify (one
implementation, R33); escalate_fable.js refuses any target without triage:'DRAFT'.

Tool fixes found by measurement:
* fix_arity_callers was blind to main entirely (globbed src/main/main*.c; main is
  src/*.c) — reported success over an empty file set through three gates. Now
  refuses when --binary selects no files.
* parallel_gate records each worker's 'failed by class' line (was truncated out of
  the 200-char tail); gater_lane retries in-tree ONLY on the diagnostic-free
  blind-worktree signature — S69 ran 22 serial retries against real cc1 errors.

docs: cookbook §376/§377/§378 (index 1033), SETUP.md, wave-playbook §4b.
2026-08-31 23:53:50 -06:00
Drew T 5c317dbe5f docs: wire the S68 tools into SETUP.md (R21) and the playbook (when to use them)
A tool nobody knows about is invisible work. Audit found neighbor_ref (built an
hour ago), residual_rules, lane_inflight and r22_verify in NEITHER doc, and
wall_sweep in the playbook but not the inventory.

SETUP.md gains a tooling-inventory row for all five with what each is FOR.

wave-playbook gains §2b: run neighbor_ref for EVERY card, placed right after the
seed_ref step because it answers the weaker and far more common question ('which
matched function should this agent READ?') that seed_ref structurally cannot. It
carries the measurement that justifies it -- a ~20x token swing on that single
variable -- and the failure it prevents: func_8017BEBC's card said 'no banked twin'
while a matched 755-instruction near-twin sat 3,700 lines up IN ITS OWN FILE.

Also states the two honest limits: an opt-level mismatch is PENALISED not merely
ranked low (§116 -- an -O2 example misleads an -O0 target), and a neighbour is a
worked example to READ, never a body to copy (§168 law 1, cousin-remap 0/26).
2026-08-31 22:25:43 -06:00
Drew T 54c0624e49 docs: §371 the module-binary -O0 carve route + the spimdisasm rodata trap; SETUP.md S68 tooling rows (R21)
§371 ★★ carving a SINGLE-OBJECT module binary. One 'unaddressable content'
message was THREE stacked causes (interior-YAML-comment symbol-list truncation, a
trailing verbatim-asm chunk with no region, bare tag forward decls) -- fix one and
the message does not change, which is why it read as an impassable wall.

Then the reusable part: spimdisasm migrates single-referenced rodata into a
function's .s ONLY within the same subseg, so a carve that moves the function
silently DROPS it, and INCLUDE_RODATA cannot bring it back (splat marks it migrated
segment-wide and emits nothing). Rename the .rodata subseg to the object its
emitters moved to; the regenerated .s coming back byte-identical is the proof.

Also recorded: the Makefile -O0 glob hunk is PART of the carve, not a follow-up;
interleave_check's DRIFT on md_MAIN_003 is PRE-EXISTING and must not be 'fixed';
the still-open second-carve refusal (UNOWNED rodata 0x800cedf8); and the §126 plan
for the remaining 8 -O0 stubs (three are ADJACENT so one region covers them).

SETUP.md (R21): three tooling-inventory rows covering gater_lane/escalate_fable/
o0_boundary, the six overlay-layout fixes, and the module-binary carve route.
2026-08-31 18:34:38 -06:00
Drew T 5bcb322283 docs: gating is fully parallel — no serial lane; jtbl unlocked via isolate_asm; launch detached with setsid 2026-08-31 14:55:39 -06:00
Drew T 00812fc62f docs(p31 s67): wave-playbook (the CURRENT pipeline) + seed_ref fix + harvest §333-§338
THE DOC GAP, and it cost tokens this session. `docs/automation-runbook.md` was titled "the
autonomous campaign, as it actually runs" while documenting the RETIRED OpenRouter/ox-alpha system
whose lanes are all deliberately DEAD. The current Claude-wave pipeline existed only as two dense
tooling-inventory rows in SETUP.md — reference, not procedure. Three of this session's costliest
mistakes were procedural and a playbook prevents each:
  * hand-typed a refill target -> invented func_80184F60 (2nd instruction of a matched function), 58k
  * hand-rolled a serial gate loop when parallel_gate existed -> ~1h for what took 103s
  * re-derived a function banked verbatim in ~20 overlays -> 102k

NEW docs/wave-playbook.md — start to finish, each guard paired with the MEASUREMENT that produced it
(that pairing is the part a generic decomp guide cannot have, and the seed of the future template).
automation-runbook.md retitled HISTORICAL with a pointer; SETUP.md §6.9 links the playbook.

NEW tools/seed_ref.py — the cross-TU banked twin, joined on corpus signature hashes (no atlas knn,
~2s fleet-wide), wired into t5_cards.py. FLEET: 87 open stubs have a banked twin; 41 of them sit in
twin_sweep's refusal ledger, invisible to BOTH tools at once. Documents twin_sweep's two holes:
load_sigs covers 141/213 binaries (main, resident, all md_MAIN_* absent), and one curated symbol
name silently disables an entire binary via a bare `except Exception: pass`.
Schema note: seed_ref's binary/fn are the EXEMPLAR's, because api_agent greps src/{binary} for {fn};
naming them after the target would send every agent grepping for itself — caught pre-ship.

HARVEST §333-§338 from the s67o2_1/pool_1 waves:
 §333 frame size is set by DECLARED aggregates, not used ones — an unreferenced trailing local is a
      dial (3 instances; one worth 30 of 32 residual rows)
 §334 a reload spill slot rounds to BIGGEST_ALIGNMENT for align AND size: one 4-byte pseudo grew a
      frame by 16 (82->53)
 §335 `extern u16 A[]` at a variable subscript allocates ~8B/access of dead stack temps that inflate
      the frame with ZERO extra instructions — invisible in a body diff (141->20)
 §336 the §5a barrier goes at the BOTTOM of the twin; find_cross_jump walks BACKWARD
 §337 the CC1-ONLY blocker class: blocker_probe's static oracle says "none" and cc1 still fails
 §338 _sltiu_bounds misreads a non-switch sltiu as a bounds check, over-spanning the table

gate_wave.py now STREAMS both lanes (R55) — it captured output and printed at the end, leaving a
zero-byte log indistinguishable from a hang.
2026-08-31 13:48:56 -06:00
Drew T 41b259e528 fix(wave): cards keyed by (binary,fn) — t5a received 48/48 ANOTHER binary's card (R48); t5_cards.py builds the target's OWN fuel (tu_ref 88% / decl_prior 98%) via wave_card_fuel.py extracted from build_wave_atlas (R33); t5_bank.sh recovers un-banked drafts via fix_tu_ret_decls (2/2 on t5a); distill workflow generalized (P31 S63 T5) 2026-08-26 20:19:41 -06:00
Drew T f8cc4c77d3 feat(tools): T5 wave draw/bank halves — t5_targets.py (routed, ledgered, band-stratified draw) + t5_bank.sh (judge → R22 sweep → commit); claude_wave_draft.js generalized; packs/judge refuse duplicate fn names (R43/R48); sweep #8 213/213 (P31 S63 T5.1) 2026-08-26 19:35:54 -06:00
Drew T 74ec441d60 feat(tools): the Claude-subagent wave harness promoted from the T4 probe — claude_wave_packs.py (identical api_agent packs), workflows/claude_wave_draft.js + claude_wave_distill.js (the 60-agent draft and the R16 distill scripts), wave_judge.py (per-arm whole-binary gate with git-diff bank detection, tree reset, --union); SETUP row (P31 S62) 2026-08-26 19:01:41 -06:00
Drew T c71cd7edb5 docs: cookbook §303 (module island pads derived at build time; island-pads/island-blocked dissolve) + SETUP row (P31 S62 T3a) 2026-08-26 14:48:42 -06:00
Drew T d718cecf9d docs(phase-31): S62 log — clean fleet sweep found 6 hidden failures (5 healed, md_SC07_003 re-measured); R60 candidate (carve-state files); SETUP: interleave_check --fix 2026-08-26 14:35:06 -06:00
Drew T ab9e5770c7 feat(tools)+docs: T2 knowledge — interleave_check.py + pads_audit.py promoted (derive carve specs from the bytes; SETUP rows), cookbook §302 (the three carve-state invariants; five reds classified), frontier-s61 Addendum 3 (red list 5 -> 0, 39/40 held banks) (P31 S62) 2026-08-26 14:29:35 -06:00
Drew T 3a5caa65b1 feat(tools)+docs(phase-31): T1 — resolver declfix wiring; masked_diff/rtu_match compare internal-j targets (jrel; §195 premise refuted, cookbook §301); diff_autopsy.sh + stub_invariant_audit.py promoted; frontier-s61 Addendum 2 (step-1 falsifier fired: 54 carve-refused, 2 DIFF autopsied+banked, 1 plumbing); S62 log 2026-08-26 14:04:54 -06:00
Drew T 0488903e57 feat(tools): decl_from_use — negative-controlled extern inference for the no-seed-decl class (Fable agent build, verified); SETUP.md rows for it + the baseline-red gate input (R21)
42-case first run: 39 were one uncompilable TU (ov_SC04_018_jr_8017AE2C.c), not draft defects —
the resolver's 'undeclared' classification needs a TU-alone compile probe first (open follow-up).
2026-08-26 00:47:38 -06:00
Drew T 433c7cd8ef feat(resolver): the integration-resolver lane — zero-token re-judging of the ledgers' shape-correct stock at the real TU (P31 S61 T10+/S61-1)
frontier-analysis-s60 §4 measured that ~571 open functions had FINISHED drafting (closeness-0 backlog
rows / reloc shape-MATCH rejects) and were being re-drafted wave after wave. tools/integration_resolver.py
treats those ledgers as an index: still-open? -> rtu_match at the real split TU (CC1: the gate ladder's
draft-side transforms, one retry) -> reloc_identity as the disagreeing oracle (rtu masks reloc fields)
-> aprop_symfix on MISMATCH/shape-MATCH -> stage -> sweep_parallel (whole-binary SHA, sole arbiter)
-> commit at once (R42). Refuses main by name (gate_main owns it), //@EDIT drafts, dirty trees, collapsed
registries; every drop is counted (R32); a negative control over recently-banked functions must pass
N/N before any verdict is trusted (R35/R39 — its first form picked carve moves as banks, 9/12 FAIL,
and was fixed before a single stock verdict was read). Ledger .run/resolver/verdicts.jsonl keyed by
(binary, fn, draft-sha, split-TU-sha) so unchanged rejects are never re-judged.

First pass (commit:2991): 1,352 nominated -> 901 already banked, 27 main -> 424 judged in 41 s ->
245 staged (57.8%; 242 raw, 3 via transforms) -> 63 banked (net INCLUDE_ASM delta; that commit's
subject says 72 = gross incl. 9 carve moves), 182 gate-refused, zero model tokens, ~10 min total.
Lane wrapper tools/lanes/resolver_lane.sh (holds .run/auto/draw.lock for judge+gate: rtu reads the
TUs a gate splices into).
2026-08-25 23:50:26 -06:00
Drew T 6f5eb90844 docs(ops): correct the truncation numbers — the denominator was wrong
I reported 240 of 244 turn-finishes truncated in wave bk and called it ~100%. That
count came from grepping lines containing 'finish=', which api_agent only prints when
a turn ends WITHOUT a tool call — so the denominator was not all turns, it was all
anomalous turns. I compared a subset against itself.

Counting every turn:
  bk (8k / 420s):   240 truncated of 3,222 turns = 7.4%
  bt (16k / 700s):   16 truncated of 1,210 turns = 1.3%

Still a ~6x improvement and the change stands on its own evidence, but it is a tax
reduction, not the collapse I described. Recorded alongside: a truncated turn is one
turn of 24, not a lost agent — the logs show the agent emitting its tool call on the
very next turn.

Both docs keep the wrong figure explicitly, with why it was wrong, so the next reader
does not re-derive it from the same grep.
2026-08-24 17:11:46 -06:00
Drew T b6ba6ab7df docs(ops): SETUP + runbook carry the probed campaign constants
SETUP gains rows for recover_rejects.py (free recovery of the 45% of drafts that never
reach the gate, 13% of which are a deterministic symbol rebase),
restart_main_lane_when_idle.sh, and a campaign-constants row recording MAXTOK 16000 /
HTTP_TIMEOUT 700 with the measurements behind them: reasoning_tokens=0 so the output
cap was the reasoning cap, 240 of 244 turn-finishes truncated at 8k, an uncapped hard
prompt wanting 8,067 tokens, ~30 tok/s, and the 1M-context / 131,072-max-completion
model ceiling that makes 16k our choice rather than a limit.

The runbook gains the same table plus the ordering rule (generation < HTTP_TIMEOUT <
stallguard's 1200s kill), the evidence that turn caps are NOT binding on the default
lane, one-lane-one-band with the size table that retired the 120-2000 slot, the
maintenance lane's new recovery job, and the main-lane restart helper.
2026-08-24 16:44:36 -06:00
Drew T df5c1a1b15 docs(ops): rewrite the automation runbook for the campaign as it actually runs
The runbook was dated 2026-06-22 and described the reach-1 grinder pivot — it named
no lane that exists today, two months and an entire toolchain later. Rewritten around
what is actually running: the six lanes and their restart rules, the OpenRouter
drafting toolchain (cards, LEVER_CRIB, per-lane budgets, the draw's admit/refuse
census, quotas as floor AND ceiling), the banking toolchain by binary class
(sweep_parallel vs gate_main vs the gate-time jtbl carve vs -O0 objects), the main
lane, the distill lane and the flywheel's measured yield, the rate/credit numbers with
their denominators, and recovery.

Two ops laws are stated where they will be read rather than rediscovered: bash parses
a while-loop up front (so code, args and draw-defaults each take effect differently),
and never pkill -f a lane by a bare name because it matches the harness's own wrapper.

SETUP.md gains rows for main_lane, the distill lane and the three restart helpers.
2026-08-24 14:50:09 -06:00
Drew T 9e44612c2a feat(jtbl): the carve→draft→bank pipeline is automated at the gate — byte-proven ×3, one per class (S59)
The carve stays INSIDE the byte-gate (harvest_verify._jtbl_prep_one, the §61b-proven
order); everything new routes work to it:

* jtbl_carve: island_probe (read-only classifier: tail/covered/island-end/island-blocked/
  island-pads/main-manual), --island-split (the one-line §260 insert, end-adjacent only),
  and apply() now recognizes a completed island split as a no-op success instead of the
  historical refusal.
* harvest_verify: _ISLAND_WALLS branch — on the §154-A refusal the gate isolates (body
  still spliced), inserts the split line, re-extracts, re-carves. Snapshot-restore covers it.
* jtbl_lane.py (new): probe → draft (--draft-dir or api_agent) → gate via the exact
  sweep_parallel worker call, HOLDING the campaign draw lock across gate+commit → commit
  named per-binary paths. One jtbl target per gate invocation (§61c).
* build_wave_atlas: probe filter + one-jtbl-card-per-binary cap (inert unless
  --levers jtbl-carve). idiom_serial: refuses the jtbl-carve lever (R43; its pre-carve ran
  the refuted order — S58: 8 attempts, 0 banks).

Proven end to end with the live campaign running (banks committed separately):
  commit:2661  ov_SC03_014/func_8017DCC0  tail: §8b adjacent merge + §8e pad recovery,
             jr_8017AE2C.o .rodata 0x14→0x28 TIGHT, sha d84b01a2 green
  commit:2663  md_SC03_076/func_801F218C  covered: §260 STAGE 2 — sha 9a165e36 identical
             with the table COMPILER-EMITTED; the first md_* jr bank ever
  commit:2664  md_SC03_135/func_801E5358  island-end: the FULL split done BY THE GATE on a
             virgin module (~1.1s, R40-checked), sha b901fda5, md.o 0x27c→0x268 + jr.o 0x14

Census (245/245 members probed, R32): 181 members / 26,445 ins reachable unattended;
main 47 parked (gate_main cadence), island-blocked 10 (stack order), island-pads 6
(needs §8e pads for modules.mk), no-jtbl 1 (atlas mislabel).
Design + failure semantics + campaign hook: docs/tool-designs/jtbl-automation-s59.md;
cookbook §260-A.
2026-08-24 12:31:14 -06:00
Drew T e9ac4db9ba fix(phase-31): wire the S56 findings into the tools, SETUP.md and memory
Audit after "did you update tooling for those findings" found three gaps
beyond the four committed fixes:

  - R21: docs/SETUP.md had no record of any of it. Adds the "four flow traps"
    table — each trap, its measured cost, and where it is now caught.
  - family_sweep --only had no coverage assertion, so the 3-vs-50 mis-scope
    could recur silently. It now resolves member addrs to their family, always
    prints the coverage line, and refuses when it resolves to zero families.
  - pregate_check modelled the banking driver's typedef strip but never checked
    the consequence. Adds [DROP-RISK] §203 USE-BEFORE-TYPEDEF. R39 NC: flags
    the known-bad splice, 0 false positives on the post-hoist file and all 7
    other wave-Z TUs (first draft read a typedef named in its own comment).

Also corrects §203 and the phase log: the text that banked was the RENAMED
variant, because gate_stage's backlog.save_draft() overwrote the original on
the failed attempt. Bytes are correct (R22 213/213); the claim "draft
byte-unchanged" was not.
2026-08-18 15:32:32 -06:00
Drew T 7ba1ae96d4 chore(phase-31): wire gap_triage into the harvest prompt, SETUP.md and the wave-closing memory
Caught by Drew: the tool was committed and nothing called it — not the harvest prompt, not the
memory, not SETUP.md. A tool nobody invokes is the same defect this session keeps finding in the
CARDS (§193-A, §194-E, §196: the answer was already computed and nothing carried it), wearing
tooling clothes.

Now: step 0 of the harvest reader prompt runs it; docs/SETUP.md carries a row with the measured
threshold rationale (R21); the wave-closing memory names it in step 6. All three restate the same
caveat because it is the part that gets forgotten — a candidate is a STARTING POINT, and an empty
candidate list is NOT evidence of novelty, since dumb term overlap misses paraphrase entirely.
2026-08-18 11:57:42 -06:00
Drew T e18738c48e feat(phase-31 T5): THE FRONTIER ATLAS — 5,139 lever-labeled crack groups over all 12,058 open fns
- tools/atlas.py: cousin units baseline + T1.5 h_seqn merges + CALIBRATED warm
  tier (measured: li-norm metric holds ~99% recall to 0.55; rule = smallest t
  with neg-accept<=0.2% AND recall>=95% -> THRESH_WARM=0.70 @ 99.1%/0.18% —
  false merges waste exemplar cracks, misses only route cheaper) + seed sweep
  (65% of open skeletons carry a >=0.55 matched seed) + kNN graph + tiny-direct
  + evidence joins (audit/backlog/ledgers/cards; unparsable=fatal) + lever
  labels with confidence measured>ledger>tell>default>UNKNOWN
- partition ASSERTED: 12,058 = progress stubs 12,051 + NM 7 EXACTLY (chased the
  +1: data blobs now excluded, reconciled against classify() buckets; T1 banks
  confirmed absent); every instance in exactly one group; main joins at the
  atlas layer only (family maps stay non-main — 4 silent-skip hazards)
- warm tier merged 1,019; top group unifies 268 drifted per-location skeletons
- lever table: head-crack 186.9k ins / UNKNOWN 138.6k (honest) / extend-tell
  76.7k / redraft 46.8k / jtbl-carve 45.7k / integration 23.4k / seeded 23.4k /
  len-vein 16.8k / swaprepeat 9.2k / plumbing 8.1k / o0 6.6k / cc1 6.4k
- atlas_features: li_norm_toks exported (shared with atlas, R33; hash-stable);
  mid_jr verifier fixed (compared ZERO rows — R32 silent no-op; now 6,444/6,444)
- make atlas = full regen chain (~10-15 min, zero tokens); --targets emits
  crack slates (12/12 .s resolved); survey 92 s
- SETUP rows (R21); docs/frontier-atlas.md committed
2026-08-14 18:06:46 -06:00
Drew T 22eed7d78a feat(phase-31 T3): main enablement — sig-main at splat-true lengths + main streams
- sig_image: --seeds accepts '0xADDR NINS' (and jsonl nins); a seeded nins is
  authoritative ([addr, addr+4*nins), bypasses func_end whose heuristic mis-sliced
  3/40 main samples); R32 guard on seeded end > hi
- corpus: s_ins_count() factored from audit() (R33, one counter) + '--seed-ends'
  CLI emitting per-stub splat-true lengths
- make sig-main: 2,002 main stubs signed -> .run/sig.main.jsonl; FULL word
  cross-check 2,002/2,002 EXE slices == .s words (0 SLICE-SUSPECT; .s word field
  is byte-order hex, not LE — first checker draft misread 1,999 false suspects).
  Deliberately splat-SEEDED; main's independent second oracle stays deferred
  (second-oracle.md; sig_is_independent(main) stays False)
- family_remap: vram_of/img_path special-case 'main' derived from splat.us.exe.yaml
  (file0-vram = code-seg vram - start = 0x8000F800; target_path); stream_words
  ('main') verified 25/25 vs .s
- regression: sig-resident re-run byte-identical after the shared read_seeds change
- SETUP §6.3 rows (R21)
2026-08-14 17:48:51 -06:00
Drew T fa425f89a5 feat(phase-31 T2): reference expansion — PsyQ SDK sample source extracted + gcc-2.7.2 completed
- PsyQ 4.0 dev-CD Track-1 walked with the frozen iso9660.py (R33): 2,374 files /
  231.6 MB -> tools/reference/psyq-sdk/ (gitignored); 400 C sources incl. the full
  PSX/SAMPLE tree (373) — drafting-agent style priors, esp. for main
- provenance find: GNU/SNGNUVER.TXT = SN's gcc build history (2.7.2.SN32.3.7.0002)
  naming SN's exact patches vs vanilla; only UNROLL.C codegen-relevant
- gcc-2.7.2 reference +6 files (calls.c + caller-save.c — §172-cited, previously
  missing — + integrate/optabs/varasm/recog), tarball sha256 recorded
- docs/psyq-sample-idioms.md seeded; SETUP §5.6 rows (R21)
2026-08-14 17:44:32 -06:00
Drew T e903713b71 feat(phase-30 S50): the mechanical A-prop draft — 256 members banked with no agent in the loop
Final S50 state: 307 instances banked, stubs 12,468 -> 12,161, fleet 95.3% instr / 90.0%
distinct / 96.65% fn-count. R22 clean rebuild 4x, check-all 213/213 every time.

- tools/aprop_autodraft.py + tools/draft_prechecks.py: seed body + symbol_map + a MINIMAL
  synthesized preamble. The seed's decl layer never travels — that layer is family_sweep's
  dominant failure (331 of 458 S49 verdicts). 256 banked at zero agent tokens, against the
  ~20M the same work would have cost as a wave.
- Macro seeds (567 of 1196 members, all 3737 de-macroize) take the DEFINITION only; the block
  stays the decl source. Pasting it whole measured 28% vs inline's 68% — func_8016AB6C's macro
  is 1,891 lines of which 108 are the function.
- IMM is a second engine, not a wall: T2a's imm_map_tier1 resolves a per-location LITERAL like
  symbol_map resolves a per-location SYMBOL. 131 of 275 IMM members resolve.
- draft_prechecks negative-controlled against ALL 205 banked drafts: zero false positives,
  catches 39 of 67 known failures. That control found two bugs in the checks themselves —
  C89 `f()` declares UNSPECIFIED parameters (not zero), and a member's own definition read as
  a call to itself. Conservative by design: a pre-check that discards good drafts is worse
  than one that lets a few builds fail.
- The A-prop pool is now priced exactly: PURE 437/37,376 ins, IMM 275/8,849, STRUCT 238/4,259.
- Cookbook §171a; SETUP rows; CURRENT_PHASE S50 FINAL checkpoint.
2026-08-14 07:42:19 -06:00
Drew T 2f7e06564b feat(phase-30 S50): the stale seed symbol — A-prop's 57% was ours, not the lane's (§171)
- REFUTES §170's open hypothesis (batched cards concentrate members into one TU ⇒ §169
  collision): 5-draft groups banked 5/5; 11 of 35 unbanked drafts were already one-per-TU;
  and the two "concentrated" groups banked 12/12 and 10/10 once the real defect was fixed.
- The cause: a per-location data symbol carried out of the seed body unrebased. match_one
  compares instruction ENCODINGS and is blind to a relocation's target NAME, so it scores
  MATCH standalone and dies at link in the host TU. 24 of 24 concentrated failures, all 1:1
  rewritable at one constant vram delta (0x4128).
- tools/aprop_symfix.py: audit + --fix, emits a gate_lane-shaped slate; deterministic and
  build-free, so it runs BEFORE the gate. The R34 second oracle for the class match_one
  cannot see.
- family_cousins.py --aprop-cards: members now carry sym_map, the explicit {seed -> member}
  renames, read from the seed's C BODY (a matched seed has no .s of its own) vs the member's
  .s. Two case-mismatch defects fixed while wiring it (sig lowercase vs splat uppercase).
- 23/24 banked. Stubs 12,468 -> 12,445. Fleet 95.2% instr / 89.9% distinct / 96.57% fn.
  R22 clean rebuild: check-all 213 passed, 0 failed of 213. dedup 2,043/0.
- A-prop's true conversion is 87% (79/91); the 320 batched members are unblocked.
- Cookbook §171 + §170 struck in place; SETUP row; decision-log (R31).
2026-08-13 23:41:36 -06:00
Drew T b3713cc3ca feat(phase-30 S49): the cousin tier — family_cousins.py similarity map + seeded wave-7 slate (§168)
- FINDING (Drew's smell, byte-verified): the '4,513 unique singletons' picture is substantially
  an h_seq exact-hash artifact — 86/120 near-pairs in the 0.85-0.99 band differ by PURE
  insertion/deletion (li-expansion tell in 25). Specimen: ov_SC06_010:0x8017bebc (753 ins,
  'singleton') is 0.987-similar to a MATCHED fn in the same binary.
- NEW tools/family_cousins.py: distinct open skeletons -> shingle index -> >=0.85 union-find ->
  matched-seed attachment -> .run/family_cousins.json + docs/family-cousins.md. R32 BOTH ways
  (independent stub recount fails loud on a stale map — negative-control-proven; partition
  assert). Reproduced the probe within +-1%; totals EXACT (11,627 inst / 584,448 ins).
- Unit table: A-prop 197u/68,729ins · seeded 418u/50,422 · cousin-multi 1,552u/249,799 ·
  cold 3,240u/215,498 — the genuinely-unique tail is 37% of the remainder, not 90%.
  Main's 'structurally barren' HOLDS at the similarity tier (94% mass <0.70).
- --targets wave slate: .run/wave7_targets.json = 40 targets / 33,304 unit ins (+33% vs
  family-ranked), 9 resolved seed C paths, size-routed 2 haiku/20 sonnet/18 opus.
- LAWS (§168): a cousin is a SEEDED CRACK never a remap; rank waves by UNIT weight; discount
  short-fn similarity. Byte-gate stays the sole arbiter (G3/P9).
- docs/family-hseq.md: this session's frontier regen (post-S48 propagations) rides along.
- cookbook §168 + SETUP inventory row (R16/R21/R30); CURRENT_PHASE S49 entry.
2026-08-12 20:53:40 -06:00
Drew T 9351b17f48 feat(phase-30 S46-2): the master IDXTAB/DESTPTR load map — and the tracker blind spot that hid it
Drew's S45 idea, delivered fleet-wide + wired into the permanent references.

- THE BLOCKER WAS OUR INSTRUMENT (R35, the 3rd time): the S45 plan ("require a
  register-verified reference to the run's address") returns ZERO for both byte-proved
  tables. They are read by gcc's indexed global-array form —
      lui $at,0x8019 ; addu $at,$at,$a0 ; lh $v0,-0x2844($at)  -> 0x8018D7BC
  — where the address exists only as (lui imm, LOAD offset) with the index add between.
  find_addr_refs killed the lui register at the addu, so the halves never rejoined and
  the tables looked unreachable. Now it carries the hi half through the index add (still
  strictly register-tracked, never window-paired) and labels those hits `-indexed`.
- tools/idxtab_map.py (NEW): fleet-wide payload -> owning binary -> load address.
  Controls-gated (refuses to emit unless ov_SC01_000 0x8017EEC8/37 + *0x801A3234, and
  ov_SC03_001 0x8018D7BC/5 + *0x801EBC68 reproduce from the images alone). Index space
  DERIVED from the extracted tree (reproduces §S44's table independently). Process-pooled.
  Rejects all-zero and majority-zero runs (132 of the first pass's 452 "tables" were that).
- RESULT: 213 binaries -> 143 with a referenced table (294), 141 with a DESTPTR (141/141
  resolved from the binary's OWN image), 61 payloads. The two dominant tables are
  fleet-wide CONSTANTS (5-entry and 37-entry, identical in all 141 overlays); the
  per-binary variable is the destination (134 distinct).
- CORRECTION 1 (R14): §S45 p6's "the SC03 trio are owned by ov_SC03_001" is refuted —
  that 5-entry table is identical in ALL 141 overlays. The byte-observed parts stand.
- CORRECTION 2 (P9): this route CANNOT settle MAIN/7+9. They are absent from all 294
  tables — but so are MAIN/13/20/34/42/44, which are byte-proved to load. Absence here
  means "not on this route", nothing more. Recorded so it is not re-derived as a finding.
- Confidence is stated per-claim in docs/idxtab-map.md: proven (controls) / high (283
  fleet-wide-class tables) / low (3 named rare rows) / UNMEASURED (recall — no oracle
  for "all tables" exists beyond the 2 controls).
- Wired in permanently: docs/idxtab-map.md (the how/when/limits), memory-map.md §S46,
  cookbook §155c (the generalizable law: "no code references X" is a claim about your
  DECODER until it is shown to recognise the forms the compiler emits), SETUP.md
  tooling inventory (R21).
2026-08-07 22:09:05 -06:00
Drew T 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
2026-08-06 13:34:50 -06:00
Drew T 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.
2026-08-04 21:48:57 -06:00
Drew T 19766a6dc3 feat(phase-30): T0.5 — prefetch_fleet.py, the fleet Ghidra-C batch orchestrator (+ SETUP row, R21; fuel manifest ride-along)
One representative per remaining h_seq distinct class + all main/resident stubs -> .run/ghidra_c/.
Resumable (skips cached); serial on the exclusive project lock; auto-stops a serving MCP (R23);
imports missing overlay programs on demand via ghidra_import_raw.sh (blob derived via
family_remap.img_path, vram from the splat yaml — R33, never guessed); R32 per-program outcome
report, continues past failures. Dry-run: 126 programs / 7,966 uncached representatives.
2026-07-30 16:49:08 -06:00
Drew T f285de46e8 feat(phase-29): T53 — fold the T51 lever into the gate; kill gather_externs' false positive; close a revert gap
Items 1-2 off T52's list, plus a third defect found by T53's own testing. TOOLING ONLY — banks
nothing; metrics unchanged by design (85.7% instr / 76.4% distinct / 90.65% fn-count).

1. THE T51 PRE-PASS IS A jtbl_family_bank STAGE (cookbook §103, AUTOMATED)
   Order: raw -> scoped -> tu-scoped -> recovered -> reconciled. After the non-invasive stages (it
   edits the TU outside the spliced body); BEFORE the recovery stages deliberately — those bend the
   DRAFT and T48 measured both at +3 ins for this class, so they cannot succeed here. The stage
   re-runs scope_data_fix against the SCOPED TU rather than reusing the raw body: composition-correct,
   since the contested symbols no longer have a file-scope decl to be dropped against.
   COUNTERFACTUAL, byte-gated on a reproduced blocker (ov_SC01_000 restored to its pre-T51 TU):
     raw        -> compile error (conflicting types)
     scoped     -> compiles, FAILS the byte check (§8d drops the decl -> the u8 CSE costs +3)
     tu-scoped  -> BANKED
   That is the evidence the stage does the work — not T52's sweep, which ran on TUs T51 had already
   scoped by hand.
   Refactor note: a stage editing outside the spliced body must RE-FIND the splice point (the stub
   offset indexes the ORIGINAL TU). Each stage now carries its base; every pre-existing stage passes
   `orig`, where the re-search returns the identical span — same operation as before, by construction.

2. gather_externs' COMMENT-SCANNING FALSE POSITIVE — FIXED (cookbook §104)
   It scanned RAW text, so a symbol named only in the draft's PROSE counted as referenced: the
   func_80135D20 warning that fired on 137/137 and was right 0 times. Fix is a two-text discipline —
   MATCH on cdecl._mask'ed text, EMIT by span from the ORIGINAL (a masked decl is all blanks, so
   "just mask it" would splice whitespace). Same change closes a second, unobserved defect of the
   class: a COMMENTED-OUT extern could be selected as the carried decl and spliced in as live code.
   MEASURED as a no-op on output (R14): 20 (exemplar, sibling) draft pairs across 4 families, old vs
   new -> 20 identical / 0 differing. Only the false warning changed.

3. UNPLANNED — A REVERT THAT DID NOT SURVIVE AN EXCEPTION (cookbook §105)
   A wrong exemplar made remap_hseq raise AFTER the carve rewrote config/ and jr_isolate created a
   region file; the exception propagated out of bank(), the revert never ran, and the tree kept a
   rewritten carve config plus an UNTRACKED region file (git checkout -- src/ does not remove it).
   In a 132-member sweep that residue rides into the next member's build. bank() is now a
   revert-guaranteed wrapper around _bank(). Negative-control proven: the crashing invocation now
   reports {'exception': 2} and leaves git status -- config/ src/ at 0.
   "Revert on failure" != "revert on every exit"; the exits are success, gate-fail, refusal, and the throw.

GATES: R22 clean-fleet 140 passed, 0 failed of 140. tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED,
cdecl, audit-binaries, report/lint/dedup 1886/0). 0 NON_MATCHING (G4).

HONEST COVERAGE GAP: no live end-to-end BANK through the refactored loop — all three big families are
137/137 and the only family with live stubs (0x80191c50) has no banked exemplar, so it refuses. The
counterfactual byte-gated the exact splice on all three candidates and the 2-member run exercised
construction/refusal/revert/tally; the next real family sweep is the true end-to-end validation.

MY ERRORS: invoked the sweep with a wrong exemplar+address for a cross-address family (an unmeasured
guess about a members file I had not read — it is what surfaced defect 3); and deleted last_err's
initializer while refactoring, which would have raised NameError on the first clean gate-fail.

cookbook §103 (AUTOMATED) + §104 + §105; SETUP inventory row updated (R21).
2026-07-28 19:26:38 -06:00
Drew T a524142eb5 feat(phase-29): T51 — the fleet-wide decl-scoping tool; func_80135260's 132 siblings unblocked
Item 1 off the SESSION-23 list. T48 proved the lever by hand on the exemplar, T50 located the same
blocker in every sibling; this builds the tool, measures the population, applies it fleet-wide, and
gates it. It BANKS NOTHING — it removes the blocker. The sweep is the next task.

MEASURED BEFORE BUILDING (R35). The blocker census over all 132 still-stubbed siblings is perfectly
uniform: 132/132 carry it, 3 contested symbols each, EXACTLY ONE file-scope decl statement per
(TU, sym), ZERO file-scope references below the decl (so the deletion is always safe), 660
block-scope re-declarations needed.

THE TOOL: tools/scope_tu_externs.py — the TU-side complement of scope_data_externs.py (§8d). §8d
fixes the incoming DRAFT and has a give-up branch that DROPS the draft's own decl when the TU already
declares the symbol at file scope. Right when the types agree; fatal when the byte-true draft needs a
different one — which is exactly how 132 byte-true siblings gate-failed wearing a codegen wall's
costume. This moves the TU's OWN file-scope decl down into every later function that references the
symbol and lacks its own block-scope decl, then deletes the file-scope line.

  FILE(u8 D_x) ... then BLOCK(u16 *D_x) below it  ->  conflicting types    (the 132 failures)
  (no file-scope decl) ... BLOCK(u8) ... BLOCK(u16 *)  ->  builds; each fn owns its own view

- contested set DERIVED, never hand-listed (R33): the remapped draft's block-scope D_ externs
  intersected with the TU's file-scope decls above the splice point; --family does it per sibling
- built on cdecl.split_statements/_mask (R33), not a 7th regex: depth-0 spans (a fn definition
  flushes at its closing '}' — a column-0 test is NOT a file-scope test, m2c emits goto labels at
  column 0 inside bodies) + length-preserving comment/string masking. That masking is what kills the
  comment-scanning false-positive class still open as item 3.
- REFUSES LOUDLY, never skips silently (R32): >1 file-scope decl above the splice point; a
  file-scope statement below the decl referencing the symbol; an unlocatable body brace
- coverage asserted as a DELTA (R32): file-scope -1, block-scope +len(consumers). An absolute
  "a block-scope decl exists" check would have passed VACUOUSLY — these TUs already carry ~18
  legitimate block-scope decls of the same symbols

VERIFIED IN TWO STEPS (T48's structure — why a 132-file edit was safe to make):
  1. the move ALONE on ov_SC01_000 -> make build -> 9052dc0e BYTE-IDENTICAL, then reverted
  2. fleet-wide -> R22 clean-fleet (make clean && extract-all && check-all) -> 140 passed, 0 failed
     of 140; make tools-health OK (corpus 0 PHANTOM + 0 TRUNCATED, cdecl, audit-binaries,
     report/lint/dedup 1886/0). Metrics UNCHANGED at 85.5% instr / 76.1% distinct / 90.62% fn-count
     — the correct result for a declaration-only change.

The diff is uniform to the line: all 132 files +11/-3. A second --family run reports 132
nothing-to-do, 0 refused (idempotent).

Deliberately NOT done: wiring this as an automatic jtbl_family_bank stage. That waits until the
sweep measures the payoff — folding an unproven pre-pass into the gate is the same unmeasured
premise this phase keeps catching.

Also preserves .run/near6/g5260_a.c (the T48 raw crack body, allowlisted) — the sweep may need it
for --raw.

cookbook §103 + SETUP tool-inventory row (R21).
2026-07-28 16:44:46 -06:00
Drew T b8e7250c78 feat(phase-29): tools/symcheck.py — the pre-gate symbol-set guard (§67a)
Builds the guard SESSION-17 left as a TODO after the func_801463A0 `_s`-alias trap, where a draft
invented extern aliases no symbol table defines, read MATCH under rtu_match, and could never bank.

- diffs the symbols a draft's object references (reloc records) against the target .s's
  %hi/%lo/jal set; reports MISSING (invented-alias signature) and INVENTED separately.
- fills a real hole: match_one/masked_diff compare relocation-MASKED words (object-vs-.s is
  symbol-agnostic BY CONSTRUCTION) and rtu_match COMPILES WITHOUT LINKING -- so both are
  structurally blind to this class. R34: a second oracle that can disagree with the first.
- NEGATIVE-CONTROL PROVEN: with one data extern renamed to an invented alias, match_one reports
  the SAME 14 mismatched as the correct draft; symcheck exits 1 naming both symbols.
- --c compiles via match_one so the pinned triple/flags can never drift (R33); or --obj.
- applied to the live func_8014D820 close=14 draft: 12/12 symbols agree, so a match there will
  link cleanly -- the §65c class is ruled out for it in advance.
- cookbook §67a + SETUP tooling-inventory row (R21). Necessary condition, NOT a match oracle:
  still finish on the whole-binary byte-gate (G3/P9).
2026-07-24 21:30:08 -06:00
Drew T ffca2e4803 feat(phase-29): T16.10 — the driver's SUCCESS path verified (free re-bank test); 2 defects fixed
- THE FREE TEST (cookbook §66): reverted func_801778A8's bank to its INCLUDE_ASM stub (stub state
  rebuilds BYTE-IDENTICAL 7ca772be — a faithful revert proves itself; needs `make extract` first,
  the R22 corollary) and re-banked it THROUGH recover_integration.py --commit --r22.
  pass1 1/1 -> exact restore -> pass2 1/1 -> commit commit:0928 -> R22 140/140 -> report.json.
  Bank confirmed from SOURCE (stub gone), never the report (§55b trap 4). EQUIVALENCE: git diff vs
  the pre-revert commit = ONE blank line (mine) -> the driver reproduced SESSION-16's state exactly.
- DEFECT 1 (SAFETY, found by reading before firing): PROPAGATION is a fleet-tier write
  (dedup_propagate --auto-from -> src/shared/engine_core.h + up to 138 overlay .c) that was both
  UNDECLARED and the DEFAULT, so --max-tier binary still permitted the widest write in the toolchain.
  assert_write_set cannot catch it (it runs before the gate; under --commit git status is clean).
  FIXED up front: propagate now requires --max-tier fleet AND --r22, and is REFUSED after a
  demacroize stage (those banks are x1 by construction; --auto-from would re-macroize and undo them).
  Both refusals negative-control-tested, exit 1. The "standing hazard" is now a refusal.
- DEFECT 2 (METRIC): gate_stage scraped the fleet % via a progress.py label that no longer exists ->
  fp=None -> 50 gate commits recorded "fleet None%". Now reads FLEET instr-weighted (legacy fallback
  + loud stderr warning if neither matches); parses 79.6.
- STALE DIGEST (R14): docs/progress.fleet.md at HEAD disagreed with HEAD's own source by 45 in the
  dedup-shared column — generated during the §65g local_type trial whose edits were then reverted.
  Regenerated (reproduced identically in-gate + standalone); headline %s unaffected.
- cookbook §66/§66a/§66b distilled in-session (R30); SETUP.md gains the missing recover_integration
  row (R21 debt). tools-health OK: corpus 0/0, cdecl green, audit-binaries 140 citizens, lint OK,
  dedup-check 1879/0. Fleet unchanged 79.6% instr / 67.7% distinct / 88.86% fn-count.
2026-07-24 15:33:25 -06:00
Drew T f1f6238251 feat(phase-29): T16.6/T16.7 — 14th bank via the generalized de-macroize; §65 distilled; R21 inventory debt cleared
- func_8012F40C banked (the callee-conflict variant): relaxing demacroize from "the draft's own
  function" to "any decl the DRAFT declares incompatibly" reaches macros that declare a CALLEE
  differently than the draft does (RotTransPers/RotTransSV). 14 banks total, R22 140/140.
- THE ONE FAILURE, kept honest: func_8012F49C was rtu-MATCH but the whole-binary gate REJECTED it.
  rtu_match is relocation-masked, so a wrong call TARGET is invisible to it -- and this was a callee
  case, exactly where the mask hides the error. Trust rtu MATCH for self-decl corrections, distrust it
  for callee ones (§65c). Reverted its edits and re-banked only the winner rather than leave
  byte-neutral churn on matched code (§57a-4).
- DISTILLED IN-SESSION (R30/R16/R31/R21): cookbook §65 + §65a-§65e (blast-radius tiers; the
  de-macroize escape and the §20 refutation; the rtu-vs-gate divergence; the existing-ladder baseline;
  two-oracle practice); decision-log entry with the HONEST multiple (~2.3x, not the projected 3.7x,
  and it lands on distinct-code not the display number); calibration.md measured table; SETUP.md rows
  for blocker_probe + demacroize PLUS the three the inventory was missing (lift_types, uniquify_type,
  fix_header_decl-as-retired).
- Carried and NAMED, not dropped: 10 match_one-MATCH drafts still blocked by stacked classes, and the
  11 `near` drafts which are unfinished drafts, not recovery fuel.
2026-07-24 14:25:28 -06:00
Drew T b3ab5c2253 feat(phase-29 Task-13A): deterministic residual classifier — the permuter's problem is TARGETING
The autopsy (hindsight-study §7) assumed the permuter loses for want of a mutation.
Measured over the whole open backlog, it loses because it is aimed at work a
search-closer provably cannot close.

- NEW tools/residual_class.py: decide a near-miss's class FROM THE BYTES. Decodes each
  mismatching MIPS word -> (op-skeleton, register-fields, immediate); drift FIRST (one
  inserted insn inflates `closeness` by the tail length), then consistent-injective
  register map -> REGALLOC-PERM (§31 S11/RC-3), same-multiset-reorder -> SCHEDULE-REORDER,
  DELAY-SLOT, WIDTH/BRANCH-POLARITY/STRENGTH/ADDRESSING/IMM-OFFSET/IMM-VALUE. Every class
  routes to a BUCKET = which tool the failure wants. Uncovered opcode -> UNKNOWN, COUNTED
  (R32). 16 synthetic unit tests (test_residual_class.py).
- NEW tools/autopsy.py: `collect` materialises the corpus Task-12's telemetry never filled
  (1 of 6,169 records had a residual) by recompiling every open draft through the EXISTING
  match_one path (R33) — 1,752 drafts in 21s at -j12. `report` -> docs/autopsy.md.
- NEW corpus.o0_sources()/is_o0(): the opt-level oracle DERIVED from the Makefile's own -O0
  rules, coverage-asserted. Scoring an -O0 target at -O2 makes the residual 100% artefact
  (the trap this phase hit four times).
- R34 cross-check baked in: residual_class's closeness vs masked_diff.structured_diff's,
  asserted per row; 1,673/1,673 agree, 0 classifier errors.

FINDING: of the 972 records the grinder's own filter admits, only 75 (7.7%) are
permuter-shaped; 547 are structural and 348 are drafts that are not the function at all.
~92% of the daemon's CPU went where it could not win — the byte-grounded explanation of
"7 banks all-time, all Phase 21, 0 since" (Phase-22 audit). grinder.candidates() now
filters on the measured bucket (1,303 -> 78) and takes its directed profile from the
measured class, not the logged label (91% carry none -> it ran on gcc defaults).
Degrades to undirected if uncollected and says so; --no-targeting A/Bs it.

Two measured corollaries (R14, not projections):
- 699 records rank as near-misses at closeness up to 278 purely from a length artefact:
  un-attempted work misfiled as a backlog of hard functions -> new `redraft` bucket.
- a 12-draft gate probe of the `integration` bucket banked 1/12 (11 PLUMBING), so the 306
  prices Task 14's reconcile ladder rather than promising free banks. func_80167714
  (104 ins, reach-134) banked x1, un-propagated by design (§55b).

Two defects fixed forward:
- masked_diff._common_typedefs() used ONE shared probe path, so parallel match_one
  processes clobbered each other: 14 of 1,752 drafts lost in a single 12-way run (0.8%),
  silently, in every parallel wave ever run. Now per-PID.
- gate_stage.match_one_closeness never passed --o0 -> phantom residuals for every -O0
  function, written straight into the backlog this autopsy reads.

R22 clean-fleet: check-all 140 passed, 0 failed of 140; tools-health OK (dedup 1847/0,
C1 234343/234343); 0 NON_MATCHING (G4). Flywheel captured in-session (R30/R31):
cookbook §60, decision-log entry, SETUP.md inventory.
2026-07-21 11:03:59 -06:00
Drew T 5e78dccf07 feat(phase-29 §8e): jtbl 8-align pad-spec filter — the 4-giant unblock tooling (fleet-neutral)
- ROOT CAUSE PINNED (the session-2 half-pin was INVERTED; both probes were vacuous, R35):
  cc1 emits .align 3 before EVERY jump table; maspsx passes it VERBATIM (the :435 'drop' is
  an inventory-only pass); as bakes the pad SECTION-RELATIVE; link placement was never guilty
  (SUBALIGN(2) + ALIGN(.,4) place 4-mod-8 carve starts tight). Merging originally-separate
  TUs fires an intra-TU align where the original packed tight -> +4 at rodata 0xCC ->
  image-wide %lo shift. Honest probes persisted: .run/probe_jtbl/ (verdict.md + objdumps).
- NEW tools/jtbl_rodata_pads.py: post-maspsx filter replaces each rodata .align 3 with the
  ORIGINAL's exact pad bytes per a JTBL_PADS spec; fail-loud on table-count drift /
  non-align-3 / non-jtbl rodata content. Byte-proven: verbatim 0xE4 pad-at-0xCC ->
  filtered 0xE0 tight (= the merged carve span).
- jtbl_carve.py: spec-aware same-subseg merge (gap 0 or 4-with-zero-payload-word; else
  NON-CONTIGUOUS -> isolate), interval-arithmetic pad specs (committed values CARRIED,
  never re-derived), JTBL_PADS target-var emission into overlays.mk + revert() restore +
  stale-.o invalidation; the false 'maspsx drops .align' docstring corrected (H5).
- Makefile: $(if $(JTBL_PADS),| jtbl_rodata_pads.py ...) stage in build/src/%.o + file-scope
  empty default (env-shield). jtbl_family_bank.stub_file: duplicate-stub fail-loud (the
  earlier 'ladder failure' was a wrong-TU splice into a stale _a.c stub, byte-witnessed).
- R22 clean-fleet WITH the fix wired: 140/140 byte-identical, tools-health green
  (dedup 1846/0, C1 234205/234205), ZERO new banks -- fleet-neutral by construction.
- cookbook §8e (the jtbl alignment law) + §8a/§8a-pad corrections; decision-log R31 entry;
  SETUP.md tool row; .gitignore allowlist for the probe verdict artifacts.
2026-07-18 02:03:48 -06:00
Drew T 427baba3bf feat(phase-27 T10): completion dashboard (main in the weighted metric) + the resident second oracle
The metrics contract (roadmap §1) wants all three metrics WITH main in the denominators, and the
second, independent boundary oracle (R34) extended beyond the overlays. Both had landmines.

10a — main into the weighted metric, safely:
- weighted_metrics off the func_-only src_stubs regex onto corpus.stubs (R33). THE LANDMINE IS
  REAL: src_stubs("SLUS_007.26") globs src/SLUS_007.26/*.c -> 0 files -> every row "matched" ->
  main 100% + fleet % silently inflates. Routing through corpus.stubs is a PROVEN 0.000pp no-op on
  the existing fleet (overlays are all func_) and closes the curated-name leak.
- a SEPARATE "MAIN game-code weighted" line (0.7%): main's Ghidra sig excludes the LINKED PsyQ
  objects (Ghidra never analysed them), which is exactly right for a game-code metric (LINKED is
  complete, counted in fn-count). Reported un-folded and caveated (month-stale sig, PROVISIONAL) —
  folding a stale/incomplete value into the decomp.dev headline would mislead the flip checkpoint.

10b — the resident second oracle:
- make sig-resident: sig_image on the resident flat blob (byte-derived, not Ghidra). corpus.
  sig_is_independent now covers resident -> audit-corpus checks its boundaries too. Probed clean
  BEFORE wiring (144 fns, all 21 stubs present, 0 phantom), verified 0 phantom + 0 truncated.
- sig-overlays now derives its payload list from config/overlays.mk, not a 0.4.dec glob that
  silently dropped the 4 SC07 index-1 overlays (the audit's own silent-skip class). tools-health
  regenerates sig-overlays + sig-resident first so the audit never crashes on an absent sig.

10c — main's second oracle: docs/second-oracle.md. sig_image can't sign the PS-X EXE yet (0x800
header offset, interleaved data/linked islands, one text range); seeding from splat would destroy
independence for the PHANTOM class specifically. Honest deferral + scoped design, not a fake oracle.

- docs/progress.fleet.md regenerated: 140 binaries · fn-count 82.16% · instr-weighted 67.0%
  (the honest post-T7 drop from 68.9%) · distinct 47.8% · MAIN game-code 0.7% (separate).
- SETUP §6.3 updated (R21).
2026-07-15 18:51:43 -06:00
Drew T 264fe6c115 feat(phase-27 T7): disc-completeness audit — onboard 4 hidden SC07 overlays (136->140) + the type sweep
The whole-binary byte-gate is structurally blind to code nobody onboarded (R34): check-all is
green over the onboarded set no matter what code sits unbuilt on the disc. This reconciles the
onboarded set against every code-bearing PAC payload.

- new_overlay.sh: optional [ENTRY] arg (default 0.4) reaches a non-0.4.dec payload. Onboarded
  ov_SC07_{006,007,010,011} from 1.4.dec (they put graphics at PAC entry 0, the code overlay at
  entry 1 — invisible to the 0.4 hardcode for a month). Each byte-identical (7ca772be / b3b95547 /
  d7b5875d / 9885af74). FLEET 136 -> 140; check-all 140/140 (T2's pass==N re-baselined cleanly).
  difficulty.py NOT in the insertion set anymore (it derives, T6) -> only 3 tool dicts touched.
- tools/disc_code_sweep.py: decode every payload (reusing sig_image.make_insn) and gate code on
  BOTH valid>=0.90 AND jr_$ra density>=0.01. The jr_$ra gate is decisive: isValid() alone flags
  389 false hits (type-0/2 structured data decodes ~100% valid but has ZERO returns); jr_$ra
  separates code (~2.9-3.4%) from data (0.000%), validated on positive+negative controls.
- FINDING (docs/disc-completeness.md): type-4 location overlays are COMPLETE (138/138). All other
  types are data EXCEPT type-1 = 40 code payloads, 1 onboarded (the resident), 39 HIDDEN
  resident-class modules (mostly MAIN.CD/FILE_XXX/1.1). They load at UNKNOWN addresses (not the
  shared overlay slot), so they are NOT mechanically onboardable — byte-verifying a build binary
  needs its load address (P9), knowable only by runtime RE (the Phase-3 method). Deferred with
  evidence, NOT force-onboarded at a guess.
- CONSEQUENCE: game-code TRUE 100% now spans 140 onboarded binaries PLUS ~39 type-1 modules
  pending load-address RE. The roadmap assumed 136 — this is a real re-baselining (the +4 overlays
  also add ~2.45 MB to the denominator; every family propagation is now x138). Flows to T10/T11.
- SETUP §6.3 tool inventory updated (R21).
2026-07-15 18:33:37 -06:00
Drew T e0a0becfaa feat(phase-27 T4): one cdecl typedef-strip primitive (was six regexes) + surface cc1 stderr
The plan named two defective regexes; the tree had SIX with complementary holes, each
silently recording the resulting compile failure as "not a match" — a plumbing error
wearing a compiler wall's clothes, the exact class the 26-A audit exists to end (R32).

- cdecl.py: the canonical primitive — typedef_names(tu_path) + strip_provided_typedefs
  (draft, provided). Built on tu_statements (robust) NOT tu_scope (which coverage-asserts
  -> would crash the byte-gate on any unrelated unparseable file-scope statement). Splits
  multi-typedef lines (split_statements, depth-aware); covers scalar AND struct typedefs;
  keeps draft-local types. lru_cached.
- harvest_verify.py: strips PER-TU (cdecl.typedef_names of the draft's real target TU) ->
  unblocks the 39 struct-typedef drafts the scalar-only _TD dropped. And SURFACES cc1
  stderr: build() stashes it; a single-draft failure is classified DIFF / PLUMBING:… /
  CC1-FAIL / SKIP -> .run/harvest_failed.classified.txt. A `redefinition` is no longer
  recorded byte-identically to a codegen miss.
- masked_diff.py: strip_scalar_typedefs() (common.h set derived from the header once, R33,
  cached) replaces SCALAR_TYPEDEF_RE.sub for the ISOLATED compile; wired into match_one +
  p16_permute. Fixes the multi-typedef-LINE skip that discarded 42 masked-MATCH drafts over
  whitespace. Unblocks B4's func_8015C32C (redefinition of 's16').
- canon_sig_reconcile / eval_lora / format_finetune keep their own copies — migrate
  per-bank, byte-gated (the audit-prescribed cadence, not a big-bang swap).

VERIFIED:
- HEADLINE known-answer: func_8015C030 -> MATCH (23 ins) UNEDITED via match_one (was
  CC1-FAIL; the multi-line typedef split alone fixes it — a live x134-family draft that
  was being discarded over whitespace).
- unit: 7/7 scalars stripped; a local struct KEPT; a TU-provided Blk16 stripped.
- classifier unit: DIFF / PLUMBING:… / CC1-FAIL / SKIP all label correctly.
- all 5 edited tools import + AST-parse clean.
- R22 clean-fleet: check-all 136/136; main clean-rebuild 143dbb89. (A mid-test c4546248
  "mismatch" was a stale-incremental artifact from concurrent compiles, cleared by a clean
  rebuild — the R22 lesson; edits touch only tools/, src/ stayed git-clean.)
- SAFETY: a strip bug can only fail-to-bank, never falsely bank (INCLUDE_ASM pastes the
  original asm; a wrong draft always changes bytes -> always fails SHA1).
2026-07-15 18:11:43 -06:00
Drew T ebdef9012b feat(phase-27 T2): make the Makefile fail-closed — the enabling fix for every downstream gate
The roadmap §5 asserted `make report` is fail-closed. It was NOT: .ONESHELL sends each
whole recipe to one `bash -c`, so with no -e only the LAST command's exit survives and
every earlier failure is swallowed. `dedup-check` "gated" purely by being last;
lint_symbol_refs / progress --audit / difficulty / dup_report were non-gates. That is the
26-A audit's own thesis (a loud failure nobody counts is as invisible as a silent one)
biting the audit's infrastructure — and until it's fixed, any R32 assertion added to a
report-invoked tool is swallowed on arrival.

- .SHELLFLAGS := -ec (global fail-closed). ONE documented opt-out: check-env (set +e — its
  contract is accumulate-every-failure-and-report, which -e would truncate at the first
  missing tool).
- check-all:610 grep -c landmine fixed (|| true): grep -c exits 1 on zero matches, which -e
  treats as fatal in a command substitution -> check-all would FAIL exactly when nothing did.
- check-all / extract-all: assert COVERAGE (pass == N), not the absence of a failure marker.
  The old `fail == 0` / `! grep -q` form was a VACUOUS PASS on an empty pipeline (R32).
- new `make tools-health` = audit-corpus + audit-cdecl + report, fail-closed — the deliberate
  pre-matching ritual the roadmap's standing invariant names, and the dependent the two
  derived oracles never had (nothing invoked them). NOT a report/build prereq — audit-cdecl
  cross-compiles every C decl through gcc (~minutes). SETUP §6.3 documents it (R21).

VERIFIED:
- NEGATIVE CONTROL (the proof): a broken lint_symbol_refs makes `make report` exit 0 under
  the old .SHELLFLAGS=-c and exit 2 under -ec. The swallow was real, not theoretical.
- the grep -c landmine + the vacuous-pass both reproduced and fixed in isolation.
- check-env still exits 0 (the opt-out works); recipe sweep found the Makefile already
  -e-aware (set -o pipefail, explicit || true) — line 610 was the only real hazard.
- R22 clean-fleet: make check-all -> 136/136 byte-identical; a forced main re-extract+rebuild
  drove the full splat->cpp->cc1->maspsx->as->ld->objcopy->check pipeline under -e -> 143dbb89.
- audit-corpus 7s / audit-cdecl green / tools-health wired.
2026-07-15 17:56:45 -06:00
Drew T a33c6f85f5 docs(phase-26a): A11 — distill + close the tooling-integrity audit (26-A COMPLETE)
Closed the inserted half-phase. tooling-audit.md: DIAGNOSIS -> AUDIT-CLOSE LEDGER
(A1-A10 outcomes + the payoff 66.5->68.6% instr + remaining/handoff); the "two
rules" -> R32/R33/R34 crisp for P10 ratification at the Phase-26 PhaseEnd.
decision-log: the A10 wall-re-test verdict (R31 -- the broken tools WERE the walls;
the payoff was banked by the fixes; the closeness-0 residual is genuine; the real
deliverable is the 3 rules + the derived-oracle pattern). SETUP: the A9d-A10 tool
changes (R21). Cookbook §51 verified complete; LAW 3 tagged R34.

Observables green: final R22 clean-fleet 136/136 BYTE-IDENTICAL; make report EXIT 0
(dedup 1840/0, C1 227211/227211 signed, lint_symbol_refs wired + passing);
audit-corpus 0 slices; audit-cdecl green. Zero src/config changes this session.

Phase 26 resumes at Task 7 (fresh session).
2026-07-15 00:23:31 -06:00