433 lines
24 KiB
Markdown
433 lines
24 KiB
Markdown
# The tail *does* draw under active contents — the trade-raid gate, decoded and fired
|
||
|
||
- **Type:** control-flow (static decode + live measurement)
|
||
- **Status:** **verified** for the trade half; the spy half is decoded but **not reached** and is
|
||
labelled a hypothesis (rule 6)
|
||
- **Confidence:** high on the trade half — the callees were entered live, the RNG word delta moved,
|
||
and the state that moved it was reached by playing a stock game with no file edited
|
||
- **Owner / date:** lane AC · 2026-09-08 · guest **VM141** (`sots-re-win10-b`, 192.168.10.143)
|
||
- **Predicts / corrects:** lane W3's open risk (**not** retired — realised), lane L3's
|
||
`trade-and-spy-workload.md` §3.5, lane V2's "closure 185 functions, draw-free" reading of the
|
||
trade slots
|
||
|
||
---
|
||
|
||
## 0. Headline
|
||
|
||
> **Yes. The strategic-turn tail draws RNG once its containers are *active*.**
|
||
> `StrategyServer::OnAllCombatDone_Tail` consumes **1 word per turn** in the state this lane built,
|
||
> against **0** on the same guest, same build and same config on the state lane L3 left.
|
||
> `TradeManager::Slot13RngCalleeA` — entered **0** times in every measurement any lane had ever
|
||
> taken — was entered **1×/turn on both instrumented turns**, and `Slot13RngCalleeB` on one of them.
|
||
>
|
||
> **Lane W3's open risk is realised, not retired. The RNG ledger and the standalone's generator
|
||
> model need extending by one draw site.**
|
||
|
||
And the condition that does it is **not** the one lane L3 named. Freighters in service do not do it
|
||
and a deployed spy would not do it either. The gate is:
|
||
|
||
> **a player-owned fleet parked exactly on a trade-sector node, whose owner's bit is set in that
|
||
> sector's `tscr` mask — which is set by researching `CCC_ComRaid` ("Commerce Raiding").**
|
||
|
||
That is the trade-raid roll. It had never fired in this campaign because no lane had ever
|
||
researched Commerce Raiding, and without it the game refuses the order in as many words:
|
||
**`Alpha Fleet cannot raid trade sector.`**
|
||
|
||
---
|
||
|
||
## 1. What was measured
|
||
|
||
Instrument: `shim.cfg.l3probe` verbatim (lane L3's, = lane H's `hp11` + `watch.mode=cont`), shim
|
||
build `ac-7e726b9-20260909T0017Z`, cross-built on the dev box. All eleven probes reported
|
||
`create=MH_OK enable=MH_OK`; no `COVERAGE:` line was emitted; the watchpoint canary printed
|
||
`SELFTEST canary writes=1 traps=1 dr7=0xdddd0055 PASS` on **every** End Turn. These are measured
|
||
numbers, not MinHook failures.
|
||
|
||
### 1.1 Entry counts, active state (turns 21→22 and 22→23 of the AC lineage)
|
||
|
||
The **baseline column is this lane's own control**, not a quotation: the same shim build, the same
|
||
`shim.cfg.l3probe`, the same guest, one End Turn on lane L3's `human-turn15-spyprogram.sav`, run
|
||
immediately after the active measurement. It reproduces lane H's and lane L3's published counts
|
||
line for line.
|
||
|
||
| probe | active, turn 1 | active, turn 2 | **AC control** (L3's turn 15) |
|
||
|---|---|---|---|
|
||
| `ServerSpyManager::vslot13` | 1 | 1 | 1 |
|
||
| `ServerSpyManager::vslot14` | 1 | 1 | 1 |
|
||
| `ServerTradeManagerImpl::vslot13` | 1 | 1 | 1 |
|
||
| `ServerTradeManagerImpl::vslot15` | 1 | 1 | 1 |
|
||
| `SpyManager::Slot13RngCallee` | 0 | 0 | 0 |
|
||
| **`TradeManager::Slot13RngCalleeA`** | **1** | **1** | **0** |
|
||
| **`TradeManager::Slot13RngCalleeB`** | **1** | **0** | **0** |
|
||
| `ServerTradeManager::CreateRaidEncounter` | 0 | **1** | 0 |
|
||
| `ServerTradeManager::GenerateTradeRaidEncounters` | 1 | 1 | 1 |
|
||
| `ServerSpyManager::vslot15` [control] | 1 | 1 | 1 |
|
||
| `ServerTradeManagerImpl::vslot14` [control] | 1 | 1 | 1 |
|
||
| `EncounterDetect::Run` [control] | NOT INSTALLED | NOT INSTALLED | — |
|
||
|
||
`Slot13RngCalleeB` firing on turn 1 and not on turn 2 is exactly what the decoded control flow
|
||
predicts: `B` is called only when `A` returns true, i.e. only when the raid roll **succeeds**. Two
|
||
turns, one success — the roll is doing what a roll does.
|
||
|
||
### 1.2 The RNG ledger
|
||
|
||
`side.rng` word deltas. **Control first** (L3's turn 15, no raider, same instrument):
|
||
|
||
```
|
||
ProcessTurn 353 -> 355 delta 2
|
||
OnAllCombatDone_Tail 355 -> 355 delta 0
|
||
```
|
||
|
||
**Active** (raider parked on sector 6's node), both turns:
|
||
|
||
```
|
||
ProcessTurn 448 -> 468 delta 20
|
||
OnAllCombatDone_Tail 468 -> 469 delta 1
|
||
ProcessTurn 469 -> 487 delta 18
|
||
OnAllCombatDone_Tail 487 -> 488 delta 1
|
||
```
|
||
|
||
**One word per turn inside the tail**, on a turn where the tail had never drawn a word before. The
|
||
draw is `FUN_0047d830(StrategyServer+0x16c + 4)` inside `Slot13RngCalleeA` — one uniform compared
|
||
against the raid odds. `Slot13RngCalleeB` and `CreateRaidEncounter` each cost 0 words on the turns
|
||
they ran, so the whole of the new consumption is `A`'s single roll.
|
||
|
||
---
|
||
|
||
## 2. The gates, decoded from the instruction stream
|
||
|
||
Read off `sots.exe` (image base `0x00400000`), disassembling to the next function start rather than
|
||
to Ghidra's reported size (rule 17).
|
||
|
||
### 2.1 `Slot13RngCalleeA` / `B` — called from `ServerTradeManagerImpl::vslot13`
|
||
|
||
Both calls are **direct** `E8 rel32`, not vtable slots:
|
||
|
||
| callee | called from | at |
|
||
|---|---|---|
|
||
| `TradeManager::Slot13RngCalleeA` `0x00820ca0` | `ServerTradeManagerImpl::vslot13` `0x0088ef80` | `0x0088f036` |
|
||
| `TradeManager::Slot13RngCalleeB` `0x0088b440` | same function | `0x0088f042` |
|
||
| `SpyManager::Slot13RngCallee` `0x008408e0` | `ServerSpyManager::vslot13` `0x008877b0` | `0x00887af2` |
|
||
|
||
`ServerTradeManagerImpl::vslot15` (`0x0082cca0`) and `ServerSpyManager::vslot14` (`0x0088db80`) do
|
||
**not** call any of the three. The only data references to the four outer callees are their vtable
|
||
slots (`0xa30770`, `0xa30774`, `0xa31ba8`, `0xa31bb0`).
|
||
|
||
**`vslot13` does not iterate trade routes or trade sectors.** It iterates
|
||
`StrategyServer+0x64..+0x68` — the **fleet** vector — and per fleet requires, in order:
|
||
|
||
```
|
||
0088ef95 test ecx,0xfffffffc ; G0 no fleets -> return
|
||
0088efb1 je 0x88f047 ; G1 null slot
|
||
0088efb9 call 0x6fe320 ; StarFleet::GetLocationAsTradeSector()
|
||
0088efc0 je 0x88f047 ; G2 location is not a trade-sector node
|
||
0088efc7 call 0x80ed50 ; the raid predicate
|
||
0088efd1 je 0x88f047 ; G3
|
||
0088efeb..0088f016 ; G4 fleet.Pos == sector.Pos, all three floats
|
||
0088f036 call 0x820ca0 ; <-- A
|
||
0088f03d je 0x88f047 ; G6 raid roll failed
|
||
0088f042 call 0x88b440 ; <-- B
|
||
```
|
||
|
||
**G2** is the one nobody had satisfied. `0x006fe320` reads `StarFleet+0xa0` (`LocID`, resolved to a
|
||
pointer) and returns it only if `node+0x14 == 2`. That kind word is written by
|
||
`StarMapNode::StarMapNode(int)` `0x00730900`, and enumerating **every** call to it gives exactly
|
||
three: `StarSystem` ctor `0x00752ea0` passes **0**, `StarFleet` ctor `0x00706140` passes **1**,
|
||
`TradeSector` ctor `0x00858020` passes **2**. So `vslot13` only ever acts on a fleet whose location
|
||
is a **trade-sector node**, never a star. Every fleet in every corpus save before this lane was
|
||
parked at a star, so G2 rejected all of them — which is why the "23-fleet Zuul save" experiment in
|
||
`tail-probes.md` §3.1 could not have worked no matter how many fleets it had.
|
||
|
||
**G3** is `FUN_0080ed50(fleet)`:
|
||
|
||
```c
|
||
if (!fleet->GetLocationAsTradeSector()) return false;
|
||
if (FUN_00813ab0(fleet, 0x80000, 0)) return false; // runtime flag pair +0xb8/+0xbc
|
||
ServerPlayer *p = fleet->[0x58]; if (!p) return false;
|
||
if (!(sector->[0x90] & (1u << p->[0x28]))) return false; // ** tscr **
|
||
if (FUN_0080e9c0(sector, p)) // ** tsct ** bit, or p->[0x5c]==4
|
||
if (!FUN_006fe1d0(fleet, 0x800)) return false; // FtFlg & 0x800
|
||
return true;
|
||
```
|
||
|
||
`FUN_0080eac0` masks six consecutive words with a low-N-players mask, which is what identifies them
|
||
as per-player bitmasks and pins the tags:
|
||
|
||
```
|
||
0080ead7 and [ecx+0x88],edx tssec 0080eae3 and [ecx+0x90],edx tscr
|
||
0080eadd and [ecx+0x8c],edx tsct 0080eae9 and [ecx+0x94],edx ptssec …
|
||
```
|
||
|
||
**G4** requires the fleet's `Pos` (`+0x18`) to be **bit-equal** to the sector's — arrived, not in
|
||
transit. The `test ah,0x44 / jp` after `fucompp` is the MSVC float-not-equal idiom.
|
||
|
||
### 2.2 `SpyManager::Slot13RngCallee` — decoded, and much further away than "deployed"
|
||
|
||
```
|
||
0088781d je 0x887e92 ; SpyCraft.deat (+0x10) == 0 -> next spy
|
||
00887856 je 0x887c5b ; SpyCraft.sdet (+0x40) == -1 -> DETECTION-ROLL branch, not P
|
||
00887867 jl 0x887e92 ; (turn - sdet) < 3 -> next spy
|
||
0088787a je 0x887c4f ; system unowned -> sdet = -1
|
||
00887891 je 0x887c4f ; system owner == spy's owner -> sdet = -1
|
||
00887af2 call 0x8408e0 ; <-- P
|
||
```
|
||
|
||
`P` itself is the **false-flag draw**: `0.25` (`0x009e5ac0`) for an ordinary owner, or `0.75`
|
||
(`0x009e5ac4`) plus a uniform pick over the other players when `owner->[0x5c] == 6`; its result is
|
||
the name that goes into `EVENT_SPY_DESTROYED`, posted to the *system* owner.
|
||
|
||
So **deploying a spy is necessary but nowhere near sufficient for `P`**: the spy must be deployed,
|
||
then **detected** on an earlier turn, then survive **three more turns** at a system still owned by
|
||
someone else. Lane L3's "the next condition is `spyon = 0`" understates the distance by three turns
|
||
and a detection roll.
|
||
|
||
**But the `sdet == -1` arm at `0x00887c5b` is itself an RNG draw** —
|
||
`FUN_008e6dd0(StrategyServer+0x16c, spy->sdo)`, gated on `FUN_00743f80(system) & 1` — and it sits
|
||
**inline in `vslot13`, not inside `P`**. That is a second, independent way for the tail to draw that
|
||
**no entry probe on `P` can see**. It is unreachable today only because `deat == 0` rejects the
|
||
docked spy two gates earlier. Rule 20, pointing the other way: instrumenting the entry was necessary
|
||
and was not sufficient — a probe on `P` reading 0 does **not** mean the spy half is draw-free.
|
||
|
||
**This is a hypothesis, not a result** (rule 6): no save in the corpus has a deployed spy, and this
|
||
lane did not manage to build one (§5).
|
||
|
||
---
|
||
|
||
## 3. `tscr` is the raid mask and `CCC_ComRaid` sets it — measured, before and after
|
||
|
||
This is the part that turns the decode into a recipe, and it was **not** predicted: this lane's
|
||
committed prediction P6 said a player fleet on a sector node would *not* fire the callees, on the
|
||
reasoning that `tscr = 252 = 0b11111100` is exactly the six NPC player slots (2–7) and excludes both
|
||
real players (0 = `re`, 1 = `The Eternal Empire`). That reading of the *value* was right and the
|
||
inference from it was **wrong**, because the mask is not fixed.
|
||
|
||
| save | `tscr`, all six sectors | player `re` `CnRad` |
|
||
|---|---|---|
|
||
| `human-turn15-spyprogram.sav` (L3) | **252** = `0b11111100` | absent/false |
|
||
| AC turn 19, after `CCC_ComRaid` completes | **253** = `0b11111101` | **true** |
|
||
|
||
Researching Commerce Raiding sets **bit 0 — the researching player's index — in every trade
|
||
sector's `tscr`**, and sets the `ServerPlayer` flag serialised as **`CnRad`**. `CnRad` is to
|
||
`CCC_ComRaid` exactly what `CnTrd` (`ServerPlayer+0xff`) is to `CCC_FTLEcon`. The AI, which never
|
||
researched it, stays out of the mask: `CnRad` false, bit 1 clear, in the same save.
|
||
|
||
So `tscr` reads: *the set of players permitted to raid this sector* — the six NPC slots by default
|
||
(which is why `TRADE_RAID_ODDS_NPC` and `TRADE_RAID_ODDS_REFUGEE` exist), plus any empire that has
|
||
bought its way in with the tech.
|
||
|
||
Two further tags fall out of the same pair of saves: `tsct` is the set of players **trading through**
|
||
the sector (bit 1 on sectors 1, 2 and 5 — the AI's; 0 on sector 6, which is mine), and it is `tsct`
|
||
that switches on the extra `FtFlg & 0x800` requirement. Sector 6 has `tsct = 0`, so my raider needed
|
||
no flag.
|
||
|
||
---
|
||
|
||
## 4. The recipe, end to end
|
||
|
||
Stock game, stock data, no file edited, starting from lane L3's `human-turn15-spyprogram.sav`.
|
||
About 45 minutes of clicking.
|
||
|
||
1. **Research `CCC_ComRaid`.** `MasterTechList.tech`: `CCC_FTLEcon -> CCC_ComRaid RP:12000
|
||
Human:100`. At this game's settings it is **one turn**. In the tech tree it is the node
|
||
**directly above FTL Economics in the C3 branch**, and at the zoom the screen opens at it is
|
||
drawn *off the top of the viewport*: zoom out two notches with the wheel, then zoom back in one
|
||
notch **with the cursor low on the screen** (`wheel 365 560 120 1`), which shifts the camera down
|
||
far enough to expose it. Vertical right-drag does **not** pan the tech tree at all — only
|
||
horizontal, and only in ~600 px steps, and even then the response is not monotonic in distance.
|
||
2. **Toggle Trade View.** Bottom bar circle **(300,714)** opens a six-icon vertical strip; the fifth
|
||
icon **(301,626)** is `Toggle Trade View`. Trade sectors then draw as large translucent wireframe
|
||
cubes with a padlock/`$` marker at the sector node itself. **Nothing else in the UI shows a trade
|
||
sector**, and there is no bar button for trade.
|
||
3. **Order a fleet to the sector node.** Select the system, select the fleet row, click **`Move`
|
||
(155,464)**, hover the sector marker — the readout reads `Sector 6 / Range 5.0 / ETA 2 Turns` —
|
||
then **plain `click`** it. This must be **one `cmd.txt`**; and note the jiggle-click variant
|
||
(`jclick`) **silently failed to register the destination** where a plain `click` worked. Confirm
|
||
from the fleet panel: it reads `Dest: Sector 6, 2 Turns`. Confirm again from the save — do not
|
||
trust the panel: on the first attempt the panel showed nothing and the save showed `LocID 48`
|
||
(unmoved).
|
||
4. **Two End Turns.** The fleet arrives: `LocID 832` = `TradeID 832` = sector 6, `Pos [0, 0, 8.0]`
|
||
bit-identical to the sector's `Pos`.
|
||
5. **End Turn with the instrument on.** `Slot13RngCalleeA` fires.
|
||
|
||
**The refusal is the oracle.** Before the tech, clicking the sector marker with a fleet selected pops
|
||
`Alpha Fleet cannot raid trade sector.` (`MOVEWARNING_NO_RAID`; the server-side twin is
|
||
`OnCommand: Fleet not able to raid.`). After the tech, the same click is accepted silently. That
|
||
single dialog is the cheapest possible test of the whole gate chain.
|
||
|
||
---
|
||
|
||
## 5. What this lane could **not** reach: a deployed spy
|
||
|
||
Reported as an absence, not inferred around.
|
||
|
||
`ARes2` on `ServerSystem` is the **asteroid-belt** field — its non-zero values in this save (1703,
|
||
1948, 2061, 2172, 2531) sit inside `SYSTEM_MIN/MAX_ASTEROID_RESOURCES` 1500–3000, and it is 0
|
||
elsewhere. Deploying a spy needs a system that is **not mine**, has **no enemy fleet present**, has
|
||
**no spy already**, and **has a belt**. In `human-turn15-spyprogram.sav` only five of 28 systems have
|
||
one:
|
||
|
||
| idx | id | name | `ARes2` | owner |
|
||
|---|---|---|---|---|
|
||
| 1 | 64 | Ka'Palum | 2061 | unowned |
|
||
| 4 | 112 | Regulus | 1703 | unowned |
|
||
| 10 | 208 | Kam'Tara | 1948 | unowned, AI fleet parked on it |
|
||
| 12 | 240 | Downbelow | 2172 | **mine** |
|
||
| 27 | 480 | Hitomi | 2531 | player index **7**, `Independent Colony` |
|
||
|
||
**No system owned by the AI has an asteroid belt.** The textbook target — an enemy colony with a
|
||
belt — does not exist in this game. The only non-own, player-owned belt system is **Hitomi**, held by
|
||
the NPC `Independent Colony`; it is 10.1 ly from the tender's berth against a hull range of 9.0, so
|
||
it needs a staging hop, and this lane ran out of session before establishing whether it is even
|
||
discovered. The `Special` menu at the tender's own system offers only
|
||
`Repair / Scuttle / Auto-repair / Flag As Guard` — no `Deploy Spy`, which is consistent with the
|
||
gate but does not locate it.
|
||
|
||
So: **the spy half of "active contents" remains unreached**, and §2.2's prediction that a deployed
|
||
spy makes the tail draw through the inline detection roll is a hypothesis for the next lane. The
|
||
save to start from is `verify/results/saves/ac-turn22-raider.sav` or L3's turn-15.
|
||
|
||
**`spies2` is still 0 in all 28 systems** in every save this lane produced, including the turn-22
|
||
one. That is unchanged from L3 and is expected: no spy was ever deployed, so the experiment that
|
||
distinguishes "`spies2` is not the spy list" from "`spies2` fills only for a deployed spy" was **not
|
||
run**. The rule-6 flag on `spies2` / `SysMem` / `mts` stays up. What this lane can add is that the
|
||
question is now cheap for whoever reaches a deploy, because `deat` (`SpyCraft+0x10`) is the field
|
||
`vslot13` uses as the system handle, and `spies2` living on `ServerSystem` makes the deployed-spy
|
||
reading the natural one.
|
||
|
||
---
|
||
|
||
## 6. Corrections to earlier findings (rule 11)
|
||
|
||
**Lane L3, `trade-and-spy-workload.md` §3.5 — "every trade sector has `tsnumflt = 0`, no freighters
|
||
in service".** Not true of the save L3 shipped. `human-turn15-spyprogram.sav`, sector `TradeID 816`
|
||
(`tsgridID 5`): `tsnumflt 1`, `tsflt 2736` — and fleet 2736 is `Freighters`, `PID 32`, the **AI's**,
|
||
with all six member systems AI-owned. The single serialised route record is the AI's too. So across
|
||
L3's seven instrumented turns the game **already had a freighter in service on an active route**, and
|
||
the trade callees were entered 0 times anyway. **"Freighters in service" was already falsified as the
|
||
gate by L3's own data**, before this lane started, and §2.1 now says why: freighters are not in the
|
||
predicate at all.
|
||
|
||
By turn 20 of this lane's lineage the AI had built two more (sector 5 `tsnumflt 2`, sector 2
|
||
`tsnumflt 1`) with no effect on any probe.
|
||
|
||
**Lane V2 — `ServerTradeManagerImpl` vftable slot 13 recorded as "closure 185 functions,
|
||
draw-free".** It is not draw-free. It reaches a `NextFloat` at `0x00820ca0` on any turn a permitted
|
||
raider is parked on a sector node. The closure was right; "draw-free" was a statement about the
|
||
states that occurred (rule 23).
|
||
|
||
**Lane W3's standing risk — "if either container ever fills, the ledger needs extending".** The risk
|
||
was correctly identified and the trigger was wrong: filling the containers changes nothing, and the
|
||
ledger needs extending anyway, for a reason that has nothing to do with either container's contents.
|
||
|
||
**`tsflt` is not a freighter list.** At turn 20, sector 6 reads `tsnumflt 1`, `tsflt 3744` — and 3744
|
||
is **my raiding fleet's** id, not a freighter. Whatever the tag is named, it counts fleets present in
|
||
the sector.
|
||
|
||
---
|
||
|
||
## 7. Controls and coverage (rules 15, 19, 26)
|
||
|
||
**Guest adoption / instrument-removed control.** `hooks=off watch=off` on `ref-turn2.sav`, one End
|
||
Turn, on VM141 with this lane's own shim build:
|
||
|
||
```
|
||
(Autosave EndTurn).sav BB4FD9AC89F41E3BC0DB2AF08B18CE83417521AC4BCEE695FC9FA6CE16E30948
|
||
(Autosave).sav 978041ACD168B56ED8EB3F5E42E78D5E70EAE6E6517D75E659A5EB7CA3D60921
|
||
```
|
||
|
||
Both **identical to the published three-run oracle** in
|
||
`verify/results/saves/determinism-hashes.txt`. That is agreement with a control already reproduced
|
||
in three fresh processes by other lanes on another guest, which is the rule-26 bar; this lane ran it
|
||
once here rather than twice, and says so.
|
||
|
||
**Instrument armed.** All eleven probes `create=MH_OK enable=MH_OK`, no `COVERAGE:` line; watchpoint
|
||
canary PASS on every End Turn; container watch read `trade routes count=46 -> 47`,
|
||
`spy programs count=1` across the two measured turns.
|
||
|
||
**Thin coverage, stated plainly.**
|
||
|
||
- **Two instrumented turns, one raiding fleet, one sector, one ship in the fleet.** The word cost is
|
||
1 on both, but 1 fleet × 1 sector is the smallest possible workload and the per-fleet loop means
|
||
the cost should scale with *permitted raiders parked on sector nodes*, which this lane did not
|
||
vary. Do not fit a constant to two observations (rule 20).
|
||
- `Slot13RngCalleeA`'s own body has a **short-circuit that skips the draw entirely**: any ship whose
|
||
`design+0x12c > 1` returns true without rolling. My fleet is one `CR Repair and Salvage`, so the
|
||
short-circuit was not exercised, and a raid by a different hull could cost **0** words while still
|
||
firing `B`. Untested.
|
||
- The odds arithmetic (`0.20` vs `0.05` base, `+0.01·nShips`, doubled when a freighter shares the
|
||
sector, scaled by a per-ship float) was read statically and **not** validated against measured
|
||
success rates: two turns, one success.
|
||
- `probes=11` is **not** determinism-neutral (lane H bisected the detour on probe index 8). These
|
||
entry counts and word deltas are comparable line-for-line with lane H's and L3's tables, which is
|
||
what they are for; they are not a byte-identity claim. `probes=8` still installs indices 0–7, i.e.
|
||
all four outer and all three inner callees, and is the configuration to use if a future lane needs
|
||
both at once.
|
||
- `EncounterDetect::Run [control]` is index 11 and `probes=11` installs 0–10, so it reports
|
||
**NOT INSTALLED**, never 0.
|
||
|
||
---
|
||
|
||
## 8. Artifacts
|
||
|
||
| what | where |
|
||
|---|---|
|
||
| turn-22 save, raider parked on a trade sector, both callees having fired | `verify/results/saves/ac-turn22-raider.sav` |
|
||
| turn-20 save, the moment the fleet arrives on the node (pre-measurement) | `verify/results/saves/ac-turn20-raider.sav` |
|
||
| probe + ledger trace, two instrumented End Turns | `verify/traces/ac-probe-turn21-turn23.jsonl` |
|
||
| shim log and watch hits | `verify/results/shim/ac/ac-probe-run-{shim.log,watch.txt}` |
|
||
| predictions, committed before the workload and again after the decode | `sots-engine` `docs/AC-predictions.md` |
|
||
| instrument | `sots-engine` `src/shim/shim.cfg.l3probe`, unchanged |
|
||
|
||
**New content these saves carry** (rule 27 — expect the coverage ratchet to break, and type the
|
||
content rather than moving the bar): a `ServerPlayer` with **`CnRad` true**; trade sectors with
|
||
**`tscr` = 253** rather than the corpus-universal 252; a `trdmgr` with **three** `rt` route records
|
||
rather than one; sectors with `tsnumflt` up to 2 and a `tsflt` entry that is a **warship** fleet id
|
||
rather than a freighter; and a `StarFleet` whose **`LocID` is a `TradeSector` id (832)** rather than a
|
||
star system id — the first fleet in the corpus located anywhere but a star, which is precisely the
|
||
state that makes the tail draw.
|
||
|
||
---
|
||
|
||
## 9. VM141 as left, and two things that cost this lane time
|
||
|
||
**Guest state.** Game stopped. `C:\SOTS\shim.cfg` is a copy of `shim.cfg.l3cont` (`hooks=off`,
|
||
`watch=on`, `watch.mode=cont` — the cheap config); the lane's dist is `C:\SOTS\shimdist-ac`
|
||
(`BUILD_ID ac-7e726b9-20260909T0017Z`) and `C:\SOTS\binkw32.dll` is that build's proxy, with the
|
||
original still at `binkw32_real.dll`. `SavedGames` has been restored from
|
||
`C:\SOTS\SavedGames-acstash` and additionally holds **`ac-turn20-raider.sav`** and L3's three saves,
|
||
so **every Load-dialog row position recorded by an earlier lane has moved on this guest** — screenshot
|
||
the dialog, do not reuse a remembered row. Also left, all harmless: `C:\SOTS\acdata\` (config text
|
||
extracted from `sots.gob`) and `C:\SOTS\ui\ac-*.ps1`.
|
||
|
||
**`click_helper.ps1` gained six verbs**, purely additive, every existing verb byte-for-byte
|
||
unchanged; pre-lane backup at `click_helper.ps1.preAC`:
|
||
|
||
```
|
||
drag x1 y1 x2 y2 left-button drag (ported from VM144, lane L3)
|
||
rdrag x1 y1 x2 y2 RIGHT-button drag, 30 steps (the only thing that pans the tech tree)
|
||
sdrag x1 y1 x2 y2 slow left drag, 40 steps
|
||
wheel x y delta [n] n wheel notches
|
||
jmove x y move with a real relative mouse_event jiggle (new; ported from lane L2's ui.ps1)
|
||
jclick x y click with the same jiggle (new)
|
||
```
|
||
|
||
**The jiggle is needed to *read* the star map and harmful when *committing* to it.** `jmove` is what
|
||
makes the map's hover readout update — without it the `Sector 6 / Range 5.0 / ETA 2 Turns` line never
|
||
appears and you are clicking blind. But `jclick` on the sector marker **did not register the
|
||
destination**, twice, silently; a plain `click` at the same coordinate did, first time. So: `jmove` to
|
||
read, plain `click` to commit. Lane L2's note that the map "needs real mouse motion" is right about
|
||
motion and wrong if read as "use the jiggle for everything".
|
||
|
||
**Verify a fleet order from the save, never from the panel.** The first move order left the fleet
|
||
panel showing no destination *and* the save showing `LocID 48` — unmoved — through two End Turns,
|
||
with no error anywhere. The second attempt showed `Dest: Sector 6, 2 Turns` in the panel and moved.
|
||
There is no failure signal; the only reliable check is `Flt.LocID` in the autosave.
|
||
|
||
**Dev-box build note (not a repo change).** `cmake/toolchain-mingw-i686.cmake` links libgcc and
|
||
libstdc++ statically but not **libwinpthread**. On the WSL dev box the i686 mingw is the *posix*
|
||
threads model, so `binkw32.dll` comes out importing `libwinpthread-1.dll` and the game dies at load
|
||
with `0xC0000135 STATUS_DLL_NOT_FOUND` — no shim log, no banner, just an exit code in
|
||
`dxvklog\run.txt`. Adding `-DCMAKE_SHARED_LINKER_FLAGS=-static` fixes it. CT111 does not have this
|
||
problem (its mingw uses the win32 model), which is why no lane has hit it before; the toolchain file
|
||
was deliberately **not** changed, because that is a shared-infrastructure decision for the integrator
|
||
rather than a lane's.
|