From 05db2b30af3025cbb1930f0f3a168d987f9e776f Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 9 Sep 2026 02:19:33 -0400 Subject: [PATCH] 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 --- campaign/DASHBOARD.md | 12 +- campaign/board.md | 2 + .../2026-09-09-fleet-id-order-residue.md | 494 ++++++++++++++++++ guides/method-rules.md | 24 +- 4 files changed, 521 insertions(+), 11 deletions(-) create mode 100644 findings/resolutions/2026-09-09-fleet-id-order-residue.md 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