33 KiB
Resolution — the AI client seed is fresh per process; the tie hypothesis is dead, the byte-match is not
- Type: resolution (course-setting; no measurement taken here)
- Trigger: lane L1's P7 falsified —
findings/subsystems/ai-client-seed-is-per-process.md - Date / author: 2026-09-08 · re-resolver
- Touches:
backlog.md§2 (Rung C and the mechanism paragraph), board rows 240 / 255 / 331 / 333 / 335,determinism-oracle.md(closing paragraph),ai-turn-logic.md§5.1 + P1,ai-order-capture.md§3.1–§3.2,lane-ai1.json::g_GlobalRNG, lane L4 and CB briefs,method-rules.md(new rule 25)
0. Decision
- The variation on
turn1-state → turn2is a seed effect. Each AIStrategyClient's generator is seeded at construction with a 32-bit word that differs in every process. The coordinator's tiebreak-by-ASLR inference, and lane AI1's all-zero-seed derivation, are both overturned. The instrument is exonerated (§2). - The byte-match ambition survives, and in a stronger form than the tie-set claim. The
original's decision layer is MT19937 from that one word — a generator we already own bit for
bit — so the AI is a deterministic function of (save, per-client seed). A deterministic
game/aithat takes the seed as an input can byte-match every run whose seeds were captured, not only the runs where a tiebreak happens to agree. That answers the user's standing question — make it deterministic and the old approach still pays — with a yes, on the condition that the seed is treated as part of the input. Rung C is restated accordingly (§5). - The tie-set claim is rescued as a statement and open as a result — and not on "equal
cost". Where seeds were never captured (the historical
turn2-state.sav) no one can reproduce the run — the original included. There the claim is membership in the outcome set of a random pick, S(D), with a four-part verification statement (§3.1). Lane L4 has one arm (k = 6, XNC) andBIO_GnModproves a second arm exists whose producer is unidentified, so "we can name all k" is to prove, not proved. That is the fallback, "C-set". - One probe, before anything else is redirected: pin the three client seeds and run the
creation turn twice — through L4's resolver instrument, then sweep seeds. Prediction:
byte-identical autosaves, same pick under the same seed, a measured word cost inside
TryResearchCandidate. If it holds, the seed is the only per-process input on that turn,turn1-state → turn2returns to the oracle set (with seeds),game/aihas a well-posed exact target, and the sweep names theBIO_GnModarm. The seed's source is not the probe. §3.1, §4. - Rule 19 gets a companion, rule 25: a control must agree with itself across two fresh processes before it can exonerate or indict anything, and "one of N moves" says nothing about mechanism. Draft in §7.
1. The surprise, stated precisely
Loading turn1-state.sav in two freshly launched processes, with Mars::RNG::Seed and
StrategyApp::RunAI detoured, produced the same eight records in the same order with the same
net ids — and three different seeds for the three AI clients in each process, none shared
between processes (0x75F692C0 / 0xF2EDAC21 / 0x165A2ADB vs 0x414F415E / 0x10B94E78 / 0xC218DBF8). The prediction committed before the build was "identical, and zero". It was wrong on
its first falsifier row.
The evidence is as strong as it looks. The Seed hook records the post-state of the object it
seeded; the one call that takes seed = 0 gives mt0..mt2 = 2443250962 / 1093594115 / 1878467924
in both processes, so the hook reproduces a known input to a known state and did not randomise
anything. The build was fresh (l1seed-20260908T2206Z, lane L1's own dist). The workload is
load-only, so no End Turn logic is involved — this is the construction path and nothing else.
Two further things the raw trace says that the report does not spell out:
- The three seeds within one process are unrelated words (high bits
0x75,0xF2,0x16), not three reads of a clock microseconds apart. They are successive outputs of a generator. That is exactly what AI1 read atOnMessage+0x955—mov ecx,0xaf6e58 ; call NextUInt— so the call site reading stands and the "never seeded" reading falls:g_GlobalRNGis live and per-process. Something initialises it that a scan of the six directRNG_Seedsites cannot see — rule 16's shape (an inlined MT init, or a state copy), or the object at 0xaf6e58 is not the one being drawn. Inference; §4.2 names the cheap check. - A fourth per-process seed exists before any AI client — record #2,
seed = 0xf22aac3a/0x16612cc3, on a heap object nobody has identified. It is either the global's source, a sibling consumer of it, or an unrelated per-process generator. Onref-turn2it changes nothing save-visible (seven fresh processes agree there); on other workloads it is unbounded.
2. The instrument — exonerated, and the reason is worth keeping
Three lanes' instruments cannot be the cause and the seed probe shows why:
- L5's pair A/B carried identical hooks and disagreed; L1's instrumented run coincided with L5's un-instrumented one; L4's runs differed with and without hooks. No hook configuration selects the pick.
ref-turn2 → turn3reproduces byte-for-byte under every hook set tried (H, L1, L4, F, V) and under none. The instruments are behaviour-neutral on a workload where the game is self-consistent.- The seed probe's built-in control (
seed = 0→ identical state) shows theSeeddetour itself does not perturb the object it observes.
But no hooks=off run on turn1-state was a control of anything — L5's, L1's and L4's
hooks=off runs gave three different files (62061918…, e43ec1d2…, d59bb9f2…). Each lane
cited its single hooks=off run as "the un-instrumented value"; together they show the
un-instrumented game has no single value there. The exoneration above rests on the identical-hooks
pair and on ref-turn2, not on those runs. That is the gap in rule 19 — §7.
3. Standing claims, one by one
| claim | where | verdict | scope / correction |
|---|---|---|---|
Per-client Mars::RNG at StrategyClient+0x134; all six AI-band NextInt sites plus cl_Chance/cl_RandRange reach it; zero AI draws on the strategic generator |
AI1 §5, board 240 (first half) | survives | L1's hive run has the strategic trajectory identical to the historical game while the pick varies — the strongest confirmation available |
"g_GlobalRNG is never seeded; every draw returns 0; every AI client is seeded with 0" |
AI1 §5.1, board 240 (second half), lane-ai1.json::g_GlobalRNG |
overturned by measurement | The author flagged it as arithmetic and wrote the falsifier that fired ("run-to-run different values mean it is time- or address-seeded"). Rule 2 working as designed. Lane H's counter-indication (rejection loop ran on that generator, impossible for an all-zero state) is now confirmed and should be credited |
AI1's five consumers of g_GlobalRNG: SNMRunAI, RunCombatRound+0x60f, 0x007c2fa0+0xc84, 0x0079ea90+0x73 (Chance), 0x005b9f00+0xc0 |
lane-ai1.json |
survives as a list; its meaning flips | Each is now a per-process input to whatever it feeds. ref-turn2 bounds them to "no save-visible effect on that turn" (seven processes). The combat consumer has never been cross-process tested on a battle turn — §6 |
| "P2 HELD: the AI is fully reproducible from a save alone" | AI1 §8.1, board 255 | qualified with scope | True of ref-turn2 → turn3, where every AI already holds a target and no decision depended on a draw. False as a general statement, as L5/L1/L4 showed. Already superseded by row 331; the row should say so |
turn1-to-turn2-nondeterminism.md — three runs, four leaves, instrument exonerated |
L5 | survives; cause now known | Its "what was not established: the cause" is answered: the per-client seed. Its hooks=off row is a sample, not a control (§2) |
Determinism oracle on ref-turn2 → turn3 |
determinism-oracle.md, board 50/253 |
survives unchanged | The closing "Mechanism (inference…)" paragraph must be replaced with the seed finding |
| Coordinator's mechanism: "only one of three varies ⇒ pointer-ordered tie under ASLR, not a time seed which would move all three" | backlog §2 last para, board 331 (5), determinism-oracle.md last bullet |
overturned | All three streams move. A seed is visible only where a decision depends on a draw; 32 and 496 never reach the candidate walk (L4 §3.2), so their picks are stream-insensitive by code path, not by luck |
Rung C = "deterministic game/ai whose choice is provably inside the original's tie set; k enumerable" |
backlog §2, board 333 | restated (§5) | The conclusion that the outcome space of the one varying decision is small and nameable survives; the mechanism is a draw, so it is the support of a random pick, not a tie. And the exact form — byte-match given the seed — is now available and is stronger |
L4 §3.2: three producers; only 512 reaches SelectResearchTarget; candidate vector length 1 = a category {2,12}; fallback not taken; k = 6 for the XNC family from data; 4 of 6 observed inside |
L4 | survives (measured parts) | Its interpretation — "taking whichever member it reaches first … the per-process quantity is order" — is overturned: the per-process quantity is the client stream inside TryResearchCandidate 0x006c8580. Inference until the draw is seen; the L4 correction in §8 says how |
| L4's engine recommendation (canonical by tech id; claim membership in a nameable set) | L4 §3.2 | survives as C-set | Needs the BIO_GnMod arm accounted for (2 of 6 observed picks are outside the XNC set), i.e. the category itself is probably drawn |
| Lane Z: "residual outside the two drivers is exactly zero" | tail-rng-ledger.md §0/§2 |
qualified with scope (L1 did it in place) | True of every turn with hives already present; on the creation turn BeginProcessTurn draws one NextInt per new hive, with the rejection loop live. The standalone's replay interval starts at BeginProcessTurn (§6) |
Rule 19's hooks=off control |
method-rules.md |
qualified | Valid only on a workload where the un-instrumented game agrees with itself across two fresh processes. Rule 25 (§7) |
| "The decision layer is not deterministic, and does not need to be" | backlog §2 | qualified | It is deterministic given the seed. "Does not need to be" holds for lockstep (decisions travel as commands); for us the seed is an input, not noise |
| Lockstep MP is consistent with a per-machine AI seed | board 331 (2) | survives | Only the machine hosting an AI client draws its seed; peers see commands. Prediction for L2 when convenient: the combat seed (the RunCombatRound consumer) must be host-distributed in the SNM stream, or MP battles would desync |
3.1 The coordinator's follow-up, ruled on
Is the tie-set claim rescued? In form, yes; in content, not yet — and not on the grounds proposed. Three rulings:
-
Do not re-ground it on "an identical-cost family". The six tier-1 XNC techs cost 2000 RP each, but the observed picks are their tier-2 successors at 13,000–30,000 RP — L4's own load-bearing detail is that the costs differ and the resolver still varies. Equal cost is not what makes the outcome set a set. What makes it a set is that the resolver's output is a function of the client stream over a finite candidate collection: S(D) = the support of a random pick, and k is the size of that support. Ground the claim on the draw and its bound, which the probe reads; never on cost.
-
"We can name all k" is not claimable today, and
BIO_GnModis why. Two of six observed picks (L5 run B, and the original session that wroteturn2-state.sav) are outside the XNC six. L4 captured the candidate stream once, in a run that went to producer C. The honest reading of the six observations is: S(D) ⊇ XNC-six ∪ {BIO_GnMod}, with the producer ofBIO_GnModunidentified. The likeliest shape, stated as inference: in theBIO_GnModruns producer A or B returned non-null for player 512 — gated by a draw on the same client stream — and the walk was never entered; then S(D) = A's outputs ∪ B's outputs ∪ C's arm, and naming k means reading three producers' gates, not one resolver. Finite, cheap, and not yet done. Carry k = 6 as L4 says: to prove. -
The verification statement, exactly. For a turn T from save X, let D be a decision whose output depends on the client stream (on
turn1-state, exactly one: player 512's research target). Then:C-set. (i) Derivation: S(D) is listed before any sweep, from the code of every producer that can set D and from shipped data plus the save (the available-tech set is a function of X, so S is per-save, not a constant). (ii) Canonical pick:
game/aiemits a fixed member of S(D) — lowest tech id, the quantity the command carries. (iii) Containment: every observed original run's D is in S(D). (iv) Falsifiable content: a sweep of pinned seeds — the seed is now a knob — produces no D outside S(D), and reaches every member of S(D) whose derived probability is not negligible.(iii) alone is a tautology if S is just the observed set; (iv) is what makes it a claim. Today (i) is done for one arm, (iii) holds 6/6 only by admitting
BIO_GnModon observation, and (iv) has not been run. C-set is therefore rescued as a statement and open as a result. C-exact (§5) does not depend on any of this and is the primary form.
On "a clean control" (R2 = hooks=off bytes). Not a control — rule 25. Two runs landing on
the same one-of-k pick is a ~1/k event per pair, and with pairs compared across L5, L1 and L4 one
coincidence is expected (L1's instrumented run also coincided with L5's hooks=off). It is
consistent with neutrality; the exoneration rests on L5's identical-hooks pair and on ref-turn2
reproducing under every hook set, as §2 says.
Is the next probe inside the resolver rather than at the seed source? Yes on the source — it
is not the probe — but the resolver probe is only an experiment if the seed is pinned. Without
the pin, two runs of L4's instrument with two unknown seeds cannot separate "draws from the
stream" from "reads something else per-process". With it, the same run answers both "what is
per-process about it" (same seed ⇒ same pick settles that it is the stream) and "what does it
iterate" (the left delta and the draw's bound name the arm's k). So the one probe is the pin
through the resolver, one run, both instruments — §4.
4. The one probe — pinned seeds through the resolver
Pin the three AI client seeds to constants; run L4's research instrument with a left region
over client+0x134 at TryResearchCandidate 0x006c8580; load turn1-state.sav in two fresh
processes; End Turn; compare autosaves and the aicand/pick/word-cost lines. Then sweep seeds.
Why this and not the alternatives the coordinator listed:
- Where the seed comes from only matters for reproducing files whose seeds were never logged —
which is impossible whatever the source turns out to be. For every future capture the
RunAIhook already logs the seed. - Whether the outcome space is enumerable is answered "yes, per decision, from the producers' gates and the draw's bound" the moment the mechanism is a draw — and it becomes irrelevant to exactness once the seed is an input. It stays relevant only for C-set, which is the fallback, and there it needs (i)–(iv) of §3.1, which the sweep supplies.
- Whether the varying empire's candidate set is tied is already answered by L4: the vector has one element. There is nothing to tie.
The pin probe separates the two hypotheses that are actually live:
- H1: the per-client seed is the only per-process input to this turn. Pinned seeds ⇒
byte-identical
(Autosave).savacross processes,(Autosave EndTurn).savstilla3f9dc4b…, strategic word positions still 192 (hive draws unaffected). - H2: something else per-process feeds the pick or the turn — the fourth seeded object (record #2), a clock read inside the AI, an address-keyed container. Pinned seeds ⇒ the autosaves still differ. Where they differ localises it: the same four leaves ⇒ H2 is inside the research pick; anything else ⇒ a per-process input outside the AI, which nobody has predicted.
Implementation, for whoever runs it (minutes, not a lane): lane L1's l1seed shim already
detours RNG_Seed and sees the seed argument. Add seed.pin=<hex>,<hex>,<hex>: when the detour's
return address is 0x0078304b (the instruction after call RNG_Seed at 0x00783046 in the
StrategyClient ctor), replace the seed with the next pinned value, in call order (the order is
deterministic — eight records, same order, both runs). Log the replaced and original values. The
existing post-state fields verify the pin: the three after states must be identical across
processes, which is the built-in control. Use process 1's own values (0x75F692C0, 0xF2EDAC21,
0x165A2ADB) so a match against any file L1 already holds is possible in principle.
Then: (i) ref-turn2 once with the pin active — expect 978041acd168b56e…; that turn's
decisions are stream-insensitive and the pin must not change that (rule 19 for the pin itself).
(ii) turn1-state twice, fresh processes. (iii) If (ii) differs in the four known leaves
only, extend the pin to record #2 (return address of whichever site seeds it — the trace has its
this) and rerun. That is the whole bisection.
(iv) The sweep, same build: quit to menu and reload advances the pin table (RunAI runs again
on every load, so each reload is a fresh sample without a relaunch — ~30 s per sample on L4's
path). Ten to twenty pinned seeds, each logging: which of the three producers returned non-null
for player 512, the aicand line, the word cost on client+0x134 inside 0x006c8580, and the
pick. That is §3.1's (iv) — the first BIO_GnMod sample names its producer, and the word cost and
bound name k for the arm that ran.
Predictions to commit before the build: H1 holds; same pinned seeds ⇒ same producer, same
aicand, same word cost, same pick, byte-identical autosave; different seeds ⇒ same {2,12},
same cost, different pick; TryResearchCandidate costs ≥ 1 word on client+0x134; the
BIO_GnMod samples come from producer A or B, not from the walk; word positions 192 / 211
unchanged throughout.
4.1 What the probe buys beyond the answer
- If H1 holds,
turn1-state → turn2with pinned seeds is a byte oracle again — and it is the only corpus pair that exercisesBeginProcessTurn's hive draw. The standalone gets its creation-turn reference back. - The same run, with L4's research instrument and a
leftregion overclient+0x134aroundTryResearchCandidate, turns L4's inference into a measurement in one shot (§8). - CB's C3/C4 runs on
turn1-statebecome reproducible captures: seeds + stream + autosave, all from one process, is the record RB needs.
4.2 Secondary probes, in order, none of them a week
- The seed's source: one hardware write watchpoint on
0xaf6e58+4(mt[0]) armed atApplication::Initializeentry, dropped at firstSNMRunAI. The hit's return address is the seeder. Static cross-check for free: scan the image for the MT init multiplier0x6C078965at instruction boundaries — an inlinedinit_genrandthat the six-siteRNG_Seedscan could not see (rule 16, exactly). - The other four consumers of
g_GlobalRNG: entry probes (rule 20), cheapest on a battle save — §6. - Record #2's owner: the
Seeddetour already has itsthis; oneNextInt/NextFloatdetour recordingthis(lane H's own suggestion for the client generators) names its consumers.
5. Rung C, restated
Replace backlog §2's Rung C bullet and the mechanism paragraph with:
- Rung C —
game/aias a deterministic function of (save, per-client seed). The original's AI is not non-deterministic; it is MT19937 from one 32-bit word per AI client, drawn once at construction (on load, or atCreateGame) from a per-process global generator, and the generator is one we already own bit for bit. Two verification strengths:
- C-exact. Given the seeds captured from an original run (three words, logged by the
RunAI/Seedhook;sots_turn --ai-seed <netid>=<hex>,…), our AI's command block equals that run's block element for element and the autosave byte-matches. This holds on every captured run, because nothing else per-process reaches the turn — that "nothing else" is what the pin probe establishes, and it is the user's framing made literal: make the AI deterministic and the old approach still pays. Precondition:game/aiconsumes the client stream in the original's order and count, including draws whose result never reaches the save (the surrenderChanceat 0 %, the one-shot schedule atagent+0x36c, theNextIntrejection loops). A per-client draw ledger, lane Z's instrument withthisrecorded, is the prerequisite; the site table already shows 8 words/turn on these boards.- C-set. Where seeds were never captured — the historical
turn2-state.sav, which no process including the original can reproduce — our canonical pick is a member of the outcome set of the one seed-sensitive decision: the image of the client stream under the resolver, named from the candidate set and the draw's bound. Lane L4 has the XNC arm (k = 6, four observed, all inside) and one arm unaccounted for (BIO_GnMod, 2 of 6 observations). The claim is "a member of a set we can name", as before; the mechanism is a draw, so the set is the support of a random pick, not a set of equal-score candidates.Mechanism, measured (lane L1): the per-client generator at
StrategyClient+0x134is seeded fromNextUInton the global generator at0x00af6e58, which is live and per-process; the seed is not in any save. Only one of three empires' picks shows the effect because only one reaches the candidate walk (lane L4); the other two are stream-insensitive by code path. The earlier "pointer-ordered tie under ASLR" inference was wrong and is withdrawn.
Two things Rung B keeps regardless: --turn-commands replay is still the no-AI path and still the
right thing for RB to build; and the canonical pair stays turn2-state → turn3-state until the pin
probe promotes the creation turn.
6. The other two items in the report
BeginProcessTurn and the standalone. Yes, the replay interval starts at
BeginProcessTurn, not ProcessTurn. What else assumed the drivers bound the turn: sots_turn's
"walk all three drivers' phase order" (backlog §1 app row) has no slot for it — add a fourth
interval with RegisterHives (the five constants L1 read live, the two predicates, NextInt(10)
with its rejection loop) and the TickHives slip. Lane Z's calibration numbers for the
standalone generator (16 vs 20 / 18) were taken on turns where the interval costs 0 and are
unaffected. The pre-turn autosave precedes BeginProcessTurn, so any standalone that starts from a
pre-turn save must create the hives itself; only the creation turn shows it, and that turn is the
pair the pin probe restores.
g_GlobalRNG's combat consumer. RunCombatRound+0x60f draws on the same per-process
generator — most plausibly the seed for Mars::CombatSim+0x108. Lane Z's one instrumented battle
(turn 55, auto-resolved) cost the strategic generator 0 words, in one process. No battle turn
has ever been compared across two processes; lane H's z2-endturn seven-process agreement was on a
turn with no encounter. So combat turns are not yet known to be cross-process reproducible, and
this is the single most likely place the seed effect reappears. It does not touch Rung A (no battle
on the canonical pair). Backlog §5's "a battle with res->+4 clear" workload should add: run it
twice in two processes, and if it differs, pin the global (RNG_Seed(0xaf6e58, K) from the shim
after Initialize) and rerun.
hooks=off and rule 19 — §7.
7. Draft rule 25 for method-rules.md
25. A control that disagrees with itself is not a control
Lane L5 ran
hooks=offonce onturn1-state → turn2, got a third value, and read it as "the instrument is not the cause". Lane L1 ranhooks=offagain and got a fourth; lane L4 a fifth. All three were right about the instrument — by luck. None of those runs was a control, because the un-instrumented game does not reproduce itself on that workload: the cause was a per-process seed on the AI clients, present with no hooks and invisible to all of them.Two consequences. (a) Rule 19 says
hooks=offin two fresh processes. This is why: cite the agreement, not the run. If the two disagree, report "the workload is non-deterministic un-instrumented" as its own result, and take the control on a workload that does agree with itself (ref-turn2,z2-endturn) or pin the per-process input first. A control workload has to be chosen, and its self-consistency stated next to the number.(b) "One of N moved" is not evidence about the mechanism. A seed moves every stream; it is visible only where a decision depends on a draw. The coordinator inferred "tie, not seed" from two stable empires and one moving one; the two stable empires were on a code path that never reaches the draw. Count the decisions that depend on draws, not the objects that changed — and when the inference has been written into the roadmap, write the probe that would falsify it on the same line.
Ranked by damage: the wrong inference reached backlog §2, determinism-oracle.md, two board rows
and two lane briefs for about a day, and was caught by a written prediction before any engine work
followed it. Below rule 24, above nothing yet — append as 25.
8. Course — lane by lane
L4 (VM145, live). Redirect mid-flight. Send:
L1's seed probe came back: every AI client's generator is seeded with a fresh per-process word (three different seeds per process, none shared across processes), so the varying pick is a seed effect, and the tie/arrival-order framing in your brief and in §3.2's interpretation is withdrawn — your own P10 falsification already pointed there. Your measured facts stand: three producers, only 512 reaches the walk, one candidate
{2,12}that is a category, fallback not taken, k = 6 for the XNC arm. What changes is what the second capture is for. Do not chase order. Add a region over the client generator atclient+0x134(left/next_index— lane Z'sRngLedgershape) at entry and exit ofTryResearchCandidate 0x006c8580, and if theaicanddump can carry it, the bound of anyNextIntit makes. Then run the creation turn twice with the seeds pinned — L1'sl1seedSeed detour, return address0x0078304b, three constants — first both processes on the same seeds (prediction: sameaicand, same word cost, same pick, byte-identical autosave), then once on different seeds (prediction: same{2,12}, same cost, different pick). Then sweep: quit-to-menu and reload advances the pin table, so ten to twenty seeds cost ten to twenty loads, no relaunch; log which producer returned non-null for 512 on each — theBIO_GnModarm is almost certainly producer A or B gated by a draw, and its first appearance under a known seed names it. That turns "the resolution varies" into "the resolution draws n words with bound b, and k = b+1 for this arm; the other arm is producer X behind gate G". Carry k = 6 as to prove until the sweep has producedBIO_GnModunder a known seed. Your C-set recommendation survives as the fallback for references whose seeds were never logged; the exact form —game/aigiven the seed — is now the primary Rung C, and the per-client draw ledger you would be starting here is its prerequisite.
CB (VM146). Not a redirect; one addition to the capture format and one to the run list. The
capture must carry the three AI seeds (RunAI's rng_seed per net id) next to the block set
— they are three words and they are the difference between a log file and a reproducible pair. Add
C5 = C3 with pinned seeds, twice; if the pin probe holds, C5's block + autosave + seeds is the
first fully reproducible creation-turn record, and RB byte-matching it is the real test of
"deterministic given the stream". P5 as written (C3/C4 differ in exactly one word) stands and is
still worth running unpinned once.
RB (engine). No redirect. Add a --ai-seed <netid>=<hex>,… input alongside
--turn-commands, unused until game/ai exists, so the capture format and the CLI agree from the
start; nothing else changes.
L2 (VM141, multiplayer combat). Untouched. One prediction to note for later, not now: the
combat-sim seed must travel host → peer in the SNM stream, because RunCombatRound draws it from
a per-machine generator. If L2's capture already has the message, it names the consumer for free.
L3 (VM144, trade/spy tail draws). Untouched — its subject is the strategic generator, which this finding leaves exactly where lane Z put it.
VM140 is free. The pin probe goes there, on the l1seed build, before L4's second capture
depends on it. Whoever runs it owns rule 25's first application: two fresh processes, agreement
cited.
Docs to correct in place (rule 11): determinism-oracle.md last bullet;
ai-turn-logic.md §5.1 (annotate, keep) and P1 (mark falsified, credit the falsifier row);
ai-order-capture.md §3.1 last two paragraphs and §3.2's "reaches first" interpretation;
lane-ai1.json::g_GlobalRNG prototype (drop "never seeded / every draw returns 0", keep the
consumer list, set status mapped); turn1-to-turn2-nondeterminism.md "what was not
established" (answered).
Proposed campaign/board.md rows
New row:
| THE AI SEED IS PER-PROCESS — tie hypothesis withdrawn, Rung C restated as (save, seed) | meta | verified | high | 100% | 2026-09-08 | Resolver, on lane L1's falsified P7 (`findings/resolutions/2026-09-08-ai-seed-per-process.md`). Each AI StrategyClient's generator is seeded at construction from NextUInt on the global generator at 0x00af6e58, which is LIVE and per-process (three unrelated words per process, none shared across processes; the seed=0 record reproduces byte-identically as the built-in control). OVERTURNED: AI1's "every client seeded with 0" (row 240 second half — its own falsifier row fired; lane H's rejection-loop counter-indication was right) and the coordinator's "one-of-three varies => pointer-ordered tie under ASLR" (rows 331(5)/333, backlog §2) — all three streams move; only 512 reaches the candidate walk (L4). SURVIVES: per-client RNG, zero AI draws on the strategic generator, the ref-turn2 oracle, L5's instrument exoneration (on the identical-hooks pair, NOT on hooks=off), L4's measured facts and k=6 XNC arm. **RUNG C RESTATED: game/ai as a deterministic function of (save, per-client seed)** — C-exact (seeds captured => block and autosave byte-match on EVERY run; the user's "make it deterministic and the old approach pays", literally) and C-set (seeds lost => canonical pick is a member of the nameable outcome set of a random draw, not a tie). ONE PROBE, VM140, minutes: pin the three client seeds in the l1seed Seed detour (ret addr 0x0078304b), turn1-state twice in fresh processes; predict byte-identical, positions 192/211 unchanged; if the four leaves still differ, extend the pin to Seed record #2. Holding => turn1->turn2 with seeds is an oracle again and the ONLY corpus pair exercising BeginProcessTurn's hive draw. OPEN: the global generator has four other consumers incl. RunCombatRound+0x60f — no battle turn has ever been compared across two processes. hooks=off on turn1-state gave THREE different files across L5/L1/L4 and was never a control: rule 25 drafted (a control must agree with itself in two fresh processes; "one of N moved" says nothing about mechanism). L4 corrected mid-flight (draw ledger on client+0x134 at 0x006c8580, pinned-seed runs); CB adds seeds to the capture and a pinned C5; RB adds --ai-seed |
Edits to existing rows (the integrator's SUPERSEDED convention):
- Row 240 — prefix the notes with:
PARTLY OVERTURNED by lane L1 (resolver 2026-09-08): the per-client RNG and "zero AI draws on the strategic generator" STAND; "never seeded / seeded with 0" is FALSE — the global at 0x00af6e58 is live and per-process. Status back to mapped for the seed half. - Row 255 — prefix:
SCOPED: true of ref-turn2 -> turn3 only (no draw-dependent decision that turn); superseded by rows 331 and the resolver row. - Row 331 — append:
(5) WITHDRAWN 2026-09-08 — it is a seed, not a tie; see the resolver row. - Row 333 — prefix:
RESTATED by the resolver 2026-09-08: the mechanism is a per-process seed, not a tie; the tie-set claim survives only as C-set (fallback for uncaptured seeds); primary Rung C is C-exact given the seed. - Row 335 — append:
CB: capture format carries the three AI seeds; add C5 = C3 with pinned seeds, twice. RB: add --ai-seed. - Row 62 — VM140 holder: the pin probe, before L4's second capture.