lane W2: the three queue items not reached, with the reason and the cheapest next step for each

TShn: the brief's address was wrong -- player+0x274 is observed_techs; the TShn map is
ServerSystem+0x274. The real obstacle is that a std::map's nodes are heap-allocated, so there is
no fixed leaf to arm; arm the map header to trap the insertion instead.

rcex: closed from the corpus, no VM time needed (see rcex-explained.md).

Trade/spy workload: not attempted, and lane H's reasons still hold. What is new is that the
watchpoint module is the instrument for confirming the workload BEFORE spending a turn on it --
both containers are one add from the arming point, and for the spy vector the trapped return
addresses would name the UI nobody has found.
This commit is contained in:
alex 2026-09-08 15:25:53 -04:00
parent 92ad44efb0
commit cb1ea723e1

View file

@ -231,9 +231,73 @@ one build and about six minutes of VM time for two turns, and it answered three
consumed parts of four lanes of static reading. **Method rule 18, again: `what writes this?` is a
watchpoint.**
Two things a future user should keep:
Two things a future user should keep — and then §8 for the queue items this lane did **not** reach,
each with the reason and the cheapest next step.
- **keep the canary self-test.** It is four lines and it is the difference between "nothing writes
this" and "the arm silently failed".
- **keep the oracle control.** Loading `ref-turn2.sav` and ending one turn costs nothing extra and
proves the run is measuring the un-instrumented game.
---
## 8. Not reached, and why — with the cheapest next step for each
Lane H's account of the trade workload was more useful than the attempt would have been. Same
discipline here.
### 8.1 `TShn` — the brief's address is wrong, and that is the finding
The lane brief said "watchpoint on the `player+0x274` map". **That is the wrong object.**
`player+0x274` is `observed_techs` (lane V, `eventlive-verification.md` §4.3). The `TShn` map is
`ServerSystem+0x274` — the `NVO` container — per `system-visibility-record.md` §8, whose own words
are "a watchpoint on `ServerSystem+0x274`'s map during one turn". Anyone who armed the briefed
address would have watched the tech-observation array and reported a confident nothing.
The real obstacle is structural, and it is why this was not just a fifth line in `WatchOnApplyAll`:
**a `std::map`'s nodes are heap-allocated, so `TShn` has no fixed address to arm before the node
exists.** Two ways round it, both cheap now that the module exists:
1. **Arm the map header** (`ServerSystem+0x274 … +0x27c`) rather than a leaf. That traps the
*insertion*, which is arguably the better question — E3's puzzle is which systems get an `NVO`
entry, and Spica-vs-Bismol is a question about the gate, not about the value.
2. **Arm the leaf directly**, by loading `turn1-state.sav`, walking the map for Spica's node once at
`ProcessTurn` entry, and pointing DR0 at that node's `TShn` word. This needs the node layout,
which `objects/` has.
Either is one arming function away. What is *not* solved is the arming point: `ApplyAllTurnCommands`
gives `S`, and `ServerSystem`s hang off `S+4+0x40`, so reaching a named system needs a name compare
in the arming code. About twenty lines.
### 8.2 `rcex` — closed without the VM
See `findings/subsystems/rcex-explained.md`. It is sixteen 4-bit per-player counters; nibble *p* is
set to 1 on the turn the system enters player *p*'s `AFlags` and ticked to 0 the next turn, 7/7
across two different games. The corpus had the answer; nobody had put the nibble index next to the
`AFlags` bit index. **The one part that still wants a watchpoint** is the site of the decrement — the
"`rcex` tick" named in `ServerSystem::ProcessTurn`'s body-order note — and `rcex` is at a fixed
offset in a fixed object, so it is the *easiest* target this module has.
### 8.3 The trade-route / spy workload — not attempted, deliberately
Lane H's reasons (`tail-probes.md` §3.2) all still hold: trade routes need trade-station
construction plus its tech, which from `ref-turn2` is tens of End Turns at 30-60 s each with a
Build/Research click path per turn, and the turn-23 Zuul save still has none; and **no lane has
identified which UI produces a spy-program entry**, so for spies there is still no click path to
write down. This lane spent its VM time on the multiplayer demonstration and the watchpoint run
because both had falsifiable predictions attached and this does not.
What is different now, and it is the thing that makes the next attempt materially cheaper:
**"confirm the workload took before spending a turn measuring it" is exactly what this module does**,
and both containers are one add from the arming point already in the code.
| container | address at `ApplyAllTurnCommands` entry (`this` = `S`) |
|---|---|
| trade-route vector (slot 15's body) | `*(void**)(S + 4 + 0x154)` → `tradeManager`, then `tradeManager + 0x3c` (`_Mylast` at `+0x40`) |
| spy-program vector (slot 14's body) | `*(void**)(S + 4 + 0x158)` → `spyManager`, then `spyManager + 0x10` (`_Mylast` at `+0x14`) |
Arm DR0/DR1 on the two `_Mylast` words and the game announces the moment either vector grows, with
the EIP and two return addresses of whoever grew it. For the spy vector that is not just workload
confirmation — **it is the answer to "which UI produces a spy-program entry", because the return
addresses name the caller.** That is a better use of the next VM slot than playing twenty turns
blind.