|
|
|
|
@ -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-*`.
|