lane ID: zero-residual id account for the canonical turn, and the collision story

Diffing the four master id lists in the Sim block accounts for every object created on
turn 2->3: ship 1760 (server counter 110), fleet 1776 (111), fleet 34 (client node 2,
counter 2), and DesignIDs unchanged at 43 -- so no design was created that turn and
1776 is a fleet, not a design. Sharpens the correction to turn-command-replay.md row 2.
Also works out what explanation (B) implies for CB's reloaded run: the client re-issues
18, collides in its own map, and the autosave still matches because only the server's
state is serialized.
This commit is contained in:
alex 2026-09-08 20:26:44 -04:00
parent 975c19a57d
commit c60ee36a0e

View file

@ -161,15 +161,29 @@ where `NMSz 16` / `NMLc 0` come from and why the host's ids all carry nibble 0.
**A correction to `turn-command-replay.md` §4 row 2** (earned rule 11). It says the server "issued
1712 and 1776 the same turn", and that design 18 and fleet 34 are "objects that do not exist in the
input save". Neither is quite right on the canonical pair:
input save". Neither is right on the canonical pair, and the master id lists say so exactly.
* 1712 is counter **107**, issued during turn **1→2** (NMnx 106 → 109 covers counters 107, 108, 109
= ids 1712, 1728, 1744). 1776 is counter 111, issued during turn **2→3** (109 → 111).
* **Design 18 is present in `turn2-state.sav`.** It is in the master `DesignIDs` list and in player
32's block, and it was allocated during turn 1→2. Only **fleet 34** is new on the canonical pair.
So the turn-2→3 build order names an id the input save already holds, and a faithful replay of
that pair has to mint exactly **one** id, not two. That is a materially smaller gap than the row
claims.
`turn2-state → turn3-state` moves NMnx **109 → 111**, so the server issued counters **110 and 111**
and nothing else. Diffing the four master lists in the `Sim` block gives a **zero-residual account
of every object created on the reference turn** — the id-level counterpart of RB's `ModCount = 24`:
| id | counter | node | list | what |
|---:|---:|---:|---|---|
| **1760** | 110 | 0 | `ShipID` 16 → 17 | the ship the build order built |
| **1776** | 111 | 0 | `FleetIDs` 7 → 8 | a server-created fleet |
| **34** | 2 | **2** | `FleetIDs` 7 → 8 | **the client-created fleet** |
| — | — | — | `DesignIDs` **43 → 43** | **no design was created that turn** |
| — | — | — | `PlayerIDs` 8, `SystemIDs` 28 | unchanged |
Three objects, three ids, and the counters on both nodes are contiguous with no gaps. So:
* 1712 is counter **107** and was issued during turn **1→2**, not 2→3 (NMnx 106 → 109 covers
counters 107, 108, 109 = ids 1712, 1728, 1744). 1776 is a **fleet**, not a design.
* **Design 18 is already in `turn2-state.sav`** — in the master `DesignIDs` list (43 entries, and
still 43 at turn 3) and in player 32's block. It was allocated during turn 1→2. The turn-2→3
build order therefore names a design the input save already holds, and **only fleet 34 is new**.
A faithful replay of the canonical pair mints exactly **one** client id, not two. That is a
materially smaller gap than the row claims.
Everything else in the row stands, including the load-bearing part: allocate on apply and you get
`1792` where the original has `34`.
@ -201,7 +215,18 @@ Two explanations survive, and this lane cannot separate them without a hook:
which never reached the save** — a local design object, a task, an order — taking counter 1, so
the fleet got counter 2. This fits the read code exactly and requires nothing unread.
**The probe is one hook and it is cheap** (earned rule 18, and rule 20 — instrument the *entry*):
**(B) has a specific form that fits everything, including the master-list diff above.** In the
*continuous* reference session (turn 1 → 3 in one process, which is where `turn2-state` and
`turn3-state` come from) the client allocated 18 for its design in turn 1→2 and 34 for its fleet in
turn 2→3 — counters 1 then 2, no gap, nothing extra needed. In CB's *reloaded* run the client's
counter started at 0 again, so its first allocation of the turn would be counter 1 = **18**, an id
the loaded board already holds; `IDMap::Insert` logs and inserts anyway; the fleet then takes
counter 2 = 34. On that reading **the collision has already happened, in a run whose autosave
matched the oracle** — which is exactly what you would expect, because the colliding object lives in
the *client's* map and only the server's state is serialized. It also predicts what the earlier
allocation was: whatever the AI client makes each turn before it makes a fleet.
That is a story, not a measurement. **The probe is one hook and it is cheap** (earned rule 18, and rule 20 — instrument the *entry*):
detour `IDMap::AllocateID` 0x008b8ae0 and log `(this, nodeIndex, resulting id, return address)` for
one `turn2-state → turn3-state` End Turn. (A) predicts the client's very first allocation that turn
returns 34; (B) predicts it returns 18 from a site that is not the fleet creator. **A count alone