One row per statically reachable draw site in BeginProcessTurn u ProcessTurn u
OnAllCombatDone_Tail, each carrying a measured firing or a decoded predicate on
save fields plus the 22-save corpus count. No total is reported: 31 rows (30
distinct sites plus one grouped SVSO row) are unread gates, and that visible
blank column is the deliverable.
Three gates decoded:
0x0082cdb8 = the addiction spread roll (Chance ADDICTION_SPREAD_ODDS 0.2f);
corpus 0 of 22 -- nadct is 0 on all 28 systems of all 22 saves. Writer found:
ServerSystem_BeginAddiction has exactly two callers, both inside vslot15, so
an INTER-EMPIRE route plus a species addict tech must seed it first.
0x0088dc43 = the spy counter-mission roll, p = cmo accumulating 0.2f per turn,
so Chance costs 0 words from the fifth turn on; corpus 0 of 22 by deat/cm.
0x0088b613 = the raid target pick. RNG_NextInt has no zero-bound early-out, so
a single-candidate list still draws -- which proves lane AC's B returned
before the draw. Failed conjunct named: the raider sat on sector 832 while
all four rt records source in 816/816/768/752. Next workload: sector 816.
Six draw sites no firing-indexed table could hold: five behind
CreateRaidEncounter's empty-candidate-list gate, and 0x00820c1b again from
Slot13RngCalleeB -- so B costs 2 words on a success, not 0.
SVSO generator closed: all 17 drawing overrides use GetGame()->+0x16c.
Rule 17 again: ServerSpyManager_vslot14 really ends at 0x0088dd2b, not 0x0088dd03.
48 KiB
The gate-indexed RNG audit — one row per statically reachable draw site, and every blank cell visible
- Type: control-flow (static; no measurement taken, no VM used)
- Owner / date: lane AG · 2026-09-09 · host +
dumps/sots.exeonly - Instrument:
objdump -b binary -m i386 -M inteloverdumps/sots.exe, decoded from real instruction boundaries to the next function start, never to Ghidra's reported size (rule 17); call graph rebuilt from scratch (E8 rel32+E9 rel32landing on a function start + lane V2's pinned virtual edges); corpus counted withverify/save-reader/save_reader.py --dumpover the 22 saves inverify/results/saves/ - Created by:
findings/resolutions/2026-09-09-tail-draws.md§8 item 3 — "the gate-indexed audit: one table, every reachable draw site, firing-or-predicate, corpus count" - Corrects (rule 11): lane V2 §4.3 caution (a) — closed; lane AC §8's "three
rtroute records" — it is four inac-turn22-raider.sav; lane Z §11.1 / lane H §4 onCreateRaidEncounter's cost — a success costs far more than "0 or 1 further word" - Ghidra is single-user and this lane did not enter it. Everything here is read from the image
on the host with the campaign's own tools; nothing was written back. The ReVa MCP server was down
(
CONNECTION_CLOSED) for the whole lane and the HTTP fallback was not needed.
0. What this table is, and what the old one was
Lane Z's ledger is indexed by firing: a site enters it when a state we happened to run reaches it. Such a table grows only by luck, and it has now been qualified twice in two days (L1's hive draw, AC's raid roll). This table is indexed by gate: one row per site the code can reach, whether or not any state we own reaches it. Each row carries either a measured firing or a decoded predicate on save fields with the corpus counted against it. A row with neither is an unread gate and is listed as one in §6.
No total is reported. Thirty-one rows are unread gates (thirty distinct draw sites — 0x0081351c is
reachable from both drivers and appears twice — plus one grouped row covering sixteen SVSO overrides
whose sites were not individually enumerated). A "complete" number over this table
would repeat exactly the mistake the lane exists to fix.
Three results the firing-indexed table could not have produced:
- Six draw sites nobody had listed, all behind
ServerTradeManager::CreateRaidEncounter's empty-candidate-list gate (§5.1). Lane H measured that function entered 3 times drawing 0 words; the ledger therefore recorded one site (0x008939ee) and could not see the five past it. - A seventh unlisted site inside
Slot13RngCalleeB's own subtree —0x00820c1b, aNextFloatinFUN_00820af0(§5.2). Lane AD's committed prediction should sayBcosts 2 words on success, not 1. - The SVSO generator is named (§4): all seventeen
SVScriptObjectoverrides that can draw useGetGame()->+0x16c— the strategic generator, the one the save serialises. V2 §4.3's standing blank is closed.
1. Method, stated so it can be repeated and attacked
tools: scratch scripts over dumps/sots.exe + dumps/functions.json (41,089 starts)
edges: every E8 rel32; every E9 rel32 whose target is a function start (lane V2 §5's rule);
plus pinned virtual edges: OnAllCombatDone_Tail -> the eleven phase-23/33 vtable targets
(V2 §3), DetectEncounters -> GenerateTradeRaidEncounters (slot 10, lane Z),
GenerateTradeRaidEncounters | 0x007d5150 -> CreateRaidEncounter (slot 17, lane H)
roots: StrategyServer::ProcessTurn 0x007dc6c0 (closure 1,541)
StrategyServer::OnAllCombatDone_Tail 0x007d92a0 (closure 1,710)
sites: every call whose target is one of the SEVEN RNG entry points (lane I §4)
inlined: lane I's image-wide tempering-immediate scan, recall proven complete over all 67
immediate occurrences -- reused, not repeated
Rule 16 is satisfied by reuse, not by a new sweep, and that is deliberate. Lane I's scan is
image-wide with proven complete recall (all 67 occurrences of both tempering immediates fall
inside decoded instructions; a brute byte scan finds nothing extra). Re-running it could only
reproduce it. What lane I's result says here is: exactly two inlined draw sites lie in the two
turn drivers' closures — 0x007aa3b6 in EncounterDetect_AssignContacts (ProcessTurn, depth 4)
and one in 0x007a7f30 (Tail, depth 3) — and the other twenty-six sites in nine functions belong
to map setup, the lobby screen, the strategy network client/server and two combat encounters,
reachable from neither driver (V2 §4.2 resolved all nine ancestries).
One correction to how the brief framed rule 16. The spy detection roll at 0x00887c8a is not
an inlined MT draw. 0x008e6dd0 is Mars::RNG::Chance, a real entry point, and 0x00887c8a is
a plain E8 call to it that any call-graph sweep sees. What is true — and is AC's actual point — is
that the call sits in ServerSpyManager::vslot13's own body rather than inside P
(0x008408e0), so an entry probe on P cannot see it. That is an instrument-choice trap, not a
recall trap, and the fix is lane Z's return-address ledger. Both traps are real; they are different.
Two site kinds excluded from every row, for the reason lane Z §11.2 gives. 0x008e6e04 (the
NextFloat inside Chance) and 0x0047d8a6 (the NextFloat inside FloatRange) are
helper-internal: their words are already charged to the caller's row. Counting them is the
double-count that made the first version of lane Z's site instrument report 44 words against a
bracket of 18.
What the sweep cannot see, said plainly. Indirect edges other than the pinned ones; call [reg]
through function-pointer members; and any draw in a subtree entered only through an unpinned vtable
slot. Every reachability claim below is therefore a lower bound, exactly as lane I's and V2's
were.
2. The table
Status key: M = measured firing with the configuration it was measured at · P = decoded
predicate on save fields with the corpus counted · U = unread gate, the audit's own to-do list.
"words" is per call unless stated. NF/NI/Ch/NU/FR = NextFloat / NextInt / Chance /
NextUInt / FloatRange.
2.1 StrategyServer::BeginProcessTurn — outside both turn drivers
| site | container | entry | st | firing, or predicate + corpus count |
|---|---|---|---|---|
0x0052770f |
SVSOSwarmQueen::RegisterHives |
NI | M | 2 words on turn1-state.sav turn 1→2 (two hives created), 0 on turn 2→3; lane L1, VM140, shim.cfg.l1hive, oracle reproduced. One word per new hive, plus an unmeasured rejection loop (bound 10 ⇒ mask 15 ⇒ E[1.45] words/hive) |
2.2 StrategyServer::ProcessTurn
| site | container | entry | st | firing, or predicate + corpus count |
|---|---|---|---|---|
0x00893426 |
GenerateTradeRaidEncounters |
Ch | M | 8 calls / 8 words per turn on ref-turn2 (8 players); 7/7 on zuul-turn23-fleet23 (7 players) — lane Z §11, lane H. One per entry of StrategyServer::Players, TRADE_RAID_ODDS_PLAYER 0.2f, no back edge |
0x00893513 |
same | Ch | M | same, TRADE_RAID_ODDS_NPC 0.05f, gated on 0.0f < S->+0x1a0 (player-independent) |
0x008935ce |
same | Ch | U | 0 of 8 turns. Gated on a subsystem manager being present (TRADE_RAID_ODDS_REFUGEE). Gate not decoded to a save field |
0x0050329d |
FUN_00503200 ← DetectEncounters→0x007d5150 |
NF | M | 1 word/turn on both corpus saves — but lane H: one word per player passing a per-player gate, and exactly one player passes on both. The gate itself is unread; "1/turn" is a coincidence of two saves (rule 20) |
0x007929a4 |
FUN_00792750, same subtree |
NI | M | same, same caveat |
0x0088df4f |
ServerPlayer::RollResearchEvent |
NF | M | 0–1 words/turn, unconditional per player that reaches it (lane T); the 18↔20 spread lane Z reported is this site and 0x00587888 |
0x00587888 |
TechTree::ProcessResearch |
NF | M | 0–1 words/turn |
0x005878bb |
same | NF | U | never in the ledger. Second research draw; branch condition not decoded |
0x007aa3b6 |
EncounterDetect_AssignContacts (inlined) |
NF | M | 0 words, 5 End Turns, 2 saves — and lane H found why: EncounterDetect_ProcessTeamRecord is called zero times, so the record vector EncounterDetect_Run receives is empty. The +0xfc gate is never evaluated. Cost is `Σ_c min(T_c, |
0x0088a08f |
ServerPlayer::RollResearchAccident |
NF | U | absent from the ledger on 3 turns. Top-of-function gates not decoded |
0x00889e40 |
same | NI | U | " |
0x00889f86 |
same | NI | U | " |
0x00889fd6 |
same | NI | U | " |
0x0088a1bd |
same → FloatRange |
FR | U | " (1 word; the two-narrowing float model is pinned in sots-engine) |
0x0088a20f |
same → FloatRange |
FR | U | " |
0x00889c33 |
FUN_00889bb0 ← RollResearchAccident |
NI | U | " |
0x00758966 |
ServerSystem::ProcessRebellion |
Ch | U | absent from the ledger. Per system; gate not decoded |
0x0074fe21 |
FUN_0074fbe0 ← rebellion |
Ch | U | " |
0x00753d87 |
FUN_00753c60 ← rebellion |
Ch | U | " |
0x007564b4 |
FUN_00756350 ← rebellion |
Ch | U | " |
0x00747fa8 |
FUN_00747f50 ← MoveFleet |
NI | U | " |
0x00747ffd |
same | NI | U | " |
0x007b677c |
ProbabilisticJump |
NF | U | unconditional per call, and the call is per fleet whose current waypoint is type 5. No corpus save is known to contain a node jump; the waypoint-type predicate was not decoded here |
0x007b67e7 |
same | NU | U | the second word, on the non-arrival branch: [dest->+0x58->+0x158] < float(unit × [+0x154]). Neither field is on the wire as far as this lane checked |
0x0081351c |
FUN_008134e0 (also in Tail) |
Ch | U | absent from the ledger on every measured turn; gate not decoded |
0x008939ee |
CreateRaidEncounter |
NI | M | entered 2× on Zuul turn 24, 1× on turn 25, 0 words every time (lane H). The function returns at 0x008938fa when the candidate vector FUN_0083b110 builds is empty — that gate is before this draw. Rule 20's case study |
0x0088f1eb |
TradeManager_SpawnEncounterSquadron ← 0x00892640 ← CreateRaidEncounter |
NI | U | NEW (§5.1). Past the empty-list gate; never reached in any measurement |
0x0088f356 |
same | NI | U | NEW. " |
0x0083b460 |
FUN_0083b1e0 ← SpawnEncounterSquadron |
NI | U | NEW. " |
0x00848fd9 |
FUN_00848e50 ← 0x00892640 |
NI | U | NEW. " |
0x00820c1b |
FUN_00820af0 ← 0x0082ce00 ← 0x00892640 |
NF | U | NEW. " — and the same site is reached from the Tail through Slot13RngCalleeB (§5.2) |
2.3 StrategyServer::OnAllCombatDone_Tail
| site | container | entry | st | firing, or predicate + corpus count |
|---|---|---|---|---|
0x00820e18 |
TradeManager::Slot13RngCalleeA ← trade vslot13 |
NF | M | 1 word/turn on ac-turn22-raider lineage, 0 on lane L3's turn-15 control; lane AC, VM141, probes=11 (not byte-neutral — the number is entry-attributed and stands, the turn totals do not). Predicate: a fleet parked on a TradeSector node with Pos bit-equal, owner's bit set in tscr, tsct/species-4 ⇒ FtFlg & 0x800. Corpus: 1 of 22 (ac-turn22-raider; ac-turn20-raider has the fleet arriving) |
0x0088b613 |
TradeManager::Slot13RngCalleeB |
NI | P | §3.3. Predicate: A succeeded and ∃ a route whose trfr endpoint resolves to the raider's own sector and whose tro owner stands at relation ≤ 0 to the raider. Corpus: 0 of 22 — and the failed conjunct is named: AC's raider sat on sector TradeID 832, whose member systems are indices 13/23/0/3; the four routes' trfr are 18/16/14/20, in sectors 816/816/768/752. B ran and returned before the draw |
0x00820c1b |
FUN_00820af0 ← Slot13RngCalleeB |
NF | P | NEW (§5.2). Same predicate as 0x0088b613, then NOT (owner species == 1 OR arg-3 species test). Corpus: 0 of 22. Its existence changes B's cost model from 1 word to 2 |
0x007ae0a5 |
StrategyServer::NodeLineDecay (phase 11) |
Ch | M | 1 word on Zuul turn 64 (lane Z §9) — one per node line whose lifetime expired, p = 0.5f, neither Chance early-out |
0x007bb69b |
FUN_007bb530 ← ApplyEncounterResult (phase 6) |
NI | U | node-cannon path (EVENT_NODECANNON_FLINGS/_KILLS). Combat-dependent; combat measured 0 strategic words on the one battle ever instrumented (lane Z §10), which is a statement about that battle |
0x00852ec7 |
FUN_00852d30 ← 0x007a0540 ← 0x007a7f30 (phase 6) |
NI | U | salvage / back-engineering special project (EVENT_SPRJBACKENG_UNLOCKED). Same caveat |
| (inlined) | 0x007a7f30 CombatResolve_SalvageBackEng (phase 6) |
NF | U | lane I's site #8, the second confirmed rule-16 instance; combat-dependent |
0x0082cdb8 |
ServerTradeManagerImpl::vslot15 |
Ch | P | §3.1 — this is the addiction-spread roll. Predicate: ∃ an rt route with trfrs == 0, trtos == 0, whose trfr system has nadct > 0. Corpus: 0 of 22 — nadct is 0 on all 28 systems of all 22 saves (616 system records). Words = Σ over such routes of that system's non-zero addiction slots |
0x0088dc43 |
ServerSpyManager::vslot14 |
Ch | P | §3.2 — the spy counter-mission roll. Predicate: ∃ a spy with deat != 0, target system owned, target not independent (hindi false), and cm ∈ {1,2,3,4}. Corpus: 0 of 22 — the only three saves with a spy all read deat 0, cm 0, cmo 0. p = cmo and Chance costs 0 words once cmo ≥ 1 |
0x00887c8a |
ServerSpyManager::vslot13 (call in the caller's body) |
Ch | U | AC decoded the chain around it: sdet == -1 arm, gated on FUN_00743f80(system) & 1. Reached only after deat != 0. Corpus: 0 of 22 by deat; the 0x00743f80 conjunct itself is not decoded to a save field |
0x008409c7 |
SpyManager::Slot13RngCallee (P) |
NI | U | past deat != 0, sdet != -1, turn − sdet ≥ 3, system owned, owner ≠ spy's owner. Corpus: 0 of 22. The inner gate on the NextInt is unread |
0x00840929 |
same | Ch | U | " (the false-flag draw, 0.25f/0.75f) |
0x00840a3c |
same | Ch | U | " |
0x0081351c |
FUN_008134e0 (also in ProcessTurn) |
Ch | U | as above |
2.4 SVSO script-object overrides — reachable from both drivers when S+0x1b4 is non-null
Seventeen overrides can reach a draw (V2 §4.3: slot 4 × 11, slot 11 × 4, slot 25 × 2). All
seventeen draw on GetGame()->+0x16c — §4. The whole surface is guarded by
mov edi,[esi+0x1b4]; cmp edi,ebx; je, and lane T and lane K both recorded S+0x1b4 as null in
a normal game.
| row | st | firing, or predicate + corpus count |
|---|---|---|
SVSOSlaversRefuel::UpdateDifficultyTier 0x00515820, two +0x16c sites |
M | 0 words on 50 consecutive End Turns (lane L1 §5.1), entered every turn, two stores at frames 2 and 50 exactly as predicted. The draws sit behind the gate that never opened |
| the other sixteen | U | Predicate not decoded, corpus not counted. The one save-visible conjunct that is known — SvSctOb carrying a live script object of that class — is where the value-domain census reports 16 constants under Sim.SvSctOb.EncObj: no corpus game ever triggered a random encounter. So the corpus count is 0 by construction for any predicate resting on those fields, and the rows stay unread rather than negative |
2.5 Sites lane I found that are in neither driver
Nine functions, twenty-six inlined sites: 0x004b1f20 (CombatNetworkClient slot 3), 0x00507ac0
(CrowRuinsEncounter slot 11, 12 sites), 0x005232a0 (SwarmEncounter slot 11), 0x006ec720 /
0x006f65f0 / 0x006f7890 (StrategyNetworkServer slots 3/4/7), 0x0079f7d0 and 0x007c4140
(StrategyNetworkClient slots 3/6), 0x007c2fa0 (StrategyLobbyScreen slot 3, 4 sites). Reachable
from no turn driver over direct + tail-jump edges; V2 §4.2 named every ancestry. Row status:
outside the interval, not unread — the interval this audit covers is
BeginProcessTurn → post-turn autosave, and lane Z measured the generator as motionless outside it.
3. The three unread gates, decoded
3.1 0x0082cdb8 — it is the addiction spread roll, and its seed path draws nothing
ServerTradeManagerImpl::vslot15 0x0082cca0 — phase 23 call 8, dispatched at 0x007d9804.
Ghidra's size 334 is right here; the body ends at 0x0082cdf0. Read in full:
routes = this->+0x3c .. +0x40 ; the SERIALISED route vector -- see below
for i in 0 .. count-1:
rt = routes[i] (bounds-checked, else 0)
if (BYTE[rt+0x25] == 0) continue ; G1
pFrom = players[rt->trfow] pTo = players[rt->trtow] ; via [this+4]+0x50 = S+0x54
if (rt->trtos == 0 && pTo != pFrom): ; --- BLOCK 1, NO DRAW ---
sysTo = systems[rt->trto] ; via GetGame()->+0x44
for k in 0..6:
if ((pFrom->[0x348 + 4k] >> 4) & 1) BeginAddiction(sysTo, k)
if (rt->trfrs != 0) continue ; G2
if (rt->trtos != 0) continue ; G3
sysFrom = systems[rt->trfr] sysTo = systems[rt->trto]
for k in 0..6: ; --- BLOCK 2 ---
if (sysFrom->addiction[k] != 0) ; G4
if (Chance(GetGame()->rng, *ADDICTION_SPREAD_ODDS)) ; <<< 0x0082cdb8, 1 WORD
BeginAddiction(sysTo, k)
How every tag was pinned, so this can be checked rather than believed.
this+0x3c/+0x40is the route vector andthis+0x1c/+0x20the sector vector, fromServerTradeManagerImpl::Write0x0082cb60, whosethisis the sub-object at +0x4c (vtable_map.py who 0x0082cb60→0x00a31b64 +0x4c slot 2): it writesNumTradeSectorsfrom[this−0x30] = obj+0x1c,SctSizefrom[this−0x44] = obj+0x08, andrtframes from[this−0x10] = obj+0x3c. The base ctor0x00841bf0zeroes four 0x10-byte vectors at+0x0c/+0x1c/+0x2c/+0x3cand loads+0x08fromTRADE_SECTOR_SIZE(image default 7.0).BYTE[rt+0x25] != 0is exactly the conditionWriteuses to decide whether to emit anrtrecord (0x0082cc3d, byte-for-byte the same test as0x0082ccd4). So G1 is not merely satisfiable — it is satisfied by everyrtin every save, by construction.Game::TradeRoutefield offsets are the campaign's own recovered layout (objects/layouts.md, grade verified):+0x04 tro,+0x08 trfow,+0x0c trfr,+0x10 trfrs,+0x14 trtow,+0x18 trto,+0x1c trtos,+0x20 trtc.FUN_00841cd0settles whattrfr/trfrsmean:if (rt->trfrs == 0) endpoint = GetGame()->systems[rt->trfr] else endpoint = registry_lookup(S+0x84, rt->trfrs).trfris a system index;trfrsis a non-system endpoint id, 0 meaning "use the index".system+0x1e4 .. +0x1ffis theint[7]addiction table serialised asnadct(count of non-zero entries) then sparse(ads = index, adt = value)pairs —findings/objects/struct-recovery.md.FUN_00745e40(system, k)returnssystem->addiction[k] != 0(its 0/1/2/3 phase computation fromADDICTION_PHASE2_START= 10 andADDICTION_PHASE3_START= 15 is discarded by thesetne althe caller reads).*ds:0x00af0c50 → 0x00af0c4cis the StrategyVarADDICTION_SPREAD_ODDS, image default 0.2f, resolved from itsGlobalConststatic-init stub (the M1 loader,loader-prototypes.md). 0.2 takes neitherChanceearly-out, so the site costs exactly one word per evaluation.FUN_0074ef70=ServerSystem::BeginAddiction(k): refuses if the system is unowned, if bit 5 ofowner->[0x348+4k]is set, or if the slot is already stamped; otherwise postsEVENTSUM_ADDICTION_OUTBREAK/EVENTMSG_ADDICTION_OUTBREAKand writessystem->addiction[k] = GetGame()->Frame. It draws nothing.
Predicate on save fields, and the corpus counted against it
Words = Σ over
rtrecords withtrfrs == 0 ∧ trtos == 0ofnadcton the system at indextrfr.
| save | rt records |
trfrs/trtos |
systems with nadct > 0 |
words |
|---|---|---|---|---|
human-turn11-spytechs |
1 | 0 / 0 | 0 of 28 | 0 |
human-turn15-spyprogram |
1 | 0 / 0 | 0 of 28 | 0 |
ac-turn20-raider |
3 | 0 / 0 | 0 of 28 | 0 |
ac-turn22-raider |
4 | 0 / 0 | 0 of 28 | 0 |
| the other 18 saves | 0 | — | 0 of 28 | 0 |
Corpus count: 0 of 22. The failed conjunct is G4 — not the route container, which four
saves have, and not trfrs/trtos, which are 0 on all nine route records. No system in the
corpus is addicted to anything.
And the writer is found (rule 28 practice 3), which is the interesting part.
ServerSystem::BeginAddiction has exactly two callers, both inside vslot15 itself — BLOCK 1
and BLOCK 2. So the only way any system becomes addicted is this one function, and BLOCK 2
cannot run until BLOCK 1 has seeded it. BLOCK 1 needs:
- a route with
trtos == 0whosetrfow != trtow— an inter-empire trade route; every route in the corpus hastrfow == trtow == 1, so BLOCK 1 has never run either; - the from owner holding bit 4 of
player->[0x348 + 4k].
player+0x348 is written by ServerPlayer::RebuildSpeciesTechFlags 0x0082bf10 (already named in
addresses.json): for each of the seven species k it reads eight tech ids from
SpeciesDef[k]+0x78 .. +0x94, tests each with TechTree_HasResearched, and sets or clears the
corresponding bit. InitTable 0x005453a0 fills SpeciesDef+0x88 — bit 4 — from
FUN_0053b620, the per-species addict tech table, whose out-of-range arm logs
"Species %s: No addict tech.":
| species k | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| addict tech id | 0x2799 | 0x279a | 0x279b | 0x279c | 0xC5 = none | 0xC5 = none | 0x279d |
(0xC5 = 197 is the image's "no tech" sentinel; these are the 10000-based TechID enum, not wire
techIds, so tools/techid_table.py does not name them.)
Recipe for whoever holds a guest next, named from the failed conjunct and not from the nearest
container: research a species' addict tech (bit 4), then establish a trade route between two
different empires (trfow != trtow) — BLOCK 1 then stamps the destination system's addiction slot
with no draw, and on the following turn any route out of that system rolls 0x0082cdb8 once per
addicted slot. Building more trade routes inside one empire, which is what the corpus has four of,
can never do it.
3.2 0x0088dc43 — the spy counter-mission roll, with an accumulating probability
ServerSpyManager::vslot14 0x0088db80 — phase 33 call 10, dispatched at 0x007d989b. Rule 17
bites again: Ghidra reports 387 bytes, ending at 0x0088dd03, which is inside the function. The
real body ends at 0x0088dd2b, followed by a five-entry jump table at 0x0088dd2c.
for each spy in this->+0x10 .. +0x14:
if (spy->deat == 0) continue ; G1
sysT = registry_lookup(S+0x84, spy->deat) ; the TARGET system
sysO = registry_lookup(S+0x84, spy->sown)
if (ServerSystem_GetOwner(sysT) == NULL) continue ; G2
if (sysT->+0x1c8 != 0) continue ; hindi -- independent ; G3
if (spy->spyon != 0 && spy->spyon != ownerId(sysT)):
<reset>; spy->sdo = 0.0f; spy->sdet = -1 ; owner changed
if (spy->cm != 0):
if ((unsigned)(spy->cm - 1) > 3) continue ; G4: cm in {1,2,3,4}
if (Chance(GetGame()->rng, spy->cmo)) ; <<< 0x0088dc43
<mission complete: FUN_00888870, advance stage>
else
spy->cmo = (float)(spy->cmo + 0.2) ; ds:0x009e20d8, a WIDENED 0.2f (rule 23)
else:
if (spy->ncp is non-empty) continue
if (GetGame()->Frame - spy->tdep < 3) continue
spy->spyon = ownerId(sysT); spy->spyat = ...
<fall through to the stage machine>
<stage machine: switch(spy->cm) 0->1, 1->(owner->CnTrd ? 2 : 3), 2->3, 3->4, 4->0;
spy->cmo = (cm ? 0.2f : 0.0f)> ; ds:0x009e1fc0 = 0.2f
Cost model, which matters more than the predicate. cmo starts at 0.2f and gains 0.2f per
failed turn. Mars::RNG::Chance draws nothing when p ≥ 1 (lane I §4). So one counter-mission
stage costs at most 4 words and then succeeds for free on the fifth turn:
| turn of the stage | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
cmo |
0.2 | 0.4 | 0.6 | 0.8 | ≥ 1.0 |
| words | 1 | 1 | 1 | 1 | 0 |
A reimplementation that models Chance without the p ≥ 1 early-out is one word out of step on
every fifth turn of every counter-mission.
Predicate and corpus count
∃ a
spywithdeat != 0andcm ∈ {1,2,3,4}, whosedeatsystem is owned and hashindi == false.
| field | across the corpus |
|---|---|
nspy |
1 in human-turn15-spyprogram, ac-turn20-raider, ac-turn22-raider; 0 in the other 19 |
deat |
0 in all three spy records |
cm |
0 in all three |
cmo |
0 in all three |
sdet |
−1 in all three |
hindi |
true on exactly 1 of 28 systems, in 10 of 22 saves — so G3 is not the binding conjunct |
Corpus count: 0 of 22. The failed conjunct is G1 (deat == 0) and, one step behind it,
cm == 0. This agrees exactly with the value-domain census, which lists deat, cm, cmo,
cbh, sdo among the 234 fields that have only ever held one value; the count is therefore 0 by
construction, not by inspection.
What lane AS needs, named from the failed conjunct. deat is the spy's target system handle
and it is not spyon. Both spyon and cm are written by vslot14 itself once deat is set
and Frame − tdep ≥ 3 with ncp empty — so the whole counter-mission machine bootstraps from
deat alone. That makes deat's writer the single question worth answering before the workload is
built; this lane did not find it (§7).
One caution for lane AS. cm's stage machine consults owner->+0xff — the field AC identified
as CnTrd (FTL Economics) — when advancing 1 → 2 or 1 → 3. A spy program run without FTL
Economics takes a different branch of the machine and reaches a different stage sequence.
3.3 0x0088b613 — the raid target pick, and why B cost 0 words when it fired
TradeManager::Slot13RngCalleeB 0x0088b440, called at 0x0088f042 immediately after A returns
true. Ghidra's 1,323 is right (body ends 0x0088b974).
if (fleet == 0) return
if (FUN_0080ec50(fleet)) fleet->Pos = sector->Pos ; snap
sector = StarFleet_GetLocationAsTradeSector(fleet); if (!sector) return
flag = FUN_0080ecf0(fleet) ; see below -- ALWAYS FALSE here
cand = {}
for each rt in this->+0x3c..+0x40:
if (rt == 0) continue
if (rt->trfr < 0 || rt->trfr >= count(this->+0x0c..+0x10)) continue ; G_B1a
if (((void**)this->+0x0c)[rt->trfr] != sector) continue ; G_B1b
if (flag) { if (rt->tro != fleet->owner->id) continue } ; unreachable arm
else {
owner = registry_lookup(S+0x84, rt->tro); if (!owner) continue ; G_B2
if (PlayerAlliances_Relation(raider->+0x28, &raider[0x168], owner->+0x28) > 0) continue ; G_B3
}
cand.push_back(rt)
if (cand.empty()) return ; G_B4
n = cand.size()
bound = n - 1
target = cand[ RNG_NextInt(GetGame()->rng, &bound) ] ; <<< 0x0088b613, >= 1 WORD
if (!FUN_00820af0(this, &out, target, fleet)) return ; <<< draws again, 0x00820c1b (§5.2)
fleet->Pos = out
if (!flag) for each player p with p->+0x5c != 4 and (sector->tssec >> p->+0x28) & 1: <post news>
RNG_NextInt has no zero-bound early-out. Read in full (0x004271c0, 139 bytes, Ghidra's size
correct): it smears *bound down into a mask and then enters the draw loop unconditionally,
rejecting while (y & mask) > *bound. With *bound == 0 the mask is 0, the first word is accepted,
and the call costs exactly one word. So a single-candidate list still draws.
That settles what lane AC measured. AC saw Slot13RngCalleeB entered once on the turn A
succeeded and the tail delta was 1 word, all of it A's. Since B cannot reach 0x0088b613
without spending a word, B returned before the draw, and the only early return past the
sector lookup is G_B4: the candidate list was empty. That is the failed conjunct, and it is not
a container that anyone had been staring at.
Why the flag arm is dead on this path. FUN_0080ecf0(fleet) returns
fleet is at a TradeSector ∧ ¬(runtime flag 0x80000) ∧ TradeSector_PlayerTradesHere(sector, owner) ∧ ¬(FtFlg & 0x800).
But vslot13's G3 (TradeSector_FleetMayRaid 0x0080ed50, AC §2.1) requires the opposite on
the last two: if PlayerTradesHere is true it demands FtFlg & 0x800, and if it is false the
flag computation returns false at its own second test. Any fleet that reaches B through
vslot13 therefore has flag == false, deterministically — so the diplomacy branch is the live
one and the "raid your own routes" branch is unreachable from the tail.
Corpus count, and the arithmetic that names the next workload
Read out of ac-turn22-raider.sav — the only corpus state where A has ever succeeded:
sector TradeID |
tsct |
member systems (indices) | fleets present |
|---|---|---|---|
| 752 | 2 | 1, 10, 20, 24, 17 | tsflt 6352 |
| 768 | 2 | 2, 7, 14, 21, 22, 27 | tsflt 5712 |
| 784 | 0 | 4, 8, 12, 25 | — |
| 800 | 0 | 6, 11, 5 | — |
| 816 | 2 | 9, 15, 16, 18, 19, 26 | tsflt 2736 (the AI's Freighters), 1010 |
| 832 | 0 | 13, 23, 0, 3 | tsflt 3744 — AC's raider |
The four routes' trfr are 18, 16, 14, 20 → sectors 816, 816, 768, 752. None is 832.
So on the one state in which A has ever fired, B's candidate list was empty because the raider
was parked on the one sector in the game that no trade route is sourced in.
G_B3 passes on that state, so it is not competing for blame. PlayerAlliances_Relation
0x006d2050 is already decoded in the campaign (lane T2): 3 = allied and self, 2 =
non-aggression, 1 = cease-fire, 0 = war, and the mask bit is the player's index field. B
rejects a route when the relation is > 0, i.e. only a route whose owner you are at war with
may be raided — and self returns 3, which is the second, independent reason the "raid your own
routes" arm is dead. In ac-turn22-raider.sav both player 0 (the raider) and player 1 (tro = 32,
the route owner) carry AL = NA = CF = 0, so the relation is 0 = war and G_B3 passes. Only
G_B1b failed.
Corpus count: 0 of 22.
The next workload, named from the failed conjunct. From ac-turn22-raider.sav, move the raider
from sector 6 (TradeID 832) to sector 5 (TradeID 816). Then:
vslot13G2 ✓ (fleet at aTradeSectornode), G3b ✓ (tscr = 253, bit 0 = the human player), G3c ✓ —tsct = 2is bit 1, the AI's bit, soTradeSector_PlayerTradesHere(sector, player 0)is false and noFtFlg & 0x800is required, exactly as on sector 832;- G4 after arrival (
Posbit-equal, two End Turns); Arolls; on successBfinds two candidates (trfr16 and 18 both lie in sector 816), drawsNextInt(bound = 1)— mask 1, no rejection possible, exactly 1 word — thenFUN_00820af0draws aNextFloatunless the species short-circuit fires (§5.2);- free rider: sector 816 also holds the AI's
Freightersfleet, which AC read statically as doubling the raid odds — the first state that can test that arithmetic.
Committed prediction, so this is falsifiable: on that state, OnAllCombatDone_Tail costs
1 word on a turn A fails and 3 words on a turn A succeeds (A 1 + B 1 + 0x00820af0
1), with B entered exactly on the succeeding turns.
The one thing this decode rests on that was not proven. this+0x0c is a 0x10-byte vector
indexed by rt->trfr, which FUN_00841cd0 proves is a system index, and whose elements B
compares for pointer identity against a TradeSector*. The only coherent reading is a per-system
"containing trade sector" table, and TradeManager_SpawnEncounterSquadron's caller uses the same
lookup and then reads ->+0x4, which for a TradeSector is its TradeID (that is the field
Write emits under the TradeID tag). This lane did not find the code that fills +0x0c. If
it is instead a sector-indexed vector of six, then trfr ∈ {14,16,18,20} fails the bounds check
G_B1a on every corpus route and 0x0088b613 is unreachable in principle. The two readings are
distinguished by exactly the experiment above: parking on sector 816 makes the draw fire under the
first reading and not under the second. Whoever runs it should report the outcome as a verdict on
this paragraph, not only as a word count.
4. The SVSO generator — named, and it is the strategic one
Lane V2 §4.3 left this open: "the draws are at depth 2–4 and the generator arrives as an argument, so which generator these use is not established."
It is GetGame()->+0x16c — StrategyServer's own generator, the one the save serialises — for
all seventeen overrides V2 lists as reaching a draw.
Method: rebuild the direct+tail-jump closure of each of the seventeen slot-4/11/25 targets, find
every mov r32,[r32+0x16c] in it, and read back where the base register came from.
| the seventeen | how the generator arrives |
|---|---|
| CrowDefenders s11, CrowRuins s11, Ortgay s11, Swarm s4, SwarmQueen s25, SlaversRefuel s4 | call GetGame (0x00578050) immediately before the +0x16c load, in 0x005108c0 / 0x00518340 / 0x004fb280 / 0x00515c20 / 0x0051ad70 / 0x00515820 |
| HiverInvasion s4, HolyLands s4, JewelsOfTheCrown s4, ProgressionWars s25, PuppetMaster s4, Sots s4, SystemKiller s4, UpstartApes s4, VonNeumann s11 | through the two global draw wrappers FUN_00579500 (NextInt) and FUN_00579ae0 (NextFloat), which both do mov ecx,0xb29f98; call 0x005f6450 — the inlined body of GetGame — then [eax+0x16c] |
| Tournament s4 | FUN_0054a550, whose esi carries +0x44/+0x48 (the systems vector) — the StrategyServer itself |
| CivilWar s4 | FUN_00554f40 → call GetGame; mov ecx,eax; call 0x00786200 / 0x00786230, and those read [ecx+0x16c] before Mars_RNG_GaussianRange |
GetGame 0x00578050 is mov ecx,0xb29f98; jmp 0x005f6450 and 0x005f6450 is mov eax,[ecx+4]; ret
— i.e. *(void**)0x00b29f9c, a singleton whose +0xc is the frame and +0x16c the generator.
Lane L1 measured SVSOSwarmQueen::RegisterHives — which reaches its generator by the same
GetGame()->+0x16c — moving the strategic word position, and the values it produced matched
two historical saves. That is an independent live confirmation of this static reading.
Two false positives worth recording so the next lane does not repeat them. A +0x16c load is
not evidence of a generator on its own: FUN_006a8650 and FUN_0074d1c0 use +0x16c/+0x170
as a std::vector begin/end pair (sub eax,[esi+0x16c]; sar 2). Image-wide, 145 functions contain
a [reg+0x16c] load; the generator claim has to come from what the pointer is used for.
5. Sites the firing-indexed table structurally could not contain
5.1 Five draw sites behind CreateRaidEncounter's empty-candidate-list gate
ServerTradeManager::CreateRaidEncounter 0x008938a0 builds a candidate vector with
FUN_0083b110 and returns false at 0x008938fa when it is empty — before every draw in the
function. Lane H measured it entered 2× on Zuul turn 24 and 1× on turn 25, 0 words each time,
which is the campaign's own case study for rule 20. What the ledger could then record was one site
(0x008939ee). Past the gate there are five more, in a subtree nobody has enumerated:
CreateRaidEncounter 0x008938a0
@0x008939ee NextInt (known)
@0x00893a25 -> 0x00892640
-> TradeManager_SpawnEncounterSquadron 0x0088f070
@0x0088f1eb NextInt @0x0088f356 NextInt
-> 0x0083b1e0 @0x0083b460 NextInt
-> 0x00848e50 @0x00848fd9 NextInt
-> 0x0082ce00 -> FUN_00820af0 @0x00820c1b NextFloat
Each NextInt carries its own rejection loop, so the cost of one successful raid encounter is
unbounded above and is certainly not "0 or 1 further word".
Correction, rule 11. tail-rng-ledger.md §11.1 says "a success costs 0 or 1 further word",
and lane H's insert reads "a successful raid roll costs 0 further words on our corpus". The second
is right about the corpus and neither is right about the code: a success whose candidate list is
non-empty costs at least six draws, and the standalone must model that before the first raid
encounter is ever created. Both sentences should gain a pointer here.
5.2 0x00820c1b — a second draw inside Slot13RngCalleeB's own subtree
FUN_00820af0(TradeManager*, Vector3* out, TradeRoute* rt, ...) computes the interception point
along the raided route:
owner = registry_lookup(S+0x84, rt->tro)
a = systems[rt->trfr]->Pos b = systems[rt->trto]->Pos
dist = |b - a| - 0.0
if (owner->+0x5c == 1) frac = 1.0 ; species 1
else if (FUN_0081a2d0(arg3) == 1) frac = 1.0
else if (FUN_0053baf0(SpeciesDef(...))) frac = 1.0
else frac = 0.0 + (1.0 - 0.0) * NextFloat() ; <<< 0x00820c1b, 1 WORD
out = a + dir * (frac*dist + <double @0x009e20a0>)
It is called from two places: 0x0082cf65 (inside the CreateRaidEncounter subtree, §5.1) and
0x0088b62c — Slot13RngCalleeB, immediately after the target pick. So B's cost on success
is 1 (NextInt) + 1 (NextFloat), minus the species short-circuit.
This should change lane AD's committed predictions before its build. The resolution's §4 lists
"B costs 0 on every success" as a prediction to commit; on this reading B costs 2 on a
success with a non-empty candidate list, and 0 only because AC's list was empty. AD's probe is
worth running exactly as briefed — it just needs the third hypothesis in the sheet.
6. The audit's own to-do list — the blank cells, listed
Thirty-one rows are unread gates — thirty distinct draw sites plus the grouped SVSO row. They are the value of this table; the firing-indexed one could not show them.
| group | rows | what is missing |
|---|---|---|
ServerPlayer::RollResearchAccident 0x00889dc0 and its subtree |
7 (0x0088a08f, 0x00889e40, 0x00889f86, 0x00889fd6, 0x0088a1bd, 0x0088a20f, 0x00889c33) |
lane I recorded "all behind top-of-function gates" and nobody has decoded them. Highest-value unread group — it is per player, in ProcessTurn, and would fire on an ordinary save |
| the rebellion subtree | 4 (0x00758966, 0x0074fe21, 0x00753d87, 0x007564b4) |
per system; no corpus save has ever had a rebellion |
MoveFleet / ProbabilisticJump |
4 (0x00747fa8, 0x00747ffd, 0x007b677c, 0x007b67e7) |
needs a fleet on a node line (waypoint type 5). human-turn3-noderoute and zuul-turn16-noderoute exist and were never bracketed for this |
CreateRaidEncounter past the gate |
5 (§5.1) | needs a non-empty raid candidate list — the same workload as §3.3 |
| the spy chain | 4 (0x00887c8a, 0x008409c7, 0x00840929, 0x00840a3c) |
corpus 0 by deat; the inner gates are unread. Lane AS's ground |
| SVSO overrides other than SlaversRefuel | 1 grouped row (16 targets) | S+0x1b4 null in a normal game; SvSctOb.EncObj has 16 census constants |
the second research draw 0x005878bb |
1 | branch condition inside TechTree::ProcessResearch |
the refugee raid roll 0x008935ce |
1 | "a subsystem manager is present" — not yet a save field |
FUN_008134e0 0x0081351c |
1 site, listed under both drivers | in both drivers, never in the ledger, gate not decoded |
| combat phase 6 | 3 (0x007bb69b, 0x00852ec7, and the inlined site in 0x007a7f30) |
combat-dependent; one battle has ever been instrumented |
Three rows are P with corpus count 0 and a recipe attached (§3.1, §3.2, §3.3) — rule 6 with an instruction sheet, which is a different thing from a negative.
7. What this lane did not settle
ServerTradeManager+0x0c's writer was not found. §3.3's last paragraph states the consequence and the experiment that decides it. This is the one load-bearing inference in the document and it is flagged as one.SpyCraft.deat's writer was not found. It is the single conjunct that gates the entire spy half, and per rule 28 practice 3 a field that reads 0 in all 22 saves is a corpus coincidence until its writer is named. Lane AS should find it before building anything.FUN_00743f80(system) & 1— the conjunct on AC's inline detection roll0x00887c8a— was not decoded to a save field.- The diplomacy conjunct is decoded but never exercised.
PlayerAlliances_Relationwas already read by lane T2 and G_B3 resolves to "at war"; the corpus satisfies it only because nobody has ever signed anything (Sim.Player.dipstatsholds 11 census constants — no corpus game reached a treaty). So the §3.3 workload gets G_B3 for free today and would lose it the moment a lane builds the treaty workload. Worth stating in AD's sheet as a conjunct that currently passes by accident. - Which of
RebuildSpeciesTechFlags' eight bits is bit 5 (the one that blocksBeginAddiction) was not pinned; only bit 4 was, throughInitTable'sSpeciesDef+0x88slot.EVENTSUM_ADDICTION_TEMPERENCEexists in the image and is the obvious lead. - Nothing here was measured. Every "M" row is another lane's measurement, cited with the
configuration it was taken at. In particular every AC number is
probes=11, which lane H bisected as not byte-neutral; the tail delta is entry-attributed and stands, the turn totals are not calibration data, and no AC autosave is an oracle pair. - The corpus is 22 saves, not 20. The brief says twenty;
verify/results/saves/holds twenty-two.savfiles and the value-domain census counts the same twenty-two. Every count in this document is over those twenty-two, listed by name in §3.1's table plus the eighteen with nortrecord.
8. Artefacts
| what | where |
|---|---|
| this document | findings/control-flow/gate-indexed-rng-audit.md |
| new addresses (12 entries, no name collides with an existing one) | ghidra/addresses.d/ag.json |
| nothing was written to Ghidra, no VM was used, no build directory was created | — |
Proposed board rows
New row:
| GATE-INDEXED RNG AUDIT: 31 unread-gate rows, 6 new draw sites, the SVSO generator named | control-flow | verified | high | 100% | 2026-09-09 | **Lane AG**, host only, no VM, no Ghidra (`findings/control-flow/gate-indexed-rng-audit.md`). The resolution's §8 item 3, delivered: one row per STATICALLY REACHABLE draw site in `BeginProcessTurn` u `ProcessTurn` u `OnAllCombatDone_Tail` over direct + tail-jump + pinned-virtual edges, each carrying a MEASURED firing or a DECODED PREDICATE on save fields with the 22-save corpus counted against it. **NO TOTAL IS REPORTED - 31 rows (30 distinct sites + one grouped SVSO row) are UNREAD GATES**, and that visible blank column is the deliverable. THREE GATES DECODED. (1) `0x0082cdb8` is the **ADDICTION SPREAD ROLL**: `ServerTradeManagerImpl::vslot15` iterates the SERIALISED route vector (its G1, `BYTE[rt+0x25]!=0`, is byte-for-byte the test `Write` uses to emit an `rt`), and for each route with `trfrs==0 && trtos==0` rolls `Chance(ADDICTION_SPREAD_ODDS=0.2f)` once per species slot the FROM system is addicted to. Corpus **0 of 22**: `nadct` is 0 on all 28 systems of all 22 saves. WRITER FOUND (rule 28 practice 3): `ServerSystem::BeginAddiction 0x0074ef70` has EXACTLY TWO CALLERS, both inside vslot15 - so BLOCK 1 (an INTER-EMPIRE route, `trfow != trtow`, plus bit 4 of `player+0x348+4k`, which `RebuildSpeciesTechFlags` sets from the per-species ADDICT TECH table `FUN_0053b620`) must seed it before the roll can ever happen. Every corpus route is intra-AI (`trfow==trtow==1`), so neither block has run. (2) `0x0088dc43` is the SPY COUNTER-MISSION roll, `p = spy->cmo` starting at 0.2f and gaining 0.2f per failure - so `Chance` costs 0 WORDS from the fifth turn on (`p>=1` early-out), a stage costs at most 4 words. Predicate `deat != 0 AND cm in {1..4}`; corpus **0 of 22** by construction (both fields are among the census's 234 constants). (3) `0x0088b613` is the RAID TARGET PICK, and **`RNG_NextInt` HAS NO ZERO-BOUND EARLY-OUT** (read in full: mask smear then an unconditional draw loop) - so a single-candidate list still costs a word, which PROVES lane AC's `B` returned BEFORE the draw. The failed conjunct is named: AC's raider sat on sector `TradeID 832`, member systems 13/23/0/3, while the four `rt` records' `trfr` are 18/16/14/20, lying in sectors 816/816/768/752. **NEXT WORKLOAD: park the raider on sector 816 instead of 832** - two candidates, `NextInt(bound=1)` cannot reject, `tsct=2` is the AI's bit so no `FtFlg&0x800` is needed, and the AI's Freighters fleet is already there to test the doubled odds. SIX NEW DRAW SITES the firing-indexed table structurally could not hold: five behind `CreateRaidEncounter`'s empty-candidate-list gate (`0x0088f1eb`, `0x0088f356`, `0x0083b460`, `0x00848fd9`, `0x00820c1b` via `0x00892640`) and `0x00820c1b` AGAIN from `Slot13RngCalleeB` at `0x0088b62c`. **SVSO GENERATOR CLOSED**: all 17 overrides that can draw use `GetGame()->+0x16c` = `*(void**)0x00b29f9c` -> +0x16c, the STRATEGIC generator - V2 §4.3 caution (a) retired. Rule 17 bit twice more (`ServerSpyManager::vslot14` real end 0x0088dd2b vs Ghidra's 0x0088dd03, jump table past it) |
Edits to existing rows:
- Row 186 (
RNG LEDGER CLOSED - zero residual) — append:GATE-INDEXED SUCCESSOR 2026-09-09 (lane AG): the site set is now enumerated by GATE rather than by firing - 43 entry-point call sites plus 2 inlined sites inside the two turn drivers' closures over direct + tail-jump + pinned-virtual edges, of which 30 distinct sites are UNREAD GATES. Six were not in any earlier inventory. No per-turn total is claimed. - Row 207 (V2's eight indirect sites) — append:
THREE OF THE FOUR UNREAD GATES ARE NOW DECODED (lane AG): 0x0082cdb8 = addiction spread (corpus 0 of 22,nadct0 everywhere, and its seed path needs an INTER-EMPIRE route plus a species addict tech); 0x0088dc43 = spy counter-mission, p = cmo accumulating 0.2f/turn so Chance costs 0 words once p>=1 (corpus 0 of 22 bydeat/cm); 0x0088b613 = raid target pick, and NextInt has no zero-bound early-out so B provably returned before it - the failed conjunct is the raider's SECTOR, not any container. Still unread: 0x00887c8a'sFUN_00743f80 & 1conjunct and the three sites inside P. - Row 399 — append:
AND lane AC's "threertroute records" is FOUR in ac-turn22-raider.sav (three in ac-turn20-raider); corrected by direct read, lane AG. - The
CreateRaidEncounterclause of row 186 /tail-rng-ledger.md§11.1 —"a success costs 0 or 1 further word"is wrong about the CODE (right about the corpus): a success with a non-empty candidate list reaches FIVE more draw sites through 0x00892640, each NextInt with its own rejection loop. Lane AG §5.1. - Lane AD's brief (resolution §4/§7) — one prediction to add before the build:
Bcosts 2 words on a success with a non-empty candidate list (NextInt 0x0088b613+NextFloat 0x00820c1binFUN_00820af0, unless the species short-circuit fires), not 0. AC's 0 was an empty list, not a free success.