diff --git a/campaign/board.md b/campaign/board.md index e923a05..7acf92e 100644 --- a/campaign/board.md +++ b/campaign/board.md @@ -399,9 +399,12 @@ Status flow: `backlog → in-progress → mapped → verified` (or `blocked`). | CORRECTION: L3's own save falsified the freighter hypothesis | verify | verified | high | 100% | 2026-09-09 | L3 3.5 says "every trade sector has `tsnumflt = 0`". **Wrong about its own save**: `human-turn15-spyprogram.sav` sector TradeID 816 has **`tsnumflt 1`, `tsflt 2736` - the AI's Freighters fleet, on an active route, across all seven of L3's instrumented turns, with the callees at 0.** So "freighters in service" was falsified BEFORE lane AC started. Also **`tsflt` is not a freighter list** - at turn 20 sector 6 reads tsflt 3744, which is AC's WARSHIP fleet. **[CORRECTED 2026-09-09 by the resolver: LANE V2 IS VINDICATED AND WAS MISQUOTED.** `indirect-edges.md` 3.1 lists trade slot 13 as **DRAWING at 0x00820e18 - the exact site that fired.** "Closure 185, draw-free" is V2's row for **slot 9**, the route rebuilder lane L3 trapped. Lane AC carried the number to the wrong slot and I published it. V2's static reading was right all along.] | | spies2 NOT settled, and the deploy state is unreachable in that game | verify | backlog | — | 0% | 2026-09-09 | AC could not deploy a spy and **declined to infer around it**. `ARes2` on ServerSystem **is the asteroid-belt field** (non-zero values 1703-2531 sit inside SYSTEM_MIN/MAX_ASTEROID_RESOURCES 1500-3000); in L3's save only **5 of 28 systems have a belt and NONE is AI-owned** - the textbook deploy target does not exist in that game. **AND L3's "the next condition is spyon" UNDERSTATES IT BY THREE TURNS AND A DETECTION ROLL**: the chain needs `deat != 0`, then `sdet` set by a detection roll on an EARLIER turn, then `turn - sdet >= 3`. **That detection roll is itself an RNG draw, inline in vslot13, not inside P** - so a probe on P reading 0 does not mean the spy half is draw-free | | AC's coverage caveat, stated | verify | backlog | — | 0% | 2026-09-09 | Two turns, one fleet, one sector, one ship. **`Slot13RngCalleeA` has a short-circuit (`design+0x12c > 1` returns true without rolling) that AC's hull did not exercise**, so a different raider could cost **0 words while still firing B**. The one-word-per-turn figure is this raider's, not the site's | -| ratchet warning: AC's saves carry new content | verify | backlog | — | 0% | 2026-09-09 | `ac-turn20-raider.sav` and `ac-turn22-raider.sav` (both --strict clean) carry **`CnRad`, `tscr = 253`, three `rt` route records, and a StarFleet whose `LocID` is a TradeSector id - the first fleet in the corpus located anywhere but a star.** Expect the coverage ratchet to break (rule 27); that is the content to type | +| ratchet warning: AC's saves carry new content | verify | backlog | — | 0% | 2026-09-09 | `ac-turn20-raider.sav` and `ac-turn22-raider.sav` (both --strict clean) carry **`CnRad`, `tscr = 253`, three `rt` route records, and a StarFleet whose `LocID` is a TradeSector id - the first fleet in the corpus located anywhere but a star.** Expect the coverage ratchet to break (rule 27); that is the content to type. **[RESOLVED 2026-09-09, PREDICTION WRONG: the ratchet did NOT break.** Both saves score 100.0% typed and round trip byte-identically - the novelty lands in fields that were already typed, so a shape-indexed instrument cannot see it. That blindness is now measured on the other axis: 234 of 724 fields have only ever held one value. See the value-axis rows below.] | | LAB: jmove reads but does not commit; static-link the shim | meta | verified | high | 100% | 2026-09-09 | **`jmove` (jiggle) is required to READ the star-map hover readout but SILENTLY FAILS TO COMMIT a move** - use plain `click` to confirm, and verify the order from `Flt.LocID` in the autosave, never from the fleet panel. And on the WSL dev box the mingw toolchain links **posix-threads**, so the shim DLL imports libwinpthread-1.dll and **the game dies at load with 0xC0000135 and NO LOG AT ALL**; `-DCMAKE_SHARED_LINKER_FLAGS=-static` fixes it. AC did not change the toolchain file - flagged for the integrator | | MY ERROR: Rung B IS affected - replay runs the tail | meta | verified | high | 100% | 2026-09-09 | I told lane RB that command-stream replay was unaffected by a tail draw "because replay runs no AI". **Wrong.** The resolver: this is a **sim draw on S+0x16c keyed on save state**, and `--turn-commands` replay **runs the tail** (`command_replay.h:83`). **Rung B needs the same generator model as Rung A.** The canonical pair happens to be untouched by the predicate (tscr = 252, no fleet at a sector), so nothing measured is invalidated - but the reasoning I gave RB was wrong and would have bitten on any raid-turn replay | | AC's numbers are at probes=11, which is NOT byte-neutral | verify | verified | high | 100% | 2026-09-09 | The resolver's exoneration is partial and precise. **The tail's +1 stands** - control 0 / active 1 under identical guest, build and config, with the callee going 0 -> 1 entries on the same turns. **But every AC number is at `probes=11`, which lane H bisected as NOT byte-neutral**, and AC's own predictions doc promised a `probes=8` primary reading **it never took**. Consequences: the turn totals 20/18 are **not calibration data**, and **no AC autosave is an oracle pair**. `ac-turn22-raider.sav` remains a valid INPUT state. A reminder that rule 19's control must be at the configuration you are quoting | | lane Z's ledger: the method is sound, the AUDIT is not | meta | verified | high | 100% | 2026-09-09 | The resolver's structural ruling. A bracket state-difference **cannot miss a draw that happens** - the instrument is sound. But **the audit is indexed by FIRING**: a site enters the table when a state reaches it, so **it can only grow by luck** (hives, now the raid roll - two qualifications in two days). **What makes the next audit complete: GATE-INDEXED** - one row per statically reachable site (lane I's 22+28, V2's 8, L1's hive, node-line decay, the SVSO overrides), each carrying **either a measured firing or a decoded predicate on save fields plus the corpus count satisfying it**. Today's unfilled rows are named: 0x0088b613, 0x0082cdb8, 0x0088dc43, the spy-13 family, and the SVSO generator which is still unknown | | rule 24 earns its keep in the OTHER direction | meta | verified | med | 100% | 2026-09-09 | The AC merge gate, run properly for the first time on **fresh** build dirs on both halves, failed the shim cross-build: `ai_orders.cpp` tripped `-Werror=array-bounds` because gcc reasons about a `reinterpret_cast` from `uintptr_t` as an object of its own once the read inlines at a constant offset. Three earlier gate runs on a **reused** `build-shim` reported the DLL present and green - they were relinking nothing. Note the direction: rule 24's first instance was a stale dir hiding a **fix** (a passing test that was really stale); this one is a stale dir hiding a **break**. A reused build directory does not bias toward good news or bad, it reports the past. Fixed with the asm barrier `watchpoints.cpp` already uses for its TIB reads; engine `0c6d0a3`, host 58/58 and shim both green on fresh dirs | +| the coverage ratchet is blind along the value axis | verify | verified | high | 100% | 2026-09-09 | Board row 402 predicted AC's two saves would break the ratchet. **They did not** - both score 100.0% typed and round trip byte-identically, because their novelty (`LocID` at a TradeSector, `tscr = 253`, three `rt` records) lands in fields that were **already typed**. `CoverageArchive` is indexed by **shape**, exactly as lane Z's ledger was indexed by **firing**: both instruments are sound at what they measure and both are blind where the surprises come from. New instrument `DomainArchive` + `test_domains` (engine `e62d974`) measures the other axis | +| 234 of 724 typed fields have only ever held one value | verify | verified | high | 100% | 2026-09-09 | The census over 22 saves: **724 fields observed, 490 vary, 234 constant across the whole corpus - 32%**. `tscr` was one of the 234 until a one-turn tech moved it. Clusters: `Sim.Player.dipstats` 11 (no corpus game reached a treaty), `SvSctOb.EncObj` 16 (**no corpus game triggered a random encounter**), `spymgr.spy` 12, `CmbR.CRPlSv2.TacReports` 9 (tactical reports never populated). Individually suspicious because non-zero and non-round: `spy.atto = 3728`, `EncObj.infest.mtrn = INT32_MAX` (a sentinel), `CD.researchBoostFraction = 0.999215`. Rule 23's thin coverage with a number on it, rule 8's eighteen fields generalised. Ratchet at 490 varying; `findings/subsystems/value-domain-census.md` | +| the spy sub-object is unexercised: 0 of 22 before lane AS starts | verify | verified | high | 100% | 2026-09-09 | Direct input to lane AS. The census reads `spymgr.spy.sdet = -1` in **all 22 saves**, with `cbh`/`cm`/`cmo`/`deat`/`sdo` all 0 - `sdet = -1` sits where a detection result would go and is a sentinel. So the **corpus count for any spy-path predicate is 0 of 22 before the lane takes a single measurement**, which is exactly what rule 28 requires before a zero may be called a negative. Also gives lane AG a mechanically computable corpus-count column: a predicate resting on any of the 234 constants has count 0 by construction | diff --git a/findings/subsystems/value-domain-census.md b/findings/subsystems/value-domain-census.md new file mode 100644 index 0000000..c05729d --- /dev/null +++ b/findings/subsystems/value-domain-census.md @@ -0,0 +1,100 @@ +# The value-domain census: a third of the save format has only ever held one value + +**Owner / date:** integrator · 2026-09-09 · host only, no VM +**Instrument:** `mars::stream::DomainArchive` (`sots-engine/src/mars/stream/probe.h`), +`tests/mars_stream/test_domains.cpp`, run over the 22-save corpus. + +## Why this exists + +Board row 402 predicted that lane AC's two new saves would break the coverage ratchet: they carry +`CnRad`, `tscr = 253`, three `rt` route records, and a StarFleet whose `LocID` is a TradeSector id — +the first fleet in the corpus located anywhere but a star. + +**The prediction was wrong, and being wrong is the finding.** Both saves parse `--strict` clean, round +trip byte-identically, and score **100.0% typed**. The ratchet did not move a hair. + +The reason is structural, and it is the same shape as the defect the resolver found in lane Z's RNG +ledger. `CoverageArchive` is indexed by **shape**: it asks "does a field name this item". Lane Z's +audit was indexed by **firing**: it asked "did a draw happen here in a state we ran". Both instruments +are sound at what they measure, and both are blind along the axis where the surprises have actually +been coming from. A save can carry content nobody has ever modelled and still score 100%, because the +novelty lands in a field that was already typed. `LocID` holding a trade-sector id is still a `LocID`. + +So the value-axis companion asks the other question: **what has the corpus ever put in this field?** + +## The number + +Over 22 saves: + +``` +724 field(s) observed: 490 vary, 234 constant across the whole corpus +``` + +**234 of 724 typed fields — 32% — have been seen holding exactly one value.** We call this format +"99.99% typed" and mean it, but for nearly a third of it the typing has never been tested against more +than a single observation. `tscr` was one of those 234 until Commerce Raiding moved it, and every lane +that read 252 and inferred a fact about the game was reading a fact about our save set (rule 28, +practice 3). + +This is rule 23's "thin coverage" with a number on it, and rule 8's eighteen fields generalised: those +eighteen split a roster identically because nothing exercised them. There are 234. + +## Where the constants cluster + +| Parent | Constants | Reading | +|---|---:|---| +| `root.Sim.Player` | 24 | incl. 11 in `dipstats` — no corpus game ever ran diplomacy to a treaty | +| `root.Sim.Sys` | 19 | | +| `root.Sim.SvSctOb.EncObj` | 16 | the random-encounter object; **no corpus game triggered an encounter** | +| `root.CD` | 13 | | +| `root.Sim.spymgr.spy` | 12 | see below — this is lane AS's ground | +| `root.Summary.Players.Slot` | 11 | | +| `root.CD.CmbR.CRPlSv2.TacReports` | 9 | tactical combat reports never populated | +| `root.Sim.trdmgr.rt` | 5 | trade routes — even with AC's three `rt` records | + +Two individually suspicious constants, both non-zero and non-round, i.e. written by *something*: + +- `root.Sim.spymgr.spy.atto = 3728` in all 22 saves. +- `root.Sim.SvSctOb.EncObj.infest.mtrn = 2147483647` (`INT32_MAX`) — a sentinel, almost certainly + "no turn scheduled". +- `root.CD.researchBoostFraction = 0.999215` constant, with `researchBoostSpend = 216383`. + +## Direct consequence for the lanes now running + +**Lane AS (the spy half)** is looking for the detection roll gated inline in `vslot13`. The census says +the entire spy sub-object is unexercised: `sdet = -1`, `cbh = 0`, `cm = 0`, `cmo = 0`, `deat = 0`, +`sdo = 0` in every save we own. `sdet = -1` is a sentinel in the position a detection result would +occupy. That is consistent with the gate never having fired in the corpus, and it means **the corpus +count for any spy-path predicate is 0 of 22 before AS starts** — which is what rule 28 asks a lane to +state before it calls a zero a negative. + +**Lane AG (the gate-indexed audit)** gets a corpus-count column it can compute mechanically rather +than by hand: for a decoded predicate over save fields, the census already knows which fields never +vary, so any predicate resting on one of the 234 has a corpus count of 0 by construction, and its +"unread gate" status is not a matter of opinion. + +## What the instrument is not + +It does not know *why* a field is constant. `.Version` is constant because we can only read one format +version; `.GameType` because every corpus save is single-player. Those two are whitelisted as explained. +The other 234 are unexplained, which is a statement about our reading, not an accusation against the +game. **A constant field is a coincidence until its writer is found** — the census names the candidates; +it does not resolve them. + +It also folds array elements into one domain per field, deliberately: twenty fleets sharing a `LocID` +kind is the observation we want, not twenty rows. + +## Ratchet + +`kVaryingBaseline = 490`, on the count of fields with observed variation. Growing the corpus must +exercise more of the format, never less; a drop means a save was removed or a shape stopped being +reached. Raise it deliberately (rule 27). The constant count is printed but not ratcheted — it is not +monotone, since a newly typed body can add constants as easily as it adds variation. + +Full list: `SOTS_DOMAIN_ALL=1 SOTS_SAVES_DIR= mars_stream_test_domains`. + +## Proposed board rows + +| the coverage ratchet is blind along the value axis | verify | verified | high | 100% | 2026-09-09 | Board row 402 predicted AC's two saves would break the ratchet. **They did not** - both score 100.0% typed and round trip byte-identically, because their novelty (`LocID` at a TradeSector, `tscr = 253`, three `rt` records) lands in fields that were **already typed**. `CoverageArchive` is indexed by **shape**, exactly as lane Z's ledger was indexed by **firing**: both instruments are sound at what they measure and both are blind where the surprises come from. New instrument `DomainArchive` + `test_domains` measures the other axis | +| 234 of 724 typed fields have only ever held one value | verify | verified | high | 100% | 2026-09-09 | The census over 22 saves: **724 fields observed, 490 vary, 234 constant across the whole corpus - 32%**. `tscr` was one of the 234 until a one-turn tech moved it. Clusters: `Sim.Player.dipstats` 11 (no corpus game reached a treaty), `SvSctOb.EncObj` 16 (**no corpus game triggered a random encounter**), `spymgr.spy` 12, `CmbR.CRPlSv2.TacReports` 9 (tactical reports never populated). Individually suspicious because non-zero and non-round: `spy.atto = 3728`, `EncObj.infest.mtrn = INT32_MAX` (a sentinel), `CD.researchBoostFraction = 0.999215`. This is rule 23's thin coverage with a number on it and rule 8's eighteen fields generalised. Ratchet at 490 varying; see `findings/subsystems/value-domain-census.md` | +| the spy sub-object is unexercised: 0 of 22 before lane AS starts | verify | verified | high | 100% | 2026-09-09 | Direct input to lane AS. The census reads `spymgr.spy.sdet = -1` in **all 22 saves**, with `cbh`/`cm`/`cmo`/`deat`/`sdo` all 0 - `sdet = -1` sits in the position a detection result would occupy and is a sentinel. So the **corpus count for any spy-path predicate is 0 of 22 before the lane takes a single measurement**, which is exactly the statement rule 28 requires before a zero may be called a negative. Also gives lane AG a mechanically computable corpus-count column: a predicate resting on any of the 234 constants has count 0 by construction |