Two results from the same VM session, both of which needed a workload the
corpus cannot supply.
CDiff, played forward. ref-turn2 loaded and 49 End Turns driven through the UI
helper to frame 51, with the entry hook emitting one record per turn:
50 calls, frames 2..51, EXACTLY TWO STORES
frame 2 -1 -> 0
frame 50 0 -> 1 <- the modelled tier transition, at the modelled frame
and 47 turns between them on which the writer ran and wrote NOTHING
predict_path was computed at entry from the transcribed threshold table on all
fifty turns and agreed with the cdiff region on every one. Frame >= 100 is still
a code read and is not claimed as a measurement.
The AI seed probe (asked for by the coordinator, ranked above CDiff). Hooks on
Mars::RNG::Seed and StrategyApp::RunAI, two launches from turn1-state, load
only. Every AI client seed differs between processes -- net 32, 496 and 512 all
move -- while the record structure is identical and one Seed call with seed=0
produces a byte-identical state in both runs.
So the turn1-state -> turn2 nondeterminism is a SEED effect, not the ordering
effect predicted, and the 'one of three varies' observation is explained by two
of the three empires having a research pick that is robust to the stream. This
falsifies lane AI1's 'every draw from the static generator returns 0', which
that lane had explicitly flagged as arithmetic rather than measurement.
Where the seed comes from is NOT established; the finding names the one hook
that would settle it and the six values it must reproduce.
VM140 left as found: the 8-file save set with its oracle bytes intact.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARBgSooAfokKUy6wKUKEyZ
7 KiB
The AI client seed is fresh in every process — the turn1-state → turn2 variation is a seed effect
- Type: subsystem (live measurement)
- Status: verified — the same save loaded in two freshly launched processes, with the seeding primitive itself hooked
- Confidence: high on the fact (every AI seed differs, structure otherwise identical); the source of the seed is not identified — §4
- Owner / date: lane L1 · 2026-09-08 · VM140
- Instrument:
sots-enginewip/l1,Mars::RNG::Seed+Game::StrategyApp::RunAIhooks, configshim.cfg.l1seed, buildl1seed-20260908T2206Z - Answers: the probe requested against
findings/subsystems/turn1-to-turn2-nondeterminism.md(lane L5) — "is the varying research pick a seed effect or an ordering effect?" - Corrects:
ghidra/addresses.d/lane-ai1.json—g_UnseededStaticRNG's "every draw returns 0"
0. The answer, and it is not the one that was predicted
Seed effect. Every AI client's generator is seeded with a different value in every process.
Two launches, same input (l1-turn1.sav = verify/results/saves/turn1-state.sav), loaded only — no
End Turn, because StrategyApp::RunAI and the client constructor run on load:
RunAI call |
player net id | process 1 rng_seed |
process 2 rng_seed |
|---|---|---|---|
| 1 | 32 | 0x75F692C0 |
0x414F415E |
| 2 | 496 | 0xF2EDAC21 |
0x10B94E78 |
| 3 | 512 (the empire lane L5 saw vary) | 0x165A2ADB |
0xC218DBF8 |
Each value reappears one record earlier as the seed argument of an Mars::RNG::Seed call on a
fresh object, which is the StrategyClient+0x134 generator being constructed. Both my prediction
and the coordinator's expectation were that these would be identical. They are not.
The structure is identical, which is what makes the difference legible. Both processes emit
exactly 8 records in exactly the same order — four Seed calls interleaved with three RunAI calls,
same net ids, same personality = 0, same ai_data = "SavedGames/l1-turn1.sav". Nothing about
which objects are seeded or when varies. Only the values do.
And one of the four seeds is deterministic, which rules out "the hook perturbs it" and gives the
comparison a built-in control: the first Seed call takes seed = 0x00000000 in both processes and
produces a byte-identical state (mt0 = 2443250962, mt1 = 1093594115, mt2 = 1878467924,
left = 624). A run in which the instrument randomised things would not have left that one alone.
1. Why only one of three empires' picks moves
Lane L5's puzzle was "a time-seeded per-client RNG would move all three". That inference does not hold, and the data says why: all three seeds do move. A different seed only becomes visible in the save where the decision it feeds actually depends on the draw. Two of the three AI players evidently have a unique best research candidate — a different random stream picks the same one — and the third does not.
So the observation "one of three varies" is not evidence for an ordering effect at all. It is evidence that two of the three decisions are robust to the stream and one is not, which is a statement about the candidate sets and not about the generator.
2. What this settles for the campaign
- The
turn1-state → turn2pair is unfixable as a byte oracle by any amount of care with the instrument. The input to the varying decision is not in the save, is not the strategic generator, and is fresh per process.determinism-oracle.mdis now scoped toref-turn2 → turn 3. - A standalone can never match a per-process seed. For anything downstream of an AI client's generator, the reachable target is a legal outcome, not the recorded one. Which leaves is exactly what lane PL put on Rung B for these shadow empires — the corroboration is now mechanical rather than statistical.
- The strategic generator is untouched by this. Lane L1's hive run reproduced the historical word
positions and
NextQvalues exactly (hive-creation-rng.md§2.2). Two generators, two different determinism stories, and only one of them is in the save.
3. Correction to lane-ai1.json
g_UnseededStaticRNG's entry reads:
its only static initialiser writes the
Mars::IStreamablevftable, NOT theMars::RNGvftable thatRNG_Seedinstalls: none of the sixRNG_Seedcall sites in the image targets it, so itsmt[624]is the zero-initialised BSS array andleftis 0. An all-zero MT19937 state is a fixed point of the twist, so EVERY draw from it returns 0. Five consumers: SNMRunAI (the AI client seed, OnMessage+0x955) …
The arithmetic is right and the conclusion is falsified by measurement: the seed SNMRunAI hands
to RunAI is neither 0 nor constant. That entry was explicitly flagged by its own author as "the
all-zero fixed point is arithmetic, not measurement", with a prediction attached — this is that
prediction coming back negative, which is the best possible outcome for having written it down.
Either the object OnMessage+0x955 draws from is not the one AI1 identified, or that object is
seeded by a path outside the six RNG_Seed call sites. §4 says how to tell.
4. What is NOT established, and the cheapest next step
- Where the seed comes from. This lane hooked
RNG_SeedandRunAI; neither says what fedRunAI's fourth argument. The candidates are a time source (timeGetTime/GetTickCount, the usual suspects for a per-process value) and a generator seeded from one. The next probe is one hook:StrategyNetworkClient::OnMessagearound+0x955, or simply a breakpoint-style read of whatever object that site draws from, in the same two-process shape. The seeds recorded here (0x75F692C0/0x414F415E…) are the values it must reproduce. - Whether the seed varies within one process. Both runs seed three clients with three different values, so something advances between them — consistent with successive draws from one generator and equally with three reads of a clock.
- Whether any of this reaches the strategic generator. It does not on the measured turns
(
hive-creation-rng.md§6.2), but that is two turns of one save. - The other four
Seedcalls' owners. Only the three that immediately precede aRunAIare identified. Theseed = 0one is unidentified and is the only deterministic one, which makes it the most interesting of the rest. is_strategic_generatorreadsfalseon every record in these runs — correctly, but only because this config installs no turn-driver hook, so the shim never learned the server pointer. It is not evidence that the strategic generator is never seeded here.
5. Files
verify/results/shim/l1/trace-seed-proc1.jsonl,trace-seed-proc2.jsonl— the two tracesverify/results/shim/l1/seed-two-processes.txt— both rendered side by sideverify/results/shim/l1/seedrep.py— the reader- Prediction, committed before the build:
sots-engine/docs/L1-predictions.md§P7