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

163 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.)*