Spine phase 4, byte for byte at 0x007dc871-0x007dc8c7. Three separate stores to the same word -- clear, OR the self bit, then conditionally OR the alliance mask -- which is what makes the alliance term an OR and is invisible in any summary of the phase. The bit is the player's POSITION IN THE VECTOR. Checked against the almem bytes the game archived: 72 of 80 player-records agree, and the other 8 are predicted by the same model (FinalizeTurnRecords also runs on load, and the load path does not run the spine, so every save's earliest archived turn carries a zero mask -- true on all 11). Reported as loudly: the corpus cannot separate `1 << vectorIndex` from `1 << PlyrIdx` (0 of 80 records differ), cannot separate the OR from a plain assignment (every observed alliance mask already contains its member's own bit), and cannot separate the ALid guard from an AL != 0 guard. Those three are instruction-stream readings only. ModCount, named and enumerated. StrategyServer::Write tags both words itself, so S+0x8 is the wire's ModCount and S+0xc is Frame -- addresses.json has the name on the wrong word, and turn-driver.md's "they stay in lockstep" is corrected in place. RTTI gives the reason for the two bases: Game::StrategySim is a base sub-object at offset 4. 29 writer sites: 20 command handlers plus 6 inlined in the command-batch applier (each an unconditional bump on ENTRY, before validation), the two turn drivers, and the abandon/chaos check -- which is gated on Abdn, false on all 28 systems of all 11 saves. So the per-turn delta is 2 + one per command applied out of every player's TurnCommands block; the 0x1b4 container stride independently confirms lane Q's block layout. It is not derivable from the pre-turn save. The residual is a watchpoint, specified with its own written prediction (exactly 12 hits on turn1-state) and four falsifiers. Direct-call reachability is stated as the lower bound it is, per lane V2's indirect-edge measurement. Engine side: sots-engine wip/alliance 5e409cf. |
||
|---|---|---|
| .. | ||
| lane-a.json | ||
| lane-a2.json | ||
| lane-b5.json | ||
| lane-i.json | ||
| lane-j.json | ||
| lane-k.json | ||
| lane-n.json | ||
| lane-o.json | ||
| lane-p2.json | ||
| lane-q.json | ||
| lane-t.json | ||
| lane-u.json | ||
| lane-v2.json | ||
| lane-w.json | ||
| lane-z.json | ||
| README.md | ||
Per-lane address fragments
addresses.json is a single shared file. When several lanes run concurrently they edit the
same lines, and three times on 2026-09-08 one lane's git add swept another's in-flight
entries into the wrong commit. Nothing was lost, but authorship and atomicity were.
A lane may instead drop its own file here:
{ "entries": [ { "name": "...", "addr": "0x...", "convention": "...",
"prototype": "...", "status": "verified", "source": "findings/..." } ] }
Name it after the lane (lane-d.json). tools/gen_addresses.py merges every fragment in
sorted order after addresses.json. A duplicate name across files is a hard error, not
last-wins — two lanes disagreeing about an address is exactly the thing we must not paper over.
The integrator folds fragments back into addresses.json once the lane's work is merged.