The ledger write recorded every entry in `ready` as `gated:rc<N>` on ANY rc. When
the gate REFUSES to start (parallel_gate on a dirty tree, a worker missing its link
inputs) it examines nothing -- yet S69's Gate37 refused with rc=1, gated nothing,
and both of its functions were recorded as gated and silently skipped on the retry.
The phantom entries had to be cleared by hand.
"Attempted" and "never looked at" are different facts and only the first justifies
suppressing a re-gate. A binary now counts as EXAMINED when its worker banked
something, wrote per-function verdict rows, or reported a draft count -- i.e. got
far enough to have an opinion (R32). Everything else stays eligible and is named
loudly rather than dropped silently (R55).
Both tools restored with `text.replace(after, before, 1)` -- the FIRST occurrence.
--any-proto (and sync-decls) collapse DISTINCT declarations of one function to the
SAME `after` text, so occurrence N received entry N's `before` in JOURNAL order,
not file order: the originals land on the wrong occurrences and the file is
corrupted while the tool prints full success.
Byte-witnessed twice in S70:
* fix_arity_callers: "restored 382, kept 0, missing 0" left the FLEET-SHARED
src/shared/engine_core.h with 97 insertions / 97 deletions (func_8012A828
rotated between three declaration sites).
* cast_self_callers: "reverted 10 edit(s)" left src/800.c with the two decls of
func_80031988 swapped.
Both were caught only by `git diff` AFTER the success line (R40: the tool's own
report is not evidence).
The occurrence->original mapping is NOT recoverable from either journal format, so
the undo now REFUSES (rc=2) when one (file, after) group maps back to differing
`before` texts, naming the file and telling the caller to git checkout it (R43:
refuse, never mishandle). Journals additionally record per-file sha_before, and a
clean undo hash-verifies its own result and reports HASH-MISMATCH loudly. Old
list-form journals are still read.
gather_externs carries file-scope externs out of the EXEMPLAR's TU and prepends
them. When the destination TU already declares the same symbol with a DIFFERENT
spelling that is a `conflicting types` error -- the documented cap on this lane.
Build the rename table BEFORE gathering so each carried extern is judged under its
DESTINATION name (R48), then drop only a GENUINE conflict; a duplicate-identical
extern is legal C and is kept, so nothing the body needs is ever removed.
HONEST SCOPE: negative-controlled A/B over all 53 d<=1 twin candidates -- 52/52
generated drafts BYTE-IDENTICAL to the pre-fix output, 0 changed. 37 of the 52 do
carry externs (152 total), so the filter had inputs and found no conflict: the decl
environment is NOT the binding constraint for this population. Kept as a correct
defensive guard, not as an unlock. Verified the guard actually runs (dest TU
resolves, tu_decls returns 2,712 symbols) rather than silently no-opping.
gate_stage runs with cwd=<worktree>, so a RELATIVE --drafts path resolved inside
the worktree. .run/ is deliberately not linked into a worktree, so every plan
pointing at the project's own scratch convention (R12: scratch lives under .run/)
landed on a nonexistent path: gate_stage found 0 drafts, banked 0, exited rc=0.
A clean success reporting a TRUE number about an EMPTY world -- the dominant
defect class in this codebase (silently-narrowed-tool-scope).
Measured: 35 binaries / 57 drafts all "banked 0" in 1-2s each, while the SAME
drafts gated IN-TREE banked 15/16 (ov_SC06_011) and 3/6 (ov_SC06_029). After the
fix the same worktree job takes 100s instead of 1s -- it is actually building.
Also refuse a job whose drafts are unreadable (R32/R43) rather than let it report
"banked 0" as though the drafts had failed -- the same shape as the existing
missing-generated-inputs refusal directly below it.
- ran residual_rules_b over the WHOLE open frontier (1,312 cases, 0 errors, ~2min, $0)
instead of a 50-row sample; artifacts in .run/S70_*
- DENOMINATOR (Drew's correction, R41): main's 960 PsyQ LINKED stubs are not
matching targets; true frontier = 355 (67 main REAL + 288 non-main), partitioned
with progress.linked_subsegs() rather than a hand-rolled filter (R33)
- discriminating test settles population-vs-coverage: fire rate DOES rise as
residuals get clean (35.3% at <=8 vs 6.1% at >64) but 57% of the cleanest band
is still UNKNOWN -> coverage binds where rules are worth writing
- hand-label 4/4 labelable to existing cookbook buckets; WIDTH/lhu!=lh has its
discriminating sig already computed and still returns top=None
- 86 REAL standalone MATCHES (closeness 0) = 24% of the frontier, blocked on TU
plumbing only -- outranks the rule work (standalone-match-is-not-bankable)
- logs 4 instrument defects in my own probe, incl. one wrong answer reported to
Drew before checking: 4 of S68's 10 autodecl MATCH drafts are STILL OPEN
- tools/r22_verify.sh from a clean tree: clean rc=0, extract-all 212+main rc=0,
check-all 213 passed / 0 failed of 213 (2m49s). Clears the S69 --no-r22 debt.
- R38 read of the recorded measurement behind the "1-2% ceiling" (S68 eval set +
.run/rules_b/eval_results.jsonl) before designing the queued probe:
* citation fix: the design is Fable-1 (.run/S69_fable/report.md:93), not Fable-2 §7.7
* denominator fix (R41): shape rules can only fire on the 39 near rows, not 113;
real fire rate 3/39 = 7.7% (5/39 with REDRAFT), and 14 are UNKNOWN
* the probe as written is unrunnable: backlog has 125 rows / 20 with residual text
and the UNKNOWN pile is 14 -- sampling 50 would report a narrower world (R41/R32)
§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.
Root cause of the 8 untracked src/ files. The merge-safety check compared:
base = sh(["git","show", pin:path]).stdout -> "" when the path is NOT at the pin
cur = open(path).read() if exists else None -> None when absent from the main tree
if cur != base: REFUSE
For a file that exists in NEITHER — exactly what a jtbl carve creates when it
splits a TU into src/<bin>/<bin>_jr_<addr>.c — that is `None != ""`, so every
carve-created file was refused as "main tree moved under them" and never added.
Nothing failed locally: the file is on disk and R22 passes. But config/splat.<bin>.yaml
names the subseg and IS committed, and 31 sibling _jr_ files in the same binary are
tracked — so a fresh clone (or a push) got the config without the source. Eight
accumulated in one session and only surfaced because the dirty-tree guard refused a
later run.
Fix: distinguish "not at the pin" from "empty at the pin" via git show's RETURN
CODE, so absent-in-both compares equal and the file is adopted. New adoptions are
reported explicitly ("N NEW file(s) created by a carve, now tracked") rather than
merged silently — adopting a brand-new source file should never be invisible (R32).
The `git add -- <adopted>` step was always correct; it simply never received these
paths.
`parallel_gate` commits with `git add -u src/`, which updates TRACKED files and
cannot add NEW ones. A jtbl carve SPLITS a TU, creating `src/<bin>/<bin>_jr_<addr>.c`
— so every carve landed its yaml change (tracked) while leaving the new source
file UNTRACKED.
Why this mattered: `config/splat.<bin>.yaml` is committed and names the subseg
(`- [0x577f8, c, ov_SC02_000_jr_8017F950]`), and 31 sibling `_jr_` files in that
same binary are tracked — so these are source by convention, not build artifacts.
R22 passed locally only because they exist on disk. A fresh clone, or Drew's
push, would have the yaml without the file.
Found because parallel_gate REFUSED to run with an unclean tree (rc=1) and listed
them — the guard did its job; the earlier `REFUSED 8 (main tree moved under them)`
line in the carve gate was the same eight files.
TODO for the tool: parallel_gate's commit step must add NEW files under
src/<binary>/ that its own carve produced (narrowly, per-binary — never a blanket
`git add src/`, per the carve-state discipline).
Caught by Drew asking whether the last waves were harvested. They were not: I
banked 1 of 5 (§398b) and left four lever sets in the notifications. Also found
two paid-for MATCHes that were never staged or gated.
(a) a fence BETWEEN two prologue loads, where source reorder does nothing —
the order is fixed before statement order matters (md_MAIN_013/func_800CB56C)
(b) SINK a call into BOTH arms and let cross_jump keep only the jal suffix;
88ins/close86 -> 92/13, then §3-T2 field order let each sh $zero fill an lhu
load-delay. Duplicate in source so the compiler merges, rather than writing
the merged form yourself (ov_SC07_001/func_8017EDC0)
(c) a $v0->$a0->$s3 DOUBLE COPY is a two-pseudo tell: SImode temp for the compare
+ separate HImode var for the tail (70->37); plus §195-N precondition 5 —
nesting `return 1` with ONE trailing `return 0` blocks jump.c's store-flag
transform so reorg fills both delay slots (18->0) (ov_SC02_017/func_8018347C)
(d) the re-tie as a BIV KILLER: a second set makes n_times_set>1 so loop.c
refuses the pseudo as a biv, killing the combined address giv. volatile was
worse, a dead read did nothing (ov_SC07_001/func_8017E4DC)
(d) makes THREE distinct uses of the zero-byte re-tie in one session — §380
un-hoists a move_movables invariant, §393 kills the scheduler's birthing boost,
§399d denies a biv. One line, three passes: when a single-set pseudo is being
treated specially, give it a second set.
Measured: 22 twin remaps gated as a batch -> 3 banked, 11 CC1-FAIL/PLUMBING, 8
DIFF. The eleven integration failures read 'syntax error before', 'undeclared',
'conflicting types', 'parse error' — the signature of a decl block written for
another TU.
family_remap rewrites the BODY correctly (per-overlay symbols, reloc targets) but
carries the source TU's typedefs/externs/callee prototypes verbatim into a
destination that already owns those names — §378c's fifth variant, at scale and by
construction. The 8 DIFFs are the h_norm class being 80%, not 100%.
Planning consequence (R41): the remap lane's realistic yield is ~15% straight
through and ~50% after the integration pass, NOT the 76-88% PURE-class rate.
Quote the straight-through number.
Tooling gap named: family_remap should emit the body with the DESTINATION TU's
decl environment; decl_prior already computes it for cards, and
cast_self_callers/fix_arity_callers already edit it.
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.
Measured the expensive way. A reach-6 cluster showed open-open, so seed_ref
correctly reported 'no banked twin' for all six. I cracked the exemplar (203k
tokens, five new levers) and then drafted four siblings at ~60k each — including
one that had already burned 257k plateauing at permuter-class NEAR.
They were EXACT clones. The agents' own diffs said so: 'label-stripped .s diff vs
the twin is EMPTY', 'an EXACT clone (asm diff = labels only)'. The moment the
exemplar banked, seed_ref returned it as a banked twin for every sibling, and
family_remap + the §378 chain banks them for ~0 tokens.
The law: a bank CHANGES THE TWIN GRAPH. The twin oracle answers 'is there a
BANKED body like this?', so its verdict for every sibling is stale the instant the
exemplar lands. crack-wave-sweep-map-regen applied one level down — the family map
is not the only stale artifact, and the twin oracle is the one the cards read.
Also: never draft two members of one cluster in parallel; if either cracks the
other is free.
§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.
Found by the Fable blocked-pile audit. `jr_isolate_all.jr_inventory` resolves each
committed .rodata carve's owner through `family_remap.reloc_targets`, whose
`nins_of` reads the gitignored `.run/sig.<binary>.jsonl`. A fresh worktree has no
`.run/sig.*`, so inside a worker EVERY carve reads UNOWNED, jr_inventory
R32-aborts, harvest_verify prints `isolate FAILED`, and the draft is booked
CARVE-REFUSED.
That verdict was about the WORKTREE, not the function. Measured on
ov_SC02_000/func_8017F950 (a RELOC-ONLY twin whose body rtu-MATCHes 117/117):
dry-run isolation passes in the main tree and aborts in the worktree with 30
phantom UNOWNED carves. Linking one file is the whole difference. When the file
is absent it is now reported in missing_generated rather than silently skipped.
This invalidates the CARVE-REFUSED rows I quoted in the S69 census — they were
instrument verdicts, and the class is far smaller than recorded.
Also adds tools/asm_verbatim.py (new): .s -> §265 file-scope __asm__ block with
decimal immediates/offsets and comma-no-space operands (maspsx dies on
`sltu $v0, $s0, $v1`), derived .frame/.mask, R43 refusals for rodata/jtbl.
Ledger MATCH 12 / NEAR 1 / REFUSED 2 plus a non-wall control. Byte-equivalent to
the stub by construction — for genuine hand-asm only; §265 accounting applies.