lane E2: T36 measured with the ship census wired in -- 29 closed / 7 regressed, and the two things it is still blocked on
standalone_report.py regenerated from sots-engine wip/turnrecord. The reference pair does not move by design: T36 does not commit by default, so 209 -> 204, closed 5 / regressed 0 and 108 -> 103, closed 5 / regressed 0 are unchanged. What moved in section 6 is the phase tally, 14/44 modelled and 8 committed -> 15/44 and 9, from the A2 and D2 merges. verified stays 0 on both tables. t36-turnrecord.md carries the measurement the default run cannot show: with --commit-blocked=T36 and a data root, the reference pair closes 29 leaves and regresses 7 (was 9). Two of the three census leaves close; the third is short by exactly one destroyer on both pairs, because no phase we run builds a ship. The census model itself reproduces 480/480 archived census leaves over 11 saves with 0 mismatches. T36 stays blocked: sav and inc on lane E1's budget, and one ship count on unmodelled construction. A field-granular commit knob is not implementable -- the archived record is one struct on the wire and has no hole to leave. Coverage: 32 of 480 census leaves are nonzero anywhere in the corpus; cls1 and cls2's platform count are unexercised, not verified.
This commit is contained in:
parent
3a44f9178b
commit
fbe3c7bd86
3 changed files with 186 additions and 42 deletions
|
|
@ -1,8 +1,8 @@
|
|||
# standalone vs the oracle
|
||||
|
||||
generated 2026-09-08T15:35:00Z binary /home/alex/sots-engine-wt-yield/build-host/src/app/sots_turn
|
||||
generated 2026-09-08T17:23:54Z binary /home/alex/sots-engine-wt-turnrecord/build-host/src/app/sots_turn
|
||||
|
||||
phases: 14/44 of the two turn drivers modelled, 8 committed (implemented 2, partial 6, blocked 6, stub 30)
|
||||
phases: 15/44 of the two turn drivers modelled, 9 committed (implemented 3, partial 6, blocked 6, stub 29)
|
||||
3/37 of the post-combat tail modelled
|
||||
|
||||
## turn1-state.sav -> turn2-state.sav (real End Turn)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"schema": "sots-standalone-status/1",
|
||||
"generated": "2026-09-08T15:35:00Z",
|
||||
"binary": "/home/alex/sots-engine-wt-yield/build-host/src/app/sots_turn",
|
||||
"generated": "2026-09-08T17:23:54Z",
|
||||
"binary": "/home/alex/sots-engine-wt-turnrecord/build-host/src/app/sots_turn",
|
||||
"reference": {
|
||||
"input": "turn1-state.sav",
|
||||
"oracle": "turn2-state.sav",
|
||||
|
|
@ -30,12 +30,12 @@
|
|||
"phases": {
|
||||
"total": 44,
|
||||
"verified": 0,
|
||||
"implemented": 2,
|
||||
"implemented": 3,
|
||||
"partial": 6,
|
||||
"blocked": 6,
|
||||
"stub": 30,
|
||||
"modelled": 14,
|
||||
"committed": 8
|
||||
"stub": 29,
|
||||
"modelled": 15,
|
||||
"committed": 9
|
||||
},
|
||||
"tailPhases": {
|
||||
"total": 37,
|
||||
|
|
@ -62,16 +62,17 @@
|
|||
" 591376 inflated bytes, 0 error(s), 0 warning(s)",
|
||||
" turn 1, frame 1, modCount 0, 8 player(s), 28 system(s), 6 fleet(s)",
|
||||
"roundtrip (untouched): byte-identical (591376 bytes)",
|
||||
"data: /tmp/claude-1000/-home-alex/ec8e34f8-af37-4ef2-a309-ed6a15293097/scratchpad/data -- 885 section(s) over 7 race(s), 46 load problem(s)",
|
||||
"",
|
||||
"phases",
|
||||
" turn drivers (the milestone's denominator): 14 of 44 modelled, 8 committed",
|
||||
" verified 0 implemented 2 partial 6 blocked 6 stub 30",
|
||||
" turn drivers (the milestone's denominator): 15 of 44 modelled, 9 committed",
|
||||
" verified 0 implemented 3 partial 6 blocked 6 stub 29",
|
||||
" post-combat tail (written to the autosave, tracked separately): 3 of 37 modelled",
|
||||
" verified 0 implemented 1 partial 0 blocked 2 stub 34",
|
||||
"",
|
||||
"this run",
|
||||
" leaves written 7",
|
||||
" leaves NOT written by a blocked phase 64",
|
||||
" leaves NOT written by a blocked phase 120",
|
||||
" generator words consumed 16 (state loaded, left untouched)",
|
||||
" generator words NOT accounted (never netted off the above):",
|
||||
" - encounter detection draws one unit value and one bounded integer per turn on every turn measured (2 words), with no derived rule behind the count -- its bound is the product of the contact and detector counts, so it is left unmodelled",
|
||||
|
|
@ -80,8 +81,8 @@
|
|||
" ! the generator advanced during this run but the save keeps its original state (--commit-rng to write it)",
|
||||
" ! the modelled words are a LOWER BOUND on the turn's cost, so a committed generator is short by the unaccounted sites below and its drawn VALUES are not the game's",
|
||||
"",
|
||||
"wrote /tmp/tmp_cz5dsbf/post-turn1-state.sav (65145 bytes gzipped, 591376 inflated)",
|
||||
"metric -> /tmp/tmp_cz5dsbf/metric-turn1-state.sav.json"
|
||||
"wrote /tmp/tmpbodkynxg/post-turn1-state.sav (65145 bytes gzipped, 591376 inflated)",
|
||||
"metric -> /tmp/tmpbodkynxg/metric-turn1-state.sav.json"
|
||||
],
|
||||
"coverage": {
|
||||
"input": true,
|
||||
|
|
@ -89,9 +90,9 @@
|
|||
"ours": true
|
||||
},
|
||||
"roots": {
|
||||
"input": "ce36f6e5a2e41ae1a90d5b7fefda8f8f",
|
||||
"oracle": "b0e2448b83ad1da254d526f4f553ff8d",
|
||||
"ours": "8836ea25910897ec7a9b7d69fcaf6c87"
|
||||
"input": "64b836b966299d9b72fc02244ed31e31",
|
||||
"oracle": "4bed514f853332b9fa35b388c3aa5558",
|
||||
"ours": "1acc372b1d410b966ab544dffbe43cb5"
|
||||
},
|
||||
"baselineDiverging": 209,
|
||||
"divergingAfterTurn": 204,
|
||||
|
|
@ -151,16 +152,16 @@
|
|||
"standalone": {
|
||||
"schema": "sots-standalone-metric/1",
|
||||
"input": "/home/alex/sots-re/verify/results/saves/turn1-state.sav",
|
||||
"output": "/tmp/tmp_cz5dsbf/post-turn1-state.sav",
|
||||
"output": "/tmp/tmpbodkynxg/post-turn1-state.sav",
|
||||
"spine": {
|
||||
"total": 44,
|
||||
"verified": 0,
|
||||
"implemented": 2,
|
||||
"implemented": 3,
|
||||
"partial": 6,
|
||||
"blocked": 6,
|
||||
"stub": 30,
|
||||
"modelled": 14,
|
||||
"committed": 8
|
||||
"stub": 29,
|
||||
"modelled": 15,
|
||||
"committed": 9
|
||||
},
|
||||
"tail": {
|
||||
"total": 37,
|
||||
|
|
@ -174,7 +175,7 @@
|
|||
},
|
||||
"run": {
|
||||
"leafWrites": 7,
|
||||
"blockedLeafWrites": 64,
|
||||
"blockedLeafWrites": 120,
|
||||
"rngWords": 16,
|
||||
"rngLoaded": true,
|
||||
"rngCommitted": false,
|
||||
|
|
@ -239,8 +240,8 @@
|
|||
"driver": "StrategyServer::ProcessTurn",
|
||||
"id": "S04",
|
||||
"name": "RebuildAllianceMasks",
|
||||
"status": "stub",
|
||||
"ran": 0,
|
||||
"status": "implemented",
|
||||
"ran": 8,
|
||||
"writes": 0,
|
||||
"blockedWrites": 0,
|
||||
"rng": 0
|
||||
|
|
@ -1002,7 +1003,7 @@
|
|||
"status": "blocked",
|
||||
"ran": 8,
|
||||
"writes": 0,
|
||||
"blockedWrites": 48,
|
||||
"blockedWrites": 104,
|
||||
"rng": 0
|
||||
},
|
||||
{
|
||||
|
|
@ -1028,16 +1029,17 @@
|
|||
" 603360 inflated bytes, 0 error(s), 0 warning(s)",
|
||||
" turn 2, frame 2, modCount 12, 8 player(s), 28 system(s), 7 fleet(s)",
|
||||
"roundtrip (untouched): byte-identical (603360 bytes)",
|
||||
"data: /tmp/claude-1000/-home-alex/ec8e34f8-af37-4ef2-a309-ed6a15293097/scratchpad/data -- 885 section(s) over 7 race(s), 46 load problem(s)",
|
||||
"",
|
||||
"phases",
|
||||
" turn drivers (the milestone's denominator): 14 of 44 modelled, 8 committed",
|
||||
" verified 0 implemented 2 partial 6 blocked 6 stub 30",
|
||||
" turn drivers (the milestone's denominator): 15 of 44 modelled, 9 committed",
|
||||
" verified 0 implemented 3 partial 6 blocked 6 stub 29",
|
||||
" post-combat tail (written to the autosave, tracked separately): 3 of 37 modelled",
|
||||
" verified 0 implemented 1 partial 0 blocked 2 stub 34",
|
||||
"",
|
||||
"this run",
|
||||
" leaves written 7",
|
||||
" leaves NOT written by a blocked phase 58",
|
||||
" leaves NOT written by a blocked phase 114",
|
||||
" generator words consumed 16 (state loaded, left untouched)",
|
||||
" generator words NOT accounted (never netted off the above):",
|
||||
" - encounter detection draws one unit value and one bounded integer per turn on every turn measured (2 words), with no derived rule behind the count -- its bound is the product of the contact and detector counts, so it is left unmodelled",
|
||||
|
|
@ -1046,8 +1048,8 @@
|
|||
" ! the generator advanced during this run but the save keeps its original state (--commit-rng to write it)",
|
||||
" ! the modelled words are a LOWER BOUND on the turn's cost, so a committed generator is short by the unaccounted sites below and its drawn VALUES are not the game's",
|
||||
"",
|
||||
"wrote /tmp/tmp_cz5dsbf/post-turn2-state.sav (66883 bytes gzipped, 603360 inflated)",
|
||||
"metric -> /tmp/tmp_cz5dsbf/metric-turn2-state.sav.json"
|
||||
"wrote /tmp/tmpbodkynxg/post-turn2-state.sav (66883 bytes gzipped, 603360 inflated)",
|
||||
"metric -> /tmp/tmpbodkynxg/metric-turn2-state.sav.json"
|
||||
],
|
||||
"coverage": {
|
||||
"input": true,
|
||||
|
|
@ -1055,9 +1057,9 @@
|
|||
"ours": true
|
||||
},
|
||||
"roots": {
|
||||
"input": "b0e2448b83ad1da254d526f4f553ff8d",
|
||||
"oracle": "7ea50b486c2e9f9f1a8ec5b9646165a8",
|
||||
"ours": "6523aa25786814380a26f48c046da22b"
|
||||
"input": "4bed514f853332b9fa35b388c3aa5558",
|
||||
"oracle": "e9c161e311f8ef8fad6f1aa1903dcf3f",
|
||||
"ours": "22071ae1303f308a93b805e940b626b5"
|
||||
},
|
||||
"baselineDiverging": 108,
|
||||
"divergingAfterTurn": 103,
|
||||
|
|
@ -1117,16 +1119,16 @@
|
|||
"standalone": {
|
||||
"schema": "sots-standalone-metric/1",
|
||||
"input": "/home/alex/sots-re/verify/results/saves/turn2-state.sav",
|
||||
"output": "/tmp/tmp_cz5dsbf/post-turn2-state.sav",
|
||||
"output": "/tmp/tmpbodkynxg/post-turn2-state.sav",
|
||||
"spine": {
|
||||
"total": 44,
|
||||
"verified": 0,
|
||||
"implemented": 2,
|
||||
"implemented": 3,
|
||||
"partial": 6,
|
||||
"blocked": 6,
|
||||
"stub": 30,
|
||||
"modelled": 14,
|
||||
"committed": 8
|
||||
"stub": 29,
|
||||
"modelled": 15,
|
||||
"committed": 9
|
||||
},
|
||||
"tail": {
|
||||
"total": 37,
|
||||
|
|
@ -1140,7 +1142,7 @@
|
|||
},
|
||||
"run": {
|
||||
"leafWrites": 7,
|
||||
"blockedLeafWrites": 58,
|
||||
"blockedLeafWrites": 114,
|
||||
"rngWords": 16,
|
||||
"rngLoaded": true,
|
||||
"rngCommitted": false,
|
||||
|
|
@ -1205,8 +1207,8 @@
|
|||
"driver": "StrategyServer::ProcessTurn",
|
||||
"id": "S04",
|
||||
"name": "RebuildAllianceMasks",
|
||||
"status": "stub",
|
||||
"ran": 0,
|
||||
"status": "implemented",
|
||||
"ran": 8,
|
||||
"writes": 0,
|
||||
"blockedWrites": 0,
|
||||
"rng": 0
|
||||
|
|
@ -1968,7 +1970,7 @@
|
|||
"status": "blocked",
|
||||
"ran": 8,
|
||||
"writes": 0,
|
||||
"blockedWrites": 48,
|
||||
"blockedWrites": 104,
|
||||
"rng": 0
|
||||
},
|
||||
{
|
||||
|
|
|
|||
142
verify/results/standalone/t36-turnrecord.md
Normal file
142
verify/results/standalone/t36-turnrecord.md
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# T36 `FinalizeTurnRecords` — the ship census wired in, and what T36 is still blocked on
|
||||
|
||||
Lane E2, 2026-09-08. Engine branch `wip/turnrecord` off `sots-engine` main `0592104`.
|
||||
Measured with `verify/state-checksum/state_checksum.py` leaves, the same currency as
|
||||
`report.txt`. **`closed` and `regressed` are reported separately on every line; nothing here
|
||||
is netted.**
|
||||
|
||||
`status.json` and `report.txt` in this directory were regenerated from the same binary.
|
||||
|
||||
## What changed in the engine
|
||||
|
||||
`ShipCensus` (lane D2) is wired into `BuildTurnRecord`. The classification walks every
|
||||
player's designs — normal and legacy, one id space — resolves each design's sections against
|
||||
the section catalog, and walks the global fleet list keyed by `Flt.PID` against each player's
|
||||
**object id**, not its vector position. The record's modelled field count goes 7 → 13.
|
||||
|
||||
Two things the standalone did not have before:
|
||||
|
||||
* **a data root.** The census cannot come from a save: a design's hull size and its
|
||||
defence-platform flag are recomputed from the section catalog whenever the design changes
|
||||
and are never written down. `sots_turn --data DIR` (or `$SOTS_DATA_DIR`) supplies it. No
|
||||
game data enters the repo, and without a root the six counters are reported unmodelled
|
||||
rather than written as six zeros.
|
||||
* **`--commit-blocked=IDS` / `--commit-blocked-except=IDS`.** `--commit-blocked` alone is
|
||||
all-or-nothing across every blocked phase, which reports one closed count and one regressed
|
||||
count for all of them together — the netting the campaign does not do. The two lists narrow
|
||||
it to named phase ids, so a phase's own contribution can be measured. Every number below
|
||||
that names T36 was measured with `--commit-blocked=T36`.
|
||||
|
||||
## Prediction, written before the code (rule 2)
|
||||
|
||||
> Default mode unchanged at 209 → 204, closed 5 / regressed 0 and 108 → 103, closed 5 /
|
||||
> regressed 0. With `--commit-blocked`, the three census leaves close and the regressed list
|
||||
> falls from 9 to 6 on pair 1 (`inc`×3 + `sav`×3, the budget) and from 11 to 8 on pair 2.
|
||||
|
||||
Falsification hypothesis 1 was: *the census is built from the fleet list as it stands, which
|
||||
is pre-construction; if a real turn's census differs from the input turn's, the leaf does not
|
||||
close and shows a third value.*
|
||||
|
||||
**That is what happened.** Two of the three closed; the third did not, and the symptom was
|
||||
exactly the one written down.
|
||||
|
||||
## Measured — reference pair `turn1-state.sav` → `turn2-state.sav`
|
||||
|
||||
| run | closed | regressed |
|
||||
|---|---:|---:|
|
||||
| default (no commit flags) — before and after this lane | 5 | 0 |
|
||||
| `--commit-blocked`, before this lane | 29 | 9 |
|
||||
| `--commit-blocked`, after | 29 | **7** |
|
||||
| `--commit-blocked=T36` alone, with a data root | 29 | 7 |
|
||||
| `--commit-blocked=T36` alone, no data root | 29 | 9 |
|
||||
|
||||
Regressed after, named: `hist[0]/stats[1]/{sav,inc}`, `hist[1]/stats[1]/{sav,inc}`,
|
||||
`hist[7]/stats[1]/{sav,inc}` — the budget, lane E1 — and `hist[1]/stats[1]/shpt[0]`, ours 0,
|
||||
archived 1.
|
||||
|
||||
Closed by the census: `hist[4]/stats[1]/shpt[2]` (5 dreadnoughts) and
|
||||
`hist[7]/stats[1]/satt[0]` (10 defence platforms).
|
||||
|
||||
## Measured — second pair `turn2-state.sav` → `turn3-state.sav`
|
||||
|
||||
| run | closed | regressed |
|
||||
|---|---:|---:|
|
||||
| default | 5 | 0 |
|
||||
| `--commit-blocked`, before this lane | 13 | 11 |
|
||||
| `--commit-blocked`, after | 13 | **9** |
|
||||
| `--commit-blocked=T36` alone, with a data root | 13 | 7 |
|
||||
| `--commit-blocked=T36` alone, no data root | 13 | 9 |
|
||||
|
||||
The two extra regressions under a full `--commit-blocked` are `Player/Status` on two players.
|
||||
They are S31's, not T36's, and the new id filter separates them cleanly — which is the whole
|
||||
reason the filter exists.
|
||||
|
||||
## The one census leaf that did not close is not a census error
|
||||
|
||||
`app_test_turn_record`, extended this lane, now compares the six counters against the record
|
||||
the game itself archived, on every save: **11 saves, 80 player-records, 1040 fields, 480 of
|
||||
them census leaves, 0 mismatches.** That is the same 480/480 lane D2 got, reproduced through
|
||||
the app's own code path (which visits slots in the original's in-memory order rather than the
|
||||
wire's) rather than through D2's test.
|
||||
|
||||
So the model reproduces the archived census exactly for the turn the save was written at. The
|
||||
remaining leaf is `shpt[0]` for one player, and the archived value is higher than ours by
|
||||
exactly one destroyer on **both** pairs (0 vs 1, then 1 vs 2). That player's build queue
|
||||
completes one ship per turn, and no phase the standalone runs creates a ship — S11's
|
||||
build-queue sub-pass is the declared input boundary. The leaf is short by the turn's
|
||||
construction, not wrong about classification.
|
||||
|
||||
**This is a new named blocker, and it is not lane E1's and not mine.**
|
||||
|
||||
## T36's status: still `Blocked`, on two named things, neither of them in this phase
|
||||
|
||||
1. `sav` and `inc` for the new turn come from P01/P02, blocked on the per-system money
|
||||
output — **lane E1**.
|
||||
2. `shpt[0]` is short by the ships the turn builds — **ship construction, unmodelled**.
|
||||
|
||||
There is **no field-granular knob that could rescue this**, and the brief's
|
||||
`--commit-blocked-except=<fields>` idea is not implementable as stated: the archived record
|
||||
is one struct on the wire, so `sav` and `inc` cannot be left out while the other eleven fields
|
||||
are written. The format has no hole. Committing is all-or-nothing at the record, so the
|
||||
choice is 29 closed with 7 confidently-wrong leaves, or neither — and 7 wrong leaves is not
|
||||
worth 29 that a later lane closes for free. What *is* implementable, and is implemented, is
|
||||
the same convention at **phase** granularity, which is what produced the isolated rows above.
|
||||
|
||||
T36 flips the moment E1 lands, minus one leaf that waits on construction.
|
||||
|
||||
## Coverage, stated as loudly as the verdict (rules 6 and 15)
|
||||
|
||||
Only **32 of the 480** archived census leaves are nonzero anywhere in the 11-save corpus.
|
||||
Per leaf (cls0 shpt/satt, cls1 shpt/satt, cls2 shpt/satt) = **18/3, 0/0, 11/0**.
|
||||
|
||||
* **cls1 (cruisers), ships and platforms both, and `satt` for cls2, have never been observed
|
||||
nonzero.** Three of the six counters are unexercised hypotheses, not verified. A zero leaf
|
||||
agrees for free. The corpus needs a cruiser and a dreadnought-hull platform
|
||||
(roadmap §5 already lists both).
|
||||
* The other four words of each class group — `shpl`, `shpk`, `satl`, `satk`, losses and kills
|
||||
— are zero throughout the corpus, and this phase writes zeros with no model behind them.
|
||||
Listed in the phase's own unmodelled list rather than passed off as agreement.
|
||||
* Hull size is an **assignment** in slot order, so the visit order is load-bearing in
|
||||
principle. The original's stats pass walks mission, command, engine; the wire writes
|
||||
command, mission, engine. This code reproduces the memory order. **Design rule A6 makes
|
||||
every shipped design class-homogeneous, so no save in the corpus can tell the two orders
|
||||
apart** — the order is carried from the instruction stream, not confirmed by these bytes.
|
||||
* `verified` stays **0**. Nothing here was compared against a running game; this lane holds no
|
||||
VM. The comparison is against bytes the original wrote, which is the strongest check
|
||||
available without one, and it is not the same thing.
|
||||
|
||||
## Gates
|
||||
|
||||
Run as separate commands, never chained (rule 13):
|
||||
|
||||
* `tools/clean_room_check.sh` — OK
|
||||
* host `ctest`, no `SOTS_SAVES_DIR` — 45/45
|
||||
* host `ctest`, `SOTS_SAVES_DIR` set — 45/45
|
||||
* host `ctest`, `SOTS_SAVES_DIR` + `SOTS_DATA_DIR` set — 44/45. The one failure is
|
||||
`game_data_realdata`, and it **fails identically on unmodified `main`** with the same data
|
||||
root: that test pins section and bank counts against the campaign's canonical extraction and
|
||||
my extraction of `sots.gob` is a different data version (9 pinned counts differ, e.g. a
|
||||
`human/DEWAR` section the pin says should be absent). It is a data-root mismatch, not a
|
||||
regression, and the census matched all 480 archived leaves against that same root anyway.
|
||||
* **Shim cross-build NOT run**: no `i686-w64-mingw32-g++` on this host. No `src/shim` file was
|
||||
touched, but per rule 13 the integrator should run the CT111 shim build before pushing.
|
||||
Loading…
Add table
Reference in a new issue