diff --git a/findings/subsystems/spy-program-draws.md b/findings/subsystems/spy-program-draws.md index f3761e9..eb818c9 100644 --- a/findings/subsystems/spy-program-draws.md +++ b/findings/subsystems/spy-program-draws.md @@ -299,3 +299,655 @@ From `as-turn14-predeploy.sav`, one Deploy Spy order, one End Turn, `probes=8`: --- *(End of the pre-registered section. Everything below is measurement.)* + +--- + +## 4. What was run + +Shim build **`ap-989c692-20260909T0425Z`**, cross-built on CT111 in this lane's own directory +`/srv/re-lab/build/sots-engine-ap` after `rm -rf` of the whole tree, from a lane-private +`git worktree` at `~/sots-engine-ap` with no local `build*` shipped (rules 21 and 24). `binkw32.dll` +sha256 `ae084ed4774caf24ea7927fcb012d1999633f10556c7c99f0209f66067416a17`. Staged to +`/srv/re-lab/shim/dist-ap`, deployed to `C:\SOTS\shimdist-ap` on VM144. **This lane wrote no engine +code**; `shim.cfg.hp8` and `shim.cfg.hoff` were used unmodified. + +| run | config | input | End Turns | purpose | +|---|---|---|---|---| +| **W** | `shim.cfg.hp8` (`probes=8`) | `MyGameas1spydep.sav` (Frame 15) | **11** (15 → 26) | target 1 | +| **D** | `shim.cfg.hp8` | `MyGameas1predeploy.sav` (Frame 14) + one `Deploy Spy` order | 1 | target 2 | +| **OA / OB** | `shim.cfg.hoff` | `MyGameas1spydep.sav` | 1 each, two fresh processes | control (§7) | + +**Instrument armed and verified from `shim.log`, not assumed** (rule 1). Both instrumented runs: +seven `drawsite:` detours `create=MH_OK enable=MH_OK`; `config: probes=8 -> 8 lane-H entry probes` +and all eight `create=MH_OK enable=MH_OK`; **zero** `COVERAGE:` lines; **zero** +`config: ignoring unknown key` lines; `draw_site_overflow = 0` on every turn. Probe indices 8–11 +report **NOT INSTALLED**, never 0. The one `MH_ERROR_ALREADY_CREATED` line is the `fpu` module +losing the race for `BeginProcessTurn` to the ledger; it is present verbatim in lanes AC, AD, AR and +AS's logs, so this is the same instrument those lanes ran. + +**`probes=11` was not used anywhere in this lane.** No number here is comparable to lane AC's turn +totals. + +### 4.1 The wait cost eleven minutes, not an evening + +The brief budgeted "~20 End Turns, expensive in wall-clock". On this state an End Turn under +`probes=8` completes in **6 seconds**; the eleven turns took **11 minutes** including the load. The +expensive part is the launch (~2 min through the intro movies) and the load (~3 min). Turn 27 hit a +Von Neumann encounter dialog at Rigel and the driver stopped on it exactly as designed; by then the +question was answered and the run was ended there rather than clicked through. + +The driver is worth carrying forward: a scheduled task (`SOTSAP`) running an in-guest loop that +clicks End Turn, then **polls the autosave's mtime** — never a button colour, which is lane AS's +lesson — snapshots `(Autosave).sav` per turn, and stops on the first stall with a screenshot. It is +left at `C:\SOTS\ui\ap\ap_loop.ps1`. + +--- + +## 5. Target 1 — the wait, measured turn by turn + +Detection landed on **Frame 22**, seven turns in. `P` therefore ran on **Frame 25**. + +### 5.1 The per-turn ledger + +Every row is `draw_sites` at the post-turn autosave marker, which the shim resets at the pre-turn +marker — so these are **per-turn** figures, not cumulative. + +| Frame | `0x00887c8a` spy 13 | `0x0088dc43` spy 14 | `0x00840a3c` `P` B1 | `0x00820e1d` trade raid | tail bracket | probe `vslot13` | probe `Slot13RngCallee` | +|---|---|---|---|---|---|---|---| +| 16 | **1** | — | — | — | **1** | 1 | 0 | +| 17 | **1** | — | — | — | **1** | 1 | 0 | +| 18 | **1** | — | — | — | **1** | 1 | 0 | +| 19 | **1** | **1** ← first ever | — | — | **2** | 1 | 0 | +| 20 | **1** | **1** | — | 2 | **4** | 1 | 0 | +| 21 | **1** | **1** | — | 4 | **6** | 1 | 0 | +| **22** | **1** ← succeeds, `sdet := 22` | **1** | — | 7 | **9** | 1 | 0 | +| 23 | **—** | **1** | — | 7 | **8** | **1** | 0 | +| 24 | **—** | **1** | — | 8 | **9** | **1** | 0 | +| **25** | **—** | **—** | **1** ← first ever | 7 | **8** | **1** | **1** ← first ever | +| 26 | — | — | — | 8 | **8** | 1 | 0 | + +Every cell is `calls = words`, `no_draw_calls = 0`, `strategic = true`. **The tail bracket equals the +sum of its rows on all eleven turns: residual 0 every turn**, and `draw_site_overflow = 0`. + +Three lines of that table are the lane: + +> **Frame 25:** `entry = Chance`, `ret_rva = 0x0044_0a41` (VA `0x00840a41`, the instruction after +> `call 0x8e6dd0` at `0x00840a3c`), `calls = 1`, `words = 1`, `no_draw_calls = 0`, +> `strategic = true`. **`0x00840a3c` had never fired in this campaign.** +> +> **Frames 23 and 24:** no row at `0x0048_7c8f` at all, while the entry probe on +> `ServerSpyManager::vslot13` reads **1** on both. That is "entered and gated", not "did not run" — +> rule 28 practice 1, in a third distinct arm of the same function. +> +> **Frame 25, the probe column:** `Game::SpyManager::Slot13RngCallee 0x008408e0` reads **1**. Lane AS +> demonstrated that this probe's **zero** said nothing about the subtree. This is the other half: +> the same probe, on the one turn the callee is actually entered, reads one. + +### 5.2 `0x00840929` and `0x008409c7` did not fire, and could not have + +Neither site appears in any of the eleven turns, including Frame 25 — the one turn `P` ran. **PA1 +held.** The reason is not a state we failed to build: `P`'s first instruction pair is +`cmp [spyOwner+0x5c], 6` / `jne`, the spy's owner is player 16 `re` with `Species = 0` (Human), and +the launch card confirms it in the UI. Branch A is a **Morrigi-only** path. + +> The brief that created this lane, lane AS's §6, board row 207 and lane AG's §6 all list +> `0x00840929` / `0x008409c7` / `0x00840a3c` as three sites behind one gate. They are **two +> mutually exclusive branches selected by species**, and no workload can fire more than one of them +> per call. To reach the other two, a lane must play a **Morrigi** empire, deploy a spy, and lose it. + +### 5.3 `sdo` — the accumulator, exact to the float32 + +Read from the eleven autosaves. Prediction PB1 was `sdo(f) = 0.0084 × (f − 14)` as a float32. + +| Frame | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | +|---|---|---|---|---|---|---|---|---|---| +| `sdo` | 0.016800 | 0.025200 | 0.033600 | 0.042000 | 0.050400 | 0.058800 | 0.067200 | 0.067200 | 0.067200 | +| bits | `3c89a027` | `3cce703a` | `3d09a027` | `3d2c0831` | `3d4e703b` | `3d70d845` | `3d89a027` | frozen | frozen | + +Seven consecutive exact steps of 0.0084, then **frozen** the moment `sdet` stamps — because branch D +is the only writer of `sdo` and `sdet != -1` skips it. The freeze is a second, save-side proof that +the branch stopped running on Frames 23 and 24, independent of the ledger's zero. + +Lane AS measured two points of this line (0 → 0.0084 → 0.0168) and derived the rest. **Nine points +now lie on it**, and `countC` stayed at 2 for the whole window, so `sdo`'s per-turn increment is a +constant on this state rather than a fitted one. + +### 5.4 The counter-mission machine, measured for the first time + +`0x0088dc43` (lane AG §3.2) had **corpus count 0 of 22** and had never fired. It fires here from +Frame 19, one call and one word per turn, and the save-side state machine is fully visible: + +| Frame | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | +|---|---|---|---|---|---|---|---|---| +| `spyon` | **32** | 32 | 32 | 32 | 32 | 32 | 32 | — | +| `cm` | **1** | 1 | **2** | 2 | **3** | 3 | **4** | — | +| `cmo` | 0.2 | 0.4 | 0.2 | 0.4 | 0.2 | 0.4 | 0.2 | — | +| `ncp` | 0 | 0 | **1** | 1 | **2** | 2 | **3** | — | +| words at `0x0088dc43` | **0** | 1 | 1 | 1 | 1 | 1 | 1 | **0** | + +Everything AG decoded statically is confirmed, and four things are added: + +1. **The bootstrap turn costs nothing.** Frame 18 is `Frame − tdep == 3`; `vslot14` sets `spyon`, + runs the stage machine `cm: 0 → 1` and `cmo := 0.2f`, and **does not draw**. The first draw is + Frame 19. PB3 predicted exactly this and it held. +2. **`cmo` steps by `0.2f` on failure and resets to `0.2f` on success**, as decoded. The stage + advanced on Frames 20, 22 and 24 — always on the second try, at `p = 0.4`. Six rolls, three + successes; expectation at (0.2, 0.4) alternating is 2.4. Consistent, not validated. +3. **`ncp` is a scalar counter of completed stages, not a container.** It is a plain `int` item in + the stream and it increments by exactly 1 on each stage completion (0,0,1,1,2,2,3), in lock-step + with `cm`. AG's decode reads the `cm == 0` branch as gated on "`ncp` non-empty"; that gate was + **never reached** here, because `cm` never returned to 0 before the spy was destroyed. It stays a + rule-6 hypothesis — and a sharper one now that `ncp`'s values are known. +4. **The `CnTrd` the stage machine consults is the TARGET's, not the spy owner's.** AG's caution to + lane AS was that a spy program run without FTL Economics takes the `1 → 3` arm. This run took + `1 → 2`, and in the save **my player (16) has `CnTrd = False` while Kepler's owner (32) has + `CnTrd = True`**. So `owner->+0xff` at that branch is the **victim** empire's FTL Economics. The + caution as written would have mispredicted this run. + +`cm`, `cmo`, `spyon` and `ncp` are four of the 234 fields the value-domain census lists as having +only ever held one value. **All four are now off zero**, with a measured trajectory each. + +### 5.5 The spy is destroyed at `sdet + 3`, and `vslot13` does it + +`nspy` goes **1 → 0** between the Frame-24 and Frame-25 autosaves, `spies2` at Kepler returns to +`count = 0`, and the `spymgr` record loses its only `spy` child. That is `vslot13`'s drain loop at +`0x00887eb0` calling manager vtable slot 10 `0x00838480`, which calls `ServerSystem::RemoveSpy +0x0074f550`. **PA4 held.** + +It also settles the ordering question in §1.4 behaviourally: `0x0088dc43` costs **0** on Frame 25 +although `vslot14`'s entry probe still reads 1. Phase 23 destroys the spy before phase 33 walks the +list. + +--- + +## 6. Target 2 — `0x0078c97f`, the site in no inventory, measured + +Run D: `MyGameas1predeploy.sav` (Frame 14) in a fresh process under `probes=8`; Kepler selected on +the map; `Special → Deploy Spy → OK`; one End Turn. + +``` +draw_sites, post-turn autosave marker, the row verbatim: + + entry = NextFloat + ret_rva = 0x0038_c984 (VA 0x0078c984 -- after `call 0x47d830` at 0x0078c97f) + calls = 1 + words = 1 + no_draw_calls = 0 + strategic = true + +boundary ledger, same turn: + + BeginProcessTurn 326 -> 326 0 + ProcessTurn 326 -> 344 18 <-- the deploy draw is in here + OnAllCombatDone_Tail 344 -> 345 1 <-- the first detection roll, sdo = 0.0084 + Autosave 345 -> 345 0 +``` + +**PC1, PC2 and PC3 all held exactly.** The site fires once per applied `SHIPACTION_DEPLOYSPY` +command, for one word, on the strategic generator, inside the `ProcessTurn` bracket — and the same +turn's tail costs exactly 1, because `ApplyAllTurnCommands` runs before `OnAllCombatDone_Tail` and +the freshly deployed spy is immediately eligible for its first detection roll. + +It appears in **no other turn of this lane**: eleven turns of run W carry no row at `0x0038c984`. +That is the stream predicate showing its shape — the site's cost is not a function of the save at +all, it is a function of what the player ordered. + +### 6.1 The sharpest prediction held: the deploy draw is reproducible across processes + +**PC4.** Lane AS's `as-turn15-spydeployed.sav` was written by a different process, on a different +day, from a different build. This lane's Frame-15 autosave was written by run D. + +``` +lane AS spy.cbh = 5.033599376678467 (float32 0x40A11333) +lane AP spy.cbh = 5.033599376678467 identical + cbh / 2pi = 0.8011222000 +``` + +The same input state plus the same order drew the same word, in two processes separated by hours and +by a rebuild. Together with `deat = 400`, `tdep = 15`, `sdo = 0.0084`, `sdet = -1` and `spies2 = [1]` +at Kepler reproducing exactly (**PC5**), this says the **strategic** generator's consumption up to +`ApplyAllTurnCommands` is deterministic given the save — the per-process AI client seed does not +reach it. It is a much stronger statement than a whole-file hash comparison, because it isolates one +word to one site. + +### 6.2 A correction to lane AS's reading of the same bytes + +AS gives the site as `cbh = NextFloat() * 2*pi`. The value is right; the expression is the +campaign's standard range idiom with `LO` supplied by an `fldz`: + +``` +0078c984 fld qword [0x009e21b0] ; HI = 6.2831854820251465 +0078c98a fldz ; LO = 0.0 +0078c994 fsub st(1),st ; HI - LO +0078c99b fmulp st(1),st ; (HI - LO) * r +0078c99d fstp [ebp+8] ; through a FLOAT32 temp +0078c9a0 fadd [ebp+8] ; LO + that +``` + +so it is `lerp(0.0, 2*pi, r)` **rounded to float32 mid-expression**, not a `double` multiply. A +reimplementation that computes `r * 6.2831854820251465` in double and stores at the end can differ in +the last bit. + +The other half of the sequence is worth writing down because it reads wrong at first glance: + +``` +0078c998 push ecx ; reserve DeploySpy's SECOND argument slot +0078c9a9 fstp [esp] ; slot := the angle +0078c9ac call 0x0080c860 ; __thiscall getter, `mov eax,[ecx+0xa8]; ret` -- NO stack args, + ; so it leaves the angle slot untouched. eax = the spy's id. +0078c9ba push eax ; the FIRST argument +0078c9bb call edx ; ServerSpyManager::DeploySpy(spyId, angle) +``` + +`0x0080c860` looks like it consumes the float and does not; the compiler built the argument list out +of order and called a zero-argument getter across the half-built frame. **`DeploySpy` takes two +arguments**, and `0x008874b0 fld [ebp+0xc]` / `0x008874c0 fstp [esi+0x18]` is where `cbh` is +written. Lane AS's *"`spy->cbh (+0x18) := `"* is exactly right; this only names +which argument and shows why an eye following `eax` misses it. + +--- + +## 7. The control — a truthful non-pair, and the exposure predicate checked against it + +Two fresh `hooks=off` processes (**OA**, **OB**) on the same input, one End Turn each, plus this +lane's instrumented run **W** and lane AS's three runs from the day before. Six processes, one +input, one End Turn. + +### 7.1 The hashes + +``` +input MyGameas1spydep.sav 854a10fa1ea602f0f7909f9bf9154942e56ea5fa1db4cce47d6d32bf53952e08 +``` + +| run | lane | config | pre-turn `(Autosave EndTurn).sav` | post-turn `(Autosave).sav` | size | +|---|---|---|---|---|---| +| A | AS | `hoff` | `98e45d3745b91450…` | `262f8bda97c9511d…` | 77,685 | +| B | AS | `hoff` | `98e45d3745b91450…` | `e34775a757e6fb3d…` | 77,649 | +| M | AS | `probes=8` | `98e45d3745b91450…` | `262f8bda97c9511d…` | 77,685 | +| **W** | **AP** | `probes=8` | — | **`262f8bda97c9511d…`** | 77,685 | +| **OA** | **AP** | `hoff` | **`98e45d3745b91450…`** | **`e296394f578b3a56…`** | 77,677 | +| **OB** | **AP** | `hoff` | *(§7.4)* | *(§7.4)* | | + +**The pre-turn save is byte-identical across four processes, two lanes and two builds** — including +this lane's own, which is not lane AS's binary. That is the strongest statement available about the +input, and it is what makes the post-turn comparison meaningful. + +**The post-turn saves are not.** Five processes have produced **three** distinct outcomes: +`262f8bda` (three times: AS's A, AS's M, this lane's W), `e34775a7` (once: AS's B), `e296394f` +(once: this lane's OA). + +> **This is not a calibration pair and must not enter `determinism-hashes.txt`.** Said in advance as +> PD3, and it is the honest verdict rather than a post-hoc excuse. + +### 7.2 What varies, localised by sub-tree + +`state_checksum --no-audit --floats bits --mask none`, OA against W: + +``` +DIVERGED: 15 leaf difference(s) + /Summary/Checksum 985948923 -> 985948837 + /Sim/turnstats/history/hist[1]/stats[15]/tch 45 -> 46 + /Sim/players/Player[32 "Revenge Fleet"]/TechTree/St[60] 3 -> 2 + /Sim/players/Player[32 "Revenge Fleet"]/TechTree/TResDone[60] 22469 -> 0 + /Sim/players/Player[32 "Revenge Fleet"]/TechTree/Tbd[60] 2 -> 1 + /Sim/players/Player[32 "Revenge Fleet"]/TechTree/St[64] 2 -> 4 + /Sim/players/Player[32 "Revenge Fleet"]/TechTree/TResDone[64] 0 -> 22469 + /Sim/players/Player[32 "Revenge Fleet"]/TechTree/TAcq[64] -1 -> 16 + /Sim/players/Player[32 "Revenge Fleet"]/TechTree/TiAcq[64] -1 -> 45 + /Sim/players/Player[32]/Events/…/EvDsc 'Research Over Budget' -> 'Research Complete' + /Sim/players/Player[32]/Events/…/EvMsg 'Research for Micro-Fusion Drives has gone overbudget.' + -> 'Tech Overthrusting has been acquired' + /Sim/players/Player[32]/Events/…/EvImg 'EVENT_RESEARCH_OVERBUDGET' -> 'EVENT_RESEARCH_COMPLETE' + /Sim/players/Player[32 "Revenge Fleet"]/ResTNm 'DRV_McroFus' -> '' + /Sim/players/Player[32 "Revenge Fleet"]/otch/.[34] only-in-B + /Sim/players/Player[32 "Revenge Fleet"]/otch/.[0] 33 -> 34 +``` + +**And the sub-tree digests say which halves of the save are affected**, which is the form the claim +should take (PD4): + +| sub-tree | OA | W | | +|---|---|---|---| +| `Sim/spymgr` | `9c3f224cf954aa54` | `9c3f224cf954aa54` | **identical** | +| `Sim/systems` | `3227b08124339fe2` | `3227b08124339fe2` | **identical** | +| `Sim/fleets` | `26a640cdfc597869` | `26a640cdfc597869` | **identical** | +| `Sim/trdmgr` | `ed99071bdc121694` | `ed99071bdc121694` | **identical** | +| `Sim/players` | `2d1c0cad92679606` | `46ddf6778ef36e0b` | differs | +| `Summary` | `66db5e1055c9fe37` | `cb8a4c8e22480af0` | differs (the derived checksum) | + +> **Every leaf this lane's numbers depend on reproduces across the pair. `spymgr` — the spy's +> `deat`, `sdet`, `sdo`, `cm`, `cmo`, `ncp`, `cbh` — is byte-identical between an instrumented run +> and an un-instrumented one, as is every system's `spies2`.** What does not reproduce is one AI +> empire's research pick and the two derived values downstream of it. + +**Three distinct picks across five processes** is worth stating plainly, because it is the first +time this outcome set has been sampled more than twice: A/M/W completed `Overthrusting` (tech 64), +B was part-way through `DRV_NodFoc`, OA went over budget on `DRV_McroFus`. AS observed A and M +agreeing and correctly flagged it as a `1/k` coincidence rather than evidence about the instrument. +With five samples the set has at least three members and the modal outcome took 3 of 5 — so the +coincidence AS refused to lean on was about a 1-in-3 event, not a 1-in-2 one, and refusing to lean +on it was right. + +### 7.3 The seed-exposure predicate, checked (rule 26 corollary (c)) + +The coordinator's instruction is to record the exposure fields next to every control hash. Here they +are for the **pre-turn** save of the control pair, and for every turn this lane played, so the +predicate can be checked rather than asserted. `Species = 4` (NPC) players are omitted; they never +decide. Net ids: player 16 is `re` (human), 32 is `Revenge Fleet` (the AI), 48 is `Spengler`. + +| pre-turn save | Frame | Player[32] `ResTNm` | `NDes` | `NOwn` | ships | exposure | +|---|---|---|---|---|---|---| +| `as-turn14-predeploy` | 14 | **`''`** | 15 | 10 | 146 | **pick** | +| **`as-turn15-spydeployed`** (the control input) | **15** | **`''`** | **15** | **10** | **146→148** | **pick + colonise + 2 ships** | +| `w-16` | 16 | `''` | 15 | 11 | 157 | pick + 9 ships | +| `w-17` | 17 | `''` | 16 | 12 | 161 | pick + design + colonise + 4 ships | +| `w-18` | 18 | `''` | 20 | 12 | 169 | pick + 4 designs + 8 ships | +| `w-19` | 19 | `''` | 20 | 12 | 181 | pick + 12 ships | +| `w-20` | 20 | `'WEP_AmWhd'` | 20 | 12 | 185 | 4 ships | +| `w-21` | 21 | `'WEP_AmWhd'` | 23 | 12 | 201 | 3 designs + 16 ships | +| `w-22` | 22 | `'WEP_AmWhd'` | 27 | 12 | 218 | 4 designs + 17 ships | +| `w-23` | 23 | `'WEP_AmWhd'` | 27 | 12 | 236 | 18 ships | +| `w-24` | 24 | `'WEP_AmWhd'` | 27 | 14 | 235 | colonise ×2 | +| `w-25` | 25 | `''` | 28 | 15 | 244 | pick + design + colonise + 9 ships | +| `w-26` | 26 | `''` | 28 | 15 | 251 | pick + 7 ships | + +**The predicate held, and it held in its strong direction.** The control input carries +`Player[32].ResTNm == ''` — a pick turn — and the control varied, in exactly the research-pick leaves +the predicate names and nothing else. Two independent lanes have now produced that signature on this +input. + +**And the state says something the predicate did not anticipate: on this map exposure is +*saturated*.** Every one of the thirteen pre-turn states above carries at least one exposure +signature; nine of thirteen carry a research pick, and every single turn adds ships. There is **no +quiet turn anywhere in this lineage** to attempt an agreeing control on — so no rung-A oracle pair is +obtainable from `as-turn15-spydeployed.sav` at any horizon by waiting, and a lane that wants one on +this map must pin the client seeds instead. That is a fact about a 2-empire game where the AI runs a +146 → 251-ship war economy, and it is the opposite end of the same axis from lane AR's quiet +turns 38–39. + +**One nuance that cuts against a naive reading of the predicate.** Exposure is necessary for +variation, not sufficient *per process pair*: W (`probes=8`) and AS's A and M (two different +configurations, two different builds, three different processes) landed on the **same** post-turn +bytes on this exposed pick turn. A single agreeing pair on an exposed turn therefore proves nothing — +which is rule 26's original point, now with a measured outcome-set size behind it. + +### 7.4 OB, and the verdict + +``` +OB (Autosave EndTurn).sav 98e45d3745b914506fc4c409ad5a5ad0938a2003ab4c845421a99a8065149c3a 76,706 +OB (Autosave).sav 262f8bda97c9511d8d2b41e6a8a0582d6f5364c2b01b1caf99e639d0e4f53ebe 77,685 +``` + +* **OA ≠ OB.** The two `hooks=off` processes of *this* lane's own control disagree, in **15 leaves, + all of them** `Player[32]`'s research pick plus `/Summary/Checksum` and the `turnstats` `tch` cell. + Nothing of my empire, the spy, `deat`, `sdet`, `sdo`, `cm`, `cmo`, `ncp`, `spies2`, the systems, + the fleets or `trdmgr` moved between them. +* **OB is byte-identical to W**, the instrumented run: `state_checksum` prints **IDENTICAL** over + **44,438 leaves**. Both are `fc85c4228b9973fa159c1ed6aa851ae0`. + +So the honest three-part statement, which is all the evidence supports: + +1. **Not a calibration pair.** Two fresh un-instrumented processes on this input do not agree, so + nothing here may enter `determinism-hashes.txt` as a rung-A pair. **PD1 and PD3 held.** +2. **The instrument is neutral on everything this lane measured.** `probes=8` reproduced an + un-instrumented run leaf for leaf, and the sub-trees carrying the spy state are identical across + all three of this lane's processes. **PD4 held.** That is a stronger neutrality statement than + lane AS could make, because AS's instrumented run agreed with only one of two controls; here it + agrees with one control *exactly and completely* while the two controls differ from each other in + a leaf class the instrument provably does not touch. +3. **The divergence is the known one, and it was predicted.** **PD2 held**, and it held with the + pre-turn exposure fields recorded in advance (§7.3) rather than diagnosed afterwards. + +**A note for whoever certifies pairs.** Lane AS reported A ≠ B and called the class "one AI player's +research pick plus the derived checksum". Two more processes, taken independently, land in the same +class and add a third member to the outcome set. That is a second lane reproducing the *class* while +failing to reproduce the *value* — which is exactly what a per-process seed feeding a small +discrete choice should look like, and it is now measured rather than asserted. + +--- + +## 8. Predictions scored — including the one that was wrong + +| | prediction | verdict | +|---|---|---| +| **PA1** | `P` reaches at most one draw site per call, selected by `Species == 6`; only `0x00840a3c` from this save | **HELD.** No row at `0x0044092e` or `0x004409cc` on any of the twelve measured turns, including the turn `P` ran | +| **PA2** | species table `0 Human … 4 _NPC, 5 Zuul, 6 Morrigi`; `0.75f` is Zuul, `0.5f` is Morrigi | **HELD** (corpus-wide; the launch card independently shows `re` = Human) | +| **PA3** | `0x00840a3c` is `Chance` on a widened `0.25f` | **HELD** — `0x009e5ac0` is `00 00 80 3e` | +| **PA4** | the spy is destroyed at the end of `vslot13` on the `P` turn | **HELD** — `nspy` 1 → 0, `spies2` emptied, no `spy` record at Frame 25 | +| **PB1** | `sdo(f) = 0.0084 × (f − 14)` exactly, until detection | **HELD** — seven exact float32 steps, then frozen | +| **PB2** | tail costs 1 on Frames 16, 17, 18 | **HELD** | +| **PB3** | `0x0088dc43` first fires on Frame 19; Frame 18 bootstraps without drawing | **HELD exactly** | +| **PB4** | **the tail costs 2 words/turn from Frame 19** | **WRONG.** It costs 2 on Frame 19 and then 4, 6, 9, 8, 9, 8, 8 | +| **PB5** | `cm`, `spyon` move on Frame 18; `cmo` steps by `0.2f` | **HELD** | +| **PB6** | on the detection turn `vslot13` still costs 1 and `sdet := T` | **HELD** — Frame 22 | +| **PB7** | `vslot13` costs **zero** on `T+1`, `T+2` while still being entered | **HELD** — Frames 23, 24: no row, probe reads 1 | +| **PB8** | `T+3`: one row at `0x0044_0a41`, 1/1/0/strategic; no `0x0048_7c8f`; `vslot14` contributes 0 | **HELD in all three parts** | +| **PB9** | the `Slot13RngCallee` probe reads 1 on `T+3` | **HELD** — its first non-zero in the campaign | +| **PB10** | 11 % by +4, 43 % by +10, 70 % by +15, 87 % by +20 | **not tested.** Detection landed at +7, where the model says 26 %. One sample is consistent with the model and does not test it | +| **PB11** | residual 0 on every turn | **HELD** on all eleven turns of W and on run D | +| **PB12** | the +20 tail case | **not reached** | +| **PC1** | `0x0038c984`, `NextFloat`, 1/1/0, strategic | **HELD exactly** | +| **PC2** | in the `ProcessTurn` bracket | **HELD** | +| **PC3** | the same turn's tail costs exactly 1 | **HELD** | +| **PC4** | `cbh` reproduces `5.033599376678467` exactly | **HELD** | +| **PC5** | `spies2` 0 → `[1]`, `deat` → 400, `tdep := 15`, `sdo := 0.0084` | **HELD** | +| **PD1** | the control pair's pre-turn saves agree, post-turn saves differ | **HELD** | +| **PD2** | the divergence is one AI's research pick + checksum + `tch`, and nothing of mine | **HELD** — 15 leaves, localised | +| **PD3** | not a calibration pair; must not enter `determinism-hashes.txt` | **HELD**, and stated in advance | +| **PD4** | the sub-trees this lane's numbers depend on reproduce | **HELD** — `spymgr`, `systems`, `fleets`, `trdmgr` digests identical; and W is byte-identical to OB over 44,438 leaves | + +### 8.1 PB4, the one that was wrong, and why it was wrong + +I predicted the **tail bracket** would cost 2 words per turn from Frame 19, on the reasoning that +`vslot13` contributes 1 and `vslot14` contributes 1. Both halves of that were right on every single +turn. The prediction was still wrong, because **the tail has a third contributor and I treated it as +a constant zero.** + +``` +Frame 16 17 18 19 20 21 22 23 24 25 26 +vslot13 1 1 1 1 1 1 1 0 0 0 0 <- predicted exactly +vslot14 0 0 0 1 1 1 1 1 1 0 0 <- predicted exactly +0x00820e1d 0 0 0 0 2 4 7 7 8 7 8 <- NOT PREDICTED +tail bracket 1 1 1 2 4 6 9 8 9 8 8 +``` + +`0x00820e1d` is the **trade-raid roll** in `TradeManager::Slot13RngCalleeA` — lane AC's site, priced +per fleet by lane AD. It was at zero on Frames 16–19 and entered the tail on Frame 20, growing to 8. +That is the AI's raider fleets arriving on trade-sector nodes as its navy went from 157 to 251 +ships: **`calls == words == the number of qualifying fleets`, exactly lane AD's per-fleet model**, +now reproduced on a different map, a different lineage and an AI-owned fleet rather than a +player-owned one. AD measured 2; this run measured 2, 4, 7, 7, 8, 7, 8 on seven consecutive turns. + +The lesson is rule 20's, in the direction nobody watches: I checked that my *sites* were priced, and +then quoted a *bracket* total as if the bracket contained only my sites. A bracket is a sum over +subsystems, and predicting one requires predicting all of them. The correct form of PB4 was "the +**spy** contribution to the tail is 2 words per turn from Frame 19", which is what was measured and +what should have been written. + +**It is also the most useful failure available**, because a wrong bracket prediction that decomposes +cleanly into "my model was right, an unmodelled neighbour moved" is a free confirmation of the +neighbour's model. The residual was 0 on every turn, so nothing is unaccounted for. + +--- + +## 9. Coverage — what this did not touch, said as loudly as what it did + +* **`0x00840929` and `0x008409c7` remain unfired, and are now known to need a Morrigi empire.** + That is a *stronger* negative than "not reached in the states measured": the gate is decoded, the + branch was taken twelve times in the other direction, and the predicate is a single field on the + spy's owner. **The workload that closes them, named from the failed conjunct:** a Morrigi + single-player game, four spy techs, a spy deployed at a foreign colony with a belt, and ~7 turns of + waiting. `0x008409c7` needs one more thing on top — the roll at `0x00840929` must *succeed* (75 %) + **and** the candidate list must be non-empty, which needs a **third** non-eliminated empire that is + neither the spy's owner nor the target's. This map has one (`Spengler`), so a 3-empire Morrigi game + reaches both. +* **`P` ran exactly once.** Everything about its cost is a single observation. The loop is over the + spy vector, so two spies detected three turns apart should give two calls on different turns and + two spies detected on the *same* turn should give two calls on one turn — **that is a reading, not + a measurement** (rule 20: do not fit a constant to one observation). +* **The `sdo` arithmetic is still validated only in its first and third terms.** `countA`, `countB` + and the species multiplier were 0 / 0 / 1.0 for all eleven turns. `countC` held at 2 throughout, + which is why the line is straight; nothing here identifies *what* `countC` counts, only that it did + not change while one fleet of one ship sat at Kepler. +* **The auto-detect arm `(TerrFl & 1) == 0` was never taken.** Lane AS flagged it as the most + load-bearing unmeasured branch in the chain and it still is. Nothing in this lane touched it. +* **The `ncp`-blocks-rebootstrap gate was never reached** (§5.4 item 3). `cm` never wrapped 4 → 0 + because the spy was destroyed at Frame 25 with `cm = 4`. Two more turns of survival would have + tested it; a second spy deployed later than the first would too. +* **`P`'s return value was not traced.** The Frame-25 roll's outcome (whether `re` was named in + Kepler's `EVENT_SPY_DESTROYED` event, a 25 % chance) was not read out of the event text. The + ledger prices the site; the semantics in §1.2 are from the instruction stream. +* **One turn, one process for the eleven-turn series.** W is a single process. The control pair + covers only its **first** turn. Frames 17–26 have no two-process control at all, and by §7.3 every + one of them is an exposed turn, so they should not be assumed reproducible. +* **Turn 27 was not played.** The driver stopped on a Von Neumann encounter dialog at Rigel, which is + where the series ends. Nothing after Frame 26 is measured. +* **`probes=11` was not used anywhere in this lane**, and no number here is comparable to lane AC's + turn totals. +* **No `Guard` region is declared by any hook in this family** — the same gap lanes Z, H, AS and AR + reported. `undeclared = 0` in these traces is vacuous. +* **The species table is read from the save's own `ISsp` list**, cross-checked on one Zuul save and + one hook report. No Morrigi, Hiver, Liir or Tarka save exists in the corpus, so indices 1, 2, 3 and + 6 are named but not exercised. + +--- + +## 10. Corrections to earlier findings (rule 11) + +* **Lane AS §6, board row 207, lane AG §6 and this lane's own brief: "`P`'s three draws".** + `0x00840929`, `0x008409c7` and `0x00840a3c` are **not three sites behind one gate**. They are two + mutually exclusive branches selected by `spyOwner->Species == 6`, and no call can reach more than + one of them. Every inventory that lists them as a group of three should say so. §1.2. +* **Lane AG §3.2's caution to lane AS: "a spy program run without FTL Economics takes a different + branch of the machine".** The `CnTrd` the stage machine consults at `cm 1 → 2/3` is the **target + system owner's**, not the spy owner's. My empire has `CnTrd = False` and the machine still took the + `1 → 2` arm, because Kepler's owner has `CnTrd = True`. §5.4 item 4. +* **Lane AG §3.2's `ncp` reading.** `ncp` is a scalar `int` in the stream, and it behaves as a + **counter of completed counter-mission stages** (0,0,1,1,2,2,3 in lock-step with `cm`). "Non-empty" + is therefore `!= 0`, and the branch that consults it was never reached here. §5.4 item 3. +* **Lane AS §1.4 / §6.1: "`cbh = NextFloat() * 2*pi`".** Correct in value; the code is + `lerp(0.0, 2*pi, r)` with a float32 round in the middle, and the angle is `DeploySpy`'s **second** + argument, set up before `0x0080c860` (a zero-argument getter) is called across the half-built + frame. AS's "`spy->cbh := `" is right; §6.2 only names which argument. +* **Lane AS §6's detection-probability table** (38 % / 65 % / 85 % at +10 / +15 / +20) is the right + model evaluated one turn late. `AccumulateDetectionOdds` runs *before* the gate, so the roll on the + End Turn producing Frame `f` uses `sdo = 0.0084 × (f − 14)`, not `0.0084 × (f − 15)`. The corrected + figures are 43 % / 70 % / 87 %. Measured `sdo` (§5.3) settles it: the Frame-16 autosave already + holds 0.0168, so the Frame-16 roll was at 0.0168. +* **Lane AG's audit and lane V2's inventory have a column they need.** `0x0078c97f`'s predicate is + *"a `SHIPACTION_DEPLOYSPY` command in this turn's command stream"*. It is now **measured**, so the + gate-indexed audit's two cell kinds (a firing, or a save predicate) become three: a firing, a save + predicate, or a **stream** predicate. This is the first entry in that column. + +--- + +## 11. Artifacts + +| what | where | +|---|---| +| the instrumented eleven-turn trace (per-turn `draw_sites` + `probe_entries` + brackets) | `verify/traces/ap-probes8-turn16-turn26.jsonl.gz` | +| the deploy-turn trace | `verify/traces/ap-probes8-deploy-turn15.jsonl.gz` | +| shim log for the eleven-turn run | `verify/results/shim/ap/ap-probes8-w.shim.log` | +| shim log for the deploy run | `verify/results/shim/ap/ap-probes8-d.shim.log` | +| **the detected-spy save** — first `sdet != -1`, first non-zero `cm`/`cmo`/`ncp`/`spyon` in the corpus | `verify/results/saves/ap-turn22-spydetected.sav` (83,188 B, `--strict` 0 errors / 0 warnings) | +| **the post-`P` save** — the spy destroyed, `nspy` back to 0, `spies2` emptied | `verify/results/saves/ap-turn25-spydestroyed.sav` (84,487 B, `--strict` clean) | +| the deploy run's Frame-15 autosave (`cbh` reproduced) | `verify/results/saves/ap-deploy-turn15.sav` | +| the two control post-turn saves | `verify/results/saves/ap-oa-turn16.sav`, `ap-ob-turn16.sav` | +| addresses this lane mints | `ghidra/addresses.d/ap.json` | +| predictions, committed before the build | this file §0–§3, commit `f8ff1b6` | +| instrument | `sots-engine` `src/shim/shim.cfg.hp8` and `shim.cfg.hoff`, **both unchanged** — this lane wrote no engine code | + +### 11.1 The hashes, for the record + +``` +input MyGameas1spydep.sav 854a10fa1ea602f0f7909f9bf9154942e56ea5fa1db4cce47d6d32bf53952e08 +A/B/M/OA/OB (Autosave EndTurn).sav 98e45d3745b914506fc4c409ad5a5ad0938a2003ab4c845421a99a8065149c3a +W (probes=8) turn 16 262f8bda97c9511d8d2b41e6a8a0582d6f5364c2b01b1caf99e639d0e4f53ebe +OB (hooks=off) turn 16 262f8bda97c9511d8d2b41e6a8a0582d6f5364c2b01b1caf99e639d0e4f53ebe +OA (hooks=off) turn 16 e296394f578b3a5668e5934b9eec8d6c4638ca7411200078f97de1abd346f4c5 + turn 22 (detection) 91381bd7e722ac3847795a402d78ebb31d74dbb5d2909f33d243aec3ef8abaf8 + turn 25 (P ran; spy destroyed) 46380fcf4498d67507e4471eb9ee3762647c53ee5473be142fe5c34e37344bc3 +deploy run, turn 15 844f2b746060dc2c4e2099cdc6ceffd5f55ef858c9e347df28c8aca3041f7165 +shim binkw32.dll ap-989c692-… ae084ed4774caf24ea7927fcb012d1999633f10556c7c99f0209f66067416a17 +``` + +**None of these is a calibration pair.** §7. + +--- + +## 12. VM144 as left + +**Restored and verified by screenshot at the main menu**, profile `re`, 2026-09-09. + +* `C:\SOTS\binkw32.dll` restored to lane L3's build from `C:\SOTS\shimdist-l3\binkw32.dll` + (sha256 `479B8614D2417603…`, byte-identical to what this lane found in place). +* `C:\SOTS\shim.cfg` restored from `C:\SOTS\ui\preAS-shim.cfg` (L3's `shim.cfg.l3probe`, + `probes=11`). +* The three autosaves restored **byte-identical** from `C:\SOTS\ui\preAS-SavedGames` + (`1985E6F4…` / `24B2E072…` / `5EC80C1E…`). This lane's eleven turns had overwritten their contents; + lane AS's snapshot is what made the restore possible and it is worth keeping. +* `C:\SOTS\shim.trace.jsonl` and `shim.log` removed before the relaunch. + +**`SavedGames` is still 15 files** — this lane added none and removed none, so **lane AS's row +positions still hold**: `MyGameas1predeploy` at **(400, 347)**, `MyGameas1spydep` at **(400, 376)**, +`OK` at (682, 624). Verified by screenshot twice this session. All fifteen files hash as AS left +them. + +Left in place, all harmless: `C:\SOTS\shimdist-ap\` (build `ap-989c692-20260909T0425Z`, 63 files), +`C:\SOTS\ui\apgo.ps1`, `C:\SOTS\ui\aphash.ps1`, `C:\SOTS\ui\ap\` (the End-Turn driver, its per-turn +saves and screenshots), and the scheduled task **`SOTSAP`**. `click_helper.ps1` was **not touched** — +this lane needed no new verbs either. + +### 12.1 Guest notes worth carrying forward + +* **The End-Turn driver is the reusable part.** `C:\SOTS\ui\ap\ap_loop.ps1`, run through the + `SOTSAP` scheduled task (an `InteractiveToken` clone of `SOTSUI`), reads `C:\SOTS\ui\ap\job.txt` + (`turns=`, `tag=`, `first=`, `maxwait=`), clicks End Turn, **polls the autosave's mtime** until it + moves, snapshots `(Autosave).sav` to `ap\saves\-.sav`, and **stops on the first stall with + a screenshot**. Eleven turns cost 11 minutes unattended. Set `job.txt`, `schtasks /Run /TN SOTSAP`, + then watch `ap\loop.log` from outside. +* **Nine minutes of this lane went into a screenshot-lag artefact, and the lesson is general.** + Four consecutive clicks appeared to select the wrong Load-dialog rows, which read exactly like a + coordinate offset and nearly produced a "the click helper is 87 px out on this guest" note. It was + not: the click task takes ~3 s to start and ~3 s to run, and a screenshot taken 4 s after + `schtasks /Run` shows the state **before** the click. **Wait ≥ 9 s between triggering the click + task and screenshotting.** Every documented coordinate on this guest is correct. +* **The first click after `fg` is swallowed** by the focus activation. Send `fg`, then `move`, then + `sleep`, then `click` — or spend a throwaway click. +* **A `wheel` zoom on the star map separates systems that overlap at the default zoom.** Lane AS lost + a run to Kepler and Midway sharing a hit test; six notches at the cluster centre and then eight more + at the target spread them to ~120 px apart, and the labels render. No affine fit was needed. +* **Selecting the system, not the fleet, is what enables `Special`.** With Kepler selected the left + panel grows a `Fleets at Kepler` list and `Manage Fleets` / `Move` / `Special` light up; + `Special → Deploy Spy` then opens a ship picker whose ship is already selected, so `OK` is the only + further click. The whole deploy is four clicks once the right star is under the cursor. +* **The pixel test for "is the main menu up" must sample the button, not its label.** (511, 536) is + inside the white text of `Load Game` and reads ~(195,191,191) on a bright frame; (470, 530) is the + button itself and reads (184, 0, 0). A test on the first point passes only by luck on a dark frame. +* **An End Turn on this state costs 6 seconds** under `probes=8` with ~150–250 AI ships. The + campaign's "30–45 s per End Turn" figure is from a different guest and a different config. +* Turn 27 raised a **Von Neumann encounter at Rigel** ("No ships available", `Done` at (233, 673)). + A driver that polls the autosave rather than the screen stalls on it cleanly instead of clicking + through something it cannot see. + +--- + +## Proposed board rows + +New row: + +``` +| THE SPY ATTRIBUTION ROLL FIRES - and P's "three draws" are TWO SPECIES BRANCHES, only one reachable per game | verify | verified | high | 100% | 2026-09-09 | **Lane AP, VM144, build `ap-989c692-20260909T0425Z`, `probes=8` (never 11), own build dir /srv/re-lab/build/sots-engine-ap rm -rf'd first.** Continues lane AS from `as-turn15-spydeployed.sav`: **11 End Turns, 6 SECONDS EACH** (the brief budgeted an evening; the cost is the launch and the load, not the turns). **DETECTION AT FRAME 22, `P` RAN AT FRAME 25.** `Mars::RNG::Chance` at **0x00840a3c** fired **1 call / 1 word / no_draw_calls=0 / strategic=true** at `ret_rva 0x00840a41` - a site that had never fired. **CORRECTION TO EVERY INVENTORY (row 207, lane AG 6, lane AS 6, and this lane's own brief): 0x00840929 / 0x008409c7 / 0x00840a3c ARE NOT THREE SITES BEHIND ONE GATE.** `SpyManager::Slot13RngCallee 0x008408e0` opens `cmp [spyOwner+0x5c], 6; jne` - `Species == 6` (MORRIGI) takes `Chance(0.75f)` at 0x00840929 and then `NextInt` at 0x008409c7 to blame a RANDOM THIRD EMPIRE (a false flag; the true owner is never named); every other species takes `Chance(0.25f)` at 0x00840a3c and on success returns the TRUE owner. **No call can reach more than one.** Species table decoded from the save's own ISsp list and cross-checked on lane V's Zuul save: **0 Human, 1 Hiver, 2 Tarkas, 3 Liir, 4 _NPC, 5 Zuul, 6 Morrigi** - which also NAMES lane AS's two unexplained multipliers in `AccumulateDetectionOdds` (0.75f = ZUUL, 0.5f = MORRIGI). Constants read as the four bytes (rule 23): 0x009e5ac0 = 0.25f, 0x009e5ac4 = 0.75f (the same word AS read as the Zuul multiplier). **0x008409c7 needs BOTH the 0.75f roll to succeed AND a third non-eliminated empire** that is neither the spy's owner nor the target's. **SECOND SITE CLOSED: 0x0088dc43** (the counter-mission roll, lane AG 3.2, corpus 0 of 22, never fired) fires 1 word/turn from **Frame 19**; the bootstrap turn (Frame == tdep+3) sets spyon/cm:=1/cmo:=0.2f and DRAWS NOTHING. `cm`, `cmo`, `spyon`, `ncp` - four of the value-domain census's 234 single-valued fields - are all OFF ZERO with a measured trajectory. **TWO CORRECTIONS TO AG 3.2:** the `CnTrd` the stage machine consults at cm 1->2/3 is the TARGET OWNER's not the spy owner's (my player has CnTrd=False and it still took 1->2, because Kepler's owner has CnTrd=True), and `ncp` is a SCALAR COUNTER of completed stages (0,0,1,1,2,2,3 in lock-step with cm), not a container. **THIRD SITE CLOSED: 0x0078c97f**, the deploy `NextFloat` in the SHIPACTION_DEPLOYSPY handler that is in NO inventory - 1 call / 1 word / strategic, in the **ProcessTurn** bracket, and **spy.cbh came out 5.033599376678467 BIT-IDENTICAL to lane AS's** from a different process, build and session. **Its predicate is on the COMMAND STREAM, and this is the first measured entry in a column the gate-indexed audit does not have.** THE POLARITY CASE, MEASURED: on Frames 23 and 24 (sdet+1, sdet+2) `vslot13` is ENTERED (probe = 1) and costs **ZERO** - a third arm of the same function, and `sdo` FREEZES at 0.0672 in the save, proving branch D stopped independently of the ledger. On Frame 25 the entry probe on `SpyManager::Slot13RngCallee 0x008408e0` reads **1** - ITS FIRST NON-ZERO EVER, the complement of lane AS's demonstration that its zero said nothing about the subtree. The spy is DESTROYED at the end of phase 23 on sdet+3 by manager vtable slot 10 `0x00838480` -> `ServerSystem::RemoveSpy`, so vslot14 costs 0 that turn: nspy 1->0, spies2 emptied. `sdo` measured on NINE points, exactly `0.0084*(f-14)` as float32 (`3c89a027 3cce703a 3d09a027 3d2c0831 3d4e703b 3d70d845 3d89a027`), which also corrects AS's detection-probability table by one turn: **43%/70%/87%** at +10/+15/+20, not 38/65/85. Residual **0 on all 12 measured turns**, overflow 0. **ONE PREDICTION WRONG AND NAMED: PB4** said the tail costs 2 words/turn from Frame 19; it costs 2,4,6,9,8,9,8,8 because the TRADE-RAID roll 0x00820e1d entered the tail at Frame 20 and grew 2,4,7,7,8,7,8 as the AI's navy went 157->251 ships - an independent live re-confirmation of lane AD's per-fleet model on a different map and an AI-owned fleet. The spy halves were predicted exactly; quoting a BRACKET total requires modelling every subsystem in it. Saves `ap-turn22-spydetected.sav`, `ap-turn25-spydestroyed.sav`, `ap-deploy-turn15.sav`; findings `findings/subsystems/spy-program-draws.md` | +``` + +``` +| CONTROL ON as-turn15: THREE OUTCOMES IN SIX PROCESSES, and exposure is SATURATED on this map | verify | verified | high | 100% | 2026-09-09 | Lane AP, VM144. Two fresh `hooks=off` processes (OA, OB) on `MyGameas1spydep.sav` + 1 End Turn, added to lane AS's A/B/M and this lane's `probes=8` run W. **Pre-turn `(Autosave EndTurn).sav` is byte-identical across four processes, two lanes and TWO DIFFERENT BUILDS** (`98e45d3745b91450...`). Post-turn: **three distinct outcomes in six processes** - `262f8bda` x4 (AS's A and M, AP's W and OB), `e34775a7` x1 (AS's B), `e296394f` x1 (AP's OA). **OA != OB, so this is NOT a calibration pair and must not enter `determinism-hashes.txt`** - stated in advance as PD3. OA vs OB diverge in **15 leaves, ALL of them Player[32]'s research pick plus /Summary/Checksum and one turnstats `tch` cell**; three distinct picks were sampled (Overthrusting completed / DRV_NodFoc in progress / DRV_McroFus over budget). **`probes=8` IS BYTE-NEUTRAL, PROVEN PROPERLY THIS TIME: W is IDENTICAL to OB over 44,438 leaves** (`state_checksum --floats bits --mask none`), while the two un-instrumented controls differ from each other - so the instrument agrees exactly with a control in a leaf class it provably does not touch. Sub-tree digests: `Sim/spymgr`, `Sim/systems`, `Sim/fleets`, `Sim/trdmgr` all IDENTICAL across the pair; only `Sim/players` and the derived `Summary` move. **RULE 26(c) EXPOSURE FIELDS RECORDED NEXT TO THE HASHES** (coordinator's request): the control input carries `Player[32].ResTNm == ''` - a pick turn - and it varied in exactly the leaves the predicate names. **AND THE PREDICATE IS SATURATED ON THIS MAP: all 13 pre-turn states from Frame 14 to Frame 26 carry at least one exposure signature** (9 of 13 a research pick, every single one a batch of ship completions, four a design, four a colonisation; Player[32]'s navy goes 146 -> 251 ships in twelve turns). **There is NO quiet turn anywhere in this lineage**, so no rung-A pair is obtainable from `as-turn15-spydeployed.sav` by waiting - the opposite end of the axis from lane AR's turns 38-39, and a lane that wants a pair on this map must pin the client seeds. Counter-nuance: exposure is necessary for variation, not sufficient per pair - W, A and M (three processes, two configurations, two builds) landed on the SAME bytes on this exposed turn, so a single agreeing pair on an exposed turn proves nothing. Saves `ap-oa-turn16.sav`, `ap-ob-turn16.sav` | +``` + +Edits to existing rows: + +- **Row 62** (guest holders) — see §12.1 for the exact replacement text. +- **Row 207** (V2's eight sites) — append: `CORRECTED AND ADVANCED 2026-09-09 by lane AP. 0x00840a3c FIRED (1 word, Frame sdet+3 from as-turn15-spydeployed.sav). 0x00840929 and 0x008409c7 are NOT two more sites behind the same gate - they are the MORRIGI branch of SpyManager::Slot13RngCallee (`cmp [spyOwner+0x5c],6`), mutually exclusive with 0x00840a3c, and unreachable in any Human/Hiver/Tarka/Liir/Zuul game. Closing them needs a MORRIGI empire (and, for 0x008409c7, a third surviving empire). Of V2's eight, only 0x0082cdb8 (trade 15) is now both reachable and unfired.` +- **Row 418** (lane AS's detection-roll row) — append: `EXTENDED 2026-09-09 by lane AP: the roll fired on seven consecutive turns and SUCCEEDED on Frame 22; `sdo` is exactly 0.0084*(f-14) as float32 over NINE points and FREEZES the moment sdet stamps. AS's probability table is one turn late - the corrected figures are 43%/70%/87% at +10/+15/+20. AS's 0x0078c97f is now MEASURED (1 word, ProcessTurn bracket) and `cbh` reproduced bit-identically across processes and builds.` +- **The `0x0088dc43` row of lane AG's audit (§3.2)** — `FIRED 2026-09-09 by lane AP, 1 word/turn from Frame 19 (= tdep + 4). The bootstrap turn draws nothing. TWO CORRECTIONS: the CnTrd consulted at cm 1->2/3 is the TARGET OWNER's, and `ncp` is a scalar counter of completed stages, not a container.` +- **The gate-indexed audit's cell taxonomy** — `A THIRD CELL KIND EXISTS AND NOW HAS ITS FIRST MEASURED ENTRY: a STREAM predicate. 0x0078c97f fires once per SHIPACTION_DEPLOYSPY command applied; no predicate over the save can express it, and no call-graph sweep can find it (stack-built ship-action table, lane B6's class).` diff --git a/ghidra/addresses.d/ap.json b/ghidra/addresses.d/ap.json new file mode 100644 index 0000000..94b4f03 --- /dev/null +++ b/ghidra/addresses.d/ap.json @@ -0,0 +1,69 @@ +{ + "_note": "Lane AP (2026-09-09). The rest of the spy program's draw sites, read from the instruction stream of dumps/sots.exe (disassembled to the NEXT FUNCTION START, rule 17 -- no Ghidra size is used below) and then MEASURED live on VM144 under probes=8, build ap-989c692-20260909T0425Z. Evidence: findings/subsystems/spy-program-draws.md. The headline is that SpyManager_Slot13RngCallee 0x008408e0 (already named in lane-h.json, not re-minted here) contains TWO MUTUALLY EXCLUSIVE BRANCHES selected by the spy owner's Species, so its three draw sites can never all be reached: 0x00840929 + 0x008409c7 are Morrigi-only and 0x00840a3c is everyone else. NOT minted here because other fragments already have them: SpyManager_Slot13RngCallee 0x008408e0 (lane H), ServerSpyManager_vslot13 0x008877b0 and vslot14 0x0088db80 (lane V2), ServerSpyManager_DeploySpy 0x00887410 and ServerSystem_AddSpy/RemoveSpy (lane AS), Mars_RNG_NextFloat/NextInt/Chance (addresses.json).", + "entries": [ + { + "name": "SpyManager_ShipActionDeploySpyHandler", + "addr": "0x0078c930", + "convention": "__thiscall", + "prototype": "bool (Game::StrategyServer* this, Game::Ship* ship) /* body 0x0078c930..0x0078c9e0 (next function start), `ret 4`. Entry of the STACK-BUILT SHIP-ACTION TABLE (lane B6's indirection class): no direct callers, no vtable slot, invisible to every call-graph sweep this campaign has run. One gate -- a 12-byte stack query object (vtable 0x00a09648) built at 0x0078c93d and asked about the ship at 0x0078c964; false => return with NO DRAW. Past it: `mov ecx,[esi+0x16c]; add ecx,4; call Mars_RNG_NextFloat` at 0x0078c97f -- ONE STRATEGIC-GENERATOR WORD PER APPLIED Deploy Spy ORDER, inside ApplyAllTurnCommands, i.e. the ProcessTurn bracket and NOT the tail's. The angle is lerp(0.0, 2*pi, r) computed through a FLOAT32 temp at [ebp+8] (fldz supplies LO; HI is the double 6.2831854820251465 at 0x009e21b0), stored to the stack slot pushed at 0x0078c998, which is DeploySpy's SECOND argument. 0x0080c860 is then called ACROSS that half-built frame and does not touch it. MEASURED lane AP 2026-09-09: 1 call / 1 word / no_draw_calls=0 / strategic=true at ret_rva 0x0078c984, and spy.cbh came out 5.033599376678467 -- BIT-IDENTICAL to lane AS's value from a different process, build and session, so this draw is reproducible from the save. ITS PREDICATE IS ON THE COMMAND STREAM, not on save fields: `a SHIPACTION_DEPLOYSPY command in this turn's command stream`. The gate-indexed audit has no column for that and should grow one */", + "status": "verified", + "source": "findings/subsystems/spy-program-draws.md (lane AP 2026-09-09)" + }, + { + "name": "Ship_GetAttachedSpyId", + "addr": "0x0080c860", + "convention": "__thiscall", + "prototype": "int (Game::Ship* this) /* 7 bytes, complete: `mov eax,[ecx+0xa8]; ret`. A zero-argument getter for the ship's attached-spy id -- the same field ServerSpyManager_DeploySpy clears when it detaches the craft from its tender. Recorded because at its ONE call site (0x0078c9ac, in the SHIPACTION_DEPLOYSPY handler) it looks like it takes a float: the caller has already pushed DeploySpy's second argument and stored the deploy angle into it, and this call runs across that half-built frame without disturbing it. An eye following eax there misses where the angle goes */", + "status": "verified", + "source": "findings/subsystems/spy-program-draws.md (lane AP 2026-09-09)" + }, + { + "name": "SpyManager_DestroySpyById", + "addr": "0x00838480", + "convention": "__thiscall", + "prototype": "void (Game::ServerSpyManager* this, int sid) /* Game::ServerSpyManager vftable 0x00a3073c SLOT 10. Looks the SpyCraft up by id through vtable slot 5, then: if spy->deat(+0x10) != 0, registry_lookup(server+0x84, deat) and ServerSystem_RemoveSpy 0x0074f550 on that system (which clears deat and erases sid from spies2); if spy->atto(+0xc) != 0, clear the tender's ship->[+0xa8] and spy->atto; erase the craft from the manager's vector at this+0x10 via 0x0059ec00; then the scalar deleting destructor with flag 1. CALLED FROM ServerSpyManager_vslot13's drain loop at 0x00887eb0, back to front over the local vector of sids collected at 0x008878a1 -- so a spy detected on turn T is DESTROYED at the end of phase 23 on turn T+3, BEFORE vslot14 runs at phase 33. MEASURED lane AP 2026-09-09: nspy 1 -> 0 and spies2 emptied between the Frame-24 and Frame-25 autosaves, and 0x0088dc43 cost 0 on Frame 25 although vslot14 was still entered */", + "status": "verified", + "source": "findings/subsystems/spy-program-draws.md (lane AP 2026-09-09)" + }, + { + "name": "SpyManager_AttributionRoll_MorrigiBranch", + "addr": "0x00840929", + "convention": "site", + "prototype": "/* NOT A FUNCTION -- the `call Mars_RNG_Chance 0x008e6dd0` at 0x00840929 inside SpyManager_Slot13RngCallee 0x008408e0, return address 0x0084092e. Reached ONLY when the destroyed spy's owner has ServerPlayer.Species (+0x5c) == 6, i.e. MORRIGI (species table read from the save's own ISsp list: 0 Human, 1 Hiver, 2 Tarkas, 3 Liir, 4 _NPC, 5 Zuul, 6 Morrigi). Probability is the WIDENED FLOAT at 0x009e5ac4 = 0.75f -- the same four bytes SpyCraft_AccumulateDetectionOdds uses as its species-5 multiplier. Generator: `mov ecx,[eax+0x16c]` -- the STRATEGIC generator OBJECT (not the +4 sub-object). One word at 0 < p < 1. On success the function walks StrategyServer+0x54..+0x58 collecting every player with [+0xfb]==0 and [+0xf8]==0 that is neither the spy's owner nor the target system's owner, and picks one -- a FALSE FLAG; the true owner is never returned for a Morrigi spy. UNFIRED: lane AP measured twelve turns including the one turn the function ran and this branch was never taken, because its Species was 0. THE WORKLOAD THAT CLOSES IT is a Morrigi empire, not a different save of a Human one */", + "status": "verified", + "source": "findings/subsystems/spy-program-draws.md (lane AP 2026-09-09)" + }, + { + "name": "SpyManager_AttributionRoll_MorrigiPick", + "addr": "0x008409c7", + "convention": "site", + "prototype": "/* NOT A FUNCTION -- the `call Mars_RNG_NextInt 0x004271c0` at 0x008409c7 inside SpyManager_Slot13RngCallee 0x008408e0, return address 0x008409cc. Generator: `mov ecx,[...+0x16c]; add ecx,4` -- the +4 SUB-OBJECT. Bound is `count - 1` passed BY POINTER at [ebp+8] (inclusive), so the index is uniform over the candidate list. TWO conditions past the Morrigi branch: the 0.75f roll at 0x00840929 must SUCCEED, and the candidate list must be NON-EMPTY -- `cmp ecx,eax; je 0x840a07` at 0x008409ac returns NULL with no draw when it is empty, which needs a third non-eliminated empire that is neither the spy's owner nor the target's. UNFIRED for the same reason as 0x00840929 */", + "status": "verified", + "source": "findings/subsystems/spy-program-draws.md (lane AP 2026-09-09)" + }, + { + "name": "SpyManager_AttributionRoll_DefaultBranch", + "addr": "0x00840a3c", + "convention": "site", + "prototype": "/* NOT A FUNCTION -- the `call Mars_RNG_Chance 0x008e6dd0` at 0x00840a3c inside SpyManager_Slot13RngCallee 0x008408e0, return address 0x00840a41. Taken for EVERY species except 6 (Morrigi), i.e. the `jne 0x840a29` at 0x00840910. Probability is the WIDENED FLOAT at 0x009e5ac0 = 0.25f. Generator: `mov ecx,[edx+0x16c]` -- the STRATEGIC generator object. One word. On success the function returns arg1, the spy's OWN owner: the victim correctly identifies who sent the spy, 25% of the time; otherwise NULL and the event names no empire. FIRED FOR THE FIRST TIME IN THIS CAMPAIGN, lane AP 2026-09-09: 1 call / 1 word / no_draw_calls=0 / strategic=true at ret_rva 0x00840a41, on the End Turn producing Frame sdet+3 (sdet=22, so Frame 25), from verify/results/saves/as-turn15-spydeployed.sav after seven End Turns. On that same turn the ENTRY PROBE on SpyManager_Slot13RngCallee 0x008408e0 read 1 -- its first non-zero ever, the complement of lane AS's demonstration that its zero said nothing about the subtree */", + "status": "verified", + "source": "findings/subsystems/spy-program-draws.md (lane AP 2026-09-09)" + }, + { + "name": "SpyManager_CounterMissionRollSite", + "addr": "0x0088dc43", + "convention": "site", + "prototype": "/* NOT A FUNCTION -- the `call Mars_RNG_Chance 0x008e6dd0` at 0x0088dc43 inside ServerSpyManager_vslot14 0x0088db80 (phase 33), return address 0x0088dc48. Decoded statically by lane AG (gate-indexed-rng-audit.md 3.2) with CORPUS COUNT 0 OF 22; FIRED FOR THE FIRST TIME lane AP 2026-09-09, 1 call / 1 word / strategic=true on each of Frames 19..24. Probability is spy.cmo (+0x28). MEASURED BEHAVIOUR: the bootstrap turn (Frame == tdep + 3, here Frame 18) sets spyon, runs the stage machine cm 0 -> 1 and cmo := 0.2f and DOES NOT DRAW; from the next turn the site costs one word until cmo reaches 1.0. cmo steps +0.2f per failure and resets to 0.2f on success. TWO CORRECTIONS TO LANE AG 3.2: (a) the CnTrd (+0xff) the stage machine consults at cm 1 -> 2/3 is the TARGET SYSTEM OWNER's, not the spy owner's -- measured, my player has CnTrd=False and the machine still took 1 -> 2 because Kepler's owner has CnTrd=True; (b) `ncp` is a scalar int counter of COMPLETED counter-mission stages (measured 0,0,1,1,2,2,3 in lock-step with cm), so the cm==0 branch's `ncp non-empty` test is `ncp != 0` -- and that branch was never reached, because vslot13 destroyed the spy at cm==4 */", + "status": "verified", + "source": "findings/subsystems/spy-program-draws.md (lane AP 2026-09-09)" + }, + { + "name": "SpyManager_DeploySpyDrawSite", + "addr": "0x0078c97f", + "convention": "site", + "prototype": "/* NOT A FUNCTION -- the `call Mars_RNG_NextFloat 0x0047d830` at 0x0078c97f inside SpyManager_ShipActionDeploySpyHandler 0x0078c930, return address 0x0078c984, ECX = GetGame()->RNG(+0x16c) + 4. ONE STRATEGIC-GENERATOR WORD PER APPLIED Deploy Spy ORDER, in the ProcessTurn bracket. Decoded but not measured by lane AS; MEASURED lane AP 2026-09-09 (1 call / 1 word / no_draw_calls=0 / strategic=true) and the resulting spy.cbh reproduced lane AS's 5.033599376678467 bit for bit across processes, builds and sessions. IN NO EARLIER INVENTORY -- not lane V2's eight, not lane I's 22, not lane AG's audit -- because it hangs off the stack-built ship-action table and leaves no static call edge. Its predicate is on the COMMAND STREAM, not on save fields */", + "status": "verified", + "source": "findings/subsystems/spy-program-draws.md (lane AP 2026-09-09)" + } + ] +} diff --git a/verify/results/saves/ap-deploy-turn15.sav b/verify/results/saves/ap-deploy-turn15.sav new file mode 100644 index 0000000..a5e8452 Binary files /dev/null and b/verify/results/saves/ap-deploy-turn15.sav differ diff --git a/verify/results/saves/ap-oa-turn16.sav b/verify/results/saves/ap-oa-turn16.sav new file mode 100644 index 0000000..73232ab Binary files /dev/null and b/verify/results/saves/ap-oa-turn16.sav differ diff --git a/verify/results/saves/ap-ob-turn16.sav b/verify/results/saves/ap-ob-turn16.sav new file mode 100644 index 0000000..c6b8b2c Binary files /dev/null and b/verify/results/saves/ap-ob-turn16.sav differ diff --git a/verify/results/saves/ap-turn22-spydetected.sav b/verify/results/saves/ap-turn22-spydetected.sav new file mode 100644 index 0000000..31f5f16 Binary files /dev/null and b/verify/results/saves/ap-turn22-spydetected.sav differ diff --git a/verify/results/saves/ap-turn25-spydestroyed.sav b/verify/results/saves/ap-turn25-spydestroyed.sav new file mode 100644 index 0000000..3c71204 Binary files /dev/null and b/verify/results/saves/ap-turn25-spydestroyed.sav differ diff --git a/verify/results/shim/ap/ap-probes8-d.shim.log b/verify/results/shim/ap/ap-probes8-d.shim.log new file mode 100644 index 0000000..888b960 --- /dev/null +++ b/verify/results/shim/ap/ap-probes8-d.shim.log @@ -0,0 +1,114 @@ +00:48:28.253 [tid 10144] ==== sots-engine shim (binkw32 proxy) build ap-989c692-20260909T0425Z ==== +00:48:28.253 [tid 10144] exe: C:\SOTS\Sword of the Stars.exe +00:48:28.253 [tid 10144] exe base=0x006b0000 (link-time image base 0x00400000, ASLR delta +2818048) pid=7108 shim=72b60000 +00:48:28.253 [tid 10144] addresses: Source: sots-re ghidra/addresses.json @ 48db3cc, generated 2026-09-08 by tools/gen_addresses.py +00:48:28.268 [tid 10144] config: hooks=trace +00:48:28.268 [tid 10144] config: hook.Shim::SelfTest::Fill=off +00:48:28.268 [tid 10144] config: hook.Mars::GlobalConsts::LoadFile=off +00:48:28.268 [tid 10144] config: hook.Game::WeaponDictionary::Init=off +00:48:28.268 [tid 10144] config: hook.Game::SectionDictionary::SectionDictionary=off +00:48:28.268 [tid 10144] config: hook.Game::ServerPlayer::ComputeBudget=off +00:48:28.268 [tid 10144] config: hook.Game::TechTree::ProcessResearch=off +00:48:28.268 [tid 10144] config: hook.Game::ServerPlayer::OnTechResearched=off +00:48:28.268 [tid 10144] config: hook.Game::ServerSystem::ProcessTurn=off +00:48:28.268 [tid 10144] config: hook.Game::ServerPlayer::ProcessTurn=off +00:48:28.268 [tid 10144] config: hook.Game::ServerSystem::GroupOutput=off +00:48:28.268 [tid 10144] config: hook.Game::ServerSystem::ComputeTotalOutput=off +00:48:28.268 [tid 10144] config: hook.Game::StrategyServer::MoveFleet=off +00:48:28.268 [tid 10144] config: hook.Game::StrategyServer::ProcessFleetMovement=off +00:48:28.268 [tid 10144] config: hook.Game::StrategyHost::Autosave=trace +00:48:28.268 [tid 10144] config: hook.Game::StrategyServer::ProcessTurn=trace +00:48:28.268 [tid 10144] config: hook.Game::StrategyServer::OnAllCombatDone_Tail=trace +00:48:28.268 [tid 10144] config: hook.Game::StrategyServer::ApplyEncounterResult=trace +00:48:28.268 [tid 10144] config: hook.Game::StrategyServer::NodeLineDecay=trace +00:48:28.268 [tid 10144] config: hook.Game::StrategyServer::ProcessNodeSpaceTravel=trace +00:48:28.268 [tid 10144] config: hook.Game::EncounterDetect::AssignContacts=trace +00:48:28.268 [tid 10144] config: hook.Game::EncounterDetect::ProcessTeamRecord=trace +00:48:28.268 [tid 10144] config: fpu.sample_turn=off +00:48:28.268 [tid 10144] config: fpu.sample_ticks=off +00:48:28.268 [tid 10144] config: trace.inline_max=64 +00:48:28.268 [tid 10144] config: trace.path=C:\SOTS\shim.trace.jsonl +00:48:28.268 [tid 10144] config: trace.flush=always +00:48:28.268 [tid 10144] config: probes=8 -> 8 lane-H entry probes +00:48:28.331 [tid 10144] trace: C:\SOTS\shim.trace.jsonl (default mode trace, inline_max 64, flush always) +00:48:28.331 [tid 10144] hook: Mars_Application_Initialize rva=0x004a0e50 -> va=00b50e50 +00:48:28.331 [tid 10144] hook: MH_Initialize -> MH_OK +00:48:28.331 [tid 10144] hook: MH_CreateHook -> MH_OK (trampoline=030e0fe0) +00:48:28.346 [tid 10144] hook: MH_EnableHook -> MH_OK +00:48:28.346 [tid 10144] cfg: GlobalConsts hook ready (scale constant 0.017453292519943295) +00:48:28.346 [tid 10144] hook: Mars::GlobalConsts::LoadFile rva=0x004b73c0 mode=off (not installed) +00:48:28.346 [tid 10144] dict: dictionaries hook ready (crt new=74c4232b delete=74c40174) +00:48:28.346 [tid 10144] hook: Game::WeaponDictionary::Init rva=0x0019a4c0 mode=off (not installed) +00:48:28.346 [tid 10144] hook: Game::SectionDictionary::SectionDictionary rva=0x00176f40 mode=off (not installed) +00:48:28.346 [tid 10144] research: ProcessResearch hook ready (Cost=0082da00, node=0x34, rng=0x9cc, fpu_cw=0x027f) +00:48:28.346 [tid 10144] hook: Game::TechTree::ProcessResearch rva=0x001876c0 mode=off (not installed) +00:48:28.346 [tid 10144] techfx: OnTechResearched hook ready (regions=15, gate=0/0, fpu_cw=0x027f) +00:48:28.346 [tid 10144] hook: Game::ServerPlayer::OnTechResearched rva=0x00491790 mode=off (not installed) +00:48:28.346 [tid 10144] hook: Game::ServerPlayer::ComputeBudget rva=0x00463030 mode=off (not installed) +00:48:28.346 [tid 10144] hook: Game::ServerSystem::ProcessTurn rva=0x003598e0 mode=off (not installed) +00:48:28.346 [tid 10144] hook: Game::ServerSystem::GroupOutput rva=0x0034b7a0 mode=off (not installed) +00:48:28.346 [tid 10144] hook: Game::ServerSystem::ComputeTotalOutput rva=0x00350480 mode=off (not installed) +00:48:28.346 [tid 10144] player_turn: ServerPlayer::ProcessTurn hook armed (ratio helper at 0082e950) +00:48:28.346 [tid 10144] hook: Game::ServerPlayer::ProcessTurn rva=0x00491340 mode=off (not installed) +00:48:28.346 [tid 10144] hook: Game::StrategyServer::MoveFleet rva=0x003d9ee0 mode=off (not installed) +00:48:28.346 [tid 10144] hook: Game::StrategyServer::ProcessFleetMovement rva=0x003da9a0 mode=off (not installed) +00:48:28.346 [tid 10144] hook: Game::StrategyHost::Autosave rva=0x00495210 -> va=00b45210 MH_CreateHook -> MH_OK (trampoline=030e0fc0) +00:48:28.362 [tid 10144] hook: Game::StrategyHost::Autosave MH_EnableHook -> MH_OK mode=trace +00:48:28.362 [tid 10144] hook: Game::StrategyServer::ProcessTurn rva=0x003dc6c0 -> va=00a8c6c0 MH_CreateHook -> MH_OK (trampoline=030e0fa0) +00:48:28.378 [tid 10144] hook: Game::StrategyServer::ProcessTurn MH_EnableHook -> MH_OK mode=trace +00:48:28.378 [tid 10144] hook: Game::StrategyServer::OnAllCombatDone_Tail rva=0x003d92a0 -> va=00a892a0 MH_CreateHook -> MH_OK (trampoline=030e0f80) +00:48:28.393 [tid 10144] hook: Game::StrategyServer::OnAllCombatDone_Tail MH_EnableHook -> MH_OK mode=trace +00:48:28.393 [tid 10144] hook: Game::StrategyServer::ApplyEncounterResult rva=0x003d8920 -> va=00a88920 MH_CreateHook -> MH_OK (trampoline=030e0f60) +00:48:28.409 [tid 10144] hook: Game::StrategyServer::ApplyEncounterResult MH_EnableHook -> MH_OK mode=trace +00:48:28.409 [tid 10144] hook: Game::StrategyServer::NodeLineDecay rva=0x003ae010 -> va=00a5e010 MH_CreateHook -> MH_OK (trampoline=030e0f40) +00:48:28.409 [tid 10144] hook: Game::StrategyServer::NodeLineDecay MH_EnableHook -> MH_OK mode=trace +00:48:28.409 [tid 10144] hook: Game::StrategyServer::ProcessNodeSpaceTravel rva=0x003a0e20 -> va=00a50e20 MH_CreateHook -> MH_OK (trampoline=030e0f20) +00:48:28.424 [tid 10144] hook: Game::StrategyServer::ProcessNodeSpaceTravel MH_EnableHook -> MH_OK mode=trace +00:48:28.424 [tid 10144] hook: Game::EncounterDetect::AssignContacts rva=0x003aa240 -> va=00a5a240 MH_CreateHook -> MH_OK (trampoline=030e0f00) +00:48:28.440 [tid 10144] hook: Game::EncounterDetect::AssignContacts MH_EnableHook -> MH_OK mode=trace +00:48:28.440 [tid 10144] hook: Game::EncounterDetect::ProcessTeamRecord rva=0x003ca640 -> va=00a7a640 MH_CreateHook -> MH_OK (trampoline=030e0ee0) +00:48:28.456 [tid 10144] hook: Game::EncounterDetect::ProcessTeamRecord MH_EnableHook -> MH_OK mode=trace +00:48:28.456 [tid 10144] hook: Game::StrategyServer::BeginProcessTurn rva=0x003d98e0 -> va=00a898e0 MH_CreateHook -> MH_OK (trampoline=030e0ec0) +00:48:28.471 [tid 10144] hook: Game::StrategyServer::BeginProcessTurn MH_EnableHook -> MH_OK mode=trace +00:48:28.471 [tid 10144] hook: Game::SVSOSwarmQueen::OnTurnBegin rva=0x00129930 -> va=007d9930 MH_CreateHook -> MH_OK (trampoline=030e0ea0) +00:48:28.487 [tid 10144] hook: Game::SVSOSwarmQueen::OnTurnBegin MH_EnableHook -> MH_OK mode=trace +00:48:28.487 [tid 10144] hook: Game::SVSOSwarmQueen::RegisterHives rva=0x00127630 -> va=007d7630 MH_CreateHook -> MH_OK (trampoline=030e0e80) +00:48:28.503 [tid 10144] hook: Game::SVSOSwarmQueen::RegisterHives MH_EnableHook -> MH_OK mode=trace +00:48:28.503 [tid 10144] hook: Game::SVSOSwarmQueen::TickHives rva=0x00127770 -> va=007d7770 MH_CreateHook -> MH_OK (trampoline=030e0e60) +00:48:28.518 [tid 10144] hook: Game::SVSOSwarmQueen::TickHives MH_EnableHook -> MH_OK mode=trace +00:48:28.518 [tid 10144] hook: Game::SVSOSlaversRefuel::UpdateDifficultyTier rva=0x00115820 -> va=007c5820 MH_CreateHook -> MH_OK (trampoline=030e0e40) +00:48:28.534 [tid 10144] hook: Game::SVSOSlaversRefuel::UpdateDifficultyTier MH_EnableHook -> MH_OK mode=trace +00:48:28.534 [tid 10144] hook: Mars::RNG::Seed rva=0x0009fdf0 -> va=0074fdf0 MH_CreateHook -> MH_OK (trampoline=030e0e20) +00:48:28.534 [tid 10144] hook: Mars::RNG::Seed MH_EnableHook -> MH_OK mode=trace +00:48:28.534 [tid 10144] hook: Game::StrategyApp::RunAI rva=0x004706f0 -> va=00b206f0 MH_CreateHook -> MH_OK (trampoline=030e0e00) +00:48:28.549 [tid 10144] hook: Game::StrategyApp::RunAI MH_EnableHook -> MH_OK mode=trace +00:48:28.565 [tid 10144] drawsite: Mars::RNG::NextFloat rva=0x0007d830 -> va=0072d830 create=MH_OK enable=MH_OK +00:48:28.581 [tid 10144] drawsite: Mars::RNG::NextInt rva=0x000271c0 -> va=006d71c0 create=MH_OK enable=MH_OK +00:48:28.596 [tid 10144] drawsite: Mars::RNG::Chance rva=0x004e6dd0 -> va=00b96dd0 create=MH_OK enable=MH_OK +00:48:28.612 [tid 10144] drawsite: Mars::RNG::NextUInt rva=0x000f7670 -> va=007a7670 create=MH_OK enable=MH_OK +00:48:28.628 [tid 10144] drawsite: Mars::RNG::FloatRange rva=0x0007d8a0 -> va=0072d8a0 create=MH_OK enable=MH_OK +00:48:28.628 [tid 10144] drawsite: Mars::RNG::IntRangeBell rva=0x004e6d80 -> va=00b96d80 create=MH_OK enable=MH_OK +00:48:28.643 [tid 10144] drawsite: Mars::RNG::GaussianRange rva=0x004e6e30 -> va=00b96e30 create=MH_OK enable=MH_OK +00:48:28.643 [tid 10144] probe: installing 8 of 12 (probes= in shim.cfg) +00:48:28.659 [tid 10144] probe: Game::ServerSpyManager::vslot13 rva=0x004877b0 -> va=00b377b0 create=MH_OK enable=MH_OK +00:48:28.674 [tid 10144] probe: Game::ServerSpyManager::vslot14 rva=0x0048db80 -> va=00b3db80 create=MH_OK enable=MH_OK +00:48:28.690 [tid 10144] probe: Game::ServerTradeManagerImpl::vslot13 rva=0x0048ef80 -> va=00b3ef80 create=MH_OK enable=MH_OK +00:48:28.706 [tid 10144] probe: Game::ServerTradeManagerImpl::vslot15 rva=0x0042cca0 -> va=00adcca0 create=MH_OK enable=MH_OK +00:48:28.706 [tid 10144] probe: Game::SpyManager::Slot13RngCallee rva=0x004408e0 -> va=00af08e0 create=MH_OK enable=MH_OK +00:48:28.721 [tid 10144] probe: Game::TradeManager::Slot13RngCalleeA rva=0x00420ca0 -> va=00ad0ca0 create=MH_OK enable=MH_OK +00:48:28.737 [tid 10144] probe: Game::TradeManager::Slot13RngCalleeB rva=0x0048b440 -> va=00b3b440 create=MH_OK enable=MH_OK +00:48:28.753 [tid 10144] probe: Game::ServerTradeManager::CreateRaidEncounter rva=0x004938a0 -> va=00b438a0 create=MH_OK enable=MH_OK +00:48:28.753 [tid 10144] watch: disabled (watch=off) +00:48:28.753 [tid 10144] aiorders: disabled (aiorders=off) +00:48:28.753 [tid 10144] fpu: module init, entry cw=0x027f 53bit-double/nearest; force=off value=0x0000 sample_ticks=off +00:48:28.753 [tid 10144] fpu: sample_turn=off (off releases StrategyServer::ProcessTurn for another hook) +00:48:28.753 [tid 10144] fpu: StrategyClient::EndTurn rva=0x00383be0 -> va=00a33be0 MH_CreateHook -> MH_OK (trampoline=030e0c00) +00:48:28.768 [tid 10144] fpu: StrategyClient::EndTurn MH_EnableHook -> MH_OK +00:48:28.768 [tid 10144] fpu: StrategyServer::BeginProcessTurn rva=0x003d98e0 -> va=00a898e0 MH_CreateHook -> MH_ERROR_ALREADY_CREATED (trampoline=00000000) +00:48:28.768 [tid 10144] fpu: StrategyServer::ProcessTurn rva=0x003dc6c0 not installed (sampler off) +00:48:28.768 [tid 10144] fpu: DemoApp::OnTick rva=0x0049a640 not installed (sampler off) +00:48:28.768 [tid 10144] selftest: Shim::SelfTest::Fill mode=off checksum=075ef0c3 records=0 +00:48:28.768 [tid 10144] Application::Initialize called (this=033a8128) +00:59:06.468 [tid 10144] fpu: sample at StrategyClient::EndTurn (this=0d69f600): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:59:09.765 [tid 10144] fpu: sample at StrategyClient::EndTurn (this=330dbd58): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:59:09.827 [tid 10144] fpu: sample at StrategyClient::EndTurn (this=330e0628): cw=0x127f 53bit-double/nearest [no fpu.force configured] diff --git a/verify/results/shim/ap/ap-probes8-w.shim.log b/verify/results/shim/ap/ap-probes8-w.shim.log new file mode 100644 index 0000000..09d52c7 --- /dev/null +++ b/verify/results/shim/ap/ap-probes8-w.shim.log @@ -0,0 +1,147 @@ +00:32:14.211 [tid 8036] ==== sots-engine shim (binkw32 proxy) build ap-989c692-20260909T0425Z ==== +00:32:14.211 [tid 8036] exe: C:\SOTS\Sword of the Stars.exe +00:32:14.211 [tid 8036] exe base=0x006b0000 (link-time image base 0x00400000, ASLR delta +2818048) pid=8872 shim=72b60000 +00:32:14.211 [tid 8036] addresses: Source: sots-re ghidra/addresses.json @ 48db3cc, generated 2026-09-08 by tools/gen_addresses.py +00:32:14.211 [tid 8036] config: hooks=trace +00:32:14.211 [tid 8036] config: hook.Shim::SelfTest::Fill=off +00:32:14.211 [tid 8036] config: hook.Mars::GlobalConsts::LoadFile=off +00:32:14.211 [tid 8036] config: hook.Game::WeaponDictionary::Init=off +00:32:14.211 [tid 8036] config: hook.Game::SectionDictionary::SectionDictionary=off +00:32:14.211 [tid 8036] config: hook.Game::ServerPlayer::ComputeBudget=off +00:32:14.211 [tid 8036] config: hook.Game::TechTree::ProcessResearch=off +00:32:14.211 [tid 8036] config: hook.Game::ServerPlayer::OnTechResearched=off +00:32:14.211 [tid 8036] config: hook.Game::ServerSystem::ProcessTurn=off +00:32:14.211 [tid 8036] config: hook.Game::ServerPlayer::ProcessTurn=off +00:32:14.211 [tid 8036] config: hook.Game::ServerSystem::GroupOutput=off +00:32:14.211 [tid 8036] config: hook.Game::ServerSystem::ComputeTotalOutput=off +00:32:14.211 [tid 8036] config: hook.Game::StrategyServer::MoveFleet=off +00:32:14.211 [tid 8036] config: hook.Game::StrategyServer::ProcessFleetMovement=off +00:32:14.211 [tid 8036] config: hook.Game::StrategyHost::Autosave=trace +00:32:14.211 [tid 8036] config: hook.Game::StrategyServer::ProcessTurn=trace +00:32:14.211 [tid 8036] config: hook.Game::StrategyServer::OnAllCombatDone_Tail=trace +00:32:14.211 [tid 8036] config: hook.Game::StrategyServer::ApplyEncounterResult=trace +00:32:14.211 [tid 8036] config: hook.Game::StrategyServer::NodeLineDecay=trace +00:32:14.211 [tid 8036] config: hook.Game::StrategyServer::ProcessNodeSpaceTravel=trace +00:32:14.211 [tid 8036] config: hook.Game::EncounterDetect::AssignContacts=trace +00:32:14.211 [tid 8036] config: hook.Game::EncounterDetect::ProcessTeamRecord=trace +00:32:14.211 [tid 8036] config: fpu.sample_turn=off +00:32:14.211 [tid 8036] config: fpu.sample_ticks=off +00:32:14.211 [tid 8036] config: trace.inline_max=64 +00:32:14.211 [tid 8036] config: trace.path=C:\SOTS\shim.trace.jsonl +00:32:14.211 [tid 8036] config: trace.flush=always +00:32:14.211 [tid 8036] config: probes=8 -> 8 lane-H entry probes +00:32:14.274 [tid 8036] trace: C:\SOTS\shim.trace.jsonl (default mode trace, inline_max 64, flush always) +00:32:14.274 [tid 8036] hook: Mars_Application_Initialize rva=0x004a0e50 -> va=00b50e50 +00:32:14.274 [tid 8036] hook: MH_Initialize -> MH_OK +00:32:14.274 [tid 8036] hook: MH_CreateHook -> MH_OK (trampoline=013f0fe0) +00:32:14.305 [tid 8036] hook: MH_EnableHook -> MH_OK +00:32:14.305 [tid 8036] cfg: GlobalConsts hook ready (scale constant 0.017453292519943295) +00:32:14.305 [tid 8036] hook: Mars::GlobalConsts::LoadFile rva=0x004b73c0 mode=off (not installed) +00:32:14.305 [tid 8036] dict: dictionaries hook ready (crt new=74c4232b delete=74c40174) +00:32:14.305 [tid 8036] hook: Game::WeaponDictionary::Init rva=0x0019a4c0 mode=off (not installed) +00:32:14.305 [tid 8036] hook: Game::SectionDictionary::SectionDictionary rva=0x00176f40 mode=off (not installed) +00:32:14.305 [tid 8036] research: ProcessResearch hook ready (Cost=0082da00, node=0x34, rng=0x9cc, fpu_cw=0x027f) +00:32:14.305 [tid 8036] hook: Game::TechTree::ProcessResearch rva=0x001876c0 mode=off (not installed) +00:32:14.305 [tid 8036] techfx: OnTechResearched hook ready (regions=15, gate=0/0, fpu_cw=0x027f) +00:32:14.305 [tid 8036] hook: Game::ServerPlayer::OnTechResearched rva=0x00491790 mode=off (not installed) +00:32:14.305 [tid 8036] hook: Game::ServerPlayer::ComputeBudget rva=0x00463030 mode=off (not installed) +00:32:14.305 [tid 8036] hook: Game::ServerSystem::ProcessTurn rva=0x003598e0 mode=off (not installed) +00:32:14.305 [tid 8036] hook: Game::ServerSystem::GroupOutput rva=0x0034b7a0 mode=off (not installed) +00:32:14.305 [tid 8036] hook: Game::ServerSystem::ComputeTotalOutput rva=0x00350480 mode=off (not installed) +00:32:14.305 [tid 8036] player_turn: ServerPlayer::ProcessTurn hook armed (ratio helper at 0082e950) +00:32:14.305 [tid 8036] hook: Game::ServerPlayer::ProcessTurn rva=0x00491340 mode=off (not installed) +00:32:14.305 [tid 8036] hook: Game::StrategyServer::MoveFleet rva=0x003d9ee0 mode=off (not installed) +00:32:14.305 [tid 8036] hook: Game::StrategyServer::ProcessFleetMovement rva=0x003da9a0 mode=off (not installed) +00:32:14.305 [tid 8036] hook: Game::StrategyHost::Autosave rva=0x00495210 -> va=00b45210 MH_CreateHook -> MH_OK (trampoline=013f0fc0) +00:32:14.321 [tid 8036] hook: Game::StrategyHost::Autosave MH_EnableHook -> MH_OK mode=trace +00:32:14.321 [tid 8036] hook: Game::StrategyServer::ProcessTurn rva=0x003dc6c0 -> va=00a8c6c0 MH_CreateHook -> MH_OK (trampoline=013f0fa0) +00:32:14.336 [tid 8036] hook: Game::StrategyServer::ProcessTurn MH_EnableHook -> MH_OK mode=trace +00:32:14.336 [tid 8036] hook: Game::StrategyServer::OnAllCombatDone_Tail rva=0x003d92a0 -> va=00a892a0 MH_CreateHook -> MH_OK (trampoline=013f0f80) +00:32:14.336 [tid 8036] hook: Game::StrategyServer::OnAllCombatDone_Tail MH_EnableHook -> MH_OK mode=trace +00:32:14.336 [tid 8036] hook: Game::StrategyServer::ApplyEncounterResult rva=0x003d8920 -> va=00a88920 MH_CreateHook -> MH_OK (trampoline=013f0f60) +00:32:14.352 [tid 8036] hook: Game::StrategyServer::ApplyEncounterResult MH_EnableHook -> MH_OK mode=trace +00:32:14.352 [tid 8036] hook: Game::StrategyServer::NodeLineDecay rva=0x003ae010 -> va=00a5e010 MH_CreateHook -> MH_OK (trampoline=013f0f40) +00:32:14.367 [tid 8036] hook: Game::StrategyServer::NodeLineDecay MH_EnableHook -> MH_OK mode=trace +00:32:14.367 [tid 8036] hook: Game::StrategyServer::ProcessNodeSpaceTravel rva=0x003a0e20 -> va=00a50e20 MH_CreateHook -> MH_OK (trampoline=013f0f20) +00:32:14.383 [tid 8036] hook: Game::StrategyServer::ProcessNodeSpaceTravel MH_EnableHook -> MH_OK mode=trace +00:32:14.383 [tid 8036] hook: Game::EncounterDetect::AssignContacts rva=0x003aa240 -> va=00a5a240 MH_CreateHook -> MH_OK (trampoline=013f0f00) +00:32:14.399 [tid 8036] hook: Game::EncounterDetect::AssignContacts MH_EnableHook -> MH_OK mode=trace +00:32:14.399 [tid 8036] hook: Game::EncounterDetect::ProcessTeamRecord rva=0x003ca640 -> va=00a7a640 MH_CreateHook -> MH_OK (trampoline=013f0ee0) +00:32:14.414 [tid 8036] hook: Game::EncounterDetect::ProcessTeamRecord MH_EnableHook -> MH_OK mode=trace +00:32:14.414 [tid 8036] hook: Game::StrategyServer::BeginProcessTurn rva=0x003d98e0 -> va=00a898e0 MH_CreateHook -> MH_OK (trampoline=013f0ec0) +00:32:14.430 [tid 8036] hook: Game::StrategyServer::BeginProcessTurn MH_EnableHook -> MH_OK mode=trace +00:32:14.430 [tid 8036] hook: Game::SVSOSwarmQueen::OnTurnBegin rva=0x00129930 -> va=007d9930 MH_CreateHook -> MH_OK (trampoline=013f0ea0) +00:32:14.446 [tid 8036] hook: Game::SVSOSwarmQueen::OnTurnBegin MH_EnableHook -> MH_OK mode=trace +00:32:14.446 [tid 8036] hook: Game::SVSOSwarmQueen::RegisterHives rva=0x00127630 -> va=007d7630 MH_CreateHook -> MH_OK (trampoline=013f0e80) +00:32:14.461 [tid 8036] hook: Game::SVSOSwarmQueen::RegisterHives MH_EnableHook -> MH_OK mode=trace +00:32:14.461 [tid 8036] hook: Game::SVSOSwarmQueen::TickHives rva=0x00127770 -> va=007d7770 MH_CreateHook -> MH_OK (trampoline=013f0e60) +00:32:14.477 [tid 8036] hook: Game::SVSOSwarmQueen::TickHives MH_EnableHook -> MH_OK mode=trace +00:32:14.477 [tid 8036] hook: Game::SVSOSlaversRefuel::UpdateDifficultyTier rva=0x00115820 -> va=007c5820 MH_CreateHook -> MH_OK (trampoline=013f0e40) +00:32:14.492 [tid 8036] hook: Game::SVSOSlaversRefuel::UpdateDifficultyTier MH_EnableHook -> MH_OK mode=trace +00:32:14.492 [tid 8036] hook: Mars::RNG::Seed rva=0x0009fdf0 -> va=0074fdf0 MH_CreateHook -> MH_OK (trampoline=013f0e20) +00:32:14.508 [tid 8036] hook: Mars::RNG::Seed MH_EnableHook -> MH_OK mode=trace +00:32:14.508 [tid 8036] hook: Game::StrategyApp::RunAI rva=0x004706f0 -> va=00b206f0 MH_CreateHook -> MH_OK (trampoline=013f0e00) +00:32:14.524 [tid 8036] hook: Game::StrategyApp::RunAI MH_EnableHook -> MH_OK mode=trace +00:32:14.524 [tid 8036] drawsite: Mars::RNG::NextFloat rva=0x0007d830 -> va=0072d830 create=MH_OK enable=MH_OK +00:32:14.539 [tid 8036] drawsite: Mars::RNG::NextInt rva=0x000271c0 -> va=006d71c0 create=MH_OK enable=MH_OK +00:32:14.555 [tid 8036] drawsite: Mars::RNG::Chance rva=0x004e6dd0 -> va=00b96dd0 create=MH_OK enable=MH_OK +00:32:14.571 [tid 8036] drawsite: Mars::RNG::NextUInt rva=0x000f7670 -> va=007a7670 create=MH_OK enable=MH_OK +00:32:14.586 [tid 8036] drawsite: Mars::RNG::FloatRange rva=0x0007d8a0 -> va=0072d8a0 create=MH_OK enable=MH_OK +00:32:14.602 [tid 8036] drawsite: Mars::RNG::IntRangeBell rva=0x004e6d80 -> va=00b96d80 create=MH_OK enable=MH_OK +00:32:14.617 [tid 8036] drawsite: Mars::RNG::GaussianRange rva=0x004e6e30 -> va=00b96e30 create=MH_OK enable=MH_OK +00:32:14.617 [tid 8036] probe: installing 8 of 12 (probes= in shim.cfg) +00:32:14.633 [tid 8036] probe: Game::ServerSpyManager::vslot13 rva=0x004877b0 -> va=00b377b0 create=MH_OK enable=MH_OK +00:32:14.649 [tid 8036] probe: Game::ServerSpyManager::vslot14 rva=0x0048db80 -> va=00b3db80 create=MH_OK enable=MH_OK +00:32:14.664 [tid 8036] probe: Game::ServerTradeManagerImpl::vslot13 rva=0x0048ef80 -> va=00b3ef80 create=MH_OK enable=MH_OK +00:32:14.680 [tid 8036] probe: Game::ServerTradeManagerImpl::vslot15 rva=0x0042cca0 -> va=00adcca0 create=MH_OK enable=MH_OK +00:32:14.696 [tid 8036] probe: Game::SpyManager::Slot13RngCallee rva=0x004408e0 -> va=00af08e0 create=MH_OK enable=MH_OK +00:32:14.711 [tid 8036] probe: Game::TradeManager::Slot13RngCalleeA rva=0x00420ca0 -> va=00ad0ca0 create=MH_OK enable=MH_OK +00:32:14.727 [tid 8036] probe: Game::TradeManager::Slot13RngCalleeB rva=0x0048b440 -> va=00b3b440 create=MH_OK enable=MH_OK +00:32:14.742 [tid 8036] probe: Game::ServerTradeManager::CreateRaidEncounter rva=0x004938a0 -> va=00b438a0 create=MH_OK enable=MH_OK +00:32:14.742 [tid 8036] watch: disabled (watch=off) +00:32:14.742 [tid 8036] aiorders: disabled (aiorders=off) +00:32:14.742 [tid 8036] fpu: module init, entry cw=0x027f 53bit-double/nearest; force=off value=0x0000 sample_ticks=off +00:32:14.742 [tid 8036] fpu: sample_turn=off (off releases StrategyServer::ProcessTurn for another hook) +00:32:14.742 [tid 8036] fpu: StrategyClient::EndTurn rva=0x00383be0 -> va=00a33be0 MH_CreateHook -> MH_OK (trampoline=013f0c00) +00:32:14.758 [tid 8036] fpu: StrategyClient::EndTurn MH_EnableHook -> MH_OK +00:32:14.758 [tid 8036] fpu: StrategyServer::BeginProcessTurn rva=0x003d98e0 -> va=00a898e0 MH_CreateHook -> MH_ERROR_ALREADY_CREATED (trampoline=00000000) +00:32:14.758 [tid 8036] fpu: StrategyServer::ProcessTurn rva=0x003dc6c0 not installed (sampler off) +00:32:14.758 [tid 8036] fpu: DemoApp::OnTick rva=0x0049a640 not installed (sampler off) +00:32:14.758 [tid 8036] selftest: Shim::SelfTest::Fill mode=off checksum=075ef0c3 records=0 +00:32:14.758 [tid 8036] Application::Initialize called (this=03438128) +00:40:12.096 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:15.284 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:15.284 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:25.878 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:29.081 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:29.096 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:39.299 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:42.503 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:42.503 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:52.768 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:55.956 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:40:55.971 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:06.237 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:09.471 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:09.471 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:19.690 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:22.893 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:22.893 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:33.096 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:36.331 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:36.331 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:46.549 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:49.799 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:41:49.799 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:00.018 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:03.237 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:03.237 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:13.503 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:16.706 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:16.706 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:26.956 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:30.174 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:30.190 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:40.378 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=0d67e8f0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:43.549 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e7ee0): cw=0x127f 53bit-double/nearest [no fpu.force configured] +00:42:43.549 [tid 8036] fpu: sample at StrategyClient::EndTurn (this=341e8d70): cw=0x127f 53bit-double/nearest [no fpu.force configured] diff --git a/verify/traces/ap-probes8-deploy-turn15.jsonl.gz b/verify/traces/ap-probes8-deploy-turn15.jsonl.gz new file mode 100644 index 0000000..36a758c Binary files /dev/null and b/verify/traces/ap-probes8-deploy-turn15.jsonl.gz differ diff --git a/verify/traces/ap-probes8-turn16-turn26.jsonl.gz b/verify/traces/ap-probes8-turn16-turn26.jsonl.gz new file mode 100644 index 0000000..4a101ab Binary files /dev/null and b/verify/traces/ap-probes8-turn16-turn26.jsonl.gz differ