← All writing
Essay 17 / 20Protocol / design

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:

ConditionBoard panelLegal-action panel
no_informationSame-length masksSame-length masks
board_onlyVisible root boardSame-length masks
legal_list_onlySame-length masksComplete visible legal list
board_plus_legal_listVisible root boardComplete 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, and 31+ 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 quantityResult
Rendered cells128
Matched factorial blocks32
Prompt bytes across cells54,704
Canonical request bytes across cells110,640
Registered board-treatment bytes across blocks2,048
Registered legal-treatment bytes across blocks2,104
Prompt-length mismatch blocks0
Request-length mismatch blocks0
Out-of-span treatment differences0
Mask leaks0
Board reconstruction failures0
Legal-list failures0
Tool-schema or action-universe mismatches0
Schema failures or nondeterministic cells0

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.

Public-surface note

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.