Five hooks recaptured, build recap-7584bad-20260908T0615Z, no source change. - ProcessResearch: the over-budget event is now a compare divergence (side.events.after.v.next_id orig=4 ours=3) instead of a 609 KB save diff. 15 calls over 5 turns, 3 diverged, RNG matched 15/15. - ComputeBudget: 4284 compared, 0 diverged, guard quiet - but only 20 distinct states and 13 of 22 slots always zero. - MoveFleet: 8 of 45 calls diverge by 1 ULP of position. B4's clean verdict was a one-sample verdict. - First guarded captures for OnTechResearched (RollResearchEvent draw fired and matched), the colony turn and MoveFleet. - Guards mapped SetResearched live and found an undeclared ObservedTech append. VM140 released at the main menu, hooks=trace.
47 lines
4 KiB
Markdown
47 lines
4 KiB
Markdown
## tracecmp report: recap-b3-compare5.jsonl
|
|
|
|
- build: recap-7584bad-20260908T0615Z started: 2026-09-08T06:28:40Z inline_max: 256
|
|
- calls: 15 compared: 15 diverged: 3 invalid records: 0 warnings: 0
|
|
- coverage: 15 guarded call(s), 13 undeclared write(s) in 2 call(s); 0 hook(s) unstated, 0 contradicted
|
|
|
|
| hook | calls | modes | compared | diverged | errors |
|
|
|---|---|---|---|---|---|
|
|
| Game::TechTree::ProcessResearch | 15 | compare:15 | 15 | 3 | 0 |
|
|
|
|
### coverage
|
|
|
|
| hook | verdict | compared regions | guards | undeclared writes | unmodelled |
|
|
|---|---|---|---|---|---|
|
|
| Game::TechTree::ProcessResearch | partial | events, node[0], node[105], node[106], node[107], node[108], +270 | player, tree_header | 13 in 2 call(s) | 5 |
|
|
|
|
#### Game::TechTree::ProcessResearch — not checked by this run
|
|
- (high) posts EVENT_RESEARCH_OVERBUDGET on the owner's EventStorage in the same branch that sets node.flag = 2 — the message text is composed from the tech name, so ours cannot synthesise it; it would have to be posted through the game's own event API. This is the defect that made a clean compare false: replace mode's autosave differed from the oracle by exactly this one event [region:events (EvNxID now diverges instead of passing silently)]
|
|
- (medium) posts EVENT_TECHS_UNLOCKED for nodes that became available this turn — the trailing unlock loop makes no draw and writes no node, but it does build a names list and post an event [region:events]
|
|
- (high) TechTree::SetResearched on completion: the turn/order stamps, the child unlock cascade, the recursive research of zero-cost children, and the owner's OnTechResearched callback — its own milestone (B2); the callback writes live player state that compare mode must not touch, and it consumes one extra RNG word [guard:player, guard:tree_header]
|
|
- (medium) bumps the tree's completion-order counter (TechTree+0x20) — part of SetResearched; the per-node `order` word is compared but the counter it comes from was not a region [guard:tree_header]
|
|
- (low) writes a completion line to the game log — log text is not simulation state
|
|
- guard hits in compare mode: player+0x10c:3, player+0x110:3, player+0x114:3, player+0x124:3, player+0x274:3, player+0x278:3, player+0x27c:3, player+0x294:4, tree_header+0x20:1, player+0x130:3, player+0x278:1
|
|
|
|
### Game::TechTree::ProcessResearch: first 3 of 3 divergent call(s)
|
|
- call_id 0 (recap-b3-compare5.jsonl:2)
|
|
side.events.after.v.next_id [exact] orig={"t":"i32","v":4} ours={"t":"i32","v":3}
|
|
- call_id 3 (recap-b3-compare5.jsonl:5)
|
|
side.events.after.v.next_id [exact] orig={"t":"i32","v":7} ours={"t":"i32","v":5}
|
|
side.node[132].after.v.cost_rp [exact] orig={"t":"i32","v":10000} ours={"t":"i32","v":2147483647}
|
|
side.node[132].after.v.state [exact] orig={"t":"i32","v":2} ours={"t":"i32","v":0}
|
|
side.node[132].after.v.turn_available [exact] orig={"t":"i32","v":4} ours={"t":"i32","v":-1}
|
|
side.node[136].after.v.cost_rp [exact] orig={"t":"i32","v":16000} ours={"t":"i32","v":2147483647}
|
|
side.node[136].after.v.state [exact] orig={"t":"i32","v":2} ours={"t":"i32","v":0}
|
|
side.node[136].after.v.turn_available [exact] orig={"t":"i32","v":4} ours={"t":"i32","v":-1}
|
|
side.node[142].after.v.cost_rp [exact] orig={"t":"i32","v":8000} ours={"t":"i32","v":2147483647}
|
|
… 4 more
|
|
- call_id 9 (recap-b3-compare5.jsonl:11)
|
|
side.events.after.v.next_id [exact] orig={"t":"i32","v":12} ours={"t":"i32","v":10}
|
|
side.node[12].after.v.cost_rp [exact] orig={"t":"i32","v":35000} ours={"t":"i32","v":2147483647}
|
|
side.node[12].after.v.state [exact] orig={"t":"i32","v":2} ours={"t":"i32","v":0}
|
|
side.node[12].after.v.turn_available [exact] orig={"t":"i32","v":6} ours={"t":"i32","v":-1}
|
|
side.node[18].after.v.cost_rp [exact] orig={"t":"i32","v":4000} ours={"t":"i32","v":2147483647}
|
|
side.node[18].after.v.state [exact] orig={"t":"i32","v":2} ours={"t":"i32","v":0}
|
|
side.node[18].after.v.turn_available [exact] orig={"t":"i32","v":6} ours={"t":"i32","v":-1}
|
|
side.node[3].after.v.cost_rp [exact] orig={"t":"i32","v":13000} ours={"t":"i32","v":2147483647}
|
|
… 4 more
|