From fca5c6cd56ea97fea866f4c3d28ad5d5c79bbcf4 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 9 Sep 2026 02:15:42 -0400 Subject: [PATCH] lane BQ merged: load-boundary scope on AR's pair, peaceful auto-resolve is not combat-free, rule 26(c) marked as a screen not a decision procedure --- campaign/DASHBOARD.md | 14 +++++++------- campaign/board.md | 11 +++++++---- findings/subsystems/determinism-oracle.md | 13 +++++++++++++ guides/method-rules.md | 23 +++++++++++++++++++++++ 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/campaign/DASHBOARD.md b/campaign/DASHBOARD.md index 6bc5d5d..9ecf760 100644 --- a/campaign/DASHBOARD.md +++ b/campaign/DASHBOARD.md @@ -1,16 +1,16 @@ # SotS RE campaign — coverage dashboard -Generated 2026-09-09 06:02 UTC · `sots-re` @ 371f789,2026-09-09 · `sots-engine` @ 1654785,2026-09-09 (241 commits) · regenerate with `tools/dashboard.py` +Generated 2026-09-09 06:15 UTC · `sots-re` @ c43f638,2026-09-09 · `sots-engine` @ 1654785,2026-09-09 (241 commits) · regenerate with `tools/dashboard.py` > **North star:** A functional reimplementation of the engine — behavior-equivalent, NOT byte-for-byte ## 1. Map coverage (campaign/board.md) -425 targets · mapped-or-better **378/425** `[█████████░] 89%` · verified **336/425** `[████████░░] 79%` +428 targets · mapped-or-better **381/428** `[█████████░] 89%` · verified **339/428** `[████████░░] 79%` | Status | Count | % | |---|---:|---:| -| verified | 336 | 79% | +| verified | 339 | 79% | | mapped | 42 | 10% | | in-progress | 4 | 1% | | backlog | 41 | 10% | @@ -19,12 +19,12 @@ Generated 2026-09-09 06:02 UTC · `sots-re` @ 371f789,2026-09-09 · `sots-engine | Type | verified | mapped | in-progress | backlog | blocked | total | |---|---:|---:|---:|---:|---:|---:| | objects | 46 | 6 | 0 | 3 | 1 | 56 | -| control-flow | 33 | 2 | 0 | 0 | 0 | 35 | +| control-flow | 34 | 2 | 0 | 0 | 0 | 36 | | subsystems | 4 | 8 | 0 | 2 | 1 | 15 | | engine | 31 | 0 | 0 | 0 | 0 | 31 | | verify | 104 | 15 | 3 | 35 | 0 | 157 | | phase2 | 13 | 3 | 1 | 0 | 0 | 17 | -| meta | 86 | 6 | 0 | 1 | 0 | 93 | +| meta | 88 | 6 | 0 | 1 | 0 | 95 | | other | 19 | 2 | 0 | 0 | 0 | 21 | ## 2. Binary understanding @@ -133,11 +133,11 @@ Most recent open: ## 9. Delta since previous dashboard -- verified targets: 333 → 336 (+3) · mapped-or-better: 375 → 378 (+3) +- verified targets: 336 → 339 (+3) · mapped-or-better: 378 → 381 (+3) - engine LOC: 58,647 → 58,647 (+0) · test files: 122 → 122 (+0) · checks: 4,257 → 4,257 (+0) - addresses verified: 1,160 → 1,160 (+0) · recovered layouts: 384 → 384 (+0) · open questions: 26 → 26 (+0) - standalone leaves closed: 45 → 45 (+0) · leaves still diverging: 63 → 63 (+0) --- warnings: board.md: unknown types subsystems; mars-rng.md: no oracle total row parsed; mars-stream.md: no oracle total row parsed; mars-vfs.md: no oracle total row parsed - + diff --git a/campaign/board.md b/campaign/board.md index 3ee08b3..e1aa777 100644 --- a/campaign/board.md +++ b/campaign/board.md @@ -59,7 +59,7 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`). | P2-B3 ProcessResearch (behavioral, RNG) | phase2 | mapped | high | 85% | 2026-09-08 | LIVE, PARTIAL PASS: 15 calls compared, 13 zero-divergence; RNG post-state matched 14/15 incl. every roll (validates MT19937 + draw mapping + odds together). 2 divergences are the declared SetResearched boundary. ORACLE FAILS by exactly one item across 40,300: an unposted EVENT_RESEARCH_OVERBUDGET - compare was blind because the event list was never a declared region. fpu_cw=0x127f => 53-bit double, x87 question SETTLED. No Zuul in the save: double roll still disassembly-only . RECAPTURED WITH GUARDS 2026-09-08 (lane R): the oracle gap is now a COMPARE DIVERGENCE - `side.events.after.v.next_id orig=4 ours=3` on call 0, its only divergent field, with node[144] progress 2879->5768 and flag 1->2 both reproduced and the single RNG draw identical. 15 calls over 5 turns: 3 diverged, **RNG 15/15** (better than the original 14/15 - no tech-effect draw in this session). Guards on the two completion calls map SetResearched: ConMod[0..2]/OutMod/PopMod, ResTNm, TechTree+0x20 order counter, and the undeclared otch vector | . **LANE V 2026-09-08 (build `eventlive-dd38117-20260908T0916Z`): the events divergence is CLOSED on the reference turn** - 3 calls, 3 compared, **0 diverged, exit 0**. Over 5 turns 15 calls / **2** diverged (was 3), both short by exactly 1 `next_id` = the unmodelled `EVENT_TECHS_UNLOCKED`. **CORRECTION to lane R's 'RNG 15/15': that was WORKLOAD LUCK, not a property.** On lane V's call 9 the completed tech had `research_roll_pending` set, so the original drew one word `ours` did not (`left` 374 vs 375, `next_index` 250 vs 249) - the `OnTechResearched` callback draw, a DECLARED out-of-scope boundary (P-events-wiring §3 says so in as many words). Honest statement: the `rng` region reproduces bit-for-bit on every call that does not complete a roll-triggering tech, and is short by exactly one draw on every call that does. Also live: `sizeof(ObservedTech)` = **44**, measured twice (`observed_techs.bytes` 440->484 and 484->528). Zuul double roll now CLOSED on a species-5 save lane V made - see its own row | RNG signatures (Ghidra) | meta | verified | high | 100% | 2026-09-08 | Seed/Twist/NextFloat/NextInt verified; draw = y/(2^32-1); NextInt [0,n] inclusive; lazy twist; left@+0x9c4. RUNTIME CONFIRMED: fpu_cw=0x127f (53-bit double, round-nearest) - our next_float model is right, float_from_pc24 is an unused contingency | | engine: game/effects | engine | verified | high | 100% | 2026-09-08 | merged: TechId enum (196 slots @10000+i), 44 ids with typed strategic effects, species flag bits, ApplyTechEffect; 254 checks | -| VM140 exclusivity (lab rule) | meta | verified | high | 100% | 2026-09-08 | one agent at a time. Holder: **VM140=FREE, oracle bytes verified intact), VM141=FREE (L2 released, 192.168.10.143, hooks=off), VM144=FREE (L3 released; workload saves left on disk, so Load-dialog row positions have moved on that guest), VM145=FREE, save set restored), VM146=FREE (CB released, stopped, restored)** (five parallel investigations 2026-09-08). VM140 remains the reference/oracle guest. Was: FREE (W3 released 2026-09-08, oracle bytes verified; **AND THE LAB IS NOW FIVE GUESTS** - VM141/144/145/146 are ZFS clones for probes and workloads, VM140 remains the exclusive reference; W2 released 2026-09-08, VM restored + oracle bytes verified; H released 2026-09-08, oracle reproduced + VM restored; N released 2026-09-08; Z released 2026-09-08; O-workloads released 2026-09-08 08:30 local, see the LANE O note at the end of this row; U-unlock released 2026-09-08 06:55 local; V-eventlive before it; F-fpucw before it, M-movefleet before that). QUEUE: empty. VM left at the MAIN MENU, `hooks=trace`, build `recap-7584bad-20260908T0615Z` restored from `C:\SOTS\shimdist-recap` (that dist also carries `shim.cfg.recap{trace,b3,b1,misc}`). Lane M also left `C:\SOTS\shimdist-mf` + `C:\SOTS\ui\mf{deploy,release}.ps1` in place - harmless, and a working template for the next lane. Windows Update DISABLED/paused on the VM. Non-holders build /srv/re-lab/build/sots-engine-, stage dist-, deploy C:\SOTS\shimdist-. GOTCHA (lane R): after `schtasks /Run /TN SOTS` the main menu can take >60 s - SCREENSHOT AND VERIFY before clicking, or the click path lands in Credits. GOTCHA (lane M): drive the load dialog ONE rui.ps1 CALL PER CLICK with a screenshot between - a single chained cmd.txt loses sync and silently ends up somewhere else. And the Load Game dialog does NOT pre-select Single Player on a fresh launch: the documented path really is Load Game (512,536) -> Single Player (512,290) -> OK (551,523) -> row -> OK (682,624) -> Launch (511,663). ref-turn2 row is at (400,436) GOTCHA (lane F, confirms lane R): the >60 s startup is REAL and cost a whole wasted run — do not sleep-and-click, **verify the main menu from a screenshot** (`verify/fpu-cw/` run scripts poll a screenshot until the Load Game / Exit buttons are bright red; 3 probes ≈ 25 s was typical). TIP (lane F): reset `SavedGames\` to a fixed file set before every run — the Load dialog row positions depend on how many files are listed, so a constant set means the click path never has to be re-derived (with the 4-file set ref-turn2 sits at (400,348), not (400,436)). PowerShell over SSH mangles quoting badly: send snippets base64 as `powershell -EncodedCommand`, or use `-ExecutionPolicy Bypass -File`. Lane F left `C:\SOTS\shimdist-fpu` + `C:\SOTS\ui\f{deploy,grab,fpu}.ps1` + `C:\SOTS\ui\preF\` (the pre-lane-F SavedGames snapshot, restored) in place. VM RESTORED: recap build `recap-7584bad-20260908T0615Z`, `hooks=trace`, SavedGames back to the 7-file pre-F set, main menu verified by screenshot. GOTCHA (lane V, third confirmation): the >60 s startup is REAL - the Kerberos intro was still on screen at t+30 s and the main menu appeared at ~t+70 s. GOTCHA (lane V): `type ` via the click helper did **NOT** register in the game's text fields this session (neither the Create Game name nor the Save Game file name took SendKeys); the workaround is to accept the default name and **rename the `.sav` on disk** - the Load dialog lists files by FILENAME. SAVEDGAMES IS NOW **8 FILES**: the pre-existing 7 plus `zuul-turn5.sav` (a species-5 save, see the Zuul row). Row positions are UNCHANGED for existing lanes because `zuul-turn5` sorts last: **ref-turn2 is still (400,436)**, zuul-turn5 is the new row 8 at (400,465), rows 29 px apart from y=262 - verified by screenshot `verify/results/shim/eventlive/load-dialog-8files.png`. The three autosaves now belong to the Zuul game. Lane V left `C:\SOTS\shimdist-v` + `C:\SOTS\ui\v{deploy,release,click}.ps1`. VM RESTORED: recap build `recap-7584bad-20260908T0615Z`, `hooks=trace`, main menu verified by screenshot. LANE U (2026-09-08): left `C:\SOTS\shimdist-u` + `C:\SOTS\ui\u{deploy,release}.ps1` in place. SavedGames is STILL 8 FILES (the three autosaves now belong to the Zuul game at turn 15) - **ref-turn2 is still (400,436)**, zuul-turn5 still (400,465). A QEMU-level screenshot (feed `screendump /tmp/vm140.ppm` to `qm monitor 140` on spicy, then scp the .ppm) is more reliable than the click helper's `shot` action and does not need the game window. FOURTH confirmation of the >60 s startup: the Kerberos intro was still on screen at t+45 s, the main menu at ~t+85 s. VM RESTORED: recap build `recap-7584bad-20260908T0615Z`, `hooks=trace`, main menu verified by screenshot. | **LANE O 2026-09-08.** **VM140 is FREE.** Holder was O-workloads; released 2026-09-08 after ~9 turns of Zuul play (turn 15 -> 23) and a fresh 3-turn Human game. **CORRECTION to lane V's `type` gotcha: the click helper's `type` DOES reach the game's text fields - you must CLICK THE FIELD FIRST.** `click 480 234` (the Save Game File Name box) then `type ` worked every time this session, and `click 500 363` + `type` worked for the Create Game name box; lane V's failure was almost certainly an unfocused field. Saves can therefore be named properly and no longer need renaming on disk (the file written is gameName + typedName). Coordinates confirmed this session at 1024x768: in-game menu = round button (1000,714) -> `Save` (937,679) / `Quit to Main Menu` (937,698) -> confirm OK (537,377); Save dialog `Save` (772,233), `Game Successfully Saved` OK (612,383), `Done` (772,542); map panel `Research` (55,124) `Design` (142,124) `Build` (221,124), `Manage Fleets` (60,464) `Move` (155,464) `Special` (229,464), `End Turn` (100,714); Build screen `Add To Queue` (125,714); **Esc leaves the Build and Research screens** - the bottom-left button on the Research screen is `Cancel research`, NOT back, and clicking it pops `Confirm Cancel`. Move mode: send `move X Y` FIRST to read the target's name / Range / ETA off the map, THEN `click`, because that readout is what tells you whether the route is a node route (2-5 turns) or a sublight crawl (37-86 turns). **Chained clicks in one `cmd.txt` DO work for repeated clicks on the SAME button** (`Add To Queue`) but drop about half at `sleep 250` and about 1 in 6 at `sleep 500` - count the result, never assume. End Turn takes 30-45 s under `hooks=trace` with ~25 ships. A mid-turn AI diplomacy popup can steal a scripted click sequence - screenshot after any batch that seems to have done nothing. **VM RESTORED and verified**: quit to the main menu (screenshot), `SavedGames` back to the ORIGINAL 8-file set with the three autosaves restored BYTE-IDENTICAL (sha256 prefixes 0630daaa / e0bf3485 / 1f0dc162), so `ref-turn2` is still (400,436) and `zuul-turn5` still (400,465); lane O's staging dir `C:\SOTS\o-stage` removed; `shim.cfg` still `hooks=trace`, build `recap-7584bad-20260908T0615Z` untouched. Lane O's 7 saves live in the REPO ONLY - push one back with `scp verify/results/saves/.sav re@192.168.10.139:C:/SOTS/SavedGames/` when a compare needs it, and remember that adding a file shifts the Load dialog rows (`MyGame*` sorts BEFORE `MyGameverify1*` **VM141 = FREE (lane AD released 2026-09-08; guest restored, SavedGames back to AC's 13-file set byte-identical PLUS `ad-turn27-two-raiders.sav`, so Load-dialog rows have MOVED AGAIN - screenshot the dialog).** Lane AD left `C:\SOTS\shimdist-ad` + `C:\SOTS\ui\ad-*.ps1`; `click_helper.ps1` unmodified. **GOTCHA: startup on this guest is ~4.5 min through THREE intro movies** (fg + `key {ESC}` x3 via the click helper, then ~90 s), and loading a turn-27 save is a further 3-4 min - budget **~9 min per run** before the first End Turn. **GOTCHA: the first End Turn click after leaving Move mode is swallowed exiting the mode**; the turn starts on the second click. **VM144 = FREE (lane AS released 2026-09-09**; restored to L3's build + `shim.cfg` + the three pre-AS autosaves byte-identical, main menu verified by screenshot). **SavedGames is now 15 FILES** - AS added `MyGameas1predeploy` (400,347) and `MyGameas1spydep` (400,376), so **ROW POSITIONS HAVE MOVED AGAIN**; screenshot the dialog. AS left `C:\SOTS\shimdist-as` + `ui\as{go,hash}.ps1` + `ui\preAS-*`; `click_helper.ps1` untouched. **VM141 = FREE (lane AR released 2026-09-09**; restored, `shim.cfg.hoff`, this lane's `binkw32.dll`, SavedGames = AD's set PLUS `ar-turn37-816raiders.sav`; with autosaves deleted the list is 12 rows from y=260 at 29 px pitch, `ar-turn37-816raiders` is **row 3 at (400,318)**). Left `C:\SOTS\shimdist-ar` + `ui\ar-*.ps1`; `click_helper.ps1` unmodified. **GOTCHA, CORRECTS THE STARTUP FIGURE: `fg` + `key {ESC}` x3 spaced ~2.5 s then ~90 s puts the main menu up in about TWO minutes, not 4.5** - the 4.5 figure is what you get by waiting the movies out. Loading a turn-37 save is a further 3-4 min and that part is NOT compressible. **GOTCHA: `rdrag x y x y` (a zero-length right-drag) IS A RIGHT-CLICK** - the fleet panel's `Add To New Fleet`/`Split Ships` menu is right-click-only and the helper has no right-click verb. **GOTCHA: a trade-sector node is a legal move target EVEN WHEN ITS CUBE IS NOT DRAWN** - in Move mode the hover readout names it; fit `screen = (A*wx + B*wz + C, D*wx + E*wz + F)` from two known points in the save and you land on any node first try. **GOTCHA: the encounter query BLOCKS End Turn** - sample the `Done` pixel at (233,673): ~(183,18,0) when a query is up, ~(8,8,8) on the map; `Auto Resolve Peacefully` (667,641), `Done` (233,673). **VM144 = FREE (lane AP released 2026-09-09**; restored to L3's build + L3's `shim.cfg` + the three pre-AS autosaves BYTE-IDENTICAL, main menu screenshot-verified). SavedGames **STILL 15 FILES** and AS's row positions **STILL HOLD** - `MyGameas1predeploy` (400,347), `MyGameas1spydep` (400,376), OK (682,624), verified twice. AP added `C:\SOTS\shimdist-ap`, `ui\ap{go,hash}.ps1`, `ui\ap\` - **an END-TURN DRIVER WORTH REUSING**: `ap_loop.ps1` + the `SOTSAP` task clicks End Turn, **polls the autosave mtime**, snapshots `(Autosave).sav` per turn and stops on the first stall with a screenshot - **11 turns unattended in 11 minutes**. **GOTCHA THAT COST NINE MINUTES: the click task takes ~6 s end to end, so a screenshot taken 4 s after `schtasks /Run` shows the state BEFORE the click** - four such shots read exactly like an 87 px coordinate offset and **nearly became a false gotcha in this very row**. WAIT >= 9 s. **GOTCHA: the first click after `fg` is swallowed by focus activation.** **GOTCHA: the main-menu pixel test must sample the BUTTON (470,530) = (184,0,0), not the white label at (511,536).** TIP: `wheel` zoom separates stars that share a hit test at default zoom (Kepler vs Midway, which cost lane AS a run) - no affine fit needed. An End Turn on this state costs **6 SECONDS** under `probes=8`. **VM146 = FREE (lane BP released 2026-09-09 restored** and hash-verified: pre-BP autosaves byte-identical, `shim.cfg` `hooks=off` and `binkw32.dll` restored to the pre-BP files, game process **not running, exactly as found**). `ad-turn27-two-raiders.sav` deliberately left on disk, so **Load-dialog rows have MOVED**: autosaves deleted gives 7 rows from y=260 at 29 px pitch, `ad-turn27-two-raiders` **row 1 at (400,260)**. Left `C:\SOTS\shimdist-bp` + `ui\bp{deploy,shot,hash}.ps1` + `ui\preBP\`. **GOTCHA THAT COST FOUR WASTED CLICKS: the lab wall's `/shot/.png` serves a CACHED frame and can be a whole poll cycle (~5 s) BEHIND your last action.** Clicking a Load row then shooting 5 s later shows it un-selected; a second click shows it selected - which reads exactly like a swallowed click and is not. **Wait ~12-16 s, or take two shots and use the second.** Loading the turn-27 save is **3.5-4 min** and is NOT compressible; End Turn on it ~35 s with the light hook set. | +| VM140 exclusivity (lab rule) | meta | verified | high | 100% | 2026-09-08 | one agent at a time. Holder: **VM140=FREE, oracle bytes verified intact), VM141=FREE (L2 released, 192.168.10.143, hooks=off), VM144=FREE (L3 released; workload saves left on disk, so Load-dialog row positions have moved on that guest), VM145=FREE, save set restored), VM146=FREE (CB released, stopped, restored)** (five parallel investigations 2026-09-08). VM140 remains the reference/oracle guest. Was: FREE (W3 released 2026-09-08, oracle bytes verified; **AND THE LAB IS NOW FIVE GUESTS** - VM141/144/145/146 are ZFS clones for probes and workloads, VM140 remains the exclusive reference; W2 released 2026-09-08, VM restored + oracle bytes verified; H released 2026-09-08, oracle reproduced + VM restored; N released 2026-09-08; Z released 2026-09-08; O-workloads released 2026-09-08 08:30 local, see the LANE O note at the end of this row; U-unlock released 2026-09-08 06:55 local; V-eventlive before it; F-fpucw before it, M-movefleet before that). QUEUE: empty. VM left at the MAIN MENU, `hooks=trace`, build `recap-7584bad-20260908T0615Z` restored from `C:\SOTS\shimdist-recap` (that dist also carries `shim.cfg.recap{trace,b3,b1,misc}`). Lane M also left `C:\SOTS\shimdist-mf` + `C:\SOTS\ui\mf{deploy,release}.ps1` in place - harmless, and a working template for the next lane. Windows Update DISABLED/paused on the VM. Non-holders build /srv/re-lab/build/sots-engine-, stage dist-, deploy C:\SOTS\shimdist-. GOTCHA (lane R): after `schtasks /Run /TN SOTS` the main menu can take >60 s - SCREENSHOT AND VERIFY before clicking, or the click path lands in Credits. GOTCHA (lane M): drive the load dialog ONE rui.ps1 CALL PER CLICK with a screenshot between - a single chained cmd.txt loses sync and silently ends up somewhere else. And the Load Game dialog does NOT pre-select Single Player on a fresh launch: the documented path really is Load Game (512,536) -> Single Player (512,290) -> OK (551,523) -> row -> OK (682,624) -> Launch (511,663). ref-turn2 row is at (400,436) GOTCHA (lane F, confirms lane R): the >60 s startup is REAL and cost a whole wasted run — do not sleep-and-click, **verify the main menu from a screenshot** (`verify/fpu-cw/` run scripts poll a screenshot until the Load Game / Exit buttons are bright red; 3 probes ≈ 25 s was typical). TIP (lane F): reset `SavedGames\` to a fixed file set before every run — the Load dialog row positions depend on how many files are listed, so a constant set means the click path never has to be re-derived (with the 4-file set ref-turn2 sits at (400,348), not (400,436)). PowerShell over SSH mangles quoting badly: send snippets base64 as `powershell -EncodedCommand`, or use `-ExecutionPolicy Bypass -File`. Lane F left `C:\SOTS\shimdist-fpu` + `C:\SOTS\ui\f{deploy,grab,fpu}.ps1` + `C:\SOTS\ui\preF\` (the pre-lane-F SavedGames snapshot, restored) in place. VM RESTORED: recap build `recap-7584bad-20260908T0615Z`, `hooks=trace`, SavedGames back to the 7-file pre-F set, main menu verified by screenshot. GOTCHA (lane V, third confirmation): the >60 s startup is REAL - the Kerberos intro was still on screen at t+30 s and the main menu appeared at ~t+70 s. GOTCHA (lane V): `type ` via the click helper did **NOT** register in the game's text fields this session (neither the Create Game name nor the Save Game file name took SendKeys); the workaround is to accept the default name and **rename the `.sav` on disk** - the Load dialog lists files by FILENAME. SAVEDGAMES IS NOW **8 FILES**: the pre-existing 7 plus `zuul-turn5.sav` (a species-5 save, see the Zuul row). Row positions are UNCHANGED for existing lanes because `zuul-turn5` sorts last: **ref-turn2 is still (400,436)**, zuul-turn5 is the new row 8 at (400,465), rows 29 px apart from y=262 - verified by screenshot `verify/results/shim/eventlive/load-dialog-8files.png`. The three autosaves now belong to the Zuul game. Lane V left `C:\SOTS\shimdist-v` + `C:\SOTS\ui\v{deploy,release,click}.ps1`. VM RESTORED: recap build `recap-7584bad-20260908T0615Z`, `hooks=trace`, main menu verified by screenshot. LANE U (2026-09-08): left `C:\SOTS\shimdist-u` + `C:\SOTS\ui\u{deploy,release}.ps1` in place. SavedGames is STILL 8 FILES (the three autosaves now belong to the Zuul game at turn 15) - **ref-turn2 is still (400,436)**, zuul-turn5 still (400,465). A QEMU-level screenshot (feed `screendump /tmp/vm140.ppm` to `qm monitor 140` on spicy, then scp the .ppm) is more reliable than the click helper's `shot` action and does not need the game window. FOURTH confirmation of the >60 s startup: the Kerberos intro was still on screen at t+45 s, the main menu at ~t+85 s. VM RESTORED: recap build `recap-7584bad-20260908T0615Z`, `hooks=trace`, main menu verified by screenshot. | **LANE O 2026-09-08.** **VM140 is FREE.** Holder was O-workloads; released 2026-09-08 after ~9 turns of Zuul play (turn 15 -> 23) and a fresh 3-turn Human game. **CORRECTION to lane V's `type` gotcha: the click helper's `type` DOES reach the game's text fields - you must CLICK THE FIELD FIRST.** `click 480 234` (the Save Game File Name box) then `type ` worked every time this session, and `click 500 363` + `type` worked for the Create Game name box; lane V's failure was almost certainly an unfocused field. Saves can therefore be named properly and no longer need renaming on disk (the file written is gameName + typedName). Coordinates confirmed this session at 1024x768: in-game menu = round button (1000,714) -> `Save` (937,679) / `Quit to Main Menu` (937,698) -> confirm OK (537,377); Save dialog `Save` (772,233), `Game Successfully Saved` OK (612,383), `Done` (772,542); map panel `Research` (55,124) `Design` (142,124) `Build` (221,124), `Manage Fleets` (60,464) `Move` (155,464) `Special` (229,464), `End Turn` (100,714); Build screen `Add To Queue` (125,714); **Esc leaves the Build and Research screens** - the bottom-left button on the Research screen is `Cancel research`, NOT back, and clicking it pops `Confirm Cancel`. Move mode: send `move X Y` FIRST to read the target's name / Range / ETA off the map, THEN `click`, because that readout is what tells you whether the route is a node route (2-5 turns) or a sublight crawl (37-86 turns). **Chained clicks in one `cmd.txt` DO work for repeated clicks on the SAME button** (`Add To Queue`) but drop about half at `sleep 250` and about 1 in 6 at `sleep 500` - count the result, never assume. End Turn takes 30-45 s under `hooks=trace` with ~25 ships. A mid-turn AI diplomacy popup can steal a scripted click sequence - screenshot after any batch that seems to have done nothing. **VM RESTORED and verified**: quit to the main menu (screenshot), `SavedGames` back to the ORIGINAL 8-file set with the three autosaves restored BYTE-IDENTICAL (sha256 prefixes 0630daaa / e0bf3485 / 1f0dc162), so `ref-turn2` is still (400,436) and `zuul-turn5` still (400,465); lane O's staging dir `C:\SOTS\o-stage` removed; `shim.cfg` still `hooks=trace`, build `recap-7584bad-20260908T0615Z` untouched. Lane O's 7 saves live in the REPO ONLY - push one back with `scp verify/results/saves/.sav re@192.168.10.139:C:/SOTS/SavedGames/` when a compare needs it, and remember that adding a file shifts the Load dialog rows (`MyGame*` sorts BEFORE `MyGameverify1*` **VM141 = FREE (lane AD released 2026-09-08; guest restored, SavedGames back to AC's 13-file set byte-identical PLUS `ad-turn27-two-raiders.sav`, so Load-dialog rows have MOVED AGAIN - screenshot the dialog).** Lane AD left `C:\SOTS\shimdist-ad` + `C:\SOTS\ui\ad-*.ps1`; `click_helper.ps1` unmodified. **GOTCHA: startup on this guest is ~4.5 min through THREE intro movies** (fg + `key {ESC}` x3 via the click helper, then ~90 s), and loading a turn-27 save is a further 3-4 min - budget **~9 min per run** before the first End Turn. **GOTCHA: the first End Turn click after leaving Move mode is swallowed exiting the mode**; the turn starts on the second click. **VM144 = FREE (lane AS released 2026-09-09**; restored to L3's build + `shim.cfg` + the three pre-AS autosaves byte-identical, main menu verified by screenshot). **SavedGames is now 15 FILES** - AS added `MyGameas1predeploy` (400,347) and `MyGameas1spydep` (400,376), so **ROW POSITIONS HAVE MOVED AGAIN**; screenshot the dialog. AS left `C:\SOTS\shimdist-as` + `ui\as{go,hash}.ps1` + `ui\preAS-*`; `click_helper.ps1` untouched. **VM141 = FREE (lane AR released 2026-09-09**; restored, `shim.cfg.hoff`, this lane's `binkw32.dll`, SavedGames = AD's set PLUS `ar-turn37-816raiders.sav`; with autosaves deleted the list is 12 rows from y=260 at 29 px pitch, `ar-turn37-816raiders` is **row 3 at (400,318)**). Left `C:\SOTS\shimdist-ar` + `ui\ar-*.ps1`; `click_helper.ps1` unmodified. **GOTCHA, CORRECTS THE STARTUP FIGURE: `fg` + `key {ESC}` x3 spaced ~2.5 s then ~90 s puts the main menu up in about TWO minutes, not 4.5** - the 4.5 figure is what you get by waiting the movies out. Loading a turn-37 save is a further 3-4 min and that part is NOT compressible. **GOTCHA: `rdrag x y x y` (a zero-length right-drag) IS A RIGHT-CLICK** - the fleet panel's `Add To New Fleet`/`Split Ships` menu is right-click-only and the helper has no right-click verb. **GOTCHA: a trade-sector node is a legal move target EVEN WHEN ITS CUBE IS NOT DRAWN** - in Move mode the hover readout names it; fit `screen = (A*wx + B*wz + C, D*wx + E*wz + F)` from two known points in the save and you land on any node first try. **GOTCHA: the encounter query BLOCKS End Turn** - sample the `Done` pixel at (233,673): ~(183,18,0) when a query is up, ~(8,8,8) on the map; `Auto Resolve Peacefully` (667,641), `Done` (233,673). **VM144 = FREE (lane AP released 2026-09-09**; restored to L3's build + L3's `shim.cfg` + the three pre-AS autosaves BYTE-IDENTICAL, main menu screenshot-verified). SavedGames **STILL 15 FILES** and AS's row positions **STILL HOLD** - `MyGameas1predeploy` (400,347), `MyGameas1spydep` (400,376), OK (682,624), verified twice. AP added `C:\SOTS\shimdist-ap`, `ui\ap{go,hash}.ps1`, `ui\ap\` - **an END-TURN DRIVER WORTH REUSING**: `ap_loop.ps1` + the `SOTSAP` task clicks End Turn, **polls the autosave mtime**, snapshots `(Autosave).sav` per turn and stops on the first stall with a screenshot - **11 turns unattended in 11 minutes**. **GOTCHA THAT COST NINE MINUTES: the click task takes ~6 s end to end, so a screenshot taken 4 s after `schtasks /Run` shows the state BEFORE the click** - four such shots read exactly like an 87 px coordinate offset and **nearly became a false gotcha in this very row**. WAIT >= 9 s. **GOTCHA: the first click after `fg` is swallowed by focus activation.** **GOTCHA: the main-menu pixel test must sample the BUTTON (470,530) = (184,0,0), not the white label at (511,536).** TIP: `wheel` zoom separates stars that share a hit test at default zoom (Kepler vs Midway, which cost lane AS a run) - no affine fit needed. An End Turn on this state costs **6 SECONDS** under `probes=8`. **VM146 = FREE (lane BP released 2026-09-09 restored** and hash-verified: pre-BP autosaves byte-identical, `shim.cfg` `hooks=off` and `binkw32.dll` restored to the pre-BP files, game process **not running, exactly as found**). `ad-turn27-two-raiders.sav` deliberately left on disk, so **Load-dialog rows have MOVED**: autosaves deleted gives 7 rows from y=260 at 29 px pitch, `ad-turn27-two-raiders` **row 1 at (400,260)**. Left `C:\SOTS\shimdist-bp` + `ui\bp{deploy,shot,hash}.ps1` + `ui\preBP\`. **GOTCHA THAT COST FOUR WASTED CLICKS: the lab wall's `/shot/.png` serves a CACHED frame and can be a whole poll cycle (~5 s) BEHIND your last action.** Clicking a Load row then shooting 5 s later shows it un-selected; a second click shows it selected - which reads exactly like a swallowed click and is not. **Wait ~12-16 s, or take two shots and use the second.** Loading the turn-27 save is **3.5-4 min** and is NOT compressible; End Turn on it ~35 s with the light hook set. **VM145 = FREE (lane BQ released 2026-09-09**, RESTORED and verified byte-for-byte to as-found: SavedGames = the same 9 files with identical sha256, `binkw32.dll` = shimdist-recap, `shim.cfg` `hooks=trace`, game stopped). Left `ui\preBQ\` + `ui\bq\` + `ui\bq{deploy,grab,click}.ps1`; `click_helper.ps1` unmodified. **GOTCHA (confirms AR's startup correction on a SECOND guest): ~2 min to the main menu**; loading a turn-38/39 save is a further 3-4 min and is NOT compressible. **GOTCHA (confirms lane M on a second guest): chained clicks through the Load dialog DROP** - a three-click chain landed only the first, twice. **ONE click per helper call with a screenshot between.** **TIP: reset SavedGames to EXACTLY ONE file (the run's input) and the Load dialog is one row at (400,262) with nothing to re-derive.** **GOTCHA: `cmd.exe` windows from the SOTSUI task appear OVER the game and cover the map** - `fg` raises the game; screenshot before believing a click landed. **CONFIRMED: the `Done` pixel at (233,673) reads (183,18,0) with a query up and (0,0,0) on the map**; `Auto Resolve Peacefully` (667,641) fills the centre panel with the dove, so the selection is confirmable from a screenshot before `Done`. | | Zuul double-roll (behavioural) | verify | verified | high | 100% | 2026-09-08 | **CLOSED by lane V 2026-09-08.** No species-5 save existed, so lane V MADE one on VM140: a custom game with only Zuul left in the Available Species pool (both players Zuul), 4 End Turns under `shim.cfg.recapb3`. **8 calls, 8 compared, 0 diverged, exit 0.** All four researching calls have `species=5` and the generator advances by TWO, not one: `left` 540->538 / 522->520 / 504->502 / 485->483, `next_index` +2 each, `mt` hash unchanged; `ours` reproduced every post-state bit-for-bit. Save `verify/results/saves/zuul-turn5-species5.sav` (48559ab5b719b332, 59,131 B, turn 5, homeworld Gallandro) and on the VM as `C:\SOTS\SavedGames\zuul-turn5.sav`. NOT yet exercised by it: a Zuul tech COMPLETION (node 144 was at ~4,700 of 5,000 after 5 turns - one more End Turn gets there), so `SetResearched` and the Zuul boarding-pod grant are still untouched. Report `findings/subsystems/eventlive-verification.md` §5 **EXTENDED by lane U 2026-09-08: the double roll and the COMPLETION PATH together.** `zuul-turn5` + 10 more End Turns (turn 5 -> 15) under the same config: **20 calls, 20 compared, 0 diverged, exit 0**, with TWO Zuul completions. Call 2 (turn 7, alloc {144,1376}) advances the generator by two (`left` 449->447, `next_index` 175->177) AND runs the unlock cascade in the same call - node 144 -> state 4 / order 21 / turn_researched 7, nodes 132 and 136 -> state 2 with cost_rp 10000 / 16000 and turn_available 7, `next_id` 10->12, `observed_techs` 396->440. Note the ZUUL tree unlocks only 132 and 136 from tech 144 where the HUMAN tree also unlocks 142, so this is an independent instance of the cascade, not a repeat. Trace `verify/traces/unlock-b3-zuul.jsonl.gz` | | budget tail coverage (expenses/aid/debt) | verify | backlog | — | 0% | 2026-09-08 | 8 ComputeBudget slots were always 0 in ref-turn2 (no sliders, no aid, no debt, no handicap). Need a save with expense sliders, a debtor and a research-aid treaty to exercise ExpenseTotal + the aid/bonus tail . CONFIRMED AND WORSE 2026-09-08 (lane R, 4284 calls): **13 of 22 slots are 0 on every call** - tradeIncome, shipCarriedPop, secondaryManager, bonusIncome, systemIncomeNeg, debtInterest, construction, expenses, researchMoneyGiven, savingsGiven, tra, researchPointsGiven, trp | **LANE O 2026-09-08.** **PARTIALLY unblocked, and three of the remaining slots are now shown to be UNREACHABLE through the 1.8 UI.** Delivered: `[11] construction` and a large `[8] maintenance` are live in `zuul-turn17-orders2.sav` (build queue of 20 DEs) and `zuul-turn23-fleet23.sav` (27 DEs in service, `Maint` 14,500 vs 3,000/1,000 in every earlier save); the empire research slider is off default (`ResRate` 0.97 vs 0.25) in all five Zuul saves. NOT delivered, with reasons. **(a) `[12] expenses`**: `Nexp` is `[]` for EVERY player in ALL 11 saves across turns 1-23, human and AI alike, and no screen in the 1.8 UI (map panel, Build, Research, Ranking, Intel Analysis, Empire Population Manager, in-game menu) exposes a per-category expense slider - the only economic sliders are the empire Savings/Research split and the per-system `SRs/SRt/SRsc/SRtf/SRi/SRoh` rates, which land in `Sys`, not `Nexp`. Working hypothesis: slot 12 is dead in 1.8. **(b) `[10] debtInterest` / `[7] systemIncomeNeg`**: a debtor cannot be manufactured quickly - a ship's savings cost is deducted at QUEUE time so the queue cannot be over-committed, and `Maint` counts only ships IN SERVICE, which arrive at construction-budget/ship-cost per turn (~4.5 DE/turn here). With income floored at 78,570 (Planetary Budget pushed fully to Construction; measured 124,055 -> 78,570 on the panel) that is ~157 destroyers, i.e. ~35 more End Turns. Reachable, just not cheap. **(c) `[13]/[14]/[19]` aid**: a treaty offer DOES occur (the AI offered a non-aggression pact on turn 3 of the Human game and it was accepted - `human-turn3-noderoute.sav`), but the accepted pact leaves the player's `aid` count 0, and SotS 1.8 has no player-initiated treaty UI - proposals are AI-driven events. Research aid needs a longer game and luck, not a click path | hook GetDifficultyMods | meta | backlog | — | 0% | 2026-09-08 | B1 derived the two difficulty rows from trace values (AI maintenance divisor 3, research x1.5) instead of snapshotting them; hook it properly so they stop being constants | @@ -344,7 +344,7 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`). | LAB HAZARDS from L2 | meta | verified | high | 100% | 2026-09-08 | (1) **`pgrep -f "tap141i0"` matches QEMU's OWN command line** (`ifname=tap141i0`), so stopping a tcpdump SIGTERMed the VM. Use `pgrep -x tcpdump`. The guest came back clean (fsutil not dirty, same IP). (2) Driving the starmap needs **REAL MOUSE MOTION** - SetCursorPos alone does not move the game's cursor - plus wheel zoom; L2's driver is in verify/harness/l2-ui/. (3) From lane L4: **`move X Y` then `click X Y`** - a bare click is delivered at the PREVIOUS cursor position about half the time, which reads as "the click did nothing" then "the next click did the previous thing" | | FOURTH cross-lane commit sweep - now with plain git commit | meta | verified | high | 100% | 2026-09-08 | Lane L1's board commit swallowed all 22 of lane L2's staged files. Content intact and verified in HEAD; L2 correctly did NOT rewrite history with other lanes live. **This is the fourth instance of the hazard addresses.d/README documents, and it now happens with plain `git commit` too, not just addresses.json** - because a lane stages by path but another lane's commit picks up whatever is in the index. The index is shared per-repo; staging by path does not protect against someone else's commit. Needs a real fix, not another rule | | STRUCTURAL FIX for the commit-sweep hazard (rule 25) | meta | verified | high | 100% | 2026-09-08 | Four instances in one session, the last with plain `git commit` and NO `-A` anywhere - **every lane had staged by path exactly as instructed and it still happened**, because `git add ` puts a file in THE REPO'S ONE INDEX and the next commit takes all of it. Staging by path was never the protection. **THE FIX IS A FORM OF THE COMMAND**: `git commit -m "msg" -- path/one path/two` commits only those paths and leaves the rest of the index untouched - verified in a scratch repo (two files staged, pathspec commit took one, the other stayed staged). Lanes now commit with a pathspec, always. Deeper fix if it recurs: a per-lane CLONE of sots-re, mirroring the per-lane worktree of sots-engine - which is precisely why sots-engine has never had this problem | -| RESOLUTION: the byte-match SURVIVES, in a stronger form | meta | verified | high | 100% | 2026-09-08 | First re-resolver run, `findings/resolutions/2026-09-08-ai-seed-per-process.md`. **The AI is not non-deterministic - it is MT19937 from ONE 32-bit word per client, drawn once at construction from a per-process global, and we own that generator bit for bit.** So game/ai is a deterministic function of (save, per-client seed). Rung C splits: **C-exact** - capture the three seeds and our block matches element for element and the autosave byte-matches ON EVERY RUN (the user's framing made literal), precondition being a per-client DRAW LEDGER so we consume the stream in the original's order and count INCLUDING draws whose result never reaches the save; **C-set** - the fallback for references whose seeds were never logged, like turn2-state.sav, which **no process including the original can reproduce**. Instrument exonerated: three unrelated 32-bit words per process are successive generator outputs, not clock reads, so AI1's call-site reading STANDS and only its "never seeded" reading falls **The shape recurs and is now a PREDICATE (resolver 2026-09-09)**: pick turns (`ResTNm == ''` at turn start: `turn1-state`, `as-turn15`) and heavy build turns (`ad-turn27`) **EXPOSE** the seed; quiet-consumer turns (`ar-turn37 -> 39`) do not, and agree byte-for-byte across three processes. **SCOPE of the C-exact proof: it was demonstrated on `turn1-state` (early game) only** - the rich-turn test is `ad-turn27` with `aiseed=pin`, queued on VM146. A control that agrees means "no exposed decision was reached", never "the AI is deterministic here". **C-EXACT FALSIFIED ON A RICH TURN 2026-09-09 (lane BP)**: with all three AI client seeds pinned, two fresh processes on `ad-turn27` still differed. **Restated: given (save, per-client seeds) the AI's command block is determined UP TO the assignment of newly allocated fleet ids to newly formed fleets** - exactly three words of the 81-element block, and the id counter itself is deterministic. **Rung C is DAMAGED, NOT LOST**: the assignment is canonicalisable the way C-set canonicalises a pick. | +| RESOLUTION: the byte-match SURVIVES, in a stronger form | meta | verified | high | 100% | 2026-09-08 | First re-resolver run, `findings/resolutions/2026-09-08-ai-seed-per-process.md`. **The AI is not non-deterministic - it is MT19937 from ONE 32-bit word per client, drawn once at construction from a per-process global, and we own that generator bit for bit.** So game/ai is a deterministic function of (save, per-client seed). Rung C splits: **C-exact** - capture the three seeds and our block matches element for element and the autosave byte-matches ON EVERY RUN (the user's framing made literal), precondition being a per-client DRAW LEDGER so we consume the stream in the original's order and count INCLUDING draws whose result never reaches the save; **C-set** - the fallback for references whose seeds were never logged, like turn2-state.sav, which **no process including the original can reproduce**. Instrument exonerated: three unrelated 32-bit words per process are successive generator outputs, not clock reads, so AI1's call-site reading STANDS and only its "never seeded" reading falls **The shape recurs and is now a PREDICATE (resolver 2026-09-09)**: pick turns (`ResTNm == ''` at turn start: `turn1-state`, `as-turn15`) and heavy build turns (`ad-turn27`) **EXPOSE** the seed; quiet-consumer turns (`ar-turn37 -> 39`) do not, and agree byte-for-byte across three processes. **SCOPE of the C-exact proof: it was demonstrated on `turn1-state` (early game) only** - the rich-turn test is `ad-turn27` with `aiseed=pin`, queued on VM146. A control that agrees means "no exposed decision was reached", never "the AI is deterministic here". **C-EXACT FALSIFIED ON A RICH TURN 2026-09-09 (lane BP)**: with all three AI client seeds pinned, two fresh processes on `ad-turn27` still differed. **Restated: given (save, per-client seeds) the AI's command block is determined UP TO the assignment of newly allocated fleet ids to newly formed fleets** - exactly three words of the 81-element block, and the id counter itself is deterministic. **Rung C is DAMAGED, NOT LOST**: the assignment is canonicalisable the way C-set canonicalises a pick. **SHARPENED 2026-09-09 (lane BQ)**: a research pick is exposed only when the **CANDIDATE LIST has more than one entry**. Measured on `ar-oracle-A-post -> 40`: Player[32] entered with `ResTNm == ''`, producers A and B returned null, the candidate walk ran, `candidatesTried=1`, and **the pick AGREED across two fresh processes**. **`ResTNm == ''` predicts a pick, not a divergence.** | | the tie-set claim: rescued as a STATEMENT, open as a RESULT | verify | mapped | high | 80% | 2026-09-08 | And **not on "equal cost"** - the resolver's sharpest correction. The 2000-RP tier-1s are not the picks; the picks are tier-2s at 13k-30k. S(D) is **the support of a random draw**, per save, not a set of equal-score candidates. `BIO_GnMod` (2 of 6 observations) proves a SECOND ARM whose producer is unidentified - likeliest producer A or B gated by a draw, never entering the walk - so **"we can name all k" is TO PROVE**. The four-part statement is in the resolution 3.1: derive S before any sweep; canonical pick = lowest tech id; containment; **and a pinned-seed sweep produces nothing outside S** - part (iv) is what makes it non-tautological | | THE ONE PROBE: pinned seeds THROUGH the resolver | verify | backlog | — | 0% | 2026-09-08 | The seed SOURCE is not the probe. The resolver probe is only an experiment **if the seed is pinned** - otherwise two runs with two unknown seeds cannot separate "draws from the stream" from "reads something else per-process". Implementation named: L1's `l1seed` Seed detour (return address 0x0078304b, three constants) + L4's instrument + a `left` region over client+0x134 at TryResearchCandidate 0x006c8580; ref-turn2 once as the pin's OWN control; turn1-state twice; then sweep by quit-to-menu/reload (the pin table advances per load, **no relaunch needed**), logging which producer returned non-null for player 512. Predictions committed in the resolution 4. If the four leaves still differ, extend the pin to the unidentified fourth seeded object | | NEW OPEN RISK: g_GlobalRNG has four other consumers | verify | backlog | — | 0% | 2026-09-08 | Including **RunCombatRound+0x60f** - and **no battle turn has ever been compared across two processes**. Add "twice, two processes" to the battle workload in backlog 5. Lane L2 verified combat lockstep between two clients in ONE game; this is the different question of whether a battle is reproducible across separate runs | @@ -421,12 +421,15 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`). | rule 28 practice 4, demonstrated rather than argued | meta | verified | high | 100% | 2026-09-09 | Lane AS ran both instruments on the same turn: the **entry probe** on `SpyManager::Slot13RngCallee 0x008408e0` read **0**, while the **return-address ledger** recorded `0x00887c8a` firing for **one word** in the caller's body. Four lanes had previously read that zero as a statement about the subtree; it was only ever a statement about which function the call sits in. **Polarity matters too and is now practice 6**: the `je` jumps PAST the roll, so belt-gone means **no draw AND the spy detected anyway** - a zero at that site means two opposite things, and the intuitive reading is backwards | | VERDICT: `TradeManager+0x0c` IS the per-system containing-sector table - the writer is found and the raid target pick FIRES | control-flow | verified | high | 100% | 2026-09-09 | **Lane AR**, VM141, `findings/control-flow/raid-target-pick-verdict.md`. Decides lane AG's §3.3 final paragraph, the one load-bearing inference AG flagged as unproven, and lane AD correctly reported it could not separate on sector 832. **BOTH INSTRUMENTS AGREE (rule 3).** STATIC: the writer AG's §7 lists as NOT FOUND is **`FUN_00841700`** (214 B, 0x00841700-0x008417d5) - it clears the +0x0c vector, RESIZES it to `count(([this+4])+0x40..+0x44)` (the SYSTEMS vector, the very container `FUN_00841cd0` indexes with `rt->trfr` at 0x00841d36), then walks the SECTOR vector x each sector's MEMBER SYSTEM vector executing `mov [this->+0x0c + m->+0x5c * 4], sector` at 0x008417a8. `ServerSystem+0x5c` is the serialised **`Idx`** field (objects/layouts.md, grade verified), so this is literally `containingSector[system.Idx] = sector` and **G_B1a cannot reject a valid trfr, BY CONSTRUCTION**. It is called from the DESERIALISER `FUN_00858a10` at 0x00858f07, so a loaded save has the table populated. LIVE: with 4 raiders parked (2 CR on sector 832, 2 DE on sector **816**, all `Pos` bit-equal), `Slot13RngCalleeA` is entered **4x/turn, 4 words**, every turn - and on turn 42 `Slot13RngCalleeB` reached its target pick: **`NextInt` at call `0x0088b613`, calls=1 words=1**, the FIRST firing of that site in the campaign. `OnAllCombatDone_Tail` cost **5** words that turn against **4** on every other turn; bracket residual **0** on all six turns. **THE SAME RUN CARRIES ITS OWN CONTROL**: on turns 38 and 40 `B` was entered by a cruiser on sector 832 and cost **0 words** - same process, same build, same instrument, same function, only the sector differs. AC's and AD's proof-carrying zero is a fact about the SECTOR, not the code path. Instrument `probes=8` (NOT 11), shim `ar-989c692-20260909T0313Z`, cfg `shim.cfg.hp8` unmodified, lane worktree + own build dir `/srv/re-lab/build/sots-engine-ar` rm -rf'd first (rules 21, 24) | | `B` costs ONE word on a success, not two - and the deciding value is a SPECIES FLAG set by a constructor for exactly two species | control-flow | verified | high | 100% | 2026-09-09 | Lane AR. Lane AG's committed §3.3 prediction was **3 words on a succeeding turn** (`A` 1 + `B`'s NextInt 1 + `FUN_00820af0`'s NextFloat 1) and §5.2 says `B` costs 2 on success. **MEASURED: the tail cost 5 with k=4 raiders and one success, i.e. `B` cost 1, and there is NO `0x00820c1b` row on that turn.** Why: `FUN_00820af0`'s third short-circuit is `SpeciesDef_HasInterceptFlag 0x0053baf0` - twelve bytes, `cmp [ecx+0x144],0; setne al` - and `InitTable 0x005453a0` sets that flag word at 0x0054562f/0x00545638 for **species 0 (HUMAN) bit 0 and species 5 (ZUUL) bit 1 ONLY**. `Get 0x00545cc0` returns `0x00b10a00 + k*0x184`, the same table InitTable fills. So **a Human or Zuul raider intercepts at frac=1.0 and draws nothing; the other five species draw a NextFloat at 0x00820c1b**. The general tail model is **`k + s + z`** words: k = permitted raiders parked with `Pos` bit-equal, s = their successes ON A SECTOR THAT SOURCES A ROUTE, z = s again unless the raider's species sets the flag. Measured at k+s with k = 4,4,4,4,4,3 and s = 0,0,0,0,1,0. THE CHEAPEST UNEXERCISED STATE IN THE TRADE HALF IS NOW A NON-HUMAN, NON-ZUUL RAIDER - z has never been observed through `B` by anyone. Rule 23's lesson pointing a new way: the deciding value was not a float literal but a FLAG WORD SET BY A CONSTRUCTOR, invisible to any amount of reading `FUN_00820af0` alone | -| ORACLE PAIR on the turn-37 raider state - and the probes=8 instrument is BYTE-NEUTRAL, whole-save | meta | verified | high | 100% | 2026-09-09 | Lane AR (rules 19, 26). Two fresh `hooks=off` processes loading `ar-turn37-816raiders.sav` and running the same two End Turns produced **BYTE-IDENTICAL** autosaves at both snapshot points: Frame 38 `15b99255e1f03dab…` and Frame 39 `7a8b3d5eb3a60eba…`; `state_checksum --floats bits --mask none` prints **IDENTICAL**, root `237020deca931f5a180289592cece7ae`, coverage PROVED (1,173,884 B rebuilt, 67,239 leaves), 0 error 0 warn. **THEN** a fourth process with `shim.cfg.hp8` installed produced the SAME TWO HASHES. So the instrument is behaviour-neutral **at whole-save byte granularity**, not merely on the sub-tree the measurement reads - the stronger form of what lane AD could show. Order matters and it was the right order: the control was reproduced FIRST and shown to be a single outcome, so rule 26's `1/k` coincidence caveat does not apply (k=1). **THIS CORRECTS THE SCOPE OF LANE AD'S ROW**: "no oracle pair for this state / the AI varies per process" is true of `ad-turn27-two-raiders.sav` and NOT true of the same lineage ten turns later, so it is a fact about that state, not about the engine. No explanation for the difference is offered and none should be inferred. `ar-turn37-816raiders.sav` + 2 End Turns is the first RUNG-A CALIBRATION PAIR from this lineage and the obvious candidate for the standalone's first trade-raid comparison **SCOPED by the resolver 2026-09-09**: the agreement is **explained** (no exposed AI decision on turns 38-39 - target held then completed, no design created, forced ship placement) and it does **NOT transfer** - Player[32] enters turn 40 with `ResTNm == ''`. Use the **second turn**, `ar-oracle-A-pre.sav -> 7a8b3d5e...`, as the standalone's pair once probe 1b reproduces it **by load**. The peaceful auto-resolve on turn 38 reproduced across three processes but almost certainly bypasses `RunCombatRound`; the combat-turn warning stands. | +| ORACLE PAIR on the turn-37 raider state - and the probes=8 instrument is BYTE-NEUTRAL, whole-save | meta | verified | high | 100% | 2026-09-09 | Lane AR (rules 19, 26). Two fresh `hooks=off` processes loading `ar-turn37-816raiders.sav` and running the same two End Turns produced **BYTE-IDENTICAL** autosaves at both snapshot points: Frame 38 `15b99255e1f03dab…` and Frame 39 `7a8b3d5eb3a60eba…`; `state_checksum --floats bits --mask none` prints **IDENTICAL**, root `237020deca931f5a180289592cece7ae`, coverage PROVED (1,173,884 B rebuilt, 67,239 leaves), 0 error 0 warn. **THEN** a fourth process with `shim.cfg.hp8` installed produced the SAME TWO HASHES. So the instrument is behaviour-neutral **at whole-save byte granularity**, not merely on the sub-tree the measurement reads - the stronger form of what lane AD could show. Order matters and it was the right order: the control was reproduced FIRST and shown to be a single outcome, so rule 26's `1/k` coincidence caveat does not apply (k=1). **THIS CORRECTS THE SCOPE OF LANE AD'S ROW**: "no oracle pair for this state / the AI varies per process" is true of `ad-turn27-two-raiders.sav` and NOT true of the same lineage ten turns later, so it is a fact about that state, not about the engine. No explanation for the difference is offered and none should be inferred. `ar-turn37-816raiders.sav` + 2 End Turns is the first RUNG-A CALIBRATION PAIR from this lineage and the obvious candidate for the standalone's first trade-raid comparison **SCOPED by the resolver 2026-09-09**: the agreement is **explained** (no exposed AI decision on turns 38-39 - target held then completed, no design created, forced ship placement) and it does **NOT transfer** - Player[32] enters turn 40 with `ResTNm == ''`. Use the **second turn**, `ar-oracle-A-pre.sav -> 7a8b3d5e...`, as the standalone's pair once probe 1b reproduces it **by load**. The peaceful auto-resolve on turn 38 reproduced across three processes but almost certainly bypasses `RunCombatRound`; the combat-turn warning stands. **LOAD-BOUNDARY SCOPE 2026-09-09 (lane BQ)**: this pair is certified **ONLY** as the two-turn continuation from `ar-turn37-816raiders.sav`. **Its second half does NOT survive being reached by load**: `ar-oracle-A-pre.sav` + load + one End Turn gives `4c356f59...`, twice, differing from `7a8b3d5e...` in **17 leaves of ONE AI fleet's movement** (`Zeta Fleet V` moved in the continuation, stayed put after a load). **Use the load->turn hashes for the standalone.** | | `CreateRaidEncounter`'s candidate list is NOT always empty - 0x008939ee fires, and rule 20's case study gets its positive | control-flow | verified | high | 100% | 2026-09-09 | Lane AR, turn 43 of `verify/traces/ar-r1-turn38-turn43.jsonl`. `ServerTradeManager::CreateRaidEncounter` entered **3x** and its subtree drew **two** words: `NextInt 0x008939ee calls=1 words=1` - the site lane H measured at 0 on every turn and that `tail-rng-ledger.md` has carried as a zero ever since, the campaign's own case study for rule 20 - and `NextFloat 0x00820c1b calls=1 words=1` through the OTHER caller `0x0082cf65`, i.e. lane AG §5.1's chain `CreateRaidEncounter -> 0x00892640 -> 0x0082ce00 -> FUN_00820af0`. `Slot13RngCalleeB` was entered **0** times that turn, so neither word is `B`'s. **Two of AG's §5.1/§5.2 unread gates are now read from opposite directions.** The honest form of the cost claim is neither "0 or 1 further word" nor "at least six draws" but **unbounded above and state-dependent**; the first non-zero observation is 2 | -| SEED EXPOSURE IS A PREDICATE ON THE PRE-TURN SAVE - why AR's control agreed where AD's and AS's did not | meta | verified | high | 100% | 2026-09-09 | Resolver, on lane AR's falsified P7 (`findings/resolutions/2026-09-09-seed-exposure-is-a-predicate.md`). Three lanes, three controls, one mechanism: the per-process AI client seed (row 326) is present on EVERY turn and reaches the save ONLY through a decision whose outcome set is larger than one given the state. Read off the saves on disk: **AD 27->28** - Player[32] completed BIO_TerBac (no pick: ResTNm held), created a design (NumDes 46->47; the two "Bravestar Mk 3" bodies are BYTE-IDENTICAL apart from DesID 2002/2018), colonised a system (NumOwn 15->16), completed THIRTEEN ships into five new fleets - and A/B differ in the build choice at Sys[272] (978 vs 962), the fleet split (B one fleet more, ModCount 1496 vs 1500), and id allocation ORDER downstream. **AS 15->16** - Player[32] entered with ResTNm == '' and picked (64 vs 62). **AR 37->38, 38->39** - Player[32] held BIO_EnvTail then completed it (pick deferred), NumDes constant at 47, two freighters a turn into EXISTING Freighters fleets, one encounter auto-resolved peacefully: ~100 leaves moved per turn, all forced, three processes agree. READING: not luck, not a pinned AI - no EXPOSED decision reached; explicable from the pre-turn save. ResTNm == '' names a pick turn (rule 28's shape); NumDes/NumOwn/batch completions name the others; 0x0069dbb0 has a 3-40-turn fuse and cannot show within two turns of a load. **Player[32] enters turn 40 with ResTNm == '' - AR's pair MUST NOT be extended by a turn.** RUNG A RULING: AR's pair is certified for its procedure; the single-turn form `ar-oracle-A-pre.sav -> 7a8b3d5e...` is the standalone's first trade-raid pair ONCE one load->End Turn run reproduces the hash (probe 1b, VM145). OPEN: the build/fleet consumer behind AD's variation is UNIDENTIFIED and reading 3 (a non-seed per-process input) is NOT excluded on rich turns - row 360 pinned an early-game turn only. PROBES: (2) VM146, `aiseed=pin` on ad-turn27 x2 fresh processes, predict IDENTICAL - if not, resolver case; (1) VM145, ar-oracle-A-post + 1 End Turn x2, predict DIFFER in Player[32] TechTree/ResTNm only; (3) per-client draw ledger on both states - the C-exact prerequisite, now decisive. No rule 29: rules 23/26 produced the right behaviour (AR refused the inference); rule 26 gets corollary (c) | +| SEED EXPOSURE IS A PREDICATE ON THE PRE-TURN SAVE - why AR's control agreed where AD's and AS's did not | meta | verified | high | 100% | 2026-09-09 | Resolver, on lane AR's falsified P7 (`findings/resolutions/2026-09-09-seed-exposure-is-a-predicate.md`). Three lanes, three controls, one mechanism: the per-process AI client seed (row 326) is present on EVERY turn and reaches the save ONLY through a decision whose outcome set is larger than one given the state. Read off the saves on disk: **AD 27->28** - Player[32] completed BIO_TerBac (no pick: ResTNm held), created a design (NumDes 46->47; the two "Bravestar Mk 3" bodies are BYTE-IDENTICAL apart from DesID 2002/2018), colonised a system (NumOwn 15->16), completed THIRTEEN ships into five new fleets - and A/B differ in the build choice at Sys[272] (978 vs 962), the fleet split (B one fleet more, ModCount 1496 vs 1500), and id allocation ORDER downstream. **AS 15->16** - Player[32] entered with ResTNm == '' and picked (64 vs 62). **AR 37->38, 38->39** - Player[32] held BIO_EnvTail then completed it (pick deferred), NumDes constant at 47, two freighters a turn into EXISTING Freighters fleets, one encounter auto-resolved peacefully: ~100 leaves moved per turn, all forced, three processes agree. READING: not luck, not a pinned AI - no EXPOSED decision reached; explicable from the pre-turn save. ResTNm == '' names a pick turn (rule 28's shape); NumDes/NumOwn/batch completions name the others; 0x0069dbb0 has a 3-40-turn fuse and cannot show within two turns of a load. **Player[32] enters turn 40 with ResTNm == '' - AR's pair MUST NOT be extended by a turn.** RUNG A RULING: AR's pair is certified for its procedure; the single-turn form `ar-oracle-A-pre.sav -> 7a8b3d5e...` is the standalone's first trade-raid pair ONCE one load->End Turn run reproduces the hash (probe 1b, VM145). OPEN: the build/fleet consumer behind AD's variation is UNIDENTIFIED and reading 3 (a non-seed per-process input) is NOT excluded on rich turns - row 360 pinned an early-game turn only. PROBES: (2) VM146, `aiseed=pin` on ad-turn27 x2 fresh processes, predict IDENTICAL - if not, resolver case; (1) VM145, ar-oracle-A-post + 1 End Turn x2, predict DIFFER in Player[32] TechTree/ResTNm only; (3) per-client draw ledger on both states - the C-exact prerequisite, now decisive. No rule 29: rules 23/26 produced the right behaviour (AR refused the inference); rule 26 gets corollary (c) **PROBE 1 RUN 2026-09-09 (lane BQ, VM145). 1b FALSIFIED**: `ar-oracle-A-pre` + **LOAD** + one End Turn gives `4c356f59...`, not `7a8b3d5e...`, **reproducibly in two processes** - the AI moved `Zeta Fleet V` in the continuation and not after a load, so the pair may **NOT** enter `determinism-hashes.txt` in AR's form; the **LOAD form** (`33e30092.../4c356f59...`) is certified instead. The resave canonicalisation is **EXONERATED**. Probe 1 **CONFIRMED** "the files differ" and `/Sim/RNG` + `/Sim/trdmgr` + player-0 fleets identical, but **FALSIFIED the mechanism**: the research pick **AGREED** (both chose `BIO_GrvAdpt`) and the variation is a **COMBAT at Venkman**. Producer run: A and B both returned null, Player[32] reached the **candidate walk**, `candidatesTried=1` - **the outcome set is a singleton by COUNT, not by producer gate**. | | 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` | | predict at the SITE, not at the bracket | meta | verified | med | 100% | 2026-09-09 | Lane AP's PB4, the one prediction it got wrong, and the lesson generalises. AP forecast every spy-half draw **exactly** and still missed its headline number, because it had predicted the whole `OnAllCombatDone_Tail` total and **an unrelated subsystem entered the bracket**: the trade-raid roll `0x00820e1d` arrived at Frame 20 and grew **2 -> 8 words** as the AI's navy went **157 -> 251 ships**. A bracket total is a sum over every subsystem inside it, so quoting one commits you to modelling all of them. **Predict and report per site, by return address; let the bracket total be the arithmetic check, not the claim.** The consolation: the surprise was itself a result - an independent live re-confirmation of lane AD's per-fleet raid model, on a different map and an AI-owned fleet. Now a corollary to rule 23 | | PINNED SEEDS DO NOT COLLAPSE A RICH TURN - C-exact is false on ad-turn27, and the residue is ONE FLEET-ID TRANSPOSITION | control-flow | verified | high | 100% | 2026-09-09 | Lane BP, VM146, probe 2 of the 2026-09-09 resolution (`findings/control-flow/pinned-seed-rich-turn.md`). Build `bp-989c692-20260909T0505Z`, fresh CT111 tree (rule 24), lane worktree (rule 21). THREE fresh processes on `ad-turn27-two-raiders.sav` (1c8baa27...), one End Turn each, lane CB's CONSTRUCTOR-ARGUMENT overwrite (`aiseed=pin`, NOT `airng.pin_seed`), every template hook off BY NAME (27 reported `mode=off`), zero unknown keys, zero COVERAGE lines, `aiseed call=1/2/3 pinned=1` in both pinned runs. **THE BRIEF WAS WRONG ABOUT THE PLAYER COUNT: the save has seven non-human players and the engine builds THREE AI clients** - RunAI fires exactly 3x, net ids 32/496/512; 528 Alien Menace, 544 Peacekeeper Enforcer, 560 Von Neumann and 576 Independent Colony get no client, no call and no seed. Nine distinct natural seeds across three processes, none shared (L1's per-process seeding, reproduced a 4th time on a turn-27 state). RESULT: **run L (unpinned) and run A (pinned to L's observed seeds) are BYTE-IDENTICAL** (`e913ff41...`, state_checksum IDENTICAL, 61147 leaves, coverage PROVED) - a pinned process reproduced an unpinned run of a rich build turn. **Run B, same pinned seeds, fresh process, DIFFERS: `724528ff...`, 35 leaves.** So P1 IS FALSIFIED and READING 3 IS ALIVE: a per-process input that is NOT the seed reaches a rich turn. **BUT THE PIN DID ALMOST ALL OF THE WORK.** AD's UNPINNED pair on the same save differed in 94 leaves across designs, ShipRecs, Sav/Maint/PvSav/FNGNum, both AI build queues, ModCount (1496 vs 1500), NumFlts, DesignIDs[], FleetIDs[] and four turnstats rows. BP's PINNED pair differs in 35, and ALL 35 ARE ONE TRANSPOSITION: fleets **1970 "Sigma Fleet VI"** and **1986 "Tau Fleet VI"** exchange their entire contents (Pos, PrvPos, FtOrig, FPlan, LocID 384<->0, HFPlan, FtTrans, FtFlg 12<->13, Ship[6976]<->Ship[5264]), plus Sys[384]/Flt[1] 1970->1986, /Sim/ShipIDs[] list-reordered and /Summary/Checksum. **ModCount, NumFlts, DesignIDs[], FleetIDs[], every build queue, every design, every ship record and /Sim/RNG are IDENTICAL.** The AI command block says the same thing from the input side: 9 blocks / 81 elements, same (pid,list,idx) sequence, same per-list counts, and - masked by the noise set the byte-identical L/A pair PROVES (list 1 w1,5,8,11; list 3 w4-11; list 5 w8-11; list 7 w2-11; list 8 w1-11; list 10 w2-11; list 12 w5-11; list 14 w2-11; list 23 w2-11) - **EXACTLY THREE WORDS DIFFER**: list 8 idx1 word0 fleetId 1986/1970, list 10 idx0 word0 systemId 384/80, list 10 idx1 word0 systemId 80/384. List 10's fleet ids are in the SAME order in both (1970, 1986, 2002, 7072), so the id counter is deterministic and what varies is WHICH NEWLY FORMED SHIP GROUP EACH ID LANDS ON. Design elements identical including the name bytes ("Egg Thief Mk 4", "Bravestar Mk 3"). **C-EXACT RESTATED: given (save, per-client seeds) the AI's command block is determined UP TO the assignment of newly allocated fleet ids to newly formed fleets; one residual per-process input decides that assignment and it is not the seed.** Rung C is damaged, not lost - the assignment is canonicalisable the way L4's C-set canonicalises a pick. INFERENCE, NOT MEASUREMENT: the residue has the shape of a container walked in pointer order under ASLR - the reading the 09-08 resolution withdrew for the research pick and never re-tested on a build turn. RULE 26 (c): the A/B control does NOT agree with itself, so it certifies nothing; the L/A pair does, and certifies only (save, procedure, hashes) reached by two of three processes - a 1/k result, NOT an oracle pair. Exposure facts on the same line: Player[32] ResTNm=BIO_TerBac HELD, NumDes 46, NumOwn 15, 13 ships completing; 496/512 hold targets with NumOwn=0/NumDes=0; the four NPC factions have ResTNm=='' AND NumOwn==0 AND NO AI CLIENT. NEXT: one more pinned process (run C) sizes the residual outcome set; probe 3's per-client draw ledger says whether the fleet-creation sites draw at all. Artefacts: `verify/results/saves/bp-{pinA,pinB}-turn28.sav`, `bp-turn28-pre.sav`, `verify/results/shim/bp/` | | rule 26(c) qualified on its first forward use: the signature is NECESSARY, not sufficient | meta | verified | high | 100% | 2026-09-09 | Lane BP, applying the corollary published hours earlier. **Four players on `ad-turn27` carry `ResTNm == ''` and contributed NONE of AD's 94 leaves** - they have `NumOwn == 0`, and more fundamentally **the engine constructs only THREE AI clients for the save's SEVEN non-human players** (`RunAI` fires exactly 3x, net ids 32/496/512; 528 Alien Menace, 544 Peacekeeper Enforcer, 560 Von Neumann and 576 Independent Colony get no client, no call and no seed). So an exposure signature must be read **against who actually runs**, not off the field alone. Corollary (c) amended in place. This is the good case of a rule being used the day it was written and coming back sharper | | the cbpin trap fixed, and made unrepeatable | engine | verified | med | 100% | 2026-09-09 | Lane BP reported two defects in `src/shim/shim.cfg.cbpin` rather than working around them, and noted its own first draft **reproduced one of them** - the definition of a trap. (1) The header comment **wrapped onto a live setting**, leaving a stray `aiseed.values=...` with a trailing backtick AHEAD of the real one at the bottom, so the file contradicted its own "filled in per run" note. (2) **Six registered hooks went unnamed**, and under `hooks=trace` an unnamed hook defaults back **ON** - so a determinism probe ran six more detours than it claimed, which is exactly what rule 19 says can move the bytes. Both fixed; `cbpin` now names all 27. New `tools/check_shim_configs.py` enforces the two syntax defects across every template and exhaustiveness **only where a template opts in with `# exhaustive`**. **My first draft of that check was wrong and I threw it away**: it enforced exhaustiveness globally and "failed" 40 configs, because `hooks=trace` legitimately means "trace all but these" - the check was wrong, not the configs. Engine `1654785` | +| PROBE 1: the load->turn pair is NOT the continuation's - AR's second hash does not survive a load, and the load form is its own reproducible pair | meta | verified | high | 100% | 2026-09-09 | **Lane BQ**, VM145, `findings/control-flow/seed-exposure-forward-test.md`, probe 1b of the 09-09 resolution. Prediction committed before the run (commit be14266): `ar-oracle-A-pre.sav` (Frame 38, `15b99255...`) + LOAD + one End Turn reproduces `7a8b3d5e...`. **FALSIFIED.** It produces `4c356f5916f0e6e4...` (107,732 B), and it produces it in TWO FRESH `hooks=off` PROCESSES BYTE FOR BYTE, at both snapshot points (`(Autosave EndTurn).sav` `33e3009254f5acd1...` in both). So the load->turn procedure AGREES WITH ITSELF and DISAGREES WITH THE CONTINUATION. 23 leaves apart from AR's file and **17 of them are ONE AI FLEET**: `Flt[1570 "Zeta Fleet V"]` (PID 32) enters turn 39 parked at Alpha Centauri with full range; in AR's continuation the AI issued it a move order (FPlan Wpt 320, FtFlg 12->13, LocID 432->0, both ships Range 9.0->7.0) and in BOTH loads it issued nothing. Plus `Sys[432]` NumFlts 0->1 and `Flt[914 "Nu Fleet III"]` HLay True/False. **RULING: `ar-oracle-A-pre.sav -> 7a8b3d5e...` MUST NOT enter determinism-hashes.txt as a load->turn pair.** What IS certified, two processes: `ar-oracle-A-pre.sav -> (load, one End Turn) -> 33e30092.../4c356f59...` - and that is still the standalone's first trade-raid pair (four raiders parked, no encounter). **THE RESAVE CANONICALISATION IS EXONERATED**: the pre-turn round trip is byte-exact but for ONE leaf, `/CD[1]/NPrvVa` (+5.00 across a load). CONSEQUENCE: **the pre-turn save does not fully determine the next turn's AI fleet-task decisions** - something `Zeta Fleet V`'s order depended on is not in the file. Resolver case; probe 3's ledger on `ar-oracle-A-pre -> 39` taken BOTH by continuation and by load names the site | +| PROBE 1: turn 40 varies - but NOT by the research pick. `Auto Resolve Peacefully` does NOT bypass combat, and a pick with ONE candidate is not exposed | control-flow | verified | high | 100% | 2026-09-09 | Lane BQ, VM145. `ar-oracle-A-post.sav` (`7a8b3d5e...`, Player[32] `ResTNm == ''`) in two fresh `hooks=off` processes, one End Turn, `Auto Resolve Peacefully` in both. **The files DIFFER** (`56980aa0...` vs `b36ac0ec...`, 144 leaves) - the committed prediction's first clause holds, and so do `/Sim/RNG` identical (2,503-B block byte-equal), `/Sim/trdmgr` identical and every player-0 fleet identical. **THE MECHANISM CLAUSE IS FALSIFIED: ZERO TechTree leaves and ZERO ResTNm leaves differ.** Both processes picked **BIO_GrvAdpt**. The 144 leaves are a COMBAT: the AI's `Sigma Fleet VII` (PID 32) hit player 16's colony Venkman, the human committed no ships, and the PLANET's defences killed 2 raiders in one process and 1 in the other (50 leaves `crep`, 41 the AI fleet, 8 `Sys[224 "Venkman"]`, 20 `Player[16]` - the prediction's "every Player[16] leaf identical" is also FALSIFIED). Both runs completed the SAME two ships (+8848,+8864); only the losses differ (7744+7952 vs 8000). **So `Auto Resolve Peacefully` means "I commit no ships", NOT "no combat occurs" - it is not a determinism-preserving resolution, and resolution 09-08 §6's combat warning is now measured, not just retained.** AR's turn-38 peaceful auto-resolve reproduced because the other side had nothing to fight with. **WHY THE PICK DID NOT VARY - the producer run, `shim.cfg.l4research` already on the guest, no build**: producers A and B were entered once per AI player and BOTH RETURNED NULL for Player[32]; it reached producer C, the candidate walk; `TryResearchCandidate` was offered **ONE** candidate and took it - `airesult sel=1 candidatesTried=1 chose='BIO_GrvAdpt'` - and the `0x006b36e0 & 7` fallback rotation was never reached (FallbackIndex/FallbackArm both 0). **The outcome set is a singleton because the CANDIDATE LIST HAS LENGTH ONE.** Not the producer gate the resolution guessed - a COUNTING criterion, and a better one | +| Rule 26 (c) needs four corrections, all measured on one lineage in one session | meta | verified | high | 100% | 2026-09-09 | Lane BQ, from probe 1. (1) **`ResTNm == ''` is NECESSARY, NOT SUFFICIENT** for research exposure: it names a turn on which a pick is MADE; exposure needs `|candidates accepted by TryResearchCandidate| > 1`, and on `ar-oracle-A-post -> 40` it is 1, so the pick agreed across two processes. Lane AS's varying pick (64 vs 62) had a longer list. The sufficient condition is NOT readable from the pre-turn save. (2) **The predicate has a false-positive class, and it was visible BEFORE the run**: four NPC players (528 Alien Menace, 544 Peacekeeper Enforcer, 560 Von Neumann, 576 Independent Colony) carry `ResTNm == ''` on EVERY turn of this lineage INCLUDING the turns whose controls agree; all four have `NumOwn == 0`. Read the predicate only for a non-eliminated AI player with NumOwn > 0. (3) **The predicate has no COMBAT term and needs one** - turn 40 was exposed through `RunCombatRound`, and the pre-turn save names the condition as plainly as it names ResTNm: an AI fleet standing at an enemy colony. (4) **A fourth term is not a predicate on the save at all**: turn 39 ran one way by continuation and another way after a load, BOTH reproducibly, so exposure is a property of (save, procedure, LOAD HISTORY), not of (save, turn). Rule 26 (c)'s instruction "record the pair as (save, procedure, hashes)" was already right and is now load-bearing for a reason it did not anticipate | diff --git a/findings/subsystems/determinism-oracle.md b/findings/subsystems/determinism-oracle.md index 288ec35..b8c5eb2 100644 --- a/findings/subsystems/determinism-oracle.md +++ b/findings/subsystems/determinism-oracle.md @@ -32,6 +32,19 @@ resets a per-player `Status` int (4 → 0) on load, which also moves the derived See "Round-trip stability" below for the canonicalization rule if a harness ever compares a loaded `(Autosave).sav` against a re-save of it. +> **CORRECTED IN PLACE 2026-09-09 (lane BQ, rule 11).** "Mask `Player.Status` and `Summary.Checksum`; +> **nothing else varies**" is **false** on the late-game saves. `--mask resave` leaves one more leaf +> standing: **`/CD[1]/NPrvVa`, which advances by exactly +5.00 across a load** (and per turn +> boundary), and falls on a combat turn. The old rule was derived on three-player early-game saves +> whose `CD[1]` diplomacy block is empty, and it was true of those. **Any load → turn comparison must +> mask `NPrvVa` too, or no load → turn run can ever reproduce a continuation's hash** — lane BQ's +> probe 1b was unsatisfiable on that leaf alone before any AI behaviour entered the question. +> +> The canonicalisation itself is **exonerated**: the pre-turn round trip is byte-exact apart from +> that one leaf. What is *not* safe is assuming a continuation's hash is reachable by load at all — +> see the board's load-boundary scope row: an AI fleet was issued a move order in the continuation +> and none after a load, **both reproducibly**. + ## Revalidated 2026-09-08 (lane H) Lane N's End Turns overwrote the three autosave *contents* on VM140 (the file set was untouched). diff --git a/guides/method-rules.md b/guides/method-rules.md index 85930c9..5207fc6 100644 --- a/guides/method-rules.md +++ b/guides/method-rules.md @@ -400,6 +400,29 @@ mechanism. > decode which consumer was reached before reasoning from the leaves — "the AI varies per process" > is a fact about a turn, not about the engine. +> **(c) is weaker than it was published as — read this before using it.** Written 2026-09-09 from +> three states read after the fact; **its first forward test falsified it twice the same day**, and +> the honest statement is now: +> +> * `ResTNm == ''` **predicts that a pick is made, not that it varies.** Exposure needs the candidate +> list to have **more than one entry**, and lane BQ measured `candidatesTried = 1` on a turn whose +> pick agreed across two processes. **That count is not readable from the pre-turn save.** +> * There is a **false-positive class visible before any run**: players carrying the signature who +> have no AI client or `NumOwn == 0`. Read the predicate only for a non-eliminated AI player that +> actually runs. +> * **There is no combat term and there needs to be one.** A turn was exposed through +> `RunCombatRound`, and the save names that condition as plainly as it names `ResTNm`: an AI fleet +> standing at an enemy colony. `Auto Resolve Peacefully` means *"I commit no ships"*, **not** +> *"no combat occurs"* — the defender's planet still fires, and its kill count varied. +> * **A fourth term is not a predicate on the save at all.** The same turn ran one way by +> continuation and another way after a load, **both reproducibly** — so exposure is a property of +> `(save, procedure, load history)`, not of `(save, turn)`. The instruction to record a pair as +> `(save, procedure, hashes)` was already right, and is now load-bearing for a reason it did not +> anticipate. +> +> So (c) is a **useful screen, not a decision procedure**: it can tell you a control is *likely* to +> vary, and it cannot certify that one will agree. A resolver holds the restatement. + ## 27. A ratchet is meant to break when the corpus grows — do not move it, and scope your control The save corpus went from 11 to 19 in one evening as lanes finally manufactured workloads nobody had