diff --git a/findings/subsystems/id-allocation.md b/findings/subsystems/id-allocation.md index 6e74cc5..6c77fd2 100644 --- a/findings/subsystems/id-allocation.md +++ b/findings/subsystems/id-allocation.md @@ -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