sots-re/tools/displacement.py

138 lines
7.6 KiB
Python

#!/usr/bin/env python3
"""Report engine displacement — how much of the game runs on OUR code, verified.
The campaign's honest progress metric. Coverage of the *save format* is closed and the
board is full of green rows, but neither answers the question the north star actually asks:
**how much of the original's behaviour has our code replaced, proven against the live game?**
Four rungs, deliberately steep:
replaced ours RAN INSTEAD OF the original on the live game, and the End-Turn autosave
oracle still matched. This is the only rung that means the original's code did
not execute. It is the number that matters.
compared ours ran ALONGSIDE the original on the live game and agreed on every declared
region. Strong, but the original still did the work — and a clean compare on a
wrong region set checks nothing (three of B4's hooks printed 0 diverged while
comparing nothing; see guides/method-rules.md rule 1).
modelled implemented in sots-engine with host tests, never run against the live game.
mapped understood and written up; no code.
Coverage caveats travel WITH the rung, never netted off it. A hook is only listed at
`replaced` if a report names the oracle it satisfied.
Source of truth is this file's table, curated at integration from the compare reports in
verify/results/compare/ and the board rows that cite them. It is deliberately hand-curated:
deriving it by grepping status words would let an over-claimed board row silently inflate
the project's headline number.
tools/displacement.py # human-readable table
tools/displacement.py --json # machine-readable, for the dashboard
"""
import json
import sys
# (subsystem, rung, live evidence, coverage caveat that travels with it)
ROWS = [
("mars/parse + text (data readers)", "modelled",
"oracles 1,531/1,531 and 64/64 vs the Python reference",
"never hooked; the original's parser still runs the game"),
("mars/vfs (.gob + native override)", "modelled",
"10,268 files CRC-clean, byte-equal spot checks",
"never hooked"),
("mars/stream (save read/write)", "modelled",
"100% named coverage, byte-identical round-trip on 11 saves, coverage proved by re-serialisation",
"the game's own load/save path is untouched; ours reads the same bytes offline"),
("mars/rng (MT19937 + 7 entry points)", "compared",
"every draw of a full turn attributed live; 3 instruments agreed on turn 5's 20 words",
"our generator is not driving the game; it reproduces the ledger"),
("GlobalConsts::LoadFile (flat-KV config)", "replaced",
"trace 19 calls / 1,088 regions -> compare 19/19 0 div -> REPLACE, End-Turn hash oracle held",
"config load only; one call site"),
("manifest / id registry (weapons)", "replaced",
"trace 22 calls -> compare 1/1 0 div -> REPLACE + oracle byte-identical",
"weapons path only; the section path still crashes in compare (board: section-loader compare crash)"),
("ServerPlayer::ComputeBudget", "replaced",
"4,437 compares 0 div; replace-mode oracle byte-identical",
"QUALIFIED: replace runs the ORIGINAL a second time to harvest slots -> a real per-turn double "
"effect (ComputeOutput repairs ships in orbit) that no region reaches. 13 of 22 slots are 0 on "
"every call; only 20 distinct states across 4,437 calls"),
("TechTree::ProcessResearch + unlock cascade", "compared",
"35 calls across 3 workloads, 0 divergences, tracecmp exit 0; advance prediction held on a "
"changed workload (unlock costs no earlier report contained); REPLACE ATTEMPTED live (lane "
"CR): ours ran instead of the original on a real completion and produced all 13 tech-tree "
"leaves the turn moves, but the save oracle missed by 16 leaves",
"compare only. The replace attempt failed on ServerPlayer::OnTechResearched, not on the "
"research model: 5 player tech-effect fields, 1 ObservedTech element and 2 event records, "
"plus 5 derived leaves. Gated on B2. Thin: 1 completion, 1 tech, 2 of 3 calls allocate zero "
"points, and the RNG region did not move at all on the replace workload"),
("ServerPlayer::OnTechResearched", "compared",
"3 completions, 0 div; float32 confirmed bit-for-bit on the game",
"compare only; RollResearchEvent's branch has fired once in four sessions"),
("ServerSystem::ComputeTotalOutput + GroupOutput", "compared",
"24,357 guarded calls, 0 undeclared writes, 1 residual ulp the caller rounds away",
"compare only; 24,357 calls is THIRTEEN distinct system states"),
("MoveFleet (position update)", "compared",
"45 calls, live 8 divergences -> 0 after the five-narrowing fix; mechanism match, not a numeric fit",
"compare only; all 15 moving calls are the same straight-run waypoint type"),
("ServerSystem::ProcessTurn (colony)", "compared",
"36 calls 0 div; RNG left-delta 0 on all 28 systems",
"compare only; 3 owned systems, gate traffic all zero, most branches untested"),
("events (EventStorage + PostEvent)", "compared",
"next_id 3->4 both sides live; observed_techs delta measured at exactly 44 bytes",
"COUNT-ONLY by design: ours never calls the game's PostEvent and replace mode writes nothing"),
("game/nav (route classifier)", "modelled",
"offline vs 58 waypoints / 46 flight plans across 11 saves, 0 failures",
"never instrumented; not one leg has executed under a hook"),
("game/combat (retreat planner)", "modelled",
"53 hand-computed checks",
"never instrumented; the resolver has never executed under a hook at all "
"(every observed encounter had the apply flag set)"),
("game/design (hull class, census)", "modelled",
"11 saves, 503 designs, 480 census leaves, 0 mismatched, computed twice independently",
"3 of 6 census leaves are unexercised: no cruiser and no DN platform in the corpus"),
("app/sots_turn (whole strategic turn)", "modelled",
"runs 11/11 saves; reference pair 209 -> 204 leaves, closed 5 regressed 0",
"14 of 44 driver phases and 2 of 37 tail phases; verified 0 BY DEFINITION (no VM this cycle)"),
("AI turn logic", "mapped", "-", "essentially unread; gates the full byte-match (Rung B)"),
("combat simulation", "mapped", "-", "parked by decision"),
("renderer / UI", "mapped", "-", "not on any path; DXVK carries rendering"),
]
RUNGS = ["replaced", "compared", "modelled", "mapped"]
BLURB = {
"replaced": "ours ran INSTEAD of the original, live, oracle held",
"compared": "ours ran alongside and agreed; the original still did the work",
"modelled": "implemented + host-tested; never run against the live game",
"mapped": "understood; no code",
}
def main():
counts = {r: sum(1 for x in ROWS if x[1] == r) for r in RUNGS}
if "--json" in sys.argv:
print(json.dumps({
"schema": "sots-displacement/1",
"counts": counts,
"total": len(ROWS),
"rows": [dict(zip(("subsystem", "rung", "evidence", "caveat"), r)) for r in ROWS],
}, indent=2))
return
print("Engine displacement — how much of the game runs on our code\n")
for r in RUNGS:
print(f" {r:9s} {counts[r]:2d} {BLURB[r]}")
print(f"\n {len(ROWS)} subsystems tracked. Only `replaced` means the original's code did not run.\n")
for r in RUNGS:
rows = [x for x in ROWS if x[1] == r]
if not rows:
continue
print(f"== {r} ==")
for name, _, ev, caveat in rows:
print(f" {name}")
if ev != "-":
print(f" live: {ev}")
print(f" caveat: {caveat}")
print()
if __name__ == "__main__":
main()