jtbl and o0 are held out of the default lane because a draft cannot BANK without
tooling first (a rodata carve; an -O0 object, and no Makefile glob matches md_* at
all). 'cc1' is not that: it records that some earlier draft failed to compile. A
compiling draft banks through the normal splice->build->SHA path regardless of its
history, and a non-compiling one fails fast at cc1 for the price of one build.
Holding 14 groups / 105 instances / 6,511 ins out of every wave bought nothing.
Effective on the next wave draw (build_wave_atlas is a fresh subprocess per draw —
no lane restart needed).
--o0 has existed since P29 and NOTHING ever passed it: api_draft.match_one(), the
oracle every wave agent iterates against, builds a fixed argv without it. Every agent
handed an -O0 target was shown an -O2 compile of its own C and a mismatch on every
instruction — feedback that cannot converge, for a reason invisible in the diff, and
it hit even the 51 functions already sitting in -O0 objects.
match_one now decides from the target (R33), with two oracles because neither alone
is sufficient (R34): the -O0 frame-pointer prologue in the target's own bytes, OR the
subseg being an object the Makefile compiles -O0. boot/start.s is built -O0 with no
ordinary prologue; an -O0 function stranded in an -O2 subseg has the prologue but
cannot bank — and match_one now says so instead of letting an agent chase it.
The prologue scan is anchored at glabel, not the top of the file: two md_MAIN_011
.s files open with a migrated jump table / .asciz blob and a naive scan read table
words as the prologue.
tools/test_o0_detect.py: 14,400 .s scanned, 167/167 signature files covered, 0 false
positives outside -O0 objects, both rodata-first files detected.
Census correction in the design doc: the handoff's refutation of md_MAIN_003/011 is
itself wrong (16 and 21 -O0 functions, byte-verified), and the 311 '$fp mentions'
figure over-counts because $fp is $s8, allocatable at -O2. True population: 167
files, of which 116 / 14,148 ins are -O0 functions the build compiles -O2 — double
the atlas estimate, with x2/x3/x4 sibling replication.
Joined the campaign ledger to each wave's cards and to the functions its own commit
banked (removed INCLUDE_ASM lines), pooled over bb/bg (tells) vs bc/bf (default):
nins default tells
0-50 303/528 57% 27/ 67 40%
50-80 43/145 30% 20/ 73 27%
80-120 9/ 41 22% 10/100 10%
120-200 1/ 30 3% 1/ 68 1%
200+ 2/ 35 6% 0/ 30 0%
At equal size the lanes are close below 80 instructions and both collapse above it.
What separated them is the card size MIX: default's cards are median 37-39 ins, the
tells pool median 89-95 (2.4x), so 'the tells lane is broken' measured the population,
not the lever. Tells now draws 5-80.
This also refutes the S58 handoff's one live hypothesis for tells (235, the phantom
symbol). Checked the recorded reloc_identity verdicts first (R38): among MISMATCH?
rows, the fraction whose instruction SHAPE already matched — the symbol-only class 235
describes — is 25/87, 16/79, 17/66, 13/57 on default waves but 6/66, 4/81, 5/47 on
tells. Tells drafts fail because the BODY is wrong, not the symbols, which is what a
2.4x larger median predicts.
Also adds tools/lanes/relaunch_drafter_shell.sh. bash parses a while...done body in
full before running it, so a lane-ARG change is invisible to the running shell and a
python bounce re-runs the OLD command line — measured at 11:02, when the bounced
python came back on the pre-S59 lane list 18 minutes after the file changed.
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
The whole split is ONE inserted config line plus jr_isolate_all --only, exactly as
the adversarial review concluded (no _pre piece, no ld_interleave change, the
- [0x0, .rodata, md_SC03_076] island piece untouched):
- [0x268, .rodata, md_SC03_076_jr_801F218C]
- [0x2d24, c, md_SC03_076_jr_801F218C]
FRESH negative control, measured now rather than inherited (the study's +8/0x144
baseline was measured on func_801F0F28 and misattributed):
baseline whole-binary sha1 9a165e368009a79bcc2ebb06bcb13e6da20d460e (green)
md_SC03_076.o .rodata sh_size 0x27c (the whole island)
after whole-binary sha1 9a165e368009a79bcc2ebb06bcb13e6da20d460e (green)
md_SC03_076.o .rodata sh_size 0x268
md_SC03_076_jr_801F218C.o .rodata sh_size 0x14 (the 5-entry table)
So the SHA is unchanged AND the object-level discriminator proves the split really
happened — the pair rules out the no-op reading of a green build.
Island census for the record (file offsets): D_801EF468 0x000-0x0D8, D_801EF540
0x0D8-0x144, then the migrated tables of func_801EFBB4 0x144, func_801F0210 0x1B4,
func_801F0734 0x1EC, func_801F0A9C 0x214, func_801F0F28 0x23C, func_801F218C 0x268
-> 0x27C = the island end. The island is a STACK: only the end-adjacent table carves
cheaply, and each isolation exposes the next one, so the module peels from the end.
jr_inventory asserts every committed .rodata piece resolves to exactly one banked
owner (R32) — true for ov_*, where every .rodata piece was created by jtbl_carve.
On md_* the 154-A leading island - [0x0, .rodata, <alias>] is the module's own
rodata blob (INCLUDE_RODATA data + every stub's still-migrated table), has no single
owner, and made the check abort: md_SC03_076 reported UNOWNED 0x801ef468 and could
not be isolated at all.
rodata_carves() now takes the alias and skips that one piece. The discriminator was
verified across all 213 splat configs: a .rodata piece at offset 0 whose subseg is
the binary's own alias exists in exactly the 42 md_* configs and in none of the
others, so this is a provable no-op for every ov_*/main config.
md_SC03_076 --only func_801F218C now plans cleanly: 1 jr, 2 region .c files, no
carve repoints.
Ships a lane-arg change without losing drafts: the running python still carries the
pre-S59 args, and killing it mid-wave discards everything the fleet has drafted for
that wave. Waits for .run/ready/<wave>.json, then pkills only the python — the
drafter SHELL, the lane that must never stop, relaunches it from .run/drafter.sh.
90-minute deadline so it never lingers.
main is the one binary splat writes to asm/nonmatchings/800/ and asm/data/ rather
than asm/<alias>/...; the hardcoded asm/<ov>/ in nine places made every main lookup
miss a directory that does not exist and report it as 'already spliced AND no stale
copy' — a true-sounding message about the wrong tree (R43).
With the path, the base and parse_config all fixed, main now reaches the real
analysis: func_8001A114 refuses because subseg 800 would host non-contiguous
.rodata carves (0x63238 = the existing LZSS table, 0x6327c = the new one), i.e.
isolate first — the same architectural refusal an overlay gets.
The island guard also moved AHEAD of jtbl_range, so an island table no longer
reports 'not found in the raw data asm — already carved / stale asm?' (true and
useless: an island table was never in the data asm). It now prints the exact line
the review prescribes: - [0x268, .rodata, md_SC03_076_jr_801F218C].
Review doc gets an S59 addendum with the measured class facts and where the two
examples now stop.
parse_config's docstring said 'the trailing run of {data,.rodata} pieces after
the last c piece'; its code took data_pieces[0], the first such piece anywhere in
the file. Those agree on 171 configs and disagree on 42: the md_* modules open
with the 154-A leading island - [0x0, .rodata, md_XXX] BEFORE their c piece, so
apply()'s splice lines[:lo] + region + lines[hi:] deleted the c line and wrote the
yaml to disk before the tool errored out for unrelated reasons.
Fixed by deriving the region from the last c piece, plus an R43 guard that refuses
outright if any c piece lands inside the window apply() rewrites wholesale.
Also, correcting what S58's blanket refusal had lumped together:
* md_* (42 configs) was the whole corruption class.
* main was never in it - its pieces are already [all c ..., data, .rodata, data].
main's real defects were the config PATH (there is no config/splat.main.yaml,
it is splat.us.exe.yaml) and the FILE BASE: the EXE has a 0x800 header, so the
delta is 0x8000F800, not the yaml's first vram: 0x80010000, and with the naive
value payload_word silently read 0x800 early. Both fixed; the base comes from
the single derivation in family_remap.vram_of (R33).
The cfg_path class refusal is lifted and replaced by an operation-level one: a carve
whose table lies below the data region is in the leading island, which a tail carve
cannot reach, so build_carve refuses and names the island-split lane (R43).
tools/test_jtbl_parse_config.py proves all three, read-only:
NC-1 regression 171/171 configs byte-unchanged by the fix
NC-2 defect 42/42 md_* lose a c line under the historical derivation; 42/42
keep every c line under the fix
NC-3 main cfg=splat.us.exe.yaml base=0x8000F800, region starts after the last c
The S58 removal cited four waves (as/aw/az/bd) and blamed the lane. All four ran
at band 120-2000. The campaign ledger splits the population by band instead:
tells @ 120-2000 228 drafts -> 18 banked = 7.9%
tells @ full band 655 drafts -> 161 banked = 24.6%
default @ full 2,996 drafts -> 1,335 banked = 44.6%
Of drafts that actually reach a gate the lanes are indistinguishable (tells 54.4%,
default 56.4%) — the whole loss is reloc_identity discarding phantom-symbol drafts,
which is cookbook 235 and a BRIEF fix, not a lane deletion (R40).
Rotation now pins lane index%4 against band index%4 so slot 1 (tells) always draws
the full band and slot 3 (the large band) is always default; asserted over 200 waves.
Takes effect on the next drafter-python bounce; the running process still carries the
old args and the drafter SHELL is never stopped to ship a change.
jtbl (36,685 ins, but 150 of 177 groups are singletons): Fable's review says the fix is SMALLER
than proposed — ONE inserted .rodata carve line plus jr_isolate_all --only. No _pre piece (it
cannot build), no ld_interleave change (the native script is already rodata-first). Harden
parse_config FIRST: it corrupts md_*/main configs on disk before erroring, which is why
jtbl_carve now hard-refuses them. The ox study's negative control is misattributed — build a
fresh one.
o0/cc1 (6,564 + 6,511 ins): the study is half refuted, and the doc header says which half.
md_MAIN_003/011 do NOT carry the -O0 fingerprint; the 311 files that contain $fp are the real
population. The unanswered load-bearing question for both is whether the EXISTING gate can bank
them unchanged — a lane that drafts what the gate cannot accept has already cost two sessions.
tells (86,602 ins): removed from drafting on four waves of evidence. aprop_autodraft is NOT the
destination (4.2% overlap, checked after I asserted it three times). The live hypothesis is
cookbook §235, the phantom symbol — one wave with it in the brief tests it cheaply.
Closes the gap that made main red for nine hours. R42 ('commit a dirty tree rather than
revert it') is correct for a per-binary gate that leaves PROVEN banks uncommitted, and WRONG
for gate_main, whose substitution is unverified by construction until the SHA matches.
Two guards, defense in depth:
1. gate_main installs atexit + SIGTERM/SIGINT/SIGHUP handlers that revert its own substitution
unless a bank actually succeeded. Killed mid-run, it now cleans up after itself.
2. ox_campaign's dirty-tree commit REFUSES top-level src/*.c (main's sources), reverting those
and committing the rest. Verified: src/800c.c and src/800.c refused, src/ov_*/... and
src/shared/engine_core.h still commit.
Also versions the autonomous lane scripts under tools/lanes/ — they lived only in gitignored
.run/, so a fresh clone had no drafter, gater, maintenance or stallguard at all.
All 1,101 novel-idiom candidates read (33 waves, 4,888 verdict lines, 4,764 notes), restricted
to functions the WHOLE-BINARY BYTE-GATE banked. Append-only: 1450 insertions, 0 deletions,
prefix md5 unchanged.
§233 header+index · §234-§257 the laws · §258 the 21 addenda (append-only forbids inserting
them into their parents) · §259 the discard ledger.
Three claims were HEDGED rather than promoted (§253, §254, §213's right-rotate rule); four
were byte-verified against asm/ before being written; §217/§230/§232 were promoted from
'single observation' to CROSS-CONFIRMED by independent cards.
~1,040 discarded across 18 ledgered clusters — ~700 self-reported 'nothing the cookbook did
not already cover' (the flywheel working), ~400 Law-1c compliance reports, ~70 bare
'reordering fixed it' with no mechanism (unindexable). Two clusters deliberately left as OPEN
PROBLEMS rather than written up as false laws: forcing own_thread_p on a call-bearing join,
and suppressing a constant-offset second giv. Both are permuter jobs.
Highest-value: §234 (store lvalue signedness picks addiu vs li/ori — 12 cards each burned a
one-instruction residual on this), §236 (the nine-class declaration-layer taxonomy — ~90 cards
banked bodies that were already instruction-correct), §235 (the phantom symbol: match_one masks
relocations so a green MATCH can name a nonexistent symbol, with NO instruction diff).
Main has been RED since ~00:20 overnight: it built to e9fffcf45 instead of 143dbb89, so no
main draft could bank regardless of quality, and R22 ran 212/213 for nine hours.
CAUSE — R42 has a gap. 'Commit a dirty tree rather than revert it' is right for a lane that
gates per-binary and leaves PROVEN banks uncommitted (sweep_parallel gates commit=False by
design). It is WRONG for a killed gate_main, whose substitution is by definition unverified.
Two auto-commits swept up abandoned main batches:
src/800c.c <- commit:2598 6 INCLUDE_ASM stubs substituted
src/800.c <- commit:2611 4 INCLUDE_ASM stubs substituted
Ten function bodies committed as banked work that never passed a byte-gate.
VERIFIED IN THE ISOLATED WORKTREE, by elimination rather than assumption:
HEAD -> e9fffcf45 RED
revert 800c.c only -> a9d9870c1 RED (still drifting)
revert 800.c + 800c.c + engine_core.h -> 143dbb89 GREEN
restore HEAD engine_core.h -> 143dbb89 GREEN (header INNOCENT)
restore HEAD 800.c -> a9d9870c1 RED (800.c guilty too)
So the minimum revert is exactly these two files. src/shared/engine_core.h is exonerated —
I had proposed its C89 prototype change as the cause and that hypothesis was WRONG.
NOTHING IS LOST: the ten bodies remain in commit:2598 and commit:2611 and can be re-gated
properly once gate_main's fixes (terminating bisect + idempotent hoist, commit:2633) are
exercised against a green baseline.
The P31 S58 main probe ran 38 minutes on 8 drafts and never produced a verdict. Two
independent non-termination bugs, both fixed and negative-controlled:
1. THE BISECT COULD NOT TERMINATE. On a failing multi-element chunk it did 'lo = head + lo',
restoring lo to exactly its prior value, so the next iteration recomputed the same head and
failed identically — forever. Replaced with an explicit-stack bisect that SPLITS a failing
chunk and pushes both halves, so work strictly decreases and termination is structural.
GATE_MAIN_MAX_STEPS (24) is a loud backstop, not the mechanism.
Verified: 8 drafts with one poisoned -> 7 banked, 1 rejected, 7 rebuilds.
2. THE TYPEDEF HOIST WAS NOT IDEMPOTENT. The block is inserted AT the anchor, so anything
hoisted previously still started after it and was re-hoisted every call, stacking a fresh
marker comment each time ('hoisted 2 typedef(s)' x150; the tree held a duplicated marker).
Now tracks the already-hoisted region and reuses the existing marker.
Verified: 3 consecutive passes hoist [Foo,Bar], [], [] with exactly 1 marker.
Together these unblock main: 170 parked drafts and ~1,041 open stubs.
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
The main probe (8 drafts) ran 38 minutes without a verdict. Two defects, neither about the
drafts:
1. FIXED — the compile-error shortcut matched only 'previous declaration of', but gcc printed
'previous implicit declaration of func_80017930'. So a batch whose culprit gcc had already
named fell through to bisection, which costs a full clean EXE rebuild per step. The matcher
now accepts the implicit and conflicting-types forms too. (resolve_conflicts is separately
blind to this class: an implicit decl comes from a call site with no prototype.)
2. NOT FIXED, documented — the typedef-hoist repair is not idempotent. It emitted 'hoisted 2
typedef(s)' 150 times and left a duplicated marker comment; it re-hoists, rebuilds, fails
identically and repeats, so it cannot converge. Make it idempotent and bound the bisect
before gating main again.
Also: ox_campaign pre-draws the next wave AFTER launching shards (doing it before left the
fleet at 8 agents while a card job ran), collect_drafts grants stragglers a grace period
instead of letting 2 of 220 shards idle the fleet for 34 minutes, and drafter bands are now
mostly full-range (the 400-2000 band drew 9 cards for a 2,000-worker fleet).
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
ox's study correctly found that the atlas o0-lane lever is MISCLASSIFIED on its probe
instance (func_801F0A9C @ md_SC03_076 has no frame pointer — verified, zero $fp under
asm/md_SC03_076/). Its proposed replacement population is fabricated: md_MAIN_003 and
md_MAIN_011 have zero $fp in either module. $fp does appear in 311 files across asm/,
which is the population an -O0 lane would really target.
Recorded rather than discarded: the misclassification finding is load-bearing (a lane
built on the lever label drafts the wrong functions) and the study named checkable files
for every claim, which is what made the refutation cheap.
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
Uncommitted src/ changes found at gate entry. These are banked functions from a lane that gates with commit=False, not residue — preserved, not reverted.
Two independent reviews (ox design study + Fable validation) found parse_config does not
implement its own documented contract on md_*/main: it can DELETE the c config line and
corrupt the yaml on disk before failing. main also has no config/splat.main.yaml (it is
splat.us.exe.yaml), which is the FileNotFoundError seen on every main jtbl target.
Refuse loudly rather than corrupt quietly (R43). Lift only after parse_config is hardened
and proven on two examples.
Also: gate() stages into a per-run dir (a killed gate's 3,186 stale drafts survived into the
next gate of the same tag and poisoned every group); reloc_filter no longer treats
NOT-A-STUB as a pass (it means ALREADY BANKED — wave an staged 480 of them over source that
already byte-matches).
Designs recorded: docs/tool-designs/jtbl-island-split.md (ox) and -review.md (Fable):
14 CONFIRMED / 8 WRONG / 1 UNVERIFIABLE, verdict GO-WITH-CHANGES, and the correct fix is
SMALLER than proposed — one inserted .rodata carve line + jr_isolate_all.py --only, no _pre
piece and no ld_interleave leading mode.