Compare commits

..

No commits in common. "45bdf7d805f4568dbffc4f88c81b3b07d1b4eb39" and "82ef52f55c4e55b28db470a00ff8b6612dec76a2" have entirely different histories.

5 changed files with 0 additions and 201 deletions

View file

@ -1,137 +0,0 @@
# R — golden-trace recapture with guards (live VM, 2026-09-08)
Every golden trace went stale when `Coverage` + guard regions landed (3f0721f+,
`docs/harness-audit.md`). This lane recaptured all five on the game. **Coverage first: two
hooks that shipped as "verified" now report divergences, and both are real.**
Build `recap-7584bad-20260908T0615Z` — the tree at `7584bad` plus four `src/shim/shim.cfg.recap*`
variants. **No source change was needed to produce any of these results**; the audit's
machinery did all of it. Workload: the reference save → Launch → End Turn (the B3 and misc
runs continue to five End Turns, because the reference turn contains no research completion).
The End-Turn oracle hashes matched on every run's first turn, so neither trace nor compare
mode perturbs the game.
| hook | mode | calls | compared | diverged | exit | coverage |
|---|---|---|---|---|---|---|
| `Game::TechTree::ProcessResearch` | compare, 1 turn | 3 | 3 | **1** | **1** | 3 guarded, 0 undeclared |
| `Game::TechTree::ProcessResearch` | compare, 5 turns | 15 | 15 | **3** | **1** | 15 guarded, **13 undeclared in 2 calls** |
| `Game::ServerPlayer::ComputeBudget` | compare | 4284 | 4284 | 0 | 0 | 4284 guarded, **0 undeclared** |
| `Game::ServerPlayer::OnTechResearched` | compare | 2 | 2 | 0 | — | 2 guarded, **6 undeclared in 2 calls** |
| `Game::ServerSystem::ProcessTurn` | compare | 140 | 140 | 0 | — | 140 guarded, **15 undeclared in 6 calls** |
| `Game::StrategyServer::MoveFleet` | compare | 45 | 45 | **8** | **1** | 45 guarded, **42 undeclared in 15 calls** |
| all five | trace (golden) | 3630 | — | — | 0 | 3630 guarded, 9 undeclared in 4 calls |
The last three share one log; its exit 1 is `MoveFleet`'s.
## 1. The over-budget event: visible, on the predicted call
```
side.events.after.v.next_id [exact] orig={"t":"i32","v":4} ours={"t":"i32","v":3}
```
One divergent call out of three on the reference turn, and that is its only divergent field.
It is bit-for-bit the signal `docs/B3.md` could previously only get by inflating and diffing a
609 KB autosave (`EvNxID 4 → 3`). Everything the milestone claimed still holds on the same
call: the node's progress and its over-budget flag both reproduce, and the single RNG draw
matches (`mt[624]` hash, stream position and remaining count all identical). The mechanism in
`docs/harness-audit.md` §2 works exactly as designed, including the masking — the `player`
guard stayed silent on that call *because* `events` is now a Result region covering the word
that moved.
Across five turns the same field diverges on the two completion calls as well, by **two** ids
each (a completion posts more than one event). The rest of those two calls' divergences are
the `SetResearched` fields B3 declared out of scope in advance. **RNG matched on 15 of 15.**
## 2. What the guards mapped for free
On the two completion calls the `player` and `tree_header` guards printed what
`SetResearched` touches, without a single new hook:
* the player's construction-cost and output modifiers, and on the other call the population
modifier — i.e. `OnTechResearched`'s own declared regions, seen from the caller. A clean
cross-check.
* the current-research-target pointer being cleared.
* **the tree's completion-order counter** — harness-audit row 9, confirmed live.
* **the player's observed-tech vector growing** (all three vector words move, so it
reallocates). *This is not in any coverage note anywhere*, it is a third list append in the
same neighbourhood as the event list, and it is serialized state. New gap.
`OnTechResearched`'s own guard reports the event counter on **both** of its calls, so B2's
"ours posts no events" gap is now measured rather than assumed — the guard names the byte,
where `docs/B2.md` gotcha 4 told readers a changed save hash was expected and therefore not a
finding.
## 3. `MoveFleet`: 8 of 45 diverge at the last bit
Every divergence is a 1-ULP position component (the largest, 64 ULP, is at a near-zero result
where the *absolute* error is the same ~1.2e-7 as the others). The error sits at half an ULP of
the **inputs**, not of the result — the signature of one rounding too many or too few in the
position update, not a formula error: the step length is right, every ship-range region
matches, and x, y and z each diverge on some call.
`docs/B4.md` reported this hook clean. It had **one** moving call, and that fleet still matches
bit-for-bit on four of its five moves here. The bug only appears once other fleets move. This
is the thin-coverage failure mode caught by widening the workload, not by changing anything.
All 15 moving calls are the same straight-run waypoint type; types 2–5 never occurred, so the
node-line step, node routes, gate teleports, the probabilistic jump and every RNG draw in this
hook remain untested. The two arrivals in the run compared clean — which still means nothing,
since no arrival machinery is declared.
## 4. `ComputeBudget`: verdict held, coverage is narrower than the number suggests
4284 compared, 0 diverged, exit 0, and the `budget_object` guard caught **nothing** in 4284
calls — including the over-budget slot the audit flagged as written but never checked (row 11).
It never took a different value here, which is weaker than "it is never written".
Honest width of that result:
* the 4284 calls hold only **20 distinct (player, output) states**; 4278 of them are the
strategy-map UI polling one player, and the End-Turn pass itself is ~8 calls.
* **13 of the 22 slots are zero on every call.** Five of the six declared *input-boundary*
slots are among them, so they prove nothing twice over. Eight modelled slots ever carry a
value; that is the real evidence.
## 5. `ServerSystem::ProcessTurn`: 140 clean calls, two moving counters
Over five turns, exactly two declared regions ever changed: the turns-developing counter (15
times) and the recon countdown (7). Decay, both bonus pools, the resource total, the halt
flags, the battle countdown and the generator were byte-identical before and after on **all
140 calls**. A 0-divergence verdict here means "two integers agreed and nothing else moved".
The generator not moving on any of 140 colony turns re-confirms B4's headline over five turns.
Its guard reports the AI home system's fleet vector growing every turn (the build queue
emitting a ship — harness-audit row 14, observed live), the civilian population vector on the
first turn only, and two spans no contract entry names. The morale-event append (row 3) never
fired: still unmeasured, not cleared.
## 6. Traps for the next lane
1. **The reference turn contains no research completion.** `OnTechResearched` produces an
empty log on the documented one-turn recipe, and an empty log still passes. Reaching a
completion took five End Turns.
2. **Only the first End Turn is reproducible.** It matched the oracle byte for byte on all
four runs, and its research calls reproduce `docs/B3.md` exactly. From the fourth turn on,
the AI picks a different research target than B3 recorded while the point totals stay
nearly identical. Treat anything past turn one as *a* run, not *the* run.
3. `ComputeBudget` is called ~4280 times per session by the UI and ~8 times by the turn pass,
so any "N compares" headline for it is dominated by UI polling.
4. Guards cost nothing worth measuring: the whole five-hook trace run wrote 3630 records with
guards on every call and the End-Turn oracle still hashed exactly.
## 7. Still owed
* The count-only `events` region recommended by `docs/E-events.md` — it would turn audit row 1
from "known defect" into "checked". Not wired here: the captures predate that module and a
good capture is not worth redoing.
* Pin the rounding in the position update against the instruction stream.
* Name the three unidentified spans (two on the system, one on the fleet) in
`ghidra/addresses.json`.
* Declare or at least note the observed-tech vector append.
* Nothing here exercised replace mode on any hook, the Zuul double roll, a rebellion, an
addiction sweep, a plague, gate traffic, waypoint types 2–5, a silent completion, or the
budget expense/aid/debt tail.
Full per-call evidence, offsets and field names live in the RE repo under
`findings/subsystems/golden-trace-recapture.md`; logs and reports under `verify/traces/recap-*`
and `verify/results/compare/recap-*`.

View file

@ -1,16 +0,0 @@
# Recapture lane R: B1 ServerPlayer::ComputeBudget in COMPARE mode, alone.
# Verdict should stay 0 divergences; the new `budget_object` guard is the reason for the run.
hooks=trace
hook.Shim::SelfTest::Fill=off
hook.Mars::GlobalConsts::LoadFile=off
hook.Game::WeaponDictionary::Init=off
hook.Game::SectionDictionary::SectionDictionary=off
hook.Game::TechTree::ProcessResearch=off
hook.Game::ServerPlayer::OnTechResearched=off
hook.Game::ServerSystem::ProcessTurn=off
hook.Game::StrategyServer::MoveFleet=off
hook.Game::StrategyServer::ProcessFleetMovement=off
hook.Game::ServerPlayer::ComputeBudget=compare
trace.path=C:\SOTS\shim.trace.jsonl
trace.inline_max=256
trace.flush=always

View file

@ -1,16 +0,0 @@
# Recapture lane R: B3 TechTree::ProcessResearch in COMPARE mode, alone.
# Expect NEW divergences on `events` (the unposted EVENT_RESEARCH_OVERBUDGET).
hooks=trace
hook.Shim::SelfTest::Fill=off
hook.Mars::GlobalConsts::LoadFile=off
hook.Game::WeaponDictionary::Init=off
hook.Game::SectionDictionary::SectionDictionary=off
hook.Game::ServerPlayer::ComputeBudget=off
hook.Game::ServerPlayer::OnTechResearched=off
hook.Game::ServerSystem::ProcessTurn=off
hook.Game::StrategyServer::MoveFleet=off
hook.Game::StrategyServer::ProcessFleetMovement=off
hook.Game::TechTree::ProcessResearch=compare
trace.path=C:\SOTS\shim.trace.jsonl
trace.inline_max=256
trace.flush=always

View file

@ -1,16 +0,0 @@
# Recapture lane R: first guarded COMPARE captures for OnTechResearched, the colony turn and
# MoveFleet. ProcessResearch stays off so OnTechResearched is not nested inside another compare.
hooks=trace
hook.Shim::SelfTest::Fill=off
hook.Mars::GlobalConsts::LoadFile=off
hook.Game::WeaponDictionary::Init=off
hook.Game::SectionDictionary::SectionDictionary=off
hook.Game::TechTree::ProcessResearch=off
hook.Game::ServerPlayer::ComputeBudget=off
hook.Game::StrategyServer::ProcessFleetMovement=off
hook.Game::ServerPlayer::OnTechResearched=compare
hook.Game::ServerSystem::ProcessTurn=compare
hook.Game::StrategyServer::MoveFleet=compare
trace.path=C:\SOTS\shim.trace.jsonl
trace.inline_max=256
trace.flush=always

View file

@ -1,16 +0,0 @@
# Recapture lane R: all five golden-trace hooks in TRACE mode with the new guards.
# Copy over C:\SOTS\shim.cfg.
hooks=trace
hook.Shim::SelfTest::Fill=off
hook.Mars::GlobalConsts::LoadFile=off
hook.Game::WeaponDictionary::Init=off
hook.Game::SectionDictionary::SectionDictionary=off
hook.Game::StrategyServer::ProcessFleetMovement=off
hook.Game::TechTree::ProcessResearch=trace
hook.Game::ServerPlayer::ComputeBudget=trace
hook.Game::ServerPlayer::OnTechResearched=trace
hook.Game::ServerSystem::ProcessTurn=trace
hook.Game::StrategyServer::MoveFleet=trace
trace.path=C:\SOTS\shim.trace.jsonl
trace.inline_max=256
trace.flush=always