# B6 — ship construction: the build queue, and where the missing destroyer actually comes from Lane B6. Branch `wip/build` off `main` `0f1c007`. This file was written and committed **before** the code (earned rule 2). Section 1 is the prediction as first written; section 2 is the falsification list; sections 3 onward are filled in after the measurement and correct section 1 in place where it was wrong. --- ## 1. Prediction, written before the build The brief's target is `hist[*]/stats[*]/shpt[0]` on both reference pairs — the archived ship census is one destroyer higher than ours, and lane E2 concluded that no phase we run builds a ship. That conclusion is right. The inference drawn from it — that `S11`'s build-queue sub-pass is what closes it — is **wrong**, and this lane predicts the pass closes **nothing**: > **Both reference pairs: closed 0, regressed 0.** > > `turn1-state.sav` and `turn2-state.sav` each carry exactly **three** `BQ` frames (one per > owned system: players 16, 32 and 576), and **every one of them holds zero orders**. Every > `hbq` in both files is `false`, so there is no ship-borne `BQ2` queue either. The only > `TurnCommands_v5` block belongs to player 16 and is the byte-identical empty one. So at the > moment the standalone loads either save, **the whole game contains no build order at all**, > and a faithful build-queue pass has nothing to advance. > > The destroyer the archive counts is created *inside* the turn: on the reference pair the > owner is player 32 ("Fane Lao"), an AI, and the same turn shows `NumDes` 5 → 6 (a design the > AI designed during the turn), `FNG/FNGNum` 0 → 1 (a fleet named), `Maint` 0 → 500, > `ShipRecs/srb[0]` 0 → 1 and a new `ShipRecs` design record, with `NMnx` 106 → 109 — three > ids issued in the order design(107), ship(108), fleet(109). The order that fed the queue was > issued by the AI and consumed in the same turn. > > **`shpt[0]` is blocked on AI order generation (`game/ai`, Rung B), not on S11.** This lane > moves the blocker; it does not close the leaf. What this lane does deliver, and what it is worth: * the build-queue advance read byte for byte from `Game::BuildQueue::ProcessTurn` (`0x00890d50`, real end `0x00891240` — Ghidra says 1230 bytes, the body runs 1264), which **independently confirms** lane B4's `sim::ProcessBuildQueue` and finds **one divergence** in it (§4, "What changed in the engine"); * the `ShipRecords` update the pass performs, whose per-class counter has **exactly one writer in the whole image**; * the pass wired into the standalone's S11 as its own reported sub-pass; * a real oracle for the pass that the campaign already owns and had not noticed: `zuul-turn16-noderoute.sav` → `zuul-turn17-rollpending.sav` is a genuine consecutive-turn pair (frames 16 and 17) in which **six orders complete and one is partially advanced** across two systems (§3, "The corpus oracle"). ## 2. How this prediction could be wrong, and the symptom of each way 1. **A build order exists somewhere I did not look.** The reader types unknown regions as `raw`; a queue hidden inside one would not show up in a tag scan. *Symptom:* the standalone reports a non-zero order count on a reference pair, and `closed` is not 0. 2. **The order is created before the spine rather than during it.** If the AI's orders were applied at load time by something the standalone also runs, the queue would be non-empty by S11 and the pass would build. *Symptom:* same as (1). (The measured `PvSav` says otherwise: Fane Lao's savings are 50,000 in the input file and the previous-turn snapshot in the output file is 38,100, so 11,900 left the treasury *between the save and phase 0* — the queue-time deduction of a build order the file does not contain.) 3. **The FIFO + stop-at-the-first-short-order model is wrong.** *Symptom:* on the `zuul-turn16 → zuul-turn17` pair there is **no** single non-negative integer point total per system that turns the observed before-queue into the observed after-queue. This is a real test: a per-order point budget, a non-FIFO order, or a "skip and continue" rule instead of "stop" each fail it. 4. **The `points <= 0` gate on the removal sweep is wrong.** Unobservable in this corpus and therefore carried as a labelled hypothesis, not a result. 5. **`shpt` is not the fleet walk.** If the census leaf were fed by `ShipRecs` rather than by walking fleets, the fix would be different. Lane E2 already falsifies this: the fleet walk reproduces 480/480 archived census leaves. --- ## 3. Measured Built on CT111 (`/srv/re-lab/build/sots-engine-b6`), report run there with `tools/standalone_report.py --binary …`. Host gates run separately, never `&&`-chained. | pair | baseline | after | **closed** | **regressed** | |---|---:|---:|---:|---:| | `turn1-state.sav` → `turn2-state.sav` | 209 | 158 | **51** | **0** | | `turn2-state.sav` → `turn3-state.sav` | 108 | 87 | **21** | **0** | Identical to the pre-lane baseline, which is the prediction holding: **this lane closed 0 and regressed 0.** `regressedPaths` is empty on both pairs. The prediction's own numbers, checked one by one: * three `BQ` frames per reference save (systems owned by players 16, 32 and 576), **0 orders in all three**, on both sides of both pairs — the standalone now prints this on its own line; * every `hbq` in `turn1/2/3-state.sav` is `false` (15, 16 and 17 of them), so **0 ship-borne orders**; * the four `CDT` ids are `Player.00000016.TurnCommands_v5` plus three `AIAgent` blobs, and the one command block is the 36-node empty one. Falsification hypotheses 1 and 2 are therefore both refuted by measurement rather than by argument, and hypothesis 3 is refuted by the corpus oracle below. Hypothesis 4 stands as a labelled hypothesis; hypothesis 5 was already refuted by lane E2. ### What the pass does on the saves that are not the reference pair `sots_turn --phases` now prints a `build queue:` line under S11 on every save: | save | queues | pending orders | demand (points) | |---|---:|---:|---:| | `turn1/2/3-state.sav` | 3 | 0 | 0 | | `human-turn2-orders.sav` | 20 | 0 | 0 | | `zuul-turn5-species5.sav` | 4 | 4 | 7,120 | | `zuul-turn16-noderoute.sav` | 4 | 7 | 12,099 | | `zuul-turn17-orders2.sav` | 4 | 2 | 5,109 | | `zuul-turn23-fleet23.sav` | 4 | 1 | 370 | Five of eleven saves exercise the pass the moment points exist. Nothing is committed: an order that advanced with no ship behind it leaves a state the game never produces. ### The corpus oracle, which the campaign already owned `zuul-turn16-noderoute.sav` (frame 16) → `zuul-turn17-rollpending.sav` (frame 17) is a real consecutive-turn pair. Two systems, six completions, one partial advance: | system | owner | before (`desID`:`conleft`) | after | solved points | |---|---|---|---|---:| | 384 | 16 | 608:1753, 576:1860, 576:1860 | 576:**1291** | **4182** | | 80 | 32 | 114:959, 114:1889 ×3 (+816:6974 appended in-turn) | 816:**3818** | **9782** | `test_construction.cpp` does not assume those totals — it **searches** 0..200,000 and asserts a solution exists and is **unique**. A per-order budget, a non-FIFO drain, or skip-instead-of-stop each make the search come back empty. The completions then reproduce the `ShipRecs` deltas the same two files carry (class-0 `srb` 2→4 and 53→57; design 608 2→3; a new record for 576; design 114 18→22; design 816 unchanged because its order did not finish). ## 4. What changed in the engine * **`game/sim/construction.{h,cpp}`** — `ShipRecords` and the completion bookkeeping. The per-class `built` counter's indexed increment has **exactly one writer in the whole image** (an image-wide byte scan for the form at that displacement returns one site, inside this pass), so a hull on the wire with that counter bumped came through the build queue and nothing else — the encounter spawner that also creates ships touches no `ShipRecords`. `RunSystemConstruction` wraps the point pass and keeps each completion's design id. * **`game/sim/colony.{h,cpp}`** — one corrected rule: with `points <= 0` the original skips the **removal sweep** as well, because the entry test branches to the epilogue. Carried as a labelled hypothesis; no corpus save can reach the state that shows it, and the workload that would is named in the header. * **`app/construction_phase.{h,cpp}`** — S11's build-queue sub-pass, reported on its own line. * **`app/phase_catalog.cpp`** — S11's blurb no longer calls the build queue the input boundary for the census leaf. ## 5. What this lane did NOT do, said plainly * **It did not close `shpt[0]`, and no build-queue work can.** The blocker moves to `game/ai`. * **It did not model the ship or the fleet at birth in code.** The chain is read end to end and written up in the RE repo (`findings/subsystems/ship-construction.md` §4): `StarShip` is 0xb0 bytes, its id comes from the object-id allocator and lands in `+0x04`, `FltID` is born NULL; the hull joins its system's **cached home fleet** (`ServerSystem+0x238`) and a fleet is created only when that slot is empty; every fleet born that way gets `FtFlg |= 0x400` — which **corrects lane B5**, that bit is not a retreat marker. It is not implemented because the newborn hull's stats are copied from design words the engine does not yet compute, and because no save exercises the path with an oracle behind it. * **The money charge has never fired.** No design in the corpus costs money, so the slot-9 refusal path is read from the bytes and never observed. * **`srl`, `srk`, `sri` carry no model.** Losses and kills are zero in all eleven saves.