diff --git a/campaign/DASHBOARD.md b/campaign/DASHBOARD.md
index 9ecf760..6905e55 100644
--- a/campaign/DASHBOARD.md
+++ b/campaign/DASHBOARD.md
@@ -1,16 +1,16 @@
# SotS RE campaign — coverage dashboard
-Generated 2026-09-09 06:15 UTC · `sots-re` @ c43f638,2026-09-09 · `sots-engine` @ 1654785,2026-09-09 (241 commits) · regenerate with `tools/dashboard.py`
+Generated 2026-09-09 06:19 UTC · `sots-re` @ fca5c6c,2026-09-09 · `sots-engine` @ 1654785,2026-09-09 (241 commits) · regenerate with `tools/dashboard.py`
> **North star:** A functional reimplementation of the engine — behavior-equivalent, NOT byte-for-byte
## 1. Map coverage (campaign/board.md)
-428 targets · mapped-or-better **381/428** `[█████████░] 89%` · verified **339/428** `[████████░░] 79%`
+430 targets · mapped-or-better **383/430** `[█████████░] 89%` · verified **341/430** `[████████░░] 79%`
| Status | Count | % |
|---|---:|---:|
-| verified | 339 | 79% |
+| verified | 341 | 79% |
| mapped | 42 | 10% |
| in-progress | 4 | 1% |
| backlog | 41 | 10% |
@@ -24,7 +24,7 @@ Generated 2026-09-09 06:15 UTC · `sots-re` @ c43f638,2026-09-09 · `sots-engine
| engine | 31 | 0 | 0 | 0 | 0 | 31 |
| verify | 104 | 15 | 3 | 35 | 0 | 157 |
| phase2 | 13 | 3 | 1 | 0 | 0 | 17 |
-| meta | 88 | 6 | 0 | 1 | 0 | 95 |
+| meta | 90 | 6 | 0 | 1 | 0 | 97 |
| other | 19 | 2 | 0 | 0 | 0 | 21 |
## 2. Binary understanding
@@ -133,11 +133,11 @@ Most recent open:
## 9. Delta since previous dashboard
-- verified targets: 336 → 339 (+3) · mapped-or-better: 378 → 381 (+3)
+- verified targets: 339 → 341 (+2) · mapped-or-better: 381 → 383 (+2)
- engine LOC: 58,647 → 58,647 (+0) · test files: 122 → 122 (+0) · checks: 4,257 → 4,257 (+0)
- addresses verified: 1,160 → 1,160 (+0) · recovered layouts: 384 → 384 (+0) · open questions: 26 → 26 (+0)
- standalone leaves closed: 45 → 45 (+0) · leaves still diverging: 63 → 63 (+0)
---
warnings: board.md: unknown types subsystems; mars-rng.md: no oracle total row parsed; mars-stream.md: no oracle total row parsed; mars-vfs.md: no oracle total row parsed
-
+
diff --git a/campaign/board.md b/campaign/board.md
index e1aa777..6ada17d 100644
--- a/campaign/board.md
+++ b/campaign/board.md
@@ -433,3 +433,5 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`).
| PROBE 1: the load->turn pair is NOT the continuation's - AR's second hash does not survive a load, and the load form is its own reproducible pair | meta | verified | high | 100% | 2026-09-09 | **Lane BQ**, VM145, `findings/control-flow/seed-exposure-forward-test.md`, probe 1b of the 09-09 resolution. Prediction committed before the run (commit be14266): `ar-oracle-A-pre.sav` (Frame 38, `15b99255...`) + LOAD + one End Turn reproduces `7a8b3d5e...`. **FALSIFIED.** It produces `4c356f5916f0e6e4...` (107,732 B), and it produces it in TWO FRESH `hooks=off` PROCESSES BYTE FOR BYTE, at both snapshot points (`(Autosave EndTurn).sav` `33e3009254f5acd1...` in both). So the load->turn procedure AGREES WITH ITSELF and DISAGREES WITH THE CONTINUATION. 23 leaves apart from AR's file and **17 of them are ONE AI FLEET**: `Flt[1570 "Zeta Fleet V"]` (PID 32) enters turn 39 parked at Alpha Centauri with full range; in AR's continuation the AI issued it a move order (FPlan Wpt 320, FtFlg 12->13, LocID 432->0, both ships Range 9.0->7.0) and in BOTH loads it issued nothing. Plus `Sys[432]` NumFlts 0->1 and `Flt[914 "Nu Fleet III"]` HLay True/False. **RULING: `ar-oracle-A-pre.sav -> 7a8b3d5e...` MUST NOT enter determinism-hashes.txt as a load->turn pair.** What IS certified, two processes: `ar-oracle-A-pre.sav -> (load, one End Turn) -> 33e30092.../4c356f59...` - and that is still the standalone's first trade-raid pair (four raiders parked, no encounter). **THE RESAVE CANONICALISATION IS EXONERATED**: the pre-turn round trip is byte-exact but for ONE leaf, `/CD[1]/NPrvVa` (+5.00 across a load). CONSEQUENCE: **the pre-turn save does not fully determine the next turn's AI fleet-task decisions** - something `Zeta Fleet V`'s order depended on is not in the file. Resolver case; probe 3's ledger on `ar-oracle-A-pre -> 39` taken BOTH by continuation and by load names the site |
| PROBE 1: turn 40 varies - but NOT by the research pick. `Auto Resolve Peacefully` does NOT bypass combat, and a pick with ONE candidate is not exposed | control-flow | verified | high | 100% | 2026-09-09 | Lane BQ, VM145. `ar-oracle-A-post.sav` (`7a8b3d5e...`, Player[32] `ResTNm == ''`) in two fresh `hooks=off` processes, one End Turn, `Auto Resolve Peacefully` in both. **The files DIFFER** (`56980aa0...` vs `b36ac0ec...`, 144 leaves) - the committed prediction's first clause holds, and so do `/Sim/RNG` identical (2,503-B block byte-equal), `/Sim/trdmgr` identical and every player-0 fleet identical. **THE MECHANISM CLAUSE IS FALSIFIED: ZERO TechTree leaves and ZERO ResTNm leaves differ.** Both processes picked **BIO_GrvAdpt**. The 144 leaves are a COMBAT: the AI's `Sigma Fleet VII` (PID 32) hit player 16's colony Venkman, the human committed no ships, and the PLANET's defences killed 2 raiders in one process and 1 in the other (50 leaves `crep`, 41 the AI fleet, 8 `Sys[224 "Venkman"]`, 20 `Player[16]` - the prediction's "every Player[16] leaf identical" is also FALSIFIED). Both runs completed the SAME two ships (+8848,+8864); only the losses differ (7744+7952 vs 8000). **So `Auto Resolve Peacefully` means "I commit no ships", NOT "no combat occurs" - it is not a determinism-preserving resolution, and resolution 09-08 §6's combat warning is now measured, not just retained.** AR's turn-38 peaceful auto-resolve reproduced because the other side had nothing to fight with. **WHY THE PICK DID NOT VARY - the producer run, `shim.cfg.l4research` already on the guest, no build**: producers A and B were entered once per AI player and BOTH RETURNED NULL for Player[32]; it reached producer C, the candidate walk; `TryResearchCandidate` was offered **ONE** candidate and took it - `airesult sel=1 candidatesTried=1 chose='BIO_GrvAdpt'` - and the `0x006b36e0 & 7` fallback rotation was never reached (FallbackIndex/FallbackArm both 0). **The outcome set is a singleton because the CANDIDATE LIST HAS LENGTH ONE.** Not the producer gate the resolution guessed - a COUNTING criterion, and a better one |
| Rule 26 (c) needs four corrections, all measured on one lineage in one session | meta | verified | high | 100% | 2026-09-09 | Lane BQ, from probe 1. (1) **`ResTNm == ''` is NECESSARY, NOT SUFFICIENT** for research exposure: it names a turn on which a pick is MADE; exposure needs `|candidates accepted by TryResearchCandidate| > 1`, and on `ar-oracle-A-post -> 40` it is 1, so the pick agreed across two processes. Lane AS's varying pick (64 vs 62) had a longer list. The sufficient condition is NOT readable from the pre-turn save. (2) **The predicate has a false-positive class, and it was visible BEFORE the run**: four NPC players (528 Alien Menace, 544 Peacekeeper Enforcer, 560 Von Neumann, 576 Independent Colony) carry `ResTNm == ''` on EVERY turn of this lineage INCLUDING the turns whose controls agree; all four have `NumOwn == 0`. Read the predicate only for a non-eliminated AI player with NumOwn > 0. (3) **The predicate has no COMBAT term and needs one** - turn 40 was exposed through `RunCombatRound`, and the pre-turn save names the condition as plainly as it names ResTNm: an AI fleet standing at an enemy colony. (4) **A fourth term is not a predicate on the save at all**: turn 39 ran one way by continuation and another way after a load, BOTH reproducibly, so exposure is a property of (save, procedure, LOAD HISTORY), not of (save, turn). Rule 26 (c)'s instruction "record the pair as (save, procedure, hashes)" was already right and is now load-bearing for a reason it did not anticipate |
+| THE PINNED-SEED RESIDUE IS A VISIT ORDER, IT IS WRITTEN INTO THE BLOCK, AND ONE RUN SEPARATES HEAP ORDER FROM A GLOBAL-RNG DRAW | meta | verified | high | 100% | 2026-09-09 | Resolver, on lane BP's falsified P1 (`findings/resolutions/2026-09-09-fleet-id-order-residue.md`). RULING ON BP'S RESTATEMENT: accepted and sharpened. The ids are NOT the variable - 1970/1986/2002 decode (id-allocation.md) to node 2 = Player[32]'s client, counters 123/124/125, minted in that order in every process. The variable is the ORDER in which AssignFleetsAndIssueOrders (0x006c16c0, pass 1) visits the ship groups needing a new fleet: A visited the group at Sys 384 first, B the group at Sys 80 first; 2002's group third in both. That order changes the id each group gets, the element order of lists 8/10 and the apply order (hence ShipIDs[] reordered) and NO decision on this turn. **AND IT IS WRITTEN INTO THE BLOCK**: list 10 = StrategyServer::OnCommand_TransferShips {systemId, fleetId, ships[]} records the assignment on every captured run, so C-EXACT SURVIVES LITERALLY with one more free input: (save, seeds, visit order) -> byte match; C-set's shape covers the uncaptured case (canonical order = one member of the outcome set). ENGINE: (1) `state_checksum --relabel-new-ids
` - bijection on that turn's new node-k ids matched by (location, ship set), master id lists as sets, Summary/Checksum masked WITH REASON, refuses to relabel anything in the pre-turn save; accept = BP's pair IDENTICAL modulo {1970<->1986}, AD's pair still DIVERGED; (2) game/ai visits groups canonically (ascending systemId, then min ship id) and mints in that order; (3) optional captured-order override for exact replay. RULING ON POINTER ORDER: not yet leading - ONE OF TWO. The record already names two unnamed AI-band consumers of the per-process global generator 0x00af6e58 (0x0079ea90+0x73 Chance, 0x005b9f00+0xc0; AI1's list) which the seed pin does not touch. What moved: pointer order now has a MEASURED PRECONDITION - BP's aielem dumps carry node heap addresses, and runs A and B (identical config, identical decisions) show the same run of consecutive same-size allocations in DIFFERENT relative address orders (list 3: A idx1idx0, idx3>idx2) - Win8+ LFH randomisation, live in this process. Precondition, not cause. Wall clock has no mechanism (AIProcessMinTime is a trailing Sleep; AI turn is synchronous in OnResumePlaying). THE PROBE (lane BR, VM146, one fresh process, no build, ~12 min): BP's shim.cfg.bppin + `airng=on` (pin_seed off) - the OnResumePlaying bracket attributes EVERY draw on EVERY generator in each client's turn by return address and generator (airngcensus rows, foreign_words/foreign_calls); lane I's inlined-draw inventory has NO site in the AI band so the observer is complete there; airng is measured behaviour-neutral (roll-parity.md). It is also run C. PREDICTIONS: P1 autosave in {e913ff41, 724528ff}; P2 foreign_words=0 in all three brackets, no census row on 0xaf6e58 or Seed record #2; P4 aiorders diff = noise set + at most the transposition. Foreign draw -> its retaddr names the consumer, follow-up = global pin (RNG_Seed(0xaf6e58,K) after Initialize) x2, predict identical. None -> RNG excluded by measurement; stage 2 = one static read of 0x006c16c0 + the gathering hub TO PLACE A HOOK on the group walk, log (element addr, systemId, minted id) in visit order x2 processes, predict order = ascending address. RUNG C restated (backlog text in §6); RUNG B not restated - value confirmed, replay is insulated by construction, and a `aiorders.deep=on` run of BP's pinned procedure landing on e913ff41 is the first rich-turn record. RULE 26(c): BP's wording corrected - the measured fact is "no client", NumOwn==0 is NOT shown to gate (496/512 have NumOwn==0 AND clients). NEW COROLLARY 26(d): when the instrument cannot be removed, pin to a natural run's observed values and require byte-identity with it before reading a pinned pair; the agreement is the pin's control AND a noise mask. No rule 29. STRUCTURAL FIX for BP's shim.cfg trap: an unnamed registered hook under a pin/capture config is a config error; print installed/registered counts. VM144 held for stage 2 / the global pin |
+| MY OVER-READ of lane BP, corrected by the resolver | meta | verified | med | 100% | 2026-09-09 | I amended rule 26(c) with "a player that has an AI client **and something to decide with** ... because they have `NumOwn == 0`". **The `NumOwn == 0` half is not measured.** The measured fact is only that the four NPC factions get **no client**, so their `ResTNm == ''` is inert. **Players 496 and 512 have `NumOwn == 0` AND have clients** - they held research targets, so the pick was not exercised either way, and whether a client with `NumOwn == 0` reaches the pick is **untested**. I read a gate into a correlation in a rule I was in the middle of correcting for over-claiming. Wording replaced with the resolver's |
diff --git a/findings/resolutions/2026-09-09-fleet-id-order-residue.md b/findings/resolutions/2026-09-09-fleet-id-order-residue.md
new file mode 100644
index 0000000..cd72ab4
--- /dev/null
+++ b/findings/resolutions/2026-09-09-fleet-id-order-residue.md
@@ -0,0 +1,494 @@
+# Resolution — the residue under pinned seeds is a visit order, it is written into the block, and it is not yet known to be RNG or heap
+
+- **Type:** resolution (course-setting; two new measurements taken here, from BP's logs already on disk)
+- **Trigger:** lane BP's P1 falsified — `findings/control-flow/pinned-seed-rich-turn.md` §3 (probe 2 of
+ `2026-09-09-seed-exposure-is-a-predicate.md`)
+- **Date / author:** 2026-09-09 · re-resolver
+- **Touches:** board rows 326 / 333 / 360 / 415; `backlog.md` §2 Rung B and Rung C; `method-rules.md`
+ rule 26 (c) wording, new corollary (d); `id-allocation.md` §5; `determinism-oracle.md`;
+ `verify/state-checksum/state_checksum.py` (a new mode); `src/shim/shim.cfg.cbpin`
+
+---
+
+## 0. Decision
+
+1. **BP's restatement is accepted and sharpened.** Given (save, per-client seeds) the AI's command
+ block on `ad-turn27` is determined **up to the order in which `AssignFleetsAndIssueOrders`
+ visits the groups that need a new fleet.** The ids are not the variable — the node-2 counter
+ hands out 1970, 1986, 2002 in that order in every process (§2.1). The visit order is. On this
+ turn the visit order changes exactly three things: which client-minted id each group receives,
+ the element order of lists 8/10, and — downstream — the apply order, which is why `ShipIDs[]`
+ comes out reordered. Every decision is invariant under it.
+2. **The residual input is not lost; it is written into the block.** List 10 carries
+ `{systemId, fleetId, ships[]}` per group, so any captured original run records its own visit
+ order for free. C-exact therefore survives **literally**, with one more per-run input that costs
+ nothing to capture: (save, seeds, **visit order as read off list 10**) → byte match. C-set's
+ shape covers the uncaptured case: our canonical order is one member of the original's outcome
+ set of orders. §3 says what the engine and the comparison tool must do.
+3. **The pointer-order reading is not yet the leading hypothesis — it is one of two, and the
+ other is already named in the record.** AI1's consumer list for the per-process global generator
+ at `0x00af6e58` includes two AI-band sites nobody has named (`0x0079ea90+0x73`, an `RNG_Chance`,
+ and `0x005b9f00+0xc0`); the seed pin does not touch that generator. What *has* moved since 09-08
+ is that pointer order now has a **measured precondition** (§2.2): in two processes with identical
+ configuration, BP's own dumps show the same run of consecutive same-size heap allocations landing
+ in different relative address orders. So the mechanism the reading needs is present in this
+ process. That is a precondition, not the cause.
+4. **One probe, one run, no build, VM146, ~12 minutes** (§4): BP's exact pinned procedure with
+ `airng=on` added (pin_seed off). The `OnResumePlaying` bracket attributes **every draw on every
+ generator** during each AI client's turn to a return address and a generator address, and the
+ inlined-draw inventory has no site in the AI band, so the observer is complete there. It is also
+ run C. **Prediction committed:** zero foreign draws in Player[32]'s bracket — the global does not
+ move — and the autosave is one of the two known files. If a foreign draw appears, its return
+ address names the consumer and the follow-up is a global pin; if none, RNG is excluded by
+ measurement and the follow-up is the container hook, placed by one static read.
+5. **Rung C is restated (§6). Rung B is not restated; its value is confirmed and it gains a
+ concrete target.** Replay applies ids off the wire in block order, so it is insulated from this
+ residue by construction. One `aiorders.deep=on` run of BP's pinned procedure that lands on
+ `e913ff41…` is the first fully reproducible rich-turn record — (pre-turn save, seeds, deep block,
+ autosave) — and the engine can be measured against it whenever the sim reaches that turn.
+6. **No rule 29.** BP's third run is the right practice and it belongs in rule 26 as corollary (d):
+ when the instrument cannot be removed, pin it to a natural run's observed values and require it to
+ reproduce that run before reading anything from a pinned pair. BP's amendment to (c) is right in
+ substance and overreaches in one clause; corrected wording in §7. The `shim.cfg` trap BP found
+ (an unnamed hook is silently installed) gets a structural fix, not a rule.
+7. **Lane BQ's result, which arrived mid-ruling, is folded in (§9) and does not change the probe.**
+ Image base is constant across processes on *both* guests (`0x00f40000` ×5 on VM145,
+ `0x005e0000` ×3 on VM146), so "pointer order" can only mean **heap** order, which §2.2 measures
+ as per-process. BQ's per-*history* input (a fleet moved by continuation, not after a load) is a
+ different input from BP's per-*process* one, and the probe holds history fixed (every run is a
+ load), so it is unconfounded. The leading mechanism for BQ's residue is **AI agent state the save
+ does not carry** (inference; `AISystem::Write` emits nothing), and the consequence for the rungs
+ is a scope, not a loss: **every byte-match claim is a claim about load→turn procedures.** Rung A:
+ BQ's reading is confirmed — the load-form pair enters `determinism-hashes.txt`, AR's continuation
+ hash does not.
+
+---
+
+## 1. The surprise, and the instrument
+
+Two fresh processes with the three AI client seeds pinned to identical words, identical build,
+identical config, identical input, identical pre-turn autosave, produced different turn-28 states —
+35 leaves, one transposition. The prediction was IDENTICAL.
+
+The evidence is as strong as reported, and stronger than a bare falsification, because the run that
+*should* have been the weak one — L, unpinned — is byte-identical to A. Instrument: fresh tree (rule
+24), lane worktree (rule 21), every registered hook off by name with the count in the log (27), zero
+unknown keys, three of three `pinned=1` in both pinned runs, identical `config:` lines, no wildcard
+hit, no fourth client. **The instrument is exonerated for the A/B difference** — the two processes
+carried the same instrument and one of them reproduced an unpinned run byte for byte, which a
+perturbing instrument could only do by a 1/k coincidence. Rule 26 applies to the A/B pair exactly as
+BP says: it does not agree with itself, so it certifies nothing. What carries weight is L = A.
+
+What no `hooks=off` control could have said here, stated so nobody looks for one: a pin cannot be
+applied without hooks, so "the un-instrumented pinned game" does not exist. The L/A agreement is the
+control this design admits (§7, corollary (d)).
+
+## 2. Two measurements this resolution adds, from BP's files
+
+### 2.1 The ids decode, and they say the counter is deterministic
+
+`id = (counter << 4) | node` (`id-allocation.md`, verified on twenty saves and the instruction stream).
+Player[32] is `PlyrIdx 1`, so its client is node **2**:
+
+| id | hex | counter | node | what |
+|---:|---|---:|---:|---|
+| 1970 | 0x7b2 | 123 | 2 | client-minted, first new fleet |
+| 1986 | 0x7c2 | 124 | 2 | second |
+| 2002 | 0x7d2 | 125 | 2 | third |
+| 7072 | 0x1ba0 | 442 | 0 | server id, an existing fleet |
+| 6976 / 5264 | 0x1b40 / 0x1490 | 436 / 329 | 0 | server-minted ships |
+
+So the AI minted three fleet ids at emission, counters 123–125 contiguous, in both processes. List
+10's elements in A are `(384,1970) (80,1986) (384,2002) (816,7072)`; in B `(80,1970) (384,1986)
+(384,2002) (816,7072)`. The id follows the visit; the group at system 80 was visited first in B and
+second in A. **The variable is the visit order of the first two groups**; 2002's group and the
+existing fleet came third and fourth in every process. (Two of three processes agree, so "third in
+every process" is two observations, not a law.)
+
+### 2.2 Same-size heap allocations land in different relative orders across processes — measured
+
+The `aielem` dump records each command node's heap address (`node=0x…`). Runs A and B have identical
+configuration and identical decisions, so the allocation sequence through the AI turn is the same
+(inference: supported by the identical outputs, not proved). Player 32's list 3 — nineteen nodes
+allocated in one pass — in visit order:
+
+- **A:** `f838 f538 f898 f4f8 f518 fa38 fc98 …` (idx1 < idx0, idx3 < idx2)
+- **B:** `e1c0 e320 e200 e380 e460 e500 e260 …` (idx1 > idx0, idx3 > idx2)
+
+Relative order differs between two processes for the same allocation sequence. That is what the
+Windows 8+ low-fragmentation heap does by design (randomised slot selection within a bucket), and it
+is the precondition any address-keyed container needs to walk in a per-process order. **Measured:
+allocation order is per-process here. Inferred: that some container on the fleet-assignment path is
+address-keyed.** The second half is what the probe chain settles.
+
+The image, by contrast, does not move: `exe base=0x005e0000` in all three of BP's processes on
+VM146 and `0x00f40000` in all five of BQ's on VM145 — per boot, not per process. So any "ASLR"
+reading that reaches for image-base randomisation is excluded on these guests; the only addresses
+that vary between processes are heap (and stack). "Pointer order" in this document means heap order.
+
+### 2.3 Time is not a live hypothesis with a mechanism
+
+`AIProcessMinTime` is a trailing minimum-duration `Sleep` (`ai-task-system.md`), not a slice; the AI
+turn runs synchronously inside `StrategyClient::OnResumePlaying` (lane PAR), and stepping order is
+save order × stable priority (AI2). No wall-clock input to the visit order is in the record. Not
+excluded — nothing is — but it has no mechanism and is ranked last.
+
+## 3. Ruling on the restatement, and what "canonicalisable" means concretely
+
+**Accepted, sharpened as in §0.1.** Two qualifications, both rule 23's shape:
+
+- "Every decision is invariant under the visit order" is a statement about *this turn*. A visit
+ order can change a decision wherever an earlier visit consumes something a later one wanted (an
+ escort, a build slot, the one move order). On `ad-turn27` the move in list 8 went to the group at
+ system 80 in both orders. The general claim needs the mechanism named and its scope read.
+- The outcome set of orders has been observed at size 2 from two distinct observations. Its true
+ size is unknown; run C (the probe run) is the first datum on it.
+
+**For `sots-engine`, three deliverables, ranked by what they unblock:**
+
+1. **Comparison side — `state_checksum.py --relabel-new-ids ` (sots-re, host
+ only).** Given the pre-turn save, compute the ids new in each post-turn save; among them, match
+ fleets with node nibble ≠ 0 by a content key that does not depend on the id — `(LocID or FPlan
+ destination, sorted ship-id set)` — and build the bijection π between the two saves. Apply π to
+ every id-valued leaf (`Flt[]` keys, `Sys[*]/Flt[*]`, ship→fleet references, `FleetIDs[]`
+ entries, list-8/10/14 words when comparing blocks), treat the master id lists `ShipIDs[]` /
+ `FleetIDs[]` / `DesignIDs[]` as sets, and report `IDENTICAL modulo π = {1970↔1986}` with π
+ printed. `Summary/Checksum` is derived and its inputs are unmodelled (rule 18's open item), so it
+ is masked *with that reason on the line* until the checksum is modelled. Acceptance: BP's
+ `bp-pinA` vs `bp-pinB` reads IDENTICAL modulo one transposition and nothing else; AD's
+ `ad-oracle-A/B-post` still DIVERGE (their difference is seed-driven and must not be absorbed).
+2. **Engine side — `game/ai`, at the point where a task forms a fleet from a ship group** (the
+ original: `AssignFleetsAndIssueOrders 0x006c16c0`, pass 1, minting via
+ `IDMap::AllocateOnLocalNode` on node `PlyrIdx+1`, emitting list 10 then any list 8/14 for that
+ fleet). Visit the groups in a **canonical order that is a function of the save alone** —
+ ascending `systemId`, then ascending minimum ship id in the group — and mint in that order. This
+ is deterministic across our own processes by construction and is one member of the original's
+ outcome set. Nothing else in `id-allocation.md` §5 changes: mint at emission, carry the id in
+ the command, the applier honours non-zero ids.
+3. **Engine side, exactness knob — an optional visit-order override** read from a captured block:
+ `--ai-visit-order =/,…` (or simply "take the list-10 order from the
+ `--turn-commands` capture when both are given"). With it, (save, seeds, order) reproduces the
+ original's exact bytes on every captured run — C-exact literal. Three words per new fleet, the
+ same cost class as `--ai-seed`. Build it after (1) and (2); it is what turns "modulo π" into "=".
+
+The comparison should never accept a relabelling that is not a bijection between *new* ids of the
+same node, and it should refuse to relabel anything present in the pre-turn save. That is the guard
+that keeps (1) from absorbing a real divergence.
+
+## 4. Hypotheses and the one probe
+
+| | hypothesis | mechanism in the record | what the probe shows |
+|---|---|---|---|
+| **H1** | an address-keyed container (or an unstable sort seeded by one) on the fleet-assignment path walks in heap order | precondition measured (§2.2); container **not** identified | zero foreign draws in the bracket; order then needs the container hook (stage 2) |
+| **H1b** | an uninitialised word read by a comparator on that path | this codebase leaves uninitialised words in command elements (L4/CB's noise set) | same as H1 at stage 1; stage 2 separates: visit order tracks neither address nor id |
+| **H2** | a draw on the per-process global generator `0x00af6e58` inside the AI turn — AI1's `0x0079ea90+0x73` (`Chance`) or `0x005b9f00+0xc0`, both unnamed; or on Seed record #2's object | named consumers exist; none has been seen firing on a turn | ≥1 `airngcensus` row with `rng != client+0x134` inside Player[32]'s bracket, with its return address |
+| H3 | wall clock | none (§2.3) | — |
+
+**The probe — lane "BR", VM146, one fresh process, no build.** `C:\SOTS\shimdist-bp` is still
+deployed with BP's build; take `shim.cfg.bppin` verbatim (seeds `32=156ebbbd,496=fe7b2826,
+512=0ed341d1` plus the wildcard, every hook off by name) and change exactly three lines: `airng=on`,
+`airng.pin_seed=off`, `airng.out=`. `aiorders` stays on with `deep=0` so the block is comparable
+with A/B. Load `ad-turn27-two-raiders.sav`, one End Turn, collect `(Autosave).sav`, `shim.log`, the
+`airng` output and the `aiorders` dump.
+
+Why this and not run C alone or probe 3's ledger alone: it **is** run C (a third pinned sample), and
+it **is** probe 3 on AD's state (the per-client ledger with `this`), and the `airng` detour has a
+measured neutrality — byte-identical to the published oracle in two fresh processes (`roll-parity.md`).
+The seven draw entry points are the only way to draw in the AI band: lane I's inlined-draw inventory
+has its eleven functions in the network, encounter, lobby and combat subsystems and **none between
+`0x680000` and `0x6e0000`**, so a draw the observer does not see during the bracket would have to
+come through a path the direct-call closure cannot reach — which the `left_delta − observed`
+residual on the client generator reports for that generator, and the census reports for every other.
+
+**Predictions, committed before the run:**
+
+- **P1.** `(Autosave).sav` ∈ {`e913ff41…`, `724528ff…`}. A third file means either the order
+ outcome set exceeds 2 or the added detour perturbed the turn; those are separated by one more run
+ with `airng=off` (plain run C), which cannot land on a third file under the first reading with
+ probability better than 1/3.
+- **P2.** In Player[32]'s `OnResumePlaying` bracket: `foreign_words=0 foreign_calls=0`, no
+ `airngcensus` row whose `rng` is `0x00af6e58` or Seed record #2's object, `residual=0`. The same
+ for clients 496 and 512.
+- **P3.** Player[32]'s own generator spends single-digit words in the bracket, at return addresses
+ in the AI band; 496 and 512 spend fewer. (Not load-bearing; recorded so the ledger has a number to
+ disagree with.)
+- **P4.** The `aiorders` dump differs from A and B only inside the proven noise set plus, at most,
+ the three-word transposition.
+
+**Course after the probe, either branch:**
+
+- *P2 fails* (a foreign draw in the bracket): the return address names the consumer. Follow-up is
+ the global pin — `RNG_Seed(0xaf6e58, K)` from the shim after `Application::Initialize` (09-08
+ resolution §6 named it for combat; ~20 lines), two fresh processes, prediction byte-identical. If
+ the draw is on record #2's object, pin that object's seed in the `Seed` detour instead.
+- *P2 holds*: RNG is excluded by measurement for the AI turn. Stage 2, one lane: read
+ `AssignFleetsAndIssueOrders 0x006c16c0` and the fleet-gathering hub it feeds (the 992-byte body
+ AI2 mapped; result vector at `[ebp-0x3c]`) far enough to find the loop that visits the ship groups
+ and the container it walks — that is a bounded static read *to place a hook*, not to derive an
+ answer (rule 18). Hook the loop; log `(element address, systemId, minted fleetId)` in visit
+ order; two fresh processes. Prediction under H1: visit order equals ascending element address in
+ both, and the two processes' address orders differ exactly where their visit orders differ. Under
+ H1b: order tracks neither. Either way the container's identity is what `game/ai` needs to know
+ it is canonicalising the right thing.
+
+Do not start stage 2's read before the probe returns; the probe is twelve minutes and a foreign
+draw would make the read moot.
+
+## 5. Standing claims
+
+| claim | verdict | scope / correction |
+|---|---|---|
+| Row 326 / 09-08: the AI client seed is per-process and is the cause of `turn1-state`'s variation | **survives unchanged** | L1's per-process seeding reproduced a fourth time (nine distinct words, three processes). |
+| Row 360: pinned seeds collapse three processes to one | **survives with scope** | True on `turn1-state`. On `ad-turn27` pinning collapses 94 leaves to 35 and every *decision* leaf; it does not collapse the fleet visit order. |
+| Row 415 / AD: "the variation is the AI's decisions" | **survives with a qualification** | Right for 59 of the 94 leaves and for every substantive choice (build at `Sys[272]`, fleet split, design, money); incomplete — the remaining 35 are a non-seed per-process visit order. |
+| 09-09 resolution §4 reading 3: "a non-seed per-process cause, not excluded on rich states" | **confirmed, and localised** | It is alive; it is confined to the fleet-assignment visit order on this turn; it is written into list 10. |
+| 09-09 §3: "AD's varying design is not the composer varying" | **survives, strengthened** | BP extends it from two save bodies to the *commands that created them*: list 1 identical including the name bytes. |
+| 09-09 §6 probe 2's cost/mechanism brief: "seven non-human players; wildcard or list every id" | **corrected** | Three AI clients (32/496/512); four NPC factions have no client. The wildcard would have pinned three and said nothing. BP's log run is what made the count a measurement. |
+| 09-08 resolution: "pointer-ordered tie under ASLR — withdrawn" | **survives as withdrawn for the research pick; re-opened as H1 for the fleet visit order** | Different decision, different consumer, and now a measured precondition (§2.2). It was wrong there; it is untested here. |
+| Backlog §2 C-exact: "nothing else per-process reaches the turn" | **overturned as written; restated (§6)** | One more per-process input reaches rich turns; it is observable in the block. |
+| Backlog §2 Rung B: replay of a captured block byte-matches | **survives, strengthened** | The residual input is in the block; replay applies ids off the wire in block order. |
+| `id-allocation.md` §5 (mint at emission; honour ids off the wire; the client counter is a function of allocation order) | **survives; one addition** | Add: the *visit order* that determines which group gets which counter is per-process in the original; `game/ai` visits canonically (§3.2). |
+| Rule 26 (c) as BP amended it | **survives with a wording fix** | §7. |
+| `determinism-oracle.md` "Qualified" | **needs the paragraph BP proposed** | Pinned seeds do not make a rich turn reproducible; the residual is the fleet visit order; comparison modulo π is defined in §3.1. |
+
+## 6. Rungs
+
+**Rung C bullet in `backlog.md` §2 — replace the C-exact sub-bullet with:**
+
+> - **C-exact.** Given the seeds captured from an original run (`sots_turn --ai-seed =`),
+> our AI's command block equals that run's block element for element and the autosave byte-matches
+> — **up to one further per-process input, measured on `ad-turn27` (lane BP, 2026-09-09): the order
+> in which the AI visits the ship groups that need a new fleet.** The seed determines every
+> decision on that turn (94 unpinned leaves → 35 pinned, all one transposition); the visit order
+> determines which client-minted id each group gets and the element order of lists 8/10. That
+> input is **written into the block** (list 10's `{systemId, fleetId, ships[]}`), so on every
+> captured run it is available for free: `(save, seeds, visit order)` → byte match, with the
+> engine visiting canonically by default (ascending system id, then minimum ship id) and taking
+> the captured order as an override for exact replay. Comparison is `state_checksum
+> --relabel-new-ids`: identical modulo a bijection on that turn's new ids, with the bijection
+> printed and `Summary/Checksum` masked with its reason. Precondition unchanged: the per-client
+> draw ledger. **Scope (lane BQ, 2026-09-09): every byte-match claim in this rung is a claim
+> about a load→one-turn procedure.** The original's AI carries state across turns that no save
+> holds (a fleet moved by continuation and not after a load, on the same file), so an engine run
+> from a save can only be compared with an original run from that save; a continuation is not a
+> target, by construction. **Open:** whether the visit order is heap order or a draw on the
+> per-process global generator (probe BR), and whether it can change a *decision* on some other
+> turn — on this one it cannot.
+
+and append to the mechanism paragraph:
+
+> The residual visit order is not the seed and not the client stream. It is per-process, and the
+> only mechanisms in the record are an address-keyed container (heap allocation order is measured
+> per-process in this game — BP's node addresses) or a draw on the global generator at
+> `0x00af6e58`, which the seed pin does not touch. One run separates them.
+
+**Rung B bullet — append, no restatement:**
+
+> **2026-09-09.** Lane BP's result makes the case for this rung from the other side: with seeds
+> pinned, the only thing that still varies between processes is a visit order that the block
+> itself records (list 10 carries the fleet id each ship group received). Replay applies ids off the
+> wire in block order and is insulated from it by construction. The first rich-turn record is one
+> `aiorders.deep=on` run of BP's pinned procedure away: if it lands on `e913ff41…` it is
+> (`ad-turn27-two-raiders.sav`, seeds `32=156ebbbd,496=fe7b2826,512=0ed341d1`, deep block,
+> `e913ff41…`) — the workload that completes thirteen ships, creates a design and colonises a
+> system, which no replay can be measured on until the sim runs `ProcessTurn` on a state that rich.
+
+Rung A: untouched.
+
+## 7. Rules
+
+**Rule 26 (c) — BP's amendment, corrected.** BP's inserted clause reads "for a player that has an AI
+client and something to decide with … because they have `NumOwn == 0` and the engine builds three AI
+clients". The measured fact is the first half only: **the four NPC factions have no client, so their
+`ResTNm == ''` is inert.** `NumOwn == 0` is not shown to be a gate — players 496 and 512 have
+`NumOwn == 0` and *do* have clients (they held targets, so the pick was not exercised either way). Replace
+the parenthetical with:
+
+> (lane BP, 2026-09-09: `ad-turn27` has seven non-human players and the engine builds **three** AI
+> clients — `RunAI` fires for net ids 32/496/512; the four NPC factions 528–576 get no client and
+> their `ResTNm == ''` is inert. A save-field predicate must be read against **who actually runs**,
+> which is a property of the player kind, not of the field. Whether a client with `NumOwn == 0`
+> reaches the pick is untested: 496/512 held targets.)
+
+**Rule 26, new corollary (d) — draft:**
+
+> **(d) When the instrument cannot be removed, make it reproduce a natural run.** A seed pin needs
+> hooks, so "the un-instrumented pinned game" does not exist and rule 19's control is unavailable.
+> Lane BP's answer: run the save once *unpinned* with the seeds logged, pin the next processes to
+> **those observed values**, and require a pinned run to reproduce the unpinned one **byte for
+> byte** before anything is read from a pinned pair. That agreement is the pin's own control — a
+> perturbing pin could match a natural run only by a 1/k coincidence — and it is a **noise mask**:
+> any word that differs between two runs with identical outputs cannot be a word the applier reads
+> (lane CB's argument). On `ad-turn27` that mask reduced a 9-block, 81-element comparison to three
+> words. The wildcard form of a pin (every client to one constant) is a valid intervention and a
+> worse one: it certifies nothing about a turn that happened, and it would have hidden that the
+> save's seven non-human players produce three clients. Pin to what was observed; count what ran.
+
+Ranked by damage: none — nothing wrong was published; BP stopped where the brief said to stop and the
+localisation is the best this campaign has produced on an AI turn. That is the brief working, not a
+rule. Corollary, not rule 29, on the same grounds as last time.
+
+**A structural fix instead of a rule, for BP's inherited defect.** Under `hooks=trace`, a registered
+hook *not named* in the config is silently installed in trace mode; `shim.cfg.cbpin` omitted six. The
+fix is in the shim, not in discipline: when any pin or capture module is on (`aiseed=pin`,
+`airng.pin_seed`, `aiorders=on`), an unnamed registered hook is a **configuration error** that aborts
+before `Application::Initialize`, and `shim.log` prints the installed-hook count next to the
+registered count on every run (BP read "27 of 28 off" by hand; make the shim print it). Also fix the
+wrapped header comment on `shim.cfg.cbpin` line 4 and add the six `hook.*=off` lines. Engine item,
+one lane-hour.
+
+## 8. Course
+
+**VM146 — lane BR, the probe, now.** Brief, one paragraph: *BP's dist is still deployed at
+`C:\SOTS\shimdist-bp`. Copy `shim.cfg.bppin` to `shim.cfg.brprobe` and change exactly three lines:
+`airng=on`, `airng.pin_seed=off`, `airng.out=`; diff the two configs' non-comment lines and quote
+the diff in the report (BP's own check). Verify `aiseed pinned=1` ×3 and `airng: bracket … MH_OK` in
+`shim.log` before reading anything. One fresh process, load `ad-turn27-two-raiders.sav` (Load-dialog
+row 1 at (400,260) per BP's row-62 note; wait 12–16 s before trusting a wall frame), one End Turn,
+collect `(Autosave).sav`, `shim.log`, the airng output, the aiorders dump. Report: the autosave hash
+and which of `e913ff41…` / `724528ff…` it equals; for each of the three brackets the `airng` row
+(`left_delta`, `observed`, `residual`, `foreign_words`, `foreign_calls`) and every `airngcensus` row
+whose `rng` is not that client's `+0x134`, with return addresses; the aiorders diff against A and B
+under BP's noise mask. Predictions are §4's P1–P4. If a foreign draw appears, stop and post its
+return address; if none, stop and post the row. Do not read the binary. Do not compare any pinned
+file with AD's unpinned files.*
+
+**VM146, same lane, after the probe, if the guest is still warm:** one more run of `shim.cfg.bppin`
+with `aiorders.deep=on` and `airng=off` — the Rung B record (§6). Report only the hash and whether the
+deep dump follows list 10's ship vectors and list 8's routes.
+
+**VM144 — lane "BS", BQ's per-history residue, now (§9.3).** Two instrumented runs on the AR
+lineage, both routes to turn 39: *(continuation)* load `ar-turn37-816raiders.sav`, End Turn, Auto
+Resolve Peacefully, End Turn; *(load)* load `ar-oracle-A-pre.sav`, End Turn. Config: `aiorders=on`
+(`deep=off`), `airng=on` (pin_seed off), `aiprobes` on every task `Execute` entry the probe table
+carries, every template hook off by name. Both routes have self-agreeing controls, so instrument
+neutrality is checkable at k = 1: the continuation must give `15b99255…`/`7a8b3d5e…`, the load
+`33e30092…`/`4c356f59…`. Predictions in §9.3. If either hash misses, stop: the instrument is not
+neutral on that route and the run is void. Stage 2 of BR (the container hook) or the global pin goes
+to whichever of VM144/VM146 frees first.
+
+**Lane AZ (VM141) — no redirect.** BQ has released VM145; its next work is BS if it wants it, since
+it holds the click path and the `Done`-pixel test for that lineage.
+
+**All lanes, one line:** *when you record pre-turn exposure facts next to a hash, record which
+players have an AI client (the `RunAI` net-id list, or "not logged"), not just `ResTNm`/`NumOwn` —
+and record the route (load or continuation) as part of the procedure.*
+
+**Host, no VM — three items, in order:** (1) `state_checksum.py --relabel-new-ids` per §3.1, with
+BP's pair as the positive acceptance and AD's pair as the negative; (2) the shim config-strictness fix
+and `shim.cfg.cbpin` repair per §7; (3) `id-allocation.md` §5 gets the canonical-visit-order item and
+`determinism-oracle.md` gets BP's "Qualified" paragraph with §3.1's definition of "modulo π".
+
+**Docs to correct in place (rule 11):** `raid-gate-multiplicity.md` §5 footnote — the 94 leaves are
+59 seed + 35 visit-order; `ai-order-emission.md` §4.5 and `turn-command-replay.md` §4 row 5 — list 10
+is `StrategyServer::OnCommand_TransferShips` (lane W3 named it; the AI-side notes still call it
+"unnamed"); `pinned-seed-rich-turn.md` §2.4's "a player with no colonies has no research income and
+does not reach the pick" — mark as inference; the measured fact is "no client";
+`determinism-oracle.md` round-trip rule — BQ's `/CD[1]/NPrvVa` correction (+5.00 per load; not
+covered by `--mask resave`); `determinism-hashes.txt` — the load-form AR pair (§9.3), route on the
+line.
+
+## 9. Lane BQ's result, folded in
+
+BQ's `seed-exposure-forward-test.md` landed while this was being written. Three of its results bear
+on this case; each is ruled here rather than in a second document, because they are one picture.
+
+### 9.1 The frame of the probe is not wrong, but there are two residues and they get two names
+
+- **BP:** two processes, *same route* (both loads), **disagree** → a **per-process** input.
+- **BQ:** two processes, same route (both loads), **agree**; the *other route* (continuation from
+ turn 37) disagrees with both → a **per-history** input, and *no* per-process input on that turn.
+
+These are different inputs in the same layer, and the second is consistent with the first: BP's
+residue lives in the visit order over groups that need a *new* fleet, and AR's turn 39 formed none
+(two ships into existing `Freighters` fleets). The visit-order model therefore predicts, after the
+fact, exactly what AR and BQ measured — no per-process variation on a turn with nothing to visit.
+Retrospective, so worth one line and no more.
+
+Probe BR holds the route fixed (every run is a load, as BP's were), so it is unconfounded by BQ's
+input. BQ's proposal — the per-client ledger on `ar-oracle-A-pre → 39` by both routes — is the right
+probe for the *second* input and is adopted as lane BS (§8), with two additions: `aiorders=on`, and
+the task-entry probes. The reason is §9.2: the leading mechanism for a per-history input is not a
+draw, and a ledger alone would report it only indirectly.
+
+### 9.2 What the per-history input can be, ranked — inference throughout
+
+1. **AI agent state the save does not carry.** `AISystem::Write` emits nothing and the three
+ `AIAgent` save blocks are cache (backlog §4.1); tasks, claims and goals created on turn 38 exist
+ on turn 39 only in the process that ran turn 38. A task that took `Zeta Fleet V` on turn 38 and
+ issued its move on 39 is exactly a continuation-only order. This is the same class as two things
+ already in the record — the `0x0069dbb0` fuse (`agent+0x36c` not persisted) and the client id
+ counter (`id-allocation.md` §3.2, restarts on load). Three named siblings; leading.
+2. **Client generator stream position** — *rejected as the mechanism.* Both routes are
+ seed-insensitive on this turn (AR's three continuation processes had three seed sets and agreed;
+ BQ's two loads had two and agreed), so no draw-dependent decision is what differs.
+3. **Server-side state not in the save** — the sim reproduces by load on the canonical pair and on
+ BQ's own 1b pair; nothing in the record points here. Last.
+
+**Predictions for lane BS, committed:** (a) the continuation's turn-39 block for pid 32 carries a
+list-8 element for fleet 1570 and its list-14 pair, and the load's block does not; (b) a task
+`Execute` entry probe fires in the continuation and not in the load — or the same task fires in both
+and gathers a different fleet set; (c) `airng` shows the two routes' per-client word counts
+differing by at most that task's cost, with zero foreign draws in both; (d) the pre-turn autosaves
+differ only in `/CD[1]/NPrvVa`. If (a) holds and (b) shows no task difference, the gathering hub's
+fleet set is the next place to look, and that is BR's stage 2 territory — the two lanes converge on
+the same 992-byte function from two directions.
+
+### 9.3 Rung A — BQ's reading confirmed
+
+`ar-oracle-A-pre.sav → (load, one End Turn) → (Autosave EndTurn) 33e30092… / (Autosave) 4c356f59…`
+enters `determinism-hashes.txt` as the standalone's first trade-raid pair, with the route, the
+exposure facts (Player[32] `BIO_EnvTail` held, no design, two completions into existing fleets, four
+raiders parked, no encounter) and the `NPrvVa` mask on the line. `ar-oracle-A-pre → 7a8b3d5e…` does
+**not** enter as a load→turn pair; AR's two-turn continuation from `ar-turn37-816raiders.sav` stays
+certified as *that* procedure (three processes) and is not an engine target, because the engine runs
+from a save. This is the §6 scope line: every engine comparison is load→turn. The canonical pair
+`turn2-state → turn3-state` already satisfies it — it was reproduced by load (CB, ID §3.2) and
+matched the continuation, which is the case where the agent carried nothing that mattered.
+
+### 9.4 Rule 26 (c) — BQ's four amendments, ruled, and the consolidated text
+
+(i) **accepted:** `ResTNm == ''` names a pick; exposure is `|candidates| > 1`, not readable from the
+save but readable from one `airesearch=on` run of it. (ii) **accepted with the §7 wording fix** —
+the false-positive class is "no AI client", and `NumOwn` is a proxy that has not been shown to gate
+(496/512). (iii) **accepted:** the combat term, and BQ's sentence for it. (iv) **accepted and
+generalised:** exposure is a property of (save, route); (c)'s "(save, procedure, hashes)" already
+carries it, and the route is part of the procedure.
+
+Replace the predicate sentence of rule 26 (c) — from "rule 28's shape:" to the semicolon before
+"one consumer (`0x0069dbb0`)" — with:
+
+> rule 28's shape, with the terms measured so far: **(1)** `ResTNm == ''` at turn start, for a
+> player that actually runs an AI client (a property of the player kind — on `ad-turn27` and the AR
+> lineage seven non-human players yield three clients, and the four NPC factions' `ResTNm == ''`
+> is inert), names a turn on which a pick is **made**; it is **exposed** only if the candidate walk
+> is offered more than one candidate, which one `airesearch=on` run of the save reads
+> (`candidatesTried`; lane BQ: 1 on `ar-oracle-A-post`, and the pick agreed); **(2)** `NumDes`
+> moving names a design turn; **(3)** a batch of completions that forms *new* fleets names a
+> fleet-assignment turn, and on such a turn a per-process visit order survives the seed pin (lane
+> BP); **(4)** an AI fleet standing at an enemy colony names a combat turn, and `Auto Resolve
+> Peacefully` means "I commit no ships", not "no combat occurs" — the planet fights and
+> `RunCombatRound` is per-process (lane BQ); **(5)** the route is part of the procedure: the same
+> file reached by continuation and by load can run its next turn differently, both reproducibly
+> (lane BQ), so a continuation's hash is never a load→turn target;
+
+---
+
+## Proposed `campaign/board.md` rows
+
+New row:
+
+```
+| THE PINNED-SEED RESIDUE IS A VISIT ORDER, IT IS WRITTEN INTO THE BLOCK, AND ONE RUN SEPARATES HEAP ORDER FROM A GLOBAL-RNG DRAW | meta | verified | high | 100% | 2026-09-09 | Resolver, on lane BP's falsified P1 (`findings/resolutions/2026-09-09-fleet-id-order-residue.md`). RULING ON BP'S RESTATEMENT: accepted and sharpened. The ids are NOT the variable - 1970/1986/2002 decode (id-allocation.md) to node 2 = Player[32]'s client, counters 123/124/125, minted in that order in every process. The variable is the ORDER in which AssignFleetsAndIssueOrders (0x006c16c0, pass 1) visits the ship groups needing a new fleet: A visited the group at Sys 384 first, B the group at Sys 80 first; 2002's group third in both. That order changes the id each group gets, the element order of lists 8/10 and the apply order (hence ShipIDs[] reordered) and NO decision on this turn. **AND IT IS WRITTEN INTO THE BLOCK**: list 10 = StrategyServer::OnCommand_TransferShips {systemId, fleetId, ships[]} records the assignment on every captured run, so C-EXACT SURVIVES LITERALLY with one more free input: (save, seeds, visit order) -> byte match; C-set's shape covers the uncaptured case (canonical order = one member of the outcome set). ENGINE: (1) `state_checksum --relabel-new-ids ` - bijection on that turn's new node-k ids matched by (location, ship set), master id lists as sets, Summary/Checksum masked WITH REASON, refuses to relabel anything in the pre-turn save; accept = BP's pair IDENTICAL modulo {1970<->1986}, AD's pair still DIVERGED; (2) game/ai visits groups canonically (ascending systemId, then min ship id) and mints in that order; (3) optional captured-order override for exact replay. RULING ON POINTER ORDER: not yet leading - ONE OF TWO. The record already names two unnamed AI-band consumers of the per-process global generator 0x00af6e58 (0x0079ea90+0x73 Chance, 0x005b9f00+0xc0; AI1's list) which the seed pin does not touch. What moved: pointer order now has a MEASURED PRECONDITION - BP's aielem dumps carry node heap addresses, and runs A and B (identical config, identical decisions) show the same run of consecutive same-size allocations in DIFFERENT relative address orders (list 3: A idx1idx0, idx3>idx2) - Win8+ LFH randomisation, live in this process. Precondition, not cause. Wall clock has no mechanism (AIProcessMinTime is a trailing Sleep; AI turn is synchronous in OnResumePlaying). THE PROBE (lane BR, VM146, one fresh process, no build, ~12 min): BP's shim.cfg.bppin + `airng=on` (pin_seed off) - the OnResumePlaying bracket attributes EVERY draw on EVERY generator in each client's turn by return address and generator (airngcensus rows, foreign_words/foreign_calls); lane I's inlined-draw inventory has NO site in the AI band so the observer is complete there; airng is measured behaviour-neutral (roll-parity.md). It is also run C. PREDICTIONS: P1 autosave in {e913ff41, 724528ff}; P2 foreign_words=0 in all three brackets, no census row on 0xaf6e58 or Seed record #2; P4 aiorders diff = noise set + at most the transposition. Foreign draw -> its retaddr names the consumer, follow-up = global pin (RNG_Seed(0xaf6e58,K) after Initialize) x2, predict identical. None -> RNG excluded by measurement; stage 2 = one static read of 0x006c16c0 + the gathering hub TO PLACE A HOOK on the group walk, log (element addr, systemId, minted id) in visit order x2 processes, predict order = ascending address. RUNG C restated (backlog text in §6); RUNG B not restated - value confirmed, replay is insulated by construction, and a `aiorders.deep=on` run of BP's pinned procedure landing on e913ff41 is the first rich-turn record. RULE 26(c): BP's wording corrected - the measured fact is "no client", NumOwn==0 is NOT shown to gate (496/512 have NumOwn==0 AND clients). NEW COROLLARY 26(d): when the instrument cannot be removed, pin to a natural run's observed values and require byte-identity with it before reading a pinned pair; the agreement is the pin's control AND a noise mask. No rule 29. STRUCTURAL FIX for BP's shim.cfg trap: an unnamed registered hook under a pin/capture config is a config error; print installed/registered counts. VM144 held for stage 2 / the global pin |
+```
+
+Edits to existing rows:
+
+- **Row 360** — take BP's proposed scope note, then append: `RESOLVER 2026-09-09: the residual is the AI's fleet-group VISIT ORDER, observable in list 10 of the block; C-exact survives with it as a third input. Heap order vs global-RNG draw separated by lane BR (VM146).`
+- **Row 415** — take BP's proposed append; add: `The 35 are a visit order, not a decision; see the resolver row of 2026-09-09.`
+- **Row 326** — take BP's proposed append unchanged.
+- **Row 333** — replace BP's proposed append with: `C-EXACT RESTATED 2026-09-09 (resolver, on lane BP): determined up to the fleet-group visit order in AssignFleetsAndIssueOrders, which the block itself records (list 10); engine visits canonically, comparison relabels new ids (state_checksum --relabel-new-ids). Rung B unchanged and strengthened.`
+- **Row 424** (AR's oracle pair) — take BQ's proposed append unchanged; add: `Resolver 2026-09-09: confirmed. The load-form pair enters determinism-hashes.txt; the continuation form is not an engine target. Every rung's byte-match claim is load->turn.`
+- **BQ's three proposed new rows** — accept as written, with one wording change in the third: replace `Read the predicate only for a non-eliminated AI player with NumOwn > 0` with `Read the predicate only for a player that actually runs an AI client (RunAI's net-id list); NumOwn is a proxy not shown to gate - 496/512 have NumOwn == 0 and clients.`
+- **BQ's `determinism-oracle.md` correction** (`/CD[1]/NPrvVa` +5.00 per load) — accept; `--mask resave` gains the leaf with its reason, and `--relabel-new-ids` (§3.1) must compose with it.
+- **Row 62** — VM146: lane BR (probe, then the deep Rung-B record); VM144: lane BS (BQ's per-history residue, both routes, `aiorders` + `airng` + task-entry probes); VM145: FREE (BQ released, restored); VM141: AZ unchanged; VM140 untouched.
diff --git a/guides/method-rules.md b/guides/method-rules.md
index 5207fc6..fffb94e 100644
--- a/guides/method-rules.md
+++ b/guides/method-rules.md
@@ -388,11 +388,13 @@ mechanism.
> existing fleets. The per-process seed was present in all three. It reaches the save only through a
> decision whose outcome set is not a singleton given the state, and whether such a decision is
> reached is a **predicate on the pre-turn save** — rule 28's shape: `ResTNm == ''` names a pick
-> turn **for a player that has an AI client and something to decide with** (lane BP, 2026-09-09:
-> four players on `ad-turn27` carry `ResTNm == ''` and contributed nothing, because they have
-> `NumOwn == 0` and the engine builds **three** AI clients for seven non-human players — the
-> signature is *necessary, not sufficient*, and a save-field predicate still has to be read
-> against who actually runs); `NumDes` moving names a design turn; a batch of completions names a fleet-assignment turn;
+> turn **for a player that has an AI client** (lane BP, 2026-09-09: `ad-turn27` has seven
+> non-human players and the engine builds **three** AI clients — `RunAI` fires for net ids
+> 32/496/512; the four NPC factions 528–576 get no client and their `ResTNm == ''` is inert. A
+> save-field predicate must be read against **who actually runs**, which is a property of the player
+> kind, not of the field. **`NumOwn == 0` is NOT shown to be a gate** — 496 and 512 have
+> `NumOwn == 0` *and* clients; my earlier wording said it was, and that was an over-read of BP's
+> report); `NumDes` moving names a design turn; a batch of completions names a fleet-assignment turn;
> one consumer (`0x0069dbb0`) has a 3–40-turn fuse and cannot show inside two turns of a load.
> So: record the pair as (save, procedure, hashes) and write the exposure facts next to the hashes;
> never extend a certified pair by a turn without its own two-process agreement (`ar-oracle-A-post`
@@ -423,6 +425,18 @@ mechanism.
> So (c) is a **useful screen, not a decision procedure**: it can tell you a control is *likely* to
> vary, and it cannot certify that one will agree. A resolver holds the restatement.
+> **(d) When the instrument cannot be removed, make it reproduce a natural run.** A seed pin needs
+> hooks, so "the un-instrumented pinned game" does not exist and rule 19's control is unavailable.
+> Lane BP's answer: run the save once *unpinned* with the seeds logged, pin the next processes to
+> **those observed values**, and require a pinned run to reproduce the unpinned one **byte for
+> byte** before anything is read from a pinned pair. That agreement is the pin's own control — a
+> perturbing pin could match a natural run only by a 1/k coincidence — and it is a **noise mask**:
+> any word that differs between two runs with identical outputs cannot be a word the applier reads
+> (lane CB's argument). On `ad-turn27` that mask reduced a 9-block, 81-element comparison to three
+> words. The wildcard form of a pin (every client to one constant) is a valid intervention and a
+> worse one: it certifies nothing about a turn that happened, and it would have hidden that the
+> save's seven non-human players produce three clients. Pin to what was observed; count what ran.
+
## 27. A ratchet is meant to break when the corpus grows — do not move it, and scope your control
The save corpus went from 11 to 19 in one evening as lanes finally manufactured workloads nobody had