sots-re/findings/control-flow/raid-gate-multiplicity.md

9.7 KiB
Raw Blame History

Trade-raid gate multiplicity — does the tail draw once per turn, or once per qualifying fleet?

  • Type: control-flow (static decode + live measurement)
  • Status: predictions committed, not yet measured — this header block is replaced when the run lands
  • Owner / date: lane AD · 2026-09-08 · guest VM141 (sots-re-win10-b, 192.168.10.143)
  • Follows: the resolution findings/resolutions/2026-09-09-tail-draws.md §4 (the one probe), lane AC's findings/control-flow/trade-raid-rng-gate.md
  • Instrument: lane Z's return-address ledger (draw_sites) + lane H's entry probes at probes=8 — the configuration lane H bisected as byte-neutral. Not probes=11.

1. Predictions, committed before the build (rule 2)

Written on the host, before the shim was built, before the workload was played and before any counter was read. What follows in §1 is frozen; everything after it is the run.

1.0 The callee, decoded in full first

Lane AC read the caller (ServerTradeManagerImpl::vslot13, 0x0088ef80) and reported the short-circuit inside the callee from a partial read. This lane decompiled the whole of TradeManager::Slot13RngCalleeA 0x00820ca0 (436 bytes, 89 decompiled lines, both the loop and its converge point visible — rule 4), because the prediction depends on where the draw sits relative to the loop. Transcribed to a model:

A(fleet, freighterInSector) -> bool:
    if fleet == null:                       return false                 # 0 words
    n = (fleet+0xa8 - fleet+0xa4) / 4                                     # ship vector count
    if n <= 0:                              return false                 # 0 words
    anyBig       = OR  over ships of ( *(design + 0x12c) > 1 )
    allSuppressed= AND over ships of ( ship+0x18 & 0x80 )
    nDE          = #{ ships : !(flags & 0x80) and *(design+0x12c) == 0 }
    nCR          = #{ ships : !(flags & 0x80) and *(design+0x12c) == 1 }
    bestScale    = max over ships of ( (flags & 0x100000) ? K : 1.0 )     # K a float global
    if anyBig:                              return TRUE                  # 0 words  <-- short-circuit
    if allSuppressed:                       return false                 # 0 words
    odds = (nCR >= 1 ? ODDS_CR : (nDE > 0 ? ODDS_DE : 0.0)) + (nCR + nDE) * PER_SHIP
    if freighterInSector: odds = odds + odds
    r = NextFloat()                                                       # <-- EXACTLY ONE WORD
    return (odds * bestScale > r)

Three consequences that the measurement is against:

  1. The draw is inside the callee, and the callee is called once per fleet. So the word cost of the site is Σ over fleets passing G0–G4 of (0 if short-circuited or shipless or all-suppressed, else 1). Not a per-sector constant.
  2. design + 0x12c takes the values 0, 1 and >1 and is used to bucket ships into two counted classes plus a third that short-circuits. That is a hull class field, and lane AC's fleet — one Repair and Salvage, built with IND_CruisCon — is the == 1 bucket. > 1 is therefore the next hull up (a dreadnought, if the ordering is DE/CR/DN), which this game has not researched.
  3. There is exactly one NextFloat on the path, with no rejection loop around it, so a fleet that reaches the roll costs exactly one word — never two.

1.1 The state this lane will build

From verify/results/saves/ac-turn22-raider.sav, read on the host before touching the guest:

field value read
player 0 PlryName "re", PlyrIdx 0, HomeSys 48, Sav 63,032,174, CnRad true
player 0 fleets exactly one: FltID 3744 "Alpha Fleet" PID 16, NShips 1, LocID 832, Pos [0, 0, 8.0], FtFlg 4, ship DesID 17 (Repair and Salvage, IND_CruisCon)
sector 6 TradeID 832, tsgridID 6, Pos [0, 0, 8.0], tssec 0, tsct 0, tscr 253, tsnumsys 4 (tssys 256, 416, 48, 96), tsnumflt 1, tsflt 3744
all six sectors tscr 253 / ptscr 253
player 0 designs Armor, Tanker, Colonizer, Extended Range, Light Defense Platform, Repair and Salvage (DesID 17), Default Assault Shuttle

Sector 6 contains the home system (48), tsct is 0 so the FtFlg & 0x800 conjunct is not armed, and tscr bit 0 is set. Every conjunct of the gate is already satisfied for a second fleet parked on the same node.

The workload: build one more Repair and Salvage cruiser at system 48, keep it as its own fleet, order it to the sector-6 node, and confirm LocID 832 + Pos bit-equality from the autosave, not the panel (lane AC's gotcha). That makes two single-cruiser fleets on one sector.

Deliberate deviation from the brief, stated up front. The brief and the resolution §4 ask for a second raider of a hull satisfying design+0x12c > 1, to price the short-circuit. §1.0 shows that field is a hull class and > 1 means a hull above cruiser, which this game has not researched and cannot research and build inside five turns. Pricing the short-circuit is also the second question; the first is multiplicity, and a second cruiser answers it with the cleanest possible signal — under H-fleet the word count doubles, under H-sector it does not move. A dreadnought would leave the word count at 1 under both hypotheses and force the entry counter to carry the whole result alone. So: second cruiser, and the short-circuit stays unpriced. Recorded as a gap, not skipped.

1.2 P1 — H-fleet (primary)

With two single-cruiser fleets on sector 6, TradeManager::Slot13RngCalleeA is entered 2 times per End Turn and StrategyServer::OnAllCombatDone_Tail costs 2 strategic words per turn, against lane AC's 1 and 1 on the same lineage with one fleet.

Confidence: high, on §1.0 plus lane AC's read of the caller's loop over StrategyServer+0x64..+0x68.

Falsification and symptom. calls = 1 on the TradeManager::Slot13RngCalleeA probe row with two fleets confirmed on the node kills it, and P2 is then the answer.

1.3 P2 — H-sector (the hypothesis this lane exists to kill)

If the roll is per sector and not per fleet, Slot13RngCalleeA is entered 1 time and the tail costs 1 word with two fleets exactly as with one.

Confidence: low. It is stated because four lanes have now been wrong about this call site by reasoning rather than measuring, and because a 1 here would mean §1.0's model — and therefore the generator model the standalone is about to gain — is wrong about the loop.

1.4 P3 — per-site attribution

draw_sites reports exactly one strategic-generator row inside the callee's body, entry kind NextFloat, ret_rva in 0x00420ca0..0x00420e53, with calls == words == 2 and no_draw_calls == 0. The site sum for the tail bracket equals the boundary ledger's tail delta.

Confidence: high on the row existing and on the equality; moderate on calls == 2, because that is P1 restated through the other instrument. The two instruments disagreeing is itself the interesting outcome and would be reported as one.

1.5 P4 — Slot13RngCalleeB and its inner NextInt

B is entered once per fleet whose roll succeeded, so 0, 1 or 2 times per turn, and it costs 0 words on every entry — i.e. no strategic draw_sites row inside 0x0048b440..0x0048b976.

Confidence: low-to-moderate. "0 words" rests on lane AC's single observation and the resolution itself flags the gate above that NextInt as unread (§3.1's unfilled rows). A row appearing there is a result, not a failure: it prices a site the gate-indexed audit currently has to leave blank.

1.6 P5 — the oracle pair

Two hooks=off runs of the same End Turn from the same input save, in two fresh processes, produce byte-identical (Autosave EndTurn).sav and (Autosave).sav.

Confidence: moderate, and this is the prediction most likely to fail. Rule 26's counter-example is exactly this shape: three hooks=off runs on turn1-state gave three different files, pinned to a per-process AI seed. That save has three AI empires; this one has seven, at turn 22, all of them making research and build choices every turn. If the pair does not agree I will localise the difference to named leaves with the state-checksum tool, report whether it is confined to the known AI-seed leaves, and — if it is not — say plainly that no oracle pair exists for this state and that the numbers in §3 are entry counts and word deltas rather than calibration data. Rule 26: a control that does not agree with itself exonerates nothing.

1.7 P6 — instrument neutrality on this state (rule 19)

The (Autosave).sav produced by the probes=8 run is byte-identical to the hooks=off oracle above. This is lane H's probes=8 neutrality claim re-tested on a state it was never tested on, and it costs nothing to check because both files exist anyway.

Confidence: moderate. Conditional on P5: if the control does not agree with itself, this comparison means nothing and will not be reported as evidence (rule 26's second half — agreeing with one run of a varying control is a 1/k coincidence).

1.8 What would make this lane report a zero (rule 28)

If Slot13RngCalleeA is entered 0 times, the failed conjunct will be named from the save, not guessed. The four to read on the post-move autosave are: Flt.LocID == 832 for both fleets; Flt.Pos bit-equal to Trade.Pos [0, 0, 8.0] for both; tscr bit 0 still set (253); and tsct still 0 on sector 6 — because if the AI starts trading through sector 6 during the five turns, tsct bit 0 arms the FtFlg & 0x800 conjunct and both fleets fail it, taking the site back to zero for a reason that has nothing to do with multiplicity.


(Sections 2 onward are written after the run.)