# Lane PAR — predictions, written before the build Question, as redirected by the coordinator: **is there *roll* parity?** Does one run of an AI client consume a **fixed number of RNG words regardless of the path it takes** — the classic lockstep discipline of drawing unconditionally so peers stay aligned? Instrument: a bracket on `StrategyClient::OnResumePlaying 0x00777480` (the whole AI turn runs inside it), measuring the per-client generator at `StrategyClient+0x134` two ways — the observer sum over the seven hooked entry points, and the raw `left` delta on the object — plus per-return-address attribution inside the bracket. Optional `airng.pin_seed` re-seeds that generator at bracket entry so two runs start from an identical state and any remaining difference is *path*, not seed. All predictions below are written before the instrument was built and before any run. --- ## P1 — the count is **not** constant across clients on the same turn On `turn2-state.sav` there are three AI clients: **32** (owns a homeworld, fleets, a build queue) and **496** / **512** (two Singularity shadow empires that own nothing at all — `Sav = 0`, no colonies, no fleets, no systems, per `ai-order-emission.md` §2). **Prediction:** client 32's bracket consumes **strictly more** words than 496's and 512's, and 496 and 512 consume the **same** count as each other. *Falsified if:* all three consume the same count ⇒ the AI really does draw unconditionally and the lockstep hypothesis survives its first and cheapest test. That would be the strong result. *Also falsified, differently, if:* 496 and 512 differ from each other ⇒ the count depends on something finer than "owns nothing", and the property is not even constant across identical boards. ## P2 — the human client's bracket is exactly 0 words `OnResumePlaying` runs for the human client too; the AI branch is `if (this->+0x12c)`. **Prediction:** the human client (pid 16) shows `agent=0` and **0 words**. *Falsified if:* it draws ⇒ there is client-side RNG on the human path and the bracket is not measuring what I think it is. ## P3 — the surrender roll costs **0** words, not 1 `RNG_Chance 0x008e6dd0` is documented `verified` as returning at `p <= 0` and `p >= 1` **without a draw**. Process Turn phase 12 is `cl_Chance` on the surrender probability, which is 0 on a healthy empire. **Prediction:** no bracket on the reference board attributes a word to phase 12's return address. *Falsified if:* a `Chance` row appears with `words > 0` ⇒ the surrender probability is non-zero on this board, which is itself interesting. *Why it matters:* the resolver's list of "draws whose result never reaches the save" names "the surrender `Chance` at 0 %". If `Chance` early-outs, that entry is **wrong** and the ~8 words/turn figure needs re-deriving. This is a rule-11 correction if it lands. ## P4 — the count is seed-dependent even when the path is fixed `RNG_NextInt 0x004271c0` is documented `verified` as a **rejection loop**: mask = smallest `2^k-1 >= n`, redraw while `(y & mask) > n`. So a `NextInt` whose bound is not `2^k-1` costs 1 word with probability `(n+1)/(mask+1)` and more otherwise. **Prediction:** with the *same save and the same path*, pinning two different seeds produces two different bracket totals for at least one client, differing by a small number of words. *Falsified if:* the total is invariant under the pinned seed ⇒ either no `NextInt` with a non-power-of-two bound runs on this path, or `NextInt`'s body is not what the header says. *Why it matters:* it separates the two things that could be meant by "fixed count". Seed-dependence is **harmless** for a reimplementation (we reproduce the rejection loop bit-for-bit). Path-dependence is the fatal one. P1 and P4 must be read together: only P1 tests the hypothesis. ## P5 — the one-shot schedule draw is **outside** this bracket `0x0069dbb0` (`if (agent->+0x36c == 0) sched = turn + 3 + NextInt(0..37)`, two `NextInt` sites at `0x0069dbfd`/`0x0069dc29`) sits in the **Prepare Turn** body, and `SEAIPrepareTurn` is raised only at agent construction (`RunAI` / `CreateGame`), never per turn. **Prediction:** those two return addresses never appear in an `OnResumePlaying` bracket; they fire once per agent per process, on load. So the *load* consumes words that no turn does, and a per-turn count that ignored the load would be wrong about the generator's position. *Falsified if:* they appear in a bracket ⇒ Prepare Turn is reachable per-turn after all and `ai-turn-logic.md` §1's "not a per-turn event" is wrong. ## P6 — the residual: `left`-delta will exceed the observer sum on at least one bracket Rule 16: inlined draws are invisible to a call-graph sweep, and the seven entry-point detours are a call-graph sweep by another name. The bracket also reads `left` on the object directly. **Prediction:** for the AI clients the two agree exactly (lane I's inlined-draw scan found no hit in the AI band 0x00680000–0x006e0000), and the instrument prints the residual on every bracket so a disagreement cannot hide. *Falsified if:* a residual appears ⇒ there is an inlined draw on the AI path that the campaign's draw-site inventory does not have, and the whole per-site table is a lower bound. ## P7 — the overall verdict, predicted Static reading already shows at least three genuinely conditional constructs on the AI path (`Chance`'s two early-outs, `NextInt`'s rejection loop, the `+0x36c` one-shot guard) and the task system is a *variable-length list* dispatched twice. **Prediction: the answer is (d) genuinely state-dependent, not (a)/(b)/(c)** — but with a useful qualification: the *variation is small and localised*, so the engine's problem is not "the count is unpredictable" but "the count is a function of the decisions", i.e. `game/ai` must get the decisions right after all, and C-exact does **not** get cheaper. *Falsified if:* P1 comes back with all three clients equal. --- ## What would make me wrong in a way I would not notice * **Rule 26.** A single run is not a control. Every configuration below is run in **two fresh processes** and must agree with itself before it is used. The canonical pair is `turn2-state → turn3-state`; `turn1-state` is known non-deterministic and is used only *with* pinned seeds. * **Rule 19.** The bracket detour is a MinHook detour on a function that runs the AI turn. If it perturbs, the autosave moves. The instrumented run's autosave is compared against the published oracle (`bb4fd9ac…` / `978041ac…`) on the unpinned configuration; a pinned run is *expected* to differ and is never compared against the oracle. * **Rule 20.** A bracket that shows 0 words for a client cannot distinguish "the client did not run" from "it ran and drew nothing". The bracket therefore records entry/exit unconditionally and prints a row even when the delta is 0, and records whether `agent != 0`.