Pre-registered — not yet run. Two small open-weight models from different families, with different tokenizers. The sender reads a change and the rules once; the receiver must make the EXP 005 gate decision from the sender's KV cache, mapped into its own, without a forward pass over that context. EXP 008 measures whether that matches text re-prefill closely enough, and fast enough, to be worth having, on public baselines only. Written, hashed and published before the counted run. Nothing in this record may change after publication; a later change is a new, dated amendment, refuted and published first, and it resets the not-before.

The question

On the 60 EXP 005 changes and 140 new EXP 008-X changes, can a receiver model from another family, with another tokenizer, make the EXP 005 gate decision from a sender's mapped KV cache, with no forward pass over the context, nearly as accurately as text re-prefill, and faster once the mapping time is counted?

A lab handoff the factory could adopt, not a measurement of the factory: no KV-compatible sender-receiver pair is deployed on the floor today. The local decoders the factory runs are hybrids (some layers attention, some recurrent or feed-forward only), and their non-attention state has no published mapping. Where this sits in the longer plan: the factory-intelligence horizon.

The changes

Three lengths of context

One builder makes every context from hashed parts (manifest sha256 2b8746a8491a5e644ff470658c11afbe8ff86e79c9b7a1b3af67379655ae3ae5): S is rules + "\n\n" + diff (276–796 tokens), M is rules + "\n\n" + base/ + "\n\n" + diff (3159–3704), L is rules + "\n\n" + base/ + "\n\n" + distractor header + distractor lines + "\n\n" + diff (15744–16245). L is primary. L is padding: each L context is M plus the first lines of a public, lint-clean library file, sized to 16K tokens. The padding is 77–80% of every L context. L tests the mechanism at length, not a real factory context of that size. The padding is the start of lodash.js 4.17.21 (MIT, sha256 52f2358d3c8eccc8f030fab6acf868f46b8b88045898589391f1630d2e1ed95c). The lint found 0 problems in 600 contexts.

The models

PairSenderReceiverUse
D1Qwen/Qwen3-1.7B at 70d244cc86cc (28 layers × 8 KV heads × 128; upstream)unsloth/Llama-3.2-3B-Instruct at 006f5dcd1393 (28 layers × 8 KV heads × 128; unsloth mirror (approved 2026-10-01))primary
D1′Qwen/Qwen3-4B at 1cfa9a720891 (36 layers × 8 KV heads × 128; upstream)unsloth/Llama-3.1-8B-Instruct at 4699cc75b550 (32 layers × 8 KV heads × 128; unsloth mirror (approved 2026-10-01))pre-stated fallback; replaces D1 only by the pair-lock rule
D2unsloth/Llama-3.2-3B-Instruct at 006f5dcd1393 (28 layers × 8 KV heads × 128; unsloth mirror (approved 2026-10-01))Qwen/Qwen3-1.7B at 70d244cc86cc (28 layers × 8 KV heads × 128; upstream)reported only
D3Qwen/Qwen3-1.7B at 70d244cc86cc (28 layers × 8 KV heads × 128; upstream)unsloth/gemma-3-1b-it at 5b11413a10db (26 layers × 1 KV heads × 256; unsloth mirror (approved 2026-10-01))reported only

Which pair is counted. After this record is frozen and before the not-before, A0 is measured for D1 on stratum L, with the frozen readout and the current render, on the practice set c001–c020 (seen items, excluded from every claim and gate). If D1's A0 gets fewer than 15 of the 20 right (accuracy below 0.75), D1′ (Qwen3-4B → Llama-3.1-8B) replaces D1, mechanically; otherwise D1 stays. The decision and its rows are written to pair-lock.json before the not-before. No other rule chooses the pair. An earlier 5-item D1 practice run (stratum L, an older render, c001, c002, c003, c004, c005) is not the lock test: its A0 answered ACCEPT on all 5 (3 of 5 correct). Applied after this record was frozen: D1's A0 on stratum L got 11 of 20 practice items (c001–c020) right, with 0 abstentions, below the bar of 15, so D1′ (Qwen3-4B → Llama-3.1-8B) is the counted pair. The rows and the decision are in experiments/latent-handoff/pair-lock.json. In the lock run, D1's A0 answered ACCEPT on all 20 practice items, so its 11 right answers are the 11 GREEN items. The record was edited after the lock run started: see the edits after the lock run started.

The decision

The gate question in the receiver's own chat template. The decision is the softmax over the two single-token answers at the first assistant position: REJECT when p(YES) / (p(YES) + p(NO)) ≥ 0.5. No sampling. An out-of-memory error, or a probability that is not a number, is an abstention and counts as a wrong answer for its arm. The question is EXP 005's: “Does this change break any of these rules?” followed by “Answer with exactly one word: YES or NO.” Readout sha256 8c4c78285bb6c09ec5ce3c560344a20985013fadef57faaf3e7dc5e4cab74f24; receiver chat templates by sha256: llama-3.2-3b 5816fce10444…, llama-3.1-8b e10ca381b1cc…, qwen3-1.7b a55ee1b16601…, gemma-3-1b 7de1c58e208e….

The arms

The transfer interface is transfer(senderCache, senderTokenOffsets, contextText) → receiverCache. A transfer may read the context text to align tokens; it may not run any forward pass over the context. The zero-prefill counter wraps the receiver: in a KV arm the receiver forwards exactly the suffix (the question and the chat-template tail) and nothing else, or the run fails.

ArmWhat the receiver gets
A0text re-prefill of the whole prompt
A1sender summary (<= 256 tokens, frozen prompt, lint-checked) as the context
A2aChar-boundary token alignment plus a per-layer ridge map from sender to receiver K and V. Our extension of arXiv 2608.03893, labelled as ours wherever it appears.
A2bProcrustes alignment, our simplified reimplementation of HeteroFold (arXiv 2609.32259), closed-form fit, without its output-aware calibration stage. Built from the paper's description: the authors released no code, so no authors' mappers were available at the freeze and none are used.
A3Reserved: a slot for any further transfer method behind the same interface. No method is assigned to it, and this record makes no claim about one. Using it needs a dated amendment.
C1the receiver's own prefix cache through the transfer plumbing; must match A0 at KL < 1e-3
C2a deranged cache: the receiver's prefix cache of another item (seed 8008)
C3no context: the question alone
BEXP 005's model-free baselines, run unchanged: heuristic lint 0.9833 and heuristic grep 0.7 accuracy on EXP 005's 60 items; on EXP 008-X by corpus-x/baselines-x.mjs.

A2a and A2b are our own implementations: A2a is our extension of one published method; A2b is our simplified reimplementation of HeteroFold (arXiv 2609.32259), closed-form fit, without its output-aware calibration stage. No authors' mappers were available for either. A weak result from ours says nothing about the published methods themselves. transfer() is Engine.transfer in runner.py; its maths is kvmap.py (an MLX port of a torch reference). Calibration runs on the Mac (MLX, BF16) on public, MIT-licensed code text fetched by URL at pinned commits and checked by sha256, disjoint from EXP 005, EXP 008-X and the L distractor. No rented GPU (the pre-stated fallback when none is available); disclosed in limits. Mapper weights are hashed, not published (v1).

The parity gate. p01–p20: segments of the pinned held-out public file in calib-sources.json parityHeldOut, minus every calibration and evaluation line; never an EXP 005 / 008-X item. Mapping: kvmap MLX port on the GPU (split-precision float32 matmul) vs kvmap torch reference, float32 CPU. Readout: receiver and cache in float32 on MLX's CPU stream (true float32). Pass: decision agreement 20/20 AND max |p_mlx - p_torch| <= 1e-3, for each mapper; any mismatch fails the run. The gate proves the PORT is correct. A float32 readout removes the receiver's own rounding noise (bf16 activations, reduced-precision GPU float32 matmul), which would otherwise dominate a 1e-3 bar regardless of the port; the run-time bf16 sensitivity is recorded separately as a descriptive property of the arms, never as a gate. Result at freeze: D1: A2a 20/20, max |Δp| 8.3e-6, A2b 20/20, max |Δp| 1.3e-5; D1′: A2a 20/20, max |Δp| 5.0e-6, A2b 20/20, max |Δp| 8.3e-6. Both pairs passed this gate before this record was frozen; whichever pair the pair lock chooses is read out with the mappers bound here.

Every file that still carries a shorter A2b label (each hash-bound, so it stays as it is, and the label above supersedes it): arms.mjs, calibrate.py, kvmap.py, mapper-freeze.json, mappers-D1.json, mappers-D1p.json, evidence/mappers-D1-attempt1-gpu-reduced-precision.json.

The claim: P1

On arm A2b, stratum L, the locked pair (D1, or D1′ by the pair-lock rule):

P1 holds only if both criteria hold and every validity gate passes. Refuting P1 is the expected outcome, and it is published the same way.

Secondary claims

Reported, never a gate.

Secondary claims are directional and decide nothing.

When a result is uninformative

Failing a global gate makes every result uninformative; failing a per-arm gate makes that arm's results uninformative. P1 needs every global gate and A2b's per-arm gates. Uninformative is never a pass.

score.mjs scores rows; the confirmatory analysis (the exclusions, Wilson interval, paired bootstrap, A2b-vs-A0 agreement, gates and the re-run comparison) is a script written after this record, before the not-before, that implements exactly the definitions here. It is added by a dated amendment that changes no other field, is refuted and published before any counted item runs, and resets the not-before.

Fences against leakage

Memory, timing and spend

Edits after the lock run started

The lock run started seconds after the record was frozen. The record on this page is not byte-identical to the one in force then: it was revised afterwards, in disclosure, wording and date fields only. One of those disclosures, a third exposure found by a refute, shrank the analysed set from 180 to 175 items (and the fallback from 40 to 39). The pair-lock rule, its bar, its practice set and the files the run used are unchanged and are checked against this record. The lock's committed record, pair-lock.json, lists every edit; its last note gives the sha256 of the record in force when the run started. The commit that held that record is not on a public branch.

The practice set is 11 GREEN and 9 RED, so a receiver that always answers ACCEPT scores 11 of 20. Computed here, after the lock run, not part of the frozen record.

Amendment 01: the confirmatory analysis script

Amended 02 Oct 2026, before any counted run. The pre-registration's analysis field reads: “score.mjs scores rows; the confirmatory analysis (the exclusions, Wilson interval, paired bootstrap, A2b-vs-A0 agreement, gates and the re-run comparison) is a script written after this record, before the not-before, that implements exactly the definitions here. It is added by a dated amendment that changes no other field, is refuted and published before any counted item runs, and resets the not-before.” This is that amendment. No counted item has run: nothing of the counted run exists when it is written, and its tests use synthetic rows only. No field of the pre-registration changes. Its not-before is replaced by the later one below, as its analysis field requires.

The script is experiments/latent-handoff/analyse.mjs (sha256 bc7e0b560f7ac6b7a96c8b5e6528d9b5b0346102c98aaab0f1e68635b32fac2b); its tests are scripts/latent-handoff-analysis.test.mjs (sha256 9858ef4358326822b313800534852692d22a15cc64872eda352b03baca794bab). It reads the counted and re-run rows, the labels through score.mjs, the pre-registration, this amendment and pair-lock.json. Before it analyses anything it checks against its sha256 every file the pre-registration binds (its files and mapper code, the EXP 005 corpus sums, inputs, labels and rules, the EXP 008-X sums, the strata manifest, the pins, the readout, the pair-lock rows and the exposure and R1 evidence) and the script and tests this amendment binds. The amendment is published byte for byte as amendment-01.json, sha256 cf96ee3871282fdbf1a6b6d3b4ab1381dcc4305ac992e0a366f6f044bfb49f83; each clause there quotes the pre-registration's own words beside its implementation.

The run. The counted run goes stratum by stratum in this order, then the re-run in the same order, each through arms.mjs (its timing protocol, seeded arm order and memory gate unchanged). --peak-gb is the measured D1′ practice peak in footprints.json (36.89 GB). A paused run is resumed under the same run id; nothing is ever re-run to change a result. Counted: counted-d1p-L-01 (L), counted-d1p-M-01 (M), counted-d1p-S-01 (S); re-run: rerun-d1p-L-01 (L), rerun-d1p-M-01 (M), rerun-d1p-S-01 (S).

The not-before. No counted item runs before 24 hours after the merge of the odin-rnd pull request that publishes this amendment (its GitHub mergedAt). That time is recorded after publication. This replaces the pre-registration's not-before, which it can only move later. If any item of the counted run or the re-run started earlier, the analysis fails its global gate not-before and the whole result is uninformative.

Amendment 02: the zero-prefill counter fix and a fresh counted run

Amended 09 Oct 2026, before any analysed item has run. In the runner, every arm resets the receiver's forward counter before it runs, except one call: when an item's seeded arm order puts C1 before A0, the runner first computes A0 once, untimed, as C1's reference, and reset the counter only after that call. Its zero-prefill check then also counted the previous item's last arm, and stopped the run with a false zero-prefill violation. The model computation was unaffected (that call builds a fresh cache); only the count was stale. The first item after a process start has an empty counter, so it passed. The first counted attempt (counted-d1p-L-01) stopped 9 times, each time at that reference A0. The excesses over the prompt were 24 tokens five times, 54 three times and 15811 once, and each equals the previous item's last arm: a KV arm's 24-token suffix, C3's 54-token prompt, or a whole A0 prompt (that stop read 31624 tokens against a 15813-token prompt, after an item that ended on A0).

The fix. engine.receiver.reset() is called immediately before the untimed reference a0() in run_item (the existing reset after it is kept). Nothing else in the runner changes. A regression test, test_the_counter_does_not_carry_over_into_the_next_items_reference_a0 in experiments/latent-handoff/test_runner.py, covers it: Item N ends on a text arm, so its forward calls stay in the counter; item N+1 puts C1 before A0. The reference A0 must be checked on its own calls only. It fails on the old runner (a zero-prefill violation on that A0) and passes on the fixed one. The record binds runner.py by sha256, and its own rule reads: “Written, hashed and published before the counted run. Nothing in this record may change after publication; a later change is a new, dated amendment, refuted and published first, and it resets the not-before.” So this is a dated amendment. It rebinds experiments/latent-handoff/runner.py (95152adafb73 → 6d41e10b9670), experiments/latent-handoff/analyse.mjs (bc7e0b560f7a → a8013f13d72c), scripts/latent-handoff-analysis.test.mjs (9858ef435832 → 5d2fb88f7d2c); every other bound file is unchanged. analyse.mjs binds this amendment (its pin, the pre-registration as parent and amendment 01, by sha256, as the prior amendment), checks runner.py at the hash rebound below with every other file the record binds unchanged, reads the -02 run ids, and takes the not-before from this amendment's merge. Every statistical clause of amendment 01 is unchanged, byte for byte.

The run. Counted: counted-d1p-L-02 (L), counted-d1p-M-02 (M), counted-d1p-S-02 (S); re-run: rerun-d1p-L-02 (L), rerun-d1p-M-02 (M), rerun-d1p-S-02 (S). The attempt counted-d1p-L-01 ran under the old runner and recorded 133 rows (19 items × 7 arms), on c001–c019 only: seen items, which corpus.analysed excludes from every claim and gate. It is void. Its rows are never scored and never analysed (the analysis refuses its run id), and no score or analysis of them exists; they were read only to diagnose the stops (the token counts above). Each stored row passed its own zero-prefill check after its own reset; the 9 stops were false positives of the mechanism above. They are disclosed with it here and on the results page.

The not-before. No counted item runs before 24 hours after the merge of the odin-rnd pull request that publishes this amendment (its GitHub mergedAt). That time is recorded after publication. This replaces amendment 01's not-before, which it can only move later. If any item of the counted run or the re-run started earlier, the analysis fails its global gate not-before and the whole result is uninformative.

The amendment is published byte for byte as amendment-02.json, sha256 8d5f1f0761118d1ecc886f97cbc0c9716a295e19ad56b70381cce1c9ea41f6ec; each clause it touches quotes the pre-registration's or amendment 01's own words beside the change.

What this does not establish

Provenance

This page is rendered from preregistration.json, the committed record at experiments/latent-handoff/preregistration.json, whose sha256 is 638d1dda53e8ae6c9c129d34e6b83fa6642f7ef60b30de8da71e7660082f7f6c. The site publishes that file byte for byte, and a repository test re-renders this page from it and fails if they differ. A validator rebuilds the record from the harness files and refuses any difference; it pins 30 files by sha256.

The counted run has not started. The results, when they exist, will be judged against this record as published, and reported as they come out.