resolver: the residue is fleet VISIT ORDER not id allocation, and the block records it; rule 26 gains (d), my 26(c) over-read corrected

This commit is contained in:
alex 2026-09-09 02:19:33 -04:00
parent fca5c6cd56
commit 05db2b30af
4 changed files with 521 additions and 11 deletions

View file

@ -1,16 +1,16 @@
# SotS RE campaign — coverage dashboard # 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 > **North star:** A functional reimplementation of the engine — behavior-equivalent, NOT byte-for-byte
## 1. Map coverage (campaign/board.md) ## 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 | % | | Status | Count | % |
|---|---:|---:| |---|---:|---:|
| verified | 339 | 79% | | verified | 341 | 79% |
| mapped | 42 | 10% | | mapped | 42 | 10% |
| in-progress | 4 | 1% | | in-progress | 4 | 1% |
| backlog | 41 | 10% | | 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 | | engine | 31 | 0 | 0 | 0 | 0 | 31 |
| verify | 104 | 15 | 3 | 35 | 0 | 157 | | verify | 104 | 15 | 3 | 35 | 0 | 157 |
| phase2 | 13 | 3 | 1 | 0 | 0 | 17 | | 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 | | other | 19 | 2 | 0 | 0 | 0 | 21 |
## 2. Binary understanding ## 2. Binary understanding
@ -133,11 +133,11 @@ Most recent open:
## 9. Delta since previous dashboard ## 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) - 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) - 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) - 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 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
<!-- dashboard-metrics {"verified": 339, "mapped_plus": 381, "targets": 428, "loc": 58647, "tests": 122, "checks": 4257, "addr_verified": 1160, "addr_total": 1291, "layouts": 384, "open_q": 26, "sa_closed": 45, "sa_left": 63} --> <!-- dashboard-metrics {"verified": 341, "mapped_plus": 383, "targets": 430, "loc": 58647, "tests": 122, "checks": 4257, "addr_verified": 1160, "addr_total": 1291, "layouts": 384, "open_q": 26, "sa_closed": 45, "sa_left": 63} -->

View file

@ -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: 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 | | 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 | | 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 <pre.sav>` - 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 idx1<idx0, idx3<idx2; B idx1>idx0, 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 |

View file

@ -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 <pre-turn.sav>` (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 <netid>=<systemId>/<minShipId>,…` (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=<path>`. `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 <netid>=<hex>`),
> 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=<path>`; 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 <pre.sav>` - 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 idx1<idx0, idx3<idx2; B idx1>idx0, 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.

View file

@ -388,11 +388,13 @@ mechanism.
> existing fleets. The per-process seed was present in all three. It reaches the save only through a > 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 > 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 > 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: > turn **for a player that has an AI client** (lane BP, 2026-09-09: `ad-turn27` has seven
> four players on `ad-turn27` carry `ResTNm == ''` and contributed nothing, because they have > non-human players and the engine builds **three** AI clients — `RunAI` fires for net ids
> `NumOwn == 0` and the engine builds **three** AI clients for seven non-human players — the > 32/496/512; the four NPC factions 528–576 get no client and their `ResTNm == ''` is inert. A
> signature is *necessary, not sufficient*, and a save-field predicate still has to be read > save-field predicate must be read against **who actually runs**, which is a property of the player
> against who actually runs); `NumDes` moving names a design turn; a batch of completions names a fleet-assignment turn; > 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. > 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; > 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` > 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 > 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. > 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 ## 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 The save corpus went from 11 to 19 in one evening as lanes finally manufactured workloads nobody had