# L4 — what the live AI will emit, written before the module was built Lane L4, 2026-09-08. Guest **VM145** (`sots-re-win10-145`, 192.168.10.145). Worktree `wip/l4` off `main` `0117495`. Rule 2: this file is committed **before** the shim module is written, and it names the falsifier for every claim. Nothing below is a summary of a run; there has been no run. The question this lane exists to answer: **does our emission model produce the same commands the game does?** `src/game/ai` models the *shape* of a `TurnCommands` block and the *cost* of each element, and decides nothing. Nobody has ever looked at a real AI block. --- ## 0. The instrument, and why it is one detour `Game::StrategySim::ApplyTurnCommandBatch` **0x0088f9b0** receives `(blocks, n)` as two stack arguments with `ecx = sim` — read straight off the prologue at `0x0088f9db`–`0x0088f9e7`, where `n` is multiplied by `0x1b4` and added to `blocks` to make the end pointer. **At that instant every player's submitted block is complete and in memory**, so one register-transparent entry stub dumps the entire turn's command traffic: six gates and twenty-seven list lengths per player, plus the element bytes. That is a strictly better instrument than counting `ModCount` bumps, which is what every previous lane has had. A bump count says *how many* commands; the block says *which lists, how many elements, and what values*. Second instrument: sixteen register-transparent entry counters (`probe_entry.cpp`'s pattern, its own table so lane H's set is untouched), on the task bodies and the emission gates. One of them — `StrategyAIAgent::RunTaskList` **0x006b3320** — additionally records its `pass` stack argument into a global, so every later probe hit is **attributed to a pass**. That is what turns AI3's P2 from a count into a measurement. **Rule 20 is the whole design.** `AITRaid`'s list-16 emit is the named open item, and a zero at `0x007635f0` means nothing unless `AITRaid::Execute 0x0068e670` is also probed: "never ran" and "ran and emitted nothing" are opposite answers. **Rule 19 control.** Both workloads have published oracle bytes. Every configuration is run against them and any configuration that moves a byte has invalidated its own numbers. --- ## 1. Workload W1 — `turn1-state.sav`, one End Turn (the turn 1→2 transition) Facts already in the corpus, from `turn1-state.sav` → `turn2-state.sav` (read this session with `verify/save-reader`, not taken from a report): | | | |---|---| | players | 8: ids 16, 32, 496, 512 (`Species` 0/2/0/2) and 528/544/560/576 (`Species == 4`) | | player 32 | `HomeSys` **288** = Ke'Dolarra, `PlyrIdx` 1 | | `ModCount` | 0 → 12 | | `ResRate` | 0.25 → **0.8** on 32, 496, 512; unchanged (0.25) on 16 | | `ResTNm` | `''` → `IND_Waldo` (32), `DRV_PlsFiss` (496), `BIO_GnMod` (512); `''` on 16 | | `Status` | 0 → 4 on 16/32/496/512; **0 on all four `Species == 4`** | | player 32 designs | 5 → 6; the new one is `DesID` **18**, `"Honor Lance"` | | `designIds` | gains **18** *and* **1712** — the second belongs to player 528, whose design count also goes 18 → 19 | | `NMnx` | 106 → 109 (ship **1728**, fleet **1744**, and one more) | | new fleet | **1744** `"Alpha Fleet"` at system 288, holding ship 1728 of design 18 | ### P1 — the block set **Prediction: `n == 4`, and the four `block+0x04` player ids are 16, 32, 496, 512 in that order.** *Falsified if* `n == 8` — then the `Species == 4` factions **do** submit, they just submit empty blocks, and AI4 §2's "no `StrategyClient` at all" is too strong (it would become "a client that never sets the always-set gate", which is a different and stranger claim). *Falsified differently if* the four ids are 0,1,2,3 — then `block+0x04` is a player **index**, not the save id, and every published reading of that field needs the same correction. ### P2 — the four blocks, list by list | block | gates set | lists non-empty | |---|---|---| | 16 (human) | rate only, value **0.25** | **none** | | 32 (AI) | rate **0.8**, target (`IND_Waldo`) | **1, 3, 5 — one element each** | | 496 (AI) | rate **0.8**, target (`DRV_PlsFiss`) | **none** | | 512 (AI) | rate **0.8**, target (`BIO_GnMod`) | **none** | Boost, group-4, the three-float Hiver gate and `CivilianRatios` **clear on all four**. That sums to AI4's twelve: 4 rate + 3 target + 3 (player 32's lists) + 2 drivers. *Falsified if:* any list outside {1, 3, 5} is non-empty on any block; or a list in {1, 3, 5} carries more than one element; or the human's block carries a list at all; or 496/512 carry a list (they own nothing, so a list element from them would mean a task fired against objects they do not have). **The sharpest falsifier is list 8/10/14 non-empty.** Player 32 gets a brand-new fleet (1744) this turn, and a fleet is exactly the thing lists 8/10/14 order. If the AI issues a fleet order on the same turn it creates the fleet, AI4's P2 attribution is wrong by exactly the fleet-order group and the twelve has to be re-derived. ### P3 — the element values * **list 3** (`{i32 ordinal, i32 designId, i32 systemId, i32 w}`): `designId == 18`, `systemId == 288`. The `w` word has never been observed and is not predicted. * **list 5** (`{i32 systemId, OutputRates}`): `systemId == 288`, i.e. **the same system the build order names**. * **list 1** (`ShipDesignDef` frame + `i32`): the element carries the id **18** somewhere in its first 48 bytes. *Why 18 matters.* 18 is not a multiple of 16 and does not come from the master `NMnx` counter — the *other* new design that turn, player 528's, got **1712** from that counter, and 528 submits no block. So there are two id spaces, and the prediction is that **a client-issued design carries a small client-local id in the command itself**, while a server-created object gets a master id. *Falsified if* the list-1 element's ids are all ≥ 512 / multiples of 16 — then 18 was assigned server-side when the command was applied and the design command carries no id at all, which is a materially different thing for a reimplementation to emit. ### P4 — the probes on W1 | probe | prediction | what a miss means | |---|---|---| | `RunTaskList 0x006b3320` | **6** = 3 AI players × 2 passes | 2 ⇒ only one player's task list runs and 496/512 reach `ResTNm` by some other path; 0 ⇒ the instrument is broken, not the game | | `BuildTurnCommands 0x00783780` | **4**, one per submitting block | ≠ 4 ⇒ P1 is wrong from the other side | | `RequestBuildForTask 0x006cea50` | **> 0 and even-ish** — entered on both passes, emits only on pass 1 | entries only in pass 1 ⇒ the caller gates before it, and AI3 §2.3's "the hub returns an empty list on pass 0" is not the whole mechanism | | `AssignFleetsAndIssueOrders 0x006c16c0`, `IssueRouteForFleets 0x006bbd50` | **entered, both passes, zero list-8/10/14 elements** | entered + elements ⇒ P2 falsified | | `AITRaid::Execute 0x0068e670` | **0** | — | | `ClientOrder 0x007635f0` (list 16) | **0** | — | | the six shared planner bodies (`AITColonize` 0x0068b400, `AITEscortGateInvade` 0x0068c7c0, `AITInvade` 0x0068d7a0, `AITNodeBore` 0x0068e590, `AITBuildPoliceShips` 0x00690380, `AITBuildDeepScanShips` 0x006901a0) | **at least one build-shaped body entered in both passes; at most one produces the single list-3 element** | none entered ⇒ the list-3 element comes from a task nobody has named, and AI3 §5's path table is not the emission path | | `AITAdvanceIdleShips::Execute 0x0068f230` | entered (priority 0, always last) | — | | `IsClaimedByAnotherTask 0x006a8d20` | **> 0** | — | **Stated in advance so it cannot be claimed afterwards: W1 almost certainly cannot settle AI3 §2.4.** A turn-1 AI with one colony and no fleet of its own has nothing to raid, so `AITRaid` is predicted not to run at all, and a zero at `0x007635f0` will then be a *non-answer* about pass 0 — exactly the "did not fire" vs "fired and found nothing" distinction rule 20 is about. The AITRaid question needs `AITRaid::Execute` to be entered, and W2 is the better chance. **Also stated in advance: this probe set cannot settle AI3's P3.** An entry counter on `IsClaimedByAnotherTask` measures the "called often" half only; the steal branch at `0x006a8ded` is *inside* the function and an entry probe cannot see it. P3 stays open. --- ## 2. Workload W2 — `ref-turn2.sav`, one End Turn (the turn 2→3 transition) `ref-turn2.sav` **is** `turn2-state.sav` (same sha256). Corpus facts for 2→3: * `ModCount` 12 → 24; `NMnx` 109 → 111 (ship **1760**, fleet **1776** `"Gamma Fleet"`); * fleet **1744** `"Alpha Fleet"` is **gone**, replaced by fleet **34** `"Beta Fleet"`, in transit (`FtTrans 1`), flight plan one waypoint `Wpt 272`, `pnd 288`, ETA 3, origin = Ke'Dolarra's position; * no `ResTNm` change on any player; `ResRate` already 0.8. AI4 §2.1 attributes the twelve as 4 rate + list 5 + list 3 + list 10 + **2 × list 14** + list 8 + 2 drivers. ### P5 — the block **Prediction:** `n == 4`; blocks 16, 496, 512 carry the rate gate and **nothing else** (no target gate this turn); block 32 carries the rate gate and lists **3 = 1, 5 = 1, 8 = 1, 10 = 1, 14 = 2**, everything else empty. ### P6 — list 14 is two elements against one fleet, and the second field is the discriminator Lane Q read the element as `{i32, i32, bool}`; `human-turn2-orders.sav` (a UI fleet move) carries exactly one, `{1456, 0, true}`. AI2's P1 says the AI's bridge calls the adder twice with mode 0 and mode 1 and the adder keys on `(fleet, mode)`. **Prediction: the two list-14 elements are `{F, 0, b}` and `{F, 1, b}` with the same `F`, and `F` is also the fleet id list 8's element names.** That converts AI2's P1 from an inference about bump counts into a read of the values. *Falsified if:* both elements carry the same second word (then the adder does not key on mode and the doubling has another cause); or the two fleet ids differ (then it is two orders against two fleets and the "one order costs two elements" rule is wrong); or list 14 has one element (then AI4's two list-14 bumps came from two different fleets and the reference turn moved two). ### P7 — which fleet id the order names **Prediction: `F == 1744`** — the fleet that exists at submit time — and **34 does not appear anywhere in the block.** 34 is created when the order is applied, from the same small-id space as design 18. *Falsified if* `F == 34`: then the client creates the fleet object *before* submitting and ships the new id in the command, which would make the small-id space **client-allocated and part of the wire protocol** — a much bigger constraint on a reimplementation than the alternative, because our engine would have to reproduce that counter exactly to get byte-identical saves. This is the one prediction I would most like to be wrong, because the falsifier is the more interesting world. ### P8 — list 10's first word Lane AI4 §4.5 refused to name list 10 (`{i32, i32, counted i32}`) on positional evidence alone. **Prediction: its first `i32` is `F`, and its counted vector holds ship ids** — on W2 that means the single ship **1728**. If so, "assign these ships to this fleet" is supported by values rather than by adjacency, and the list can be named. *Falsified if* the counted vector is empty or holds system ids (W2's route is a single hop to 272, so a `{…, 272}` tail would make it a route-shaped command instead). ### P9 — AITRaid, the second attempt Player 32 now owns a fleet, so `AITRaid` has a candidate. **Prediction: `AITRaid::Execute` is entered on both passes and `ClientOrder 0x007635f0` is entered zero times**, i.e. list 16 stays empty and AI3 §2.4's inference holds — but *for the reason the probe can see*, which is the point. *Falsified if* `0x007635f0` is entered with `pass == 0` recorded: then pass 0 **does** emit, AI3's P2 is falsified, and every `ModCount` arithmetic in `ai-order-emission.md` that assumes one emitting sweep is off by a factor. --- ## 3. Rule 19 — the controls, and what they are compared against Published oracle bytes (sha256 prefixes, `findings/subsystems/running-the-game.md`): | workload | `(Autosave EndTurn).sav` | `(Autosave).sav` | |---|---|---| | W1 from `turn1-state.sav` | `a3f9dc4b…` | `ab4ac2d7…` | | W2 from `ref-turn2.sav` | `bb4fd9ac…` | `978041ac…` | Three configurations per workload, each a separate launch: | run | `shim.cfg` | purpose | |---|---|---| | **C** | `hooks=off` | proves **this clone** reproduces the oracle at all. VM145 is a ZFS clone; nothing has ever checked that it is byte-faithful. If C fails, the lane reports that and stops — every later number would be measured against an unknown baseline. | | **A** | `aiorders=on`, `aiprobes=off` | the block dump alone: one detour | | **B** | `aiorders=on`, `aiprobes=all` | the dump plus sixteen entry probes | **Prediction:** C, A and B all reproduce both oracle hashes. **Falsified if** A or B moves a byte — in which case that configuration's numbers are reported as unmeasured, not as results, and the sixteen-probe set is bisected with `aiprobes=N`. Lane H's precedent is explicit that a MinHook detour has changed this game's behaviour once already, and that the probe module is the one it happened to. --- ## 4. What this lane will not do, said now 1. **It does not close AI3's P3** (§1, P4). An entry probe cannot see a branch inside the callee. 2. **It does not test the eleven "free" lists** (AI4 P1's untested half) unless one of them turns up populated, which P2/P5 predict will not happen. If one does, that is the result. 3. **It does not settle the Hiver gate** (AI4 P3). The corpus has no Hiver player and this lane is not manufacturing one. 4. **It reads element bytes, not element types.** The dump emits a fixed 48-byte window per element plus the same bytes as ints and floats; decoding to lane Q's records happens offline, in the report, where a wrong record is visible as a wrong value rather than being baked into the instrument. Where a list's in-memory element is larger than 48 bytes the dump says so rather than silently truncating. 5. **The dump's own coverage check**: for every list it walks the node chain *and* reads `_Mysize`, and logs a `MISMATCH` line if they disagree. A wrong list layout would otherwise print a confident, wrong zero (rule 1).