CB: drop the list-5 rates record rather than ship a wrong one

Lane RB consumed the capture and found the record was wrong: the element is dumped in
memory order and its memory field order is not its wire order. Memory member 1 is wire
member SRsc; six members are unread. RB's first replay regressed two leaves on exactly
that.

The values now ship as rates_memory_order_{u32,f32} with wire: null, and the .tcb emits
the seven '?' tokens RB's own converter emits, so a replayer refuses rather than guesses.
A missing field is honest; a mislabelled one propagates -- and a capture is meant to
outlive whatever adapter happened to read it.

Also records the outcomes against every prediction, including the one the consumer
falsified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARBgSooAfokKUy6wKUKEyZ
This commit is contained in:
alex 2026-09-08 19:10:28 -04:00
parent c3d95dd44f
commit c2eba45850

View file

@ -240,3 +240,51 @@ pushes the tech's `std::string` as a `char*`, which is why it is loggable at all
turn-1 target gates (ids 144, 90, 288) resolve to three names, of which player 512's is the one that
moves between runs. Adding those three detours is a **rule-19 risk on a workload that has no valid
control**, so it goes on the turn-1 runs only, never on the canonical pair.
---
# Outcomes recorded against the predictions above
## P1 held. C0, `hooks=off`, reproduced the oracle exactly
`bb4fd9ac89f41e3b…` / 66,732 B and `978041acd168b56e…` / 67,219 B. VM146 is a faithful reference
guest and the control is real.
## P2 held. C1's autosaves are byte-identical to C0's, so the capture binds
One detour (the block dump) plus one (the seed reader), both read-only, and both oracle hashes
came back. The stream and the autosave are from the same run.
## P3 held, with P3b half-falsified
`272` for the route (P3a, exactly as predicted from the output save's waypoint). **`1728` for list
10's counted vector — a master-counter id, not a client-allocated one (P3b's falsifier).** The
`Population` body is 24 bytes and turn-dependent (P3c).
## P7's false-positive warning was right, and it cost nothing
At `words=32` the generic vector follower fires on neighbouring heap: 18 `aivec` lines on the
canonical block, of which **three** sit at a word index a record names. The decoder reads only those
three, so the noise is visible and inert — which is the outcome the warning was written for.
## A defect found by the consumer, not by me — list 5
Lane RB consumed this capture and reported that my list-5 record was **wrong**: the element is
dumped in *memory* order and its memory field order is not its wire order. **Memory member 1 is
wire member `SRsc`**; six members are unread. RB predicted zero regressions on its first replay and
got two, the AI's one non-zero slider landing in `SRt` where the oracle holds `SRsc = 1.0`.
The record is now **dropped**, per RB's recommendation and against my own first instinct to
relabel it positionally: the values ship as `rates_memory_order_{u32,f32}` with `wire: null`, and
the `.tcb` emits the seven `?` tokens lane RB's own converter emits. A replayer that needs them
must refuse rather than guess. **A missing field is honest; a mislabelled one propagates** — and it
would have propagated inside the artefact that is supposed to outlive the adapter that read it.
## What the capture bought, reported by the consumer
`sots_turn --turn-commands` puts `/Sim/ModCount` on **24 exactly, zero residual**: 2 drivers + 4
research-rate gates + list 3 + list 5 + list 10 + 2 × list 14 + list 8, with the list-23 population
element **free**. That leaf has been "not derivable from a save" since lane A2. The 1–16 / 17–27
cost boundary in `src/game/ai/orders.h` held on the first workload that ever populated the free
half.