BS: predictions for the per-history residue, committed before the runs

This commit is contained in:
alex 2026-09-09 02:33:01 -04:00
parent a05f54d2b0
commit 15c44711c1

View file

@ -0,0 +1,215 @@
# The per-history residue — the same save, two routes to turn 39, both reproducible
- **Type:** control-flow / determinism (two instrumented runs; one build, no static reading)
- **Owner / date:** lane **BS** · 2026-09-09 · guest **VM144** (`sots-re-win10-144`, 192.168.10.144)
- **Spec:** `findings/resolutions/2026-09-09-fleet-id-order-residue.md` §8 (VM144 paragraph) and §9
- **Tests:** §9.2's ranked candidates for the per-history input; §9.3's committed predictions (a)–(d)
- **Depends on:** `findings/control-flow/seed-exposure-forward-test.md` (lane BQ — the residue, the
click path, the `Done`-pixel test); `findings/control-flow/pinned-seed-rich-turn.md` (lane BP — the
*per-process* residue, which this is not)
- **Rules exercised:** 2 (predict first), 15, 19, 20, 21, 24, 25, 26 and its corollary (c), 28
---
## 1. Predictions, committed before the build was deployed and before the game was launched (rule 2)
*Everything in §1–§3 was written and committed before `binkw32.dll` was copied onto VM144 and before
any hash was read. It is not edited afterwards, only annotated with verdicts in §5.*
### 1.1 What is at stake
Lane BQ measured something the campaign had no model for: **`ar-oracle-A-pre.sav` runs turn 39 one
way when the state is reached by continuation and another way when it is reached by a load, and both
routes are reproducible.**
| route | processes | `(Autosave EndTurn)` | `(Autosave)` |
|---|---|---|---|
| continuation — load `ar-turn37-816raiders.sav`, End Turn, Auto Resolve Peacefully, End Turn | 3 (lane AR) | — | `15b99255…` (F38) then **`7a8b3d5e…`** (F39) |
| load — load `ar-oracle-A-pre.sav`, End Turn | 2 (lane BQ) | **`33e30092…`** | **`4c356f59…`** |
23 leaves apart, **17 of them one AI fleet**: `Flt[1570 "Zeta Fleet V"]` (PID 32). Both routes'
turn-39 input has it parked at Alpha Centauri (`LocID 432`, `HFPlan False`, `FtFlg 12`, both ships at
`Range 9.0`) — and so does turn 37's save, so it had already sat still for a turn. The continuation
issued it a move order on turn 39; **both** loads issued nothing.
So the pre-turn save does not fully determine the next turn's AI fleet-task decisions. §9.2 ranks the
candidates and this lane's job is to say which survives:
1. **AI agent state the save does not carry** — `AISystem::Write` emits nothing; the three `AIAgent`
save blocks are cache. A task created on turn 38 exists on turn 39 only in the process that ran
turn 38. Same class as the `0x0069dbb0` fuse and the client id counter. **Leading.**
2. **Client generator stream position** — *rejected by the resolver*: 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).
3. **Server-side state not in the save** — nothing in the record points here. Last.
### 1.2 P0 — the neutrality gate, and it is checkable at k = 1 here
Rule 19 asks for the measurement with the instrument removed. Unusually, **both** routes already have
`hooks=off` controls that agree with themselves, so rule 26 is satisfied on both sides and an
instrumented run agreeing with one is *not* a 1/k coincidence — k is 1 on each route.
> **P0 — committed.** With `shim.cfg.bsrun` installed:
>
> * **continuation:** the `(Autosave).sav` after End Turn #1 is `15b99255e1f03dab…` and the
> `(Autosave).sav` after End Turn #2 is `7a8b3d5eb3a60eba…`;
> * **load:** the `(Autosave EndTurn).sav` is `33e3009254f5acd1…` and the `(Autosave).sav` is
> `4c356f5916f0e6e4…`.
>
> **If either route misses either hash, that route is VOID.** The instrument is not neutral on it and
> nothing — not one probe count, not one `airng` row — may be read from it. This is stated as a stop
> condition, not as a caveat.
### 1.3 The measurements — §9.3's (a)–(d), committed as mine, plus one of my own
> **P1 (= §9.3 a) — the block.** In the **continuation's turn-39** `aiorders` batch, the block for
> `pid=32` carries a **list-8 element whose first word is 1570** and at least one **list-14 element
> whose first word is 1570**. In the **load's turn-39** block for `pid=32`, **neither is present.**
>
> Basis: in lane BP's dump the first word of a list-8, list-10 and list-14 element is the fleet id
> (list 8 idx0 `ints=[ 2002 … ]`, list 14 idx0 `ints=[ 1970 0 … ]`), and the save-side difference BQ
> measured is exactly a `FPlan` with one waypoint (`Wpt 320`) appearing only in the continuation.
> **P2 (= §9.3 b) — the task.** The two routes' `aiprobe` counts for the turn-39 window differ in at
> least one row, and the difference is on a **task `Execute` entry** (probe idx 2–9) — either a task
> that fires in the continuation and not in the load, or the same task firing with a different
> `AcquireFleetsForTask` (13) / `IsClaimedByAnotherTask` (14) count. Probe 11
> (`AssignFleetsAndIssueOrders`) is entered in **both** routes, and probe 0 (`RunTaskList`) is
> non-zero in both — without which every other row is meaningless (rule 20 is built into the report).
> **P3 (= §9.3 c) — the draws.** In every `OnResumePlaying` bracket on **both** routes:
> `foreign_words=0`, `foreign_calls=0`, `residual=0`, and no `airngcensus` row whose `rng` is
> anything but that client's `+0x134`. The two routes' per-client word counts differ by at most the
> cost of the task P2 names. **RNG is not the mechanism**, and this is the measurement that says so
> rather than the inference.
> **P4 (= §9.3 d) — the pre-turn states.** The continuation's own frame-38 autosave and the load
> run's `(Autosave EndTurn).sav` differ in **exactly one leaf**, `/CD[1]/NPrvVa` (+5.00), under
> `--floats bits --mask resave`. Restated: the two routes enter turn 39 from *the same state*, so
> whatever differs is not in the file. (BQ measured this without an instrument; if it moves under
> mine, the instrument moved it and P0 will have said so first.)
> **P5 — mine, the exposure fact the standing instruction asks for.** `airng` emits one bracket row
> per `StrategyClient::OnResumePlaying` entry carrying `agent=`, so it reports **which clients
> actually run an AI turn**. I predict **three** clients run on both routes — `pid` 32, 496 and 512 —
> and that players 528/544/560/576 produce **no bracket row with a non-null agent**, matching lane
> BP's count of three clients from seven non-human players on the same lineage. `NumOwn == 0` gates
> nothing: 496 and 512 have `NumOwn == 0` and are predicted to run.
**How each can be wrong, and the symptom** (rule 2):
| way it could be wrong | symptom | what it would mean |
|---|---|---|
| the residue is not in the AI's emission at all | **P1 fails in the null direction**: both routes' pid-32 blocks are element-for-element identical | the divergence is downstream of `BuildTurnCommands` — in the server's application of the block, or in `ProcessFleetMovement`. **This relocates the question and is the more valuable outcome**, because it would exonerate the whole AI-agent-state candidate in one run |
| the block differs but no probe does | P1 holds, **P2 fails** | the task set is the same and the *gathering* differs — the fleet set `AcquireFleetsForTask` returns. That is BR's stage-2 territory (`AssignFleetsAndIssueOrders 0x006c16c0` and its 992-byte hub), and the two lanes converge on one function from two directions |
| a draw is involved after all | **P3 fails**: a non-zero `foreign_words`, or a census row on `0x00af6e58` | the resolver's rejection of candidate 2 was wrong; the return address names the consumer and the follow-up is a global pin |
| the routes do not enter turn 39 from the same state | **P4 fails on a leaf other than `NPrvVa`** | there is a *save-visible* difference and the whole "not in the file" framing is wrong. This is the one failure that would overturn BQ's headline rather than extend it |
| the instrument perturbs | **P0 fails** | the run is void; report the miss and stop |
| more or fewer than three clients run | **P5 fails** | the client count is a property of the state, not of the lineage, and rule 26 (c)'s "read the predicate against who actually runs" needs a per-save count |
**A null result is a real result.** If both routes produce identical AI fleet-task probe sequences
*and* identical blocks, I will report that as the finding, not hunt for a difference elsewhere.
### 1.4 What this lane will not settle (rule 15)
* **One process per route.** Each route's reproducibility is already established by two or three
`hooks=off` processes (AR, BQ); this lane adds the *instrument*, not a third control. So it can say
what the AI emitted on each route; it cannot add evidence about whether a route is reproducible.
* **No static reading, no build of `game/ai`.** If the answer is "the fleet set differs", naming the
container is BR's stage 2, not this lane's.
* **The `0x0069dbb0` fuse cannot fire** in a load→one-turn run by construction, so the load arm says
nothing about it. The continuation arm runs two turns from a load, which is still inside the
3–40-turn fuse window, so neither arm bears on it in either direction.
* **The research probes (16–19) are not installed.** BQ measured `candidatesTried=1` and an agreeing
pick on this lineage; four fewer detours is four fewer ways to fail P0. So this lane says nothing
about the research half.
* **Turn 38's encounter is auto-resolved peacefully on the continuation arm, and that is only safe
here because the other side has nothing to fight with** (lane BQ: `Auto Resolve Peacefully` means
"I commit no ships", not "no combat occurs"; on a later turn a *planet* fired and killed a different
number of raiders in each process). AR's turn-38 encounter reproduced across three processes, which
is the only reason this procedure is admissible at all. **It does not generalise.**
* **`aiorders.deep=off`**, so list 8's route vector and list 10's counted vector are behind pointers
and are *absent* from the dump. The fleet id is in the element window; the waypoint list is not.
---
## 2. Exposure facts, read off the inputs before the runs (rule 26 (c), with the route)
Read with `verify/state-checksum/state_checksum.py`'s leaf walk over the typed tree. The standing
instruction: record **which players have an AI client**, and record **the route** as part of the
procedure. The client list is a *run-time* fact, so it is predicted in P5 and reported in §4; what the
saves carry is below.
### 2.1 `ar-turn37-816raiders.sav` — Frame 37, the continuation arm's input
| PID | name | NPC | `ResTNm` | `NumOwn` |
|---|---|---|---|---|
| 16 | `re` (human) | no | `''` | 3 |
| **32** | **The Eternal Empire** | no | **`BIO_EnvTail`** | **18** |
| 496 | Spengler | yes | `DRV_PlsFiss` | 0 |
| 512 | Spengler | yes | `XNC_TrnsMorr2` | 0 |
| 528 / 544 / 560 / 576 | Alien Menace / Peacekeeper Enforcer / Von Neumann / Independent Colony | yes | `''` | 0 |
`Flt[1570 "Zeta Fleet V"]` (PID 32) on **turn 37**: `LocID 432`, `HFPlan False`, `FtFlg 12`,
`NShips 2`, both ships `Range 9.0`. **Identical to its turn-38 state** — so the AI left it parked on
turn 38 in the continuation too, and the order it issued on turn 39 is not a continuation of an order
in flight.
### 2.2 `ar-oracle-A-pre.sav` — Frame 38, the load arm's input and the continuation arm's midpoint
Per-player table as lane BQ published it (§2.1 there): the only player that can research holds a
target, `NumDes` 47 constant, `NumOwn` 18 constant, two completions a turn into *existing*
`Freighters` fleets — **no new fleet is formed on this turn**, which is why BP's per-process visit
order has nothing to vary and why this turn shows no per-process residue.
`Flt[1570 "Zeta Fleet V"]`: `LocID 432`, `HFPlan False`, `FtFlg 12`, `NShips 2`, ships 5840 and 6144
both at `Range 9.0`. **This is the leaf set the two routes disagree about.**
**Predicate reading, both routes:** no research pick (target held), no design turn, no
fleet-assignment batch, no AI fleet at an enemy colony. Rule 26 (c) screens this turn as *not
exposed* — and it is exposed anyway, through a term the predicate does not have. That is the point of
the lane.
---
## 3. Procedure and configuration
### 3.1 The two runs
| | route | procedure |
|---|---|---|
| **run C** | **continuation** | `SavedGames` reset to exactly `ar-turn37-816raiders.sav`; fresh process; Load → Single Player → row 1 → OK; **End Turn**; the turn-38 encounter resolved with **Auto Resolve Peacefully** then **Done**; **End Turn** |
| **run L** | **load** | `SavedGames` reset to exactly `ar-oracle-A-pre.sav`; fresh process; Load → Single Player → row 1 → OK; **End Turn** |
Both saves are copied in from `verify/results/saves/`, which moves the Load-dialog rows; with exactly
one file the dialog is one row and it is screenshotted and re-derived anyway (lane BQ's tip). The
`Done` pixel at (233,673) is sampled to detect the encounter query rather than assumed
(≈(183,18,0) query up, near-black on the map). One click per helper call with a screenshot between
(lane M, confirmed by BQ on a second guest); ≥ 9 s after `schtasks /Run` before believing a shot
(lane AP's nine-minute gotcha).
### 3.2 Configuration — `shim.cfg.bsrun`, one file, both runs
| item | value |
|---|---|
| guest | VM144, **held by lane BS** |
| build | `C:\SOTS\shimdist-bs\binkw32.dll`, built fresh in `/srv/re-lab/build/sots-engine-bs` (`rm -rf`'d first, rule 24) from worktree `wip/bs` (rule 21) |
| config | `shim.cfg.bsrun` — `hooks=trace` with **all 27 registered hooks named `off`** (the `# exhaustive` marker; `tools/check_shim_configs.py` passes) |
| instrument | `aiorders=on` `aiorders.words=32` `aiorders.deep=off`; `aiprobes=16` (probes 0–15: the control+pass recorder, **every task `Execute` entry the table carries**, the three emission exits, the hub, the claim test, `BuildTurnCommands`); `airng=on` `airng.pin_seed=off` |
| off | `probes=off`, `watch=off`, `airesearch=off`, `aidesign=off`, `aiseed=off`, no pin of any kind |
`aiseed=off` means the **`RunAI` net-id list is not logged** — the brief's config says every template
hook off by name, and `aiseed=log` would have been one more detour on `RunAI` than the brief asked
for, on a run whose whole value is a k = 1 neutrality check. The exposure fact is taken instead from
`airng`'s per-bracket rows, which carry `pid` and `agent` for every `OnResumePlaying` entry — the
clients that actually run, measured at the bracket rather than at the seed. §4 reports it as such.
### 3.3 VM144 as found, recorded so it can be put back
```
binkw32.dll 22,972,439 B sha256 479b8614d2417603b64e66482e429ecd3cdfe4033a2c99969ca91553642e517d
(= shimdist-l3, BUILD_ID l3cont2-0117495-20260908T2124Z)
shim.cfg sha256 692dabb6f00343bfcc2f86d62ade827c295acc4630a18a1f2ddcebd53640978d
SavedGames 15 files (hashes in §8)
game RUNNING at the main menu (pid 8408), screenshot-verified
```