The data factory, and the switch that was left off
support-onlyevidence: none: no retained result of its ownteacherReplays many games and labels every visited position with its remaining lifetime, producing training data for the rest of the family.
Replay a great many games and write down every position they passed through, each one labelled with how many moves that game still had left. This approach produces no result of its own; it is the data other approaches in this family learn from.
Why a label like that is worth having
The obvious training target is score. Score in this game mode is a poor target:
it is dominated by a flat 17,000-point bonus, it is heavy-tailed, and there is
exactly one label per game. Remaining lifetime is the same quantity with the
noise taken out — final score and moves survived correlate at r = 0.9995
(finding-01), and it gives
one label per move instead of one per game.
The intuition: seven answers per position, and only one of them written down
Here is one real position from the rules engine. Seven columns are legal and the next disc is a . Two of the seven possibilities:
A three-wave chain worth 155 points. The board ends the move with two fewer discs on it than it started, even after adding one: this is the kind of move that keeps a game alive.
Nothing happens. No clear, no reveal, no points, and the board is one disc fuller. Same position, same disc, and a completely different future.
A model that is going to choose a column has to tell those apart. The repository's most repeated failure is a model trained only on the move that was actually played and then asked, at play time, to rank the six it never saw — the sibling trap. The obvious fix is data that contains all seven.
This generator can write all seven, and the corpus that was actually made contains one. That is the single most important fact about this approach and it is on the record.
How it works
- Play whole games with the repository's own searches and write, for every move, a fixed-width record: the public board before the move, the visible next disc, how many drops remain before the next rise, which columns were legal, which one was played, how many discs cleared and how many gray discs were revealed, and the labels, which are how many moves and how many rises that game still had ahead of it.
- Mix the behaviour rather than branching at each position. The generator was run four times with searches of one, two, three and four moves of look-ahead, each injecting a small fraction of deliberately random legal moves. Positions a strong policy never reaches — crowded, nearly-dead boards — come from the weak arm; positions near where the real policy plays come from the strong arm.
- Optionally, write the panel. A second record type stores, for a sampled position, the resolved board and immediate effects of every legal column under the same randomness. This is the successor-closed data the benchmark contract asks for.
- Keep training and evaluation seeds apart by construction. The generator refuses to run outside its lease, and evaluation seeds live under a different prefix entirely.
What happened
There is no retained result for this approach. It has no row in the experiment index, no entry in the ledger and no finding document of its own. What is recorded is what it produced and who consumed it.
The corpus exists and was used. Four training blocks were leased and opened —
65,536 games at two moves of look-ahead, 16,384 at one, 4,096 at three and 768 at
four, with deliberately-random move fractions of 8%, 15%, 5% and 3% respectively
(lease-map). The resulting state files are the
training data behind the
learned leaf, whose result is
retained, and behind the afterstate net,
whose outcome is not recorded anywhere.
The all-sibling panel was built and left switched off. An independent
read-only review of the working tree
(reconciliation-01) found that the panel
is gated behind an option that defaults to disabled, that every corpus
summary from the four training blocks records zero panel records, that only a
16-game smoke run ever produced a panel file, and that the reader used by the
model trained on this data contains no panel reader at all. So the capability was
deliberately built and the corpus that was consumed holds one row per played
move, not one row per legal column. Coverage of alternatives comes instead from
behaviour mixing: a column that one arm avoids appears in the data as a column
another arm played, in a different game, which is not the same thing as having
the alternatives at the same position.
A lease collision was caught and the data destroyed rather than filtered. The
first corpus run was sized in games and its end address was not checked against
the allocation table, so it overlapped the scenario-minting range by 4,096 seeds.
Both ranges are development data, so no protected seed was touched and no
qualification claim is affected, but positions minted for a benchmark could have
appeared in the training data for a model later graded on that benchmark. The
contaminated corpus, 1,029,206 records, was destroyed rather than filtered,
and regenerating it from a clean range cost 103 seconds
(lease-map, incident of 2026-08-20).
The record, and what it does not contain
Sources. The generator's own source comments; the seed allocation table
lease-map; and the read-only working-tree review
reconciliation-01 §"Sibling coverage".
There is no result record, no preregistration and no finding document for this
approach, and none is implied by the existence of the files.
Record shapes, from the source. The per-move record carries the 49 public cells, the next disc, the moves remaining before the rise, the legal-column mask, the chosen column, moves-to-death and rises-to-death as labels, clears and reveals for that move, the behaviour depth, a flag for whether the move was a deliberate random deviation, a censored-game flag, occupancy, the move index and the game seed. The panel record additionally carries the resolved afterstate and immediate effects of every legal column at a sampled position.
Common random numbers come for free in this design: the engine derives its reveal randomness from the game seed and the number of moves played, which are identical across the siblings of one position, so the panel compares columns under the same luck without any extra machinery.
Leases. SEEDLEASE-A52 blocks 0xa5200000–0xa520ffff (depth 2),
0xa5210000–0xa5213fff (depth 1), 0xa5214000–0xa5214fff (depth 3) and
0xa5215000–0xa52152ff (depth 4), all with the training role. Once read
these can never become confirmation evidence. Withdrawn:
0xa51d9000–0xa51dcfff, the destroyed first attempt.
What is not established. Nothing here is evidence that any policy is stronger. This approach produced no games under a benchmark tier, no paired comparison, and no bound of any kind.
What this taught us, and what is still open
- A capability that ships disabled is not a capability. The panel record is the exact thing six of this repository's seventeen failed learned models are recorded as having lacked, and it was present in the generator and off in every run that mattered. It is worth checking, for any corpus, what was actually written rather than what the writer supports.
- Behaviour mixing is not successor closure. Both give a model variety; only one gives it the alternatives at a position it will actually be asked about. The planner distillation work later built a corpus that does have full sibling coverage — 108,462 labelled columns with zero gaps, and found that closing this gap, on its own, was not enough to beat the reference search.
- Sizing a seed range in games rather than addresses is a real hazard, and the recorded process change — print the arithmetic and check it against the allocation table before launch — came from this approach's own incident.
- Still open: whether re-running the generator with the panel enabled, on a fresh training lease, would produce data worth the disk it costs. Nothing here answers that, and the later distillation result is a reason to be cautious rather than optimistic.
PanelRecordV2 (--panel2): sibling panels with CRN continuation outcomes
Added 2026-08-23 for EX-20260823-sol-corpus-and-offline-gate-4d3d86e4
(P-SOL-v1, stage G0). The v1 record layouts and code paths are untouched;
with the flag absent the generator's state and panel output is byte-identical
to the pre-change binary (gated against a pristine build from git HEAD in
runs/RUN-20260823T215500Z-sol/g0/gates.log).
The technical record
Record layout — 992 bytes per root, fixed stride. A 96-byte header
(version 0x0200, sequential seed-order recordId, originSeed for split
bookkeeping only, moveIndex, root next disc / moves-to-rise / legal mask,
behaviour chosenColumn, fair-D4 referenceColumn computed only when
recordId % 16 == 0 else 255, engineId 0=D1 / 1=D2 / 2=D3 N7M6, K, H,
panelFlags bit0 = reference computed bit1 = ε-root, the 49-cell root board,
24 zero pad) followed by seven 128-byte sibling slots: resolved one-step
afterstate (afterBoard, afterNextDisc, afterMovesToRise, survived,
legal, clears/reveals/max-depth, i32 afterScoreDelta — same true-tape
semantics as the v1 panel), then u8 contLifetime[K], u8 contDeathRise[K]
(0 = censored at H, else rise bin 1–12), then u32 contClearsTotal,
u32 contRevealsTotal, zero pad to 128. Illegal columns keep their slot,
fully zeroed.
Implementation completion, not a protocol change. The design's
per-sibling table left everything after contDeathRise[K] as pad. Per the
work-package instruction, the layout was completed so each legal sibling
carries every continuation summary the labels need: the KM hazard-vector
inputs (per-rise survival counts over K) and the restricted-mean lifetime sum
are derivable exactly from contLifetime/contDeathRise (censoring is type I
at fixed H, so the KM restricted mean equals the mean capped lifetime), the
censor count is #{deathRise == 0}, and the clears/reveals totals over the K
continuations occupy 8 bytes of the former pad. Consequence: K ≤ 30
(the design said "K ≤ 32 supported"; this experiment uses K = 6 and K = 16).
CRN continuation tapes never see the seed. Continuations run in the
canonical (mirror-normalized) orientation of the public root. The tape seed
for continuation j is mix32(FNV1a(canonical board, next disc, moves-to-rise) ^ 0x50534f4c ^ (j+1)·0x9e3779b9), and move ordinal t of a
continuation reseeds its own Mulberry32 stream via
mix32(tapeSeed ^ 0x434f4e54 ^ (t+1)·0x85ebca6b), so cascade length cannot
desynchronize siblings and alignment is by continuation move ordinal. The
same tape serves all 7 siblings and every engine. Because the tape and the
engines are pure functions of the canonical root, mirror invariance of the
labels is exact (gate C), and regenerating a recordId reproduces
byte-identical labels (gate B). This differs deliberately from the design
text's Hash(originSeed, moveIndex, j): the frozen lease record
(SL-20260823T215000Z-a5216000) and the work-package instruction both fix
tapes to public root hashes with dedicated domains, never the seed, which
also makes the labels a pure function of the deployable information boundary.
Engines. D1/D2 are the frozen parameterized fair search at depths 1/2
(5 strata, work 3,200,000, cache 60,000); D3 N7M6 is the C0 arm exactly as
retained in runs/RUN-A525-reveal/d3-n7-m6.json (depth 3, N=7, M=6, work
51,084,852, cache 87,025). All three run through a faithful copy of
reveal-sampling/search.cpp's FactoredSearch (whose gate B proved the
M=1 collapse is bit-identical to the single-knob search).
Lifetime semantics. contLifetime counts moves survived from the root,
the sibling placement being the first move: 0 if that placement is
immediately terminal, H if alive at the horizon (then contDeathRise = 0,
censored); otherwise contDeathRise = min(12, rises completed at death + 1).
Root sampling. The grid is every --panel-stride-th move from
--panel2-start (design: stride 4 from move 8); --panel2-roots R keeps R
evenly spaced grid roots per game (floor((i+0.5)·n/R); R = 1 keeps the
median grid root — the disclosed G0 ladder rule producing one root per
behaviour game), 0 keeps all. Output is written in seed order with
deterministic recordIds for any thread count.
Readers and gates. approaches/lifetime-objective/p-sol/panel2_reader.py
(numpy reader + KM helpers), check_panel2.py (legality/layout gate),
ladder.py (within-root Kendall τ between engines with cluster bootstrap by
origin, seed 0xb0071eaf).