The Control Arm Was Full of Chess
Before paying for 4,800 model answers, we made the intervention prove that it actually existed
The most misleading condition in our next experiment is called
no_information.
It contains a complete FEN. It tells the model whose turn it is. It preserves a
64-slot board panel, the number of legal actions, the byte length of every UCI
move, the response task, and the exact same submit_move function used by every
other arm.
What it does not contain is the visible identity of the pieces in the board panel or the moves in the legal-action panel. Those characters are replaced by same-length masks.
That is not “no chess information” in any ordinary sense. It is the structural control for a much narrower question:
Holding root state, action universe, task, panel shape, request length, and response interface fixed, what changes when a redundant board representation or the identities of the complete legal moves are made easier to access?
The distinction matters because a beautifully randomized experiment can still manipulate the wrong thing. A105 was already preregistered as a four-cell board-by-legal-list factorial. Its abstract design audit balanced 4,800 planned model cells. But a balanced spreadsheet is not an executable intervention. The real request compiler could still leak a move through its tool schema, omit an en-passant action, change one delimiter, serialize one arm differently, or “repair” a length mismatch with unregistered padding.
So before collecting an outcome, we built A105-R0: a deterministic compiler preflight that turns the causal design into bytes and then tries to break it.
It made no provider calls, no network calls, no chess-engine calls, and no model calls. That is the result reported here.
The experiment before the experiment
A105 has four conditions:
| Condition | Board panel | Legal-action panel |
|---|---|---|
no_information | Same-length masks | Same-length masks |
board_only | Visible root board | Same-length masks |
legal_list_only | Same-length masks | Complete visible legal list |
board_plus_legal_list | Visible root board | Complete visible legal list |
Every position is also compiled in two panel orders: board first and legal list first. Order is part of the registered presentation, not an accidental property of a dictionary or template engine.
For each fixed position and order, the compiler creates all four canonical JSON request envelopes. It records the exact byte spans occupied by the 64 board tokens and every legal UCI token. Then it compares each treatment with the masked control.
The pass rule is deliberately literal:
diff(no_information, board_only)
= registered board-token byte positions
diff(no_information, legal_list_only)
= registered legal-token byte positions
diff(no_information, board_plus_legal_list)
= their exact union
An extra changed comma fails. A changed task word fails. A missing legal move fails. A tool-schema enum fails. A one-byte drift outside a registered content span fails. The runner is not allowed to pad the request, drop a move, change the interface, or fall back to the historical conversational parser to make a test pass.
This is stronger than comparing total prompt lengths. Two requests can have the same number of bytes while changing several constructs at once. A registered span contract asks a harder question: which exact bytes are permitted to differ?
A control is allowed to preserve structure—but we must name it
Masking preserves more than length.
The board control still exposes a 64-square layout. The legal-list control still exposes how many legal actions exist and whether each UCI token has four or five characters. The common FEN already contains the full root state for a model that can parse it. The task announces the side to move. These are not bugs hidden by the audit. They define the estimand.
If the visible board arm improves a future outcome, A105 will not show that the model received otherwise unavailable state. The state was already present in FEN. It will show that, in this protocol, adding a redundant spatial representation changed the total system.
Likewise, a visible legal list does not merely add move identities. The masked arm preserves the action count and token-length pattern, while the visible arm makes each action directly addressable. A future contrast can estimate the effect of revealing those identities under this structural control. It cannot prove that legal enumeration is universally useful, that the model internally used each move, or that search improved.
This is why the condition name is less important than its full contract. A label
like no_information is convenient inside a factorial table. The publication
must say what remains visible.
Edge cases are where an interface confesses
The preflight uses 16 frozen instrumentation positions. They are not benchmark observations and they will never enter the A105 outcome analysis. Their job is to attack the compiler.
The fixture bank includes:
- White and Black to move;
- check evasion;
- legal castling;
- legal en passant;
- promotion moves with five-character UCI strings;
- opening, middlegame, sparse, and endgame structures; and
- legal-move counts in the
1–10,11–20,21–30, and31+buckets.
This catches failures a starting-position smoke test cannot. A four-character
mask is wrong for a7a8q. A legal-list generator that forgets en passant can be
complete on thousands of ordinary positions and still invalidate the exact
action-universe claim. Castling and check evasion test whether “legal” means
rules-legal in the current state rather than pseudo-legal or template-derived.
The legal list comes from python-chess rules enumeration, not an evaluator or
search engine. It is sorted canonically before rendering. The board panel is
emitted from a8 through h1 as one-byte piece symbols or empty-square tokens.
Visible board tokens must reconstruct the FEN placement; visible move tokens
must equal the complete legal set.
The fixture bytes are frozen in their own manifest. Changing a FEN, tag, order, or newline changes the fixture identity before the compiler runs.
The run manifest also binds the Python implementation and version, the
python-chess and jsonschema versions, and the complete dependency-lock hash.
Legal enumeration and schema validation are implementation-dependent software
operations; recording their identities makes the byte claim reproducible
without pretending that a package version is scientific evidence.
The response interface must not answer the question
A legal-list experiment can accidentally put the treatment inside the tool schema.
Suppose one arm defines move as an enum of legal UCI values while another
accepts an unrestricted string. The enum arm may improve syntax and execution
without the prompt panel doing anything. Worse, the action universe is now
available through a channel that the “masked” condition was supposed to hide.
A105-R0 therefore uses one required, non-enumerated function:
submit_move(move: string[4..5])
The schema exposes no legal-move enum, examples, defaults, constants, move-dependent pattern, candidate menu, or alternate branch. Its canonical SHA-256 is identical in all 128 fixture cells:
55a4e9c90cd7285ec8b91315e6ea53d1a026cd39478fd697feff5f82c3aed139
That hash is not evidence that a provider will serialize or enforce the schema identically. It proves the local compiler produced one schema. The realized campaign must still retain provider and model metadata and audit the request body actually sent.
What the preflight found
The frozen run compiled 16 fixtures across four conditions and two panel orders:
| A105-R0 quantity | Result |
|---|---|
| Rendered cells | 128 |
| Matched factorial blocks | 32 |
| Prompt bytes across cells | 54,704 |
| Canonical request bytes across cells | 110,640 |
| Registered board-treatment bytes across blocks | 2,048 |
| Registered legal-treatment bytes across blocks | 2,104 |
| Prompt-length mismatch blocks | 0 |
| Request-length mismatch blocks | 0 |
| Out-of-span treatment differences | 0 |
| Mask leaks | 0 |
| Board reconstruction failures | 0 |
| Legal-list failures | 0 |
| Tool-schema or action-universe mismatches | 0 |
| Schema failures or nondeterministic cells | 0 |
The cell manifest contains hashes, lengths, conditions, orders, legal-universe identities, and registered spans—not the provider payloads of a paid run. A second execution produced byte-identical artifacts.
The analysis report identifies itself as
pass_local_request_compiler_only. That suffix is the most important part of
the status.
Passing a compiler test does not validate a causal claim
A105-R0 closes one implementation risk: the registered manipulation can be constructed locally on the edge-case bank without unregistered byte drift.
It does not establish any of the following:
- that Luna or DeepSeek benefits from either panel;
- that a model attends to the revealed content;
- that provider-side serialization preserves the local envelope;
- that the final 300 positions satisfy the same contract;
- that the outcome evaluator is independent of the candidate source;
- that attrition will be balanced;
- that a move is good merely because it is legal; or
- that a local paired effect transfers to full-game Elo.
The final campaign remains blocked on the terminal DeepSeek freeze, joint pilot leakage qualification, the final 600-position benchmark freeze, a distinct outcome evaluator, binding the 300 A105 development and held-out positions, realized-request auditing, and a separate paid-call authorization.
When those inputs exist, the same compiler must be rerun on every bound position. The edge fixtures are a systems gate, not a substitute sample.
Why we were willing to stop here
It would have been easy to call the abstract A105 schedule “ready” and begin collecting model responses. The local compiler probably would have worked. But “probably” is exactly how a hidden interface difference becomes a causal story.
The preflight changes the project in three ways.
First, it makes the intervention executable before it is expensive. Second, it turns vague matching language into a machine-checkable set of allowed byte differences. Third, it sharpens the public claim: A105 is about redundant representation and action accessibility under a structurally rich control—not about creating information from nothing.
That is progress without an effect size.
The experiment has not answered whether a board helps. It has answered the question that must come first:
If a future difference appears, can we show that the intended content—and only that registered content—changed?
On the frozen fixture bank, the local answer is yes. The model answer does not exist yet.
Reproduce the preflight
From the repository root:
uv run python research/experiments/run_a105_renderer_preflight.py
uv run pytest -q tests/test_a105_information_renderer.py
The protocol is frozen in
A105_RENDERER_PREFLIGHT_PROTOCOL.md.
The strict metadata schema is
a105_rendered_cell.schema.json,
the generated analysis is
analysis.json, and the
metadata-only cell inventory is
cell_manifest.jsonl.
Every artifact preserves the same boundary: zero model outcomes, zero chess effects, and zero causal claims.
Operational paths, raw logs, credentials, mutable run metadata, and unpublished artifacts are intentionally excluded. Research references resolve to the versioned publication bundle rather than the host filesystem.