Task & notation
Task (diffuse). K=3 cells sit on a ring c1, c2, c3 (with c3 adjacent to c1) at random
digits. Each step, all cells update simultaneously to the sum mod 10 of their two ring neighbours:
ci(t)=(ci−1(t−1)+ci+1(t−1))mod10.
The rule runs for T steps; then one cell is queried, answered as a single digit \boxed{d}. The
query appears after the latent block, so the latents must carry the whole row.
Notation (latent-reasoning conventions). Sizes for this model: K = 3 cells, 10 digit
values (0–9), soft-token dimension d_vocab = K·10 = 30, model residual width 4096.
c(t) ∈ {0,…,9}^K — the ground-truth state (the K-cell row after step t); c_i(t) is cell i.
h — the residual stream (a 4096-vector at a token position).
z_t ∈ ℝ^{d_vocab} — the autoregressively-fed soft token at step t (t = 1..T), viewed as
a K×10 array of per-cell digit distributions: z_t[i] ∈ ℝ^{10} is cell i's distribution over the
10 digits (z_t[i,d] = P(cell i = d)). So .
The point: one reasoning step = one token
Each z_t is a single autoregressive position whose residual h holds the entire K-cell
row, and the model computes the next row c_{t+1} for all K cells in that one forward step. The
latent chain is a strict single-token Markov chain
prompt→z1→z2→⋯→zT→answer,
where z_1 reads the prompt, z_t (for t>1) attends only z_{t-1}, and the answer attends
only z_T. (Contrast the sibling markov-diffuse organisms, which spent K token positions per
step.)
Feedback (the soft token)
A head reads the residual h at z_t and emits the K per-cell digit distributions
z_t[i] ∈ ℝ^{10} (i = 1..K). These are embedded through a learned codebook
C ∈ ℝ^{K×10×4096} — one 4096-vector C[i,d] per (cell i, digit d) symbol — giving the vector
fed into z_{t+1}:
fed=∑i=1K∑d=09zt[i,d]C[
C is initialised from K·10 = 30 distinct in-distribution token embeddings so the K cells
occupy separable directions — a near-identical per-cell init makes the fed token a
positionally-ambiguous digit-sum and training stalls at chance.
Because every feedback is a combination of those 30 codebook vectors, it lives in mean(C) + U,
where U is the 29-dim span of the (centred) 30 codebook vectors — the "soft-token subspace" of
the 4096-dim residual (30 symbols → rank 29 after centring).
What the U-patch does (used in the results). With Q ∈ ℝ^{4096×29} an orthonormal basis of U
and projector P = Q·Qᵀ, for each fed vector v (this problem) and its donor counterpart w at the
same chain position, the U-patch sets v ← (I−P)·v + P·w (keep this problem's part orthogonal
to U, take the donor's part in U); the complement patch does the reverse, v ← P·v + (I−P)·w.
Only the fed soft-token vectors are touched (not the residual, head, or codebook). Since every fed
vector lies in mean(C) + U they share the same U⊥-part, so the complement swap is a no-op (answer
follows the prompt) and the U-swap moves the entire message (answer follows the donor).
Readout
The latents are the computation; the answer is decoded from the final token z_T by the same
per-cell head, selecting the queried cell. There is a single read-out path (the head) — no separate
text/LM route to bypass — so the answer is, mechanistically, this head decode of z_T's residual.
The probe in Figure 2 confirms each token's residual linearly encodes the whole row.
Training (annealing SFT)
LoRA (r=32, α=64) on a frozen bf16 Qwen3-8B; trainable: the adapter, the
per-cell head, the codebook C, and the step-1 query. Data is synthetic and unlimited.
- Loss = a single per-cell cross-entropy: the head predicts every cell of
c_t at every step
(including the queried cell at the final step), weighted 4×. Because
the answer is read from this same head, there is no separate answer objective that could bypass the
per-step state encoding (an earlier LM-head answer path did exactly that and had to be removed).
- Scheduled-sampling teacher forcing — with prob annealed
1 → 0 over 1500
steps, the fed token is the ground-truth one-hot row; then handed off to the self-generated chain.
- Mixed chain lengths — each step draws
T from [2, 3, 4, 5, 6], so the single per-step recurrence is
shared across lengths and extrapolates to longer chains at test time.
- Separable codebook init (see above) — the fix that lets the single token carry all
K cells.
- AdamW lr 0.0001, grad-clip 1.0; mastery curriculum (stop at acc ≥ 0.9
twice at
tf=0).

Training curve. The per-cell state decodability (orange) rises first — the head learns to
read the whole row from one token; the length curriculum (purple) then grows T from 2 to 6; and the
free-running readout (blue) reaches 1.0, all before teacher forcing (grey) finishes annealing to 0.
The separable codebook is what lets a single token hold the whole row.
Results — causal load-bearing battery
Run free-running, then intervene on the T-token chain and re-read the queried cell from z_T
via the per-cell head (n=400; latent_threads/eval_single_report.py).

Figure 1. (a) Shuffling the step-order of the chain or replacing it breaks the answer. (b)
Overwriting the chain with a different problem's tokens makes the answer follow that donor
(1.00) not the prompt (0.00); patching only the codebook subspace U (29-dim) reproduces it
(1.00) while its complement U⊥ does nothing (0.00). (c) Free-running accuracy vs chain
length T — the green band is the trained range; accuracy holds on longer chains.

Figure 2. (a) The answer read-out is near-deterministic (P(correct) 1.00). (b)
A linear probe recovers the whole K-cell row c_t from each single token's residual, reaching
1.00 by layer 36 — confirming one token holds all K cells.
Table with columns: measurement, value, what it shows| measurement | value | what it shows |
|---|
| free-running accuracy (trained length) | 1.000 | solves it (chance 0.10) |
ablate z_1 ↛ prompt | 0.040 | no input → chance |
| worst single-step noise | 0.040 | every step is load-bearing |
| shuffle step order | 0.465 | order matters |
| donor patch → donor / prompt | 1.000 / 0.000 | follows latents, not prompt |
Single-cell patch — does editing one c_i(t) change the answer the expected way?
The donor/subspace patches above swap the whole chain. This one is surgical: free-run to step
t, then overwrite one cell's distribution z_t[i] ∈ ℝ^{10} with the one-hot of a target digit
d ∈ {0,…,9} — i.e. force c_i(t) = d, leaving the other K−1 cells untouched — re-embed via the
codebook, and continue free-running so the change propagates through the learned CA rule; then
compare the answer to the true CA evolved from c(t) with cell i set to d.

The edit is causal and almost never ignored — the answer keeps its original value only
0.07 of the time. It matches the exact CA-propagated counterfactual the majority of the
time (0.56 overall, 0.73 at one propagation step; chance 0.10). It is not the clean
~1.0 of the whole-chain patch: forcing one cell to an arbitrary value yields an off-trajectory row
that the model's learned CA computes less reliably than a full in-distribution chain, and the
unpatched z_t residual still feeds the original cell via attention at the boundary. So a single
c_i(t) is load-bearing and propagates through the rule the majority of the time, though
counterfactual single-cell edits are not computed perfectly. A systematic sweep over all 15
(t,i) sites × 7 problems reproduces this (0.55 overall) and shows it is site-dependent —
s1.c1 propagates perfectly (7/7) while a few late/edge sites fall to ≈0.29
(eval_single_cellpatch.py --sweep).
Files & reproduction
LoRA adapter + tokenizer + single_extra.pt (per-cell head, codebook C, step-1 query) +
lt_cfg.json; interventions.png / decode.png / training.png; training_code/.
- Train:
python -m latent_threads.train_single --config latent_threads/configs/single_k3m6.json
- Eval:
python -m latent_threads.eval_single_report --ckpt <dir> --n 400
Part of the latent-threads collection. A clean, fully-controlled organism where latent
chain-of-thought is provably load-bearing and length-generalising, with one token per step.