From 648e9aae77d6b53c3c73f68348ae3671cf31e3fb Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 8 Sep 2026 08:30:46 -0400 Subject: [PATCH] dashboard: count addresses.d fragments (was undercounting 615 vs 722), read layouts.json (22 -> 384), fix the layouts denominator and the 'objects' type row --- campaign/DASHBOARD.md | 17 ++++++++--------- tools/dashboard.py | 38 ++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/campaign/DASHBOARD.md b/campaign/DASHBOARD.md index 7f463d9..4b2c9ab 100644 --- a/campaign/DASHBOARD.md +++ b/campaign/DASHBOARD.md @@ -1,6 +1,6 @@ # SotS RE campaign — coverage dashboard -Generated 2026-09-08 12:29 UTC · `sots-re` @ 136f98b,2026-09-08 · `sots-engine` @ c883a32,2026-09-08 (106 commits) · regenerate with `tools/dashboard.py` +Generated 2026-09-08 12:30 UTC · `sots-re` @ e1f038e,2026-09-08 · `sots-engine` @ c883a32,2026-09-08 (106 commits) · regenerate with `tools/dashboard.py` > **North star:** A functional reimplementation of the engine — behavior-equivalent, NOT byte-for-byte @@ -18,20 +18,19 @@ Generated 2026-09-08 12:29 UTC · `sots-re` @ 136f98b,2026-09-08 · `sots-engine | Type | verified | mapped | in-progress | backlog | blocked | total | |---|---:|---:|---:|---:|---:|---:| -| objects | 5 | 2 | 0 | 0 | 0 | 7 | +| objects | 20 | 6 | 0 | 2 | 1 | 29 | | control-flow | 2 | 2 | 0 | 1 | 0 | 5 | | subsystems | 3 | 7 | 1 | 3 | 1 | 15 | | engine | 16 | 0 | 0 | 0 | 0 | 16 | | verify | 23 | 3 | 0 | 8 | 0 | 34 | | phase2 | 10 | 3 | 1 | 2 | 0 | 16 | | meta | 19 | 4 | 0 | 1 | 0 | 24 | -| other | 15 | 4 | 0 | 2 | 1 | 22 | ## 2. Binary understanding - RTTI type descriptors: **1,924** (`Game::` 1,404, `Mars::` 194; serializable types 179) -- Classes with recovered member layouts: **22** / 179 serializable types `[█░░░░░░░░░] 12%` — heuristic: distinct `Game::X`/`Mars::X` in `##`–`####` headings of `struct-recovery.md` + `schema-gaps-resolved.md` -- Functions: **41,411** (parsed from `01-fingerprint.md`); named/annotated in the **address contract** (`ghidra/addresses.json`, not Ghidra's full rename count): **615**, verified **607** `[██████████] 99%` +- Classes with recovered member layouts: **384** / 1,598 named classes `[██░░░░░░░░] 24%` — `objects/layouts.json` (serializer recovery) plus classes recovered by hand in `struct-recovery.md` + `schema-gaps-resolved.md`. Note 179 types are *serializable*; the recovery also reaches non-serializable ones, so this is not a subset of that +- Functions: **41,411** (parsed from `01-fingerprint.md`); named/annotated in the **address contract** (`ghidra/addresses.json`, not Ghidra's full rename count): **723**, verified **703** `[██████████] 97%` ## 3. Data layer @@ -96,10 +95,10 @@ Most recent open: ## 8. Delta since previous dashboard -- verified targets: 92 → 93 (+1) · mapped-or-better: 117 → 118 (+1) +- verified targets: 93 → 93 (+0) · mapped-or-better: 118 → 118 (+0) - engine LOC: 35,208 → 35,208 (+0) · test files: 94 → 94 (+0) · checks: 3,142 → 3,142 (+0) -- addresses verified: 607 → 607 (+0) · recovered layouts: 22 → 22 (+0) · open questions: 26 → 26 (+0) +- addresses verified: 702 → 703 (+1) · recovered layouts: 384 → 384 (+0) · open questions: 26 → 26 (+0) --- -warnings: board.md: unknown types objects; mars-rng.md: no oracle total row parsed; mars-stream.md: no oracle total row parsed; mars-vfs.md: no oracle total row parsed - +warnings: mars-rng.md: no oracle total row parsed; mars-stream.md: no oracle total row parsed; mars-vfs.md: no oracle total row parsed + diff --git a/tools/dashboard.py b/tools/dashboard.py index 4522535..f0e3694 100755 --- a/tools/dashboard.py +++ b/tools/dashboard.py @@ -11,6 +11,7 @@ Parsing is tolerant: malformed inputs are reported in the footer "warnings" line """ import argparse import datetime +import glob import json import os import re @@ -22,7 +23,7 @@ WARN = [] FUNCTIONS_FALLBACK = 41411 # findings/01-fingerprint.md, in case the number cannot be parsed STATUSES = ["verified", "mapped", "in-progress", "backlog", "blocked"] TYPE_ROWS = [ # (label, board Type values) - ("objects", ("object",)), ("control-flow", ("control-flow",)), ("subsystems", ("subsystem",)), + ("objects", ("object", "objects")), ("control-flow", ("control-flow",)), ("subsystems", ("subsystem",)), ("engine", ("engine",)), ("verify", ("verify",)), ("phase2", ("phase2",)), ("meta", ("meta",)), ] @@ -102,9 +103,17 @@ def parse_functions(path): def recovered_layouts(paths): - """Heuristic: unique `Game::X` / `Mars::X` names appearing in ##/###/#### headings of the - struct-recovery docs, excluding purely descriptive headings (no backticked class).""" + """Classes with a recovered member layout. + + Primary source is objects/layouts.json (the serializer-recovery output — machine-generated, + hundreds of classes). The heading heuristic over the hand-written struct-recovery docs is kept + as a supplement: it catches classes recovered by hand that the serializer pass never saw.""" names = set() + try: + d = json.loads(read(rp("objects", "layouts.json")) or "{}") + names.update(k for k in d if "::" in k) + except json.JSONDecodeError as e: + warn(f"layouts.json: {e}") for p in paths: for ln in read(p).splitlines(): if re.match(r"^#{2,4} ", ln): @@ -114,12 +123,16 @@ def recovered_layouts(paths): def parse_addresses(path): - try: - d = json.loads(read(path) or "{}") - entries = d.get("entries", d if isinstance(d, list) else []) - except json.JSONDecodeError as e: - warn(f"addresses.json: {e}") - entries = [] + # addresses.json plus every per-lane ghidra/addresses.d/*.json fragment — the same set + # gen_addresses.py merges. Counting only the base file undercounts while lanes are in flight. + sources = [path] + sorted(glob.glob(os.path.join(os.path.dirname(path), "addresses.d", "*.json"))) + entries = [] + for src in sources: + try: + d = json.loads(read(src) or "{}") + entries += d.get("entries", d if isinstance(d, list) else []) + except json.JSONDecodeError as e: + warn(f"{os.path.basename(src)}: {e}") total = len(entries) verified = sum(1 for e in entries if str(e.get("status", "")).startswith("verified")) return total, verified @@ -375,9 +388,10 @@ def render(engine, prev): a_total, a_ver = parse_addresses(rp("ghidra", "addresses.json")) L += ["## 2. Binary understanding", "", f"- RTTI type descriptors: **{n(rtti)}** (`Game::` {n(cg)}, `Mars::` {n(cm)}; serializable types {n(ser)})", - f"- Classes with recovered member layouts: **{len(layouts)}** / {ser} serializable types " - f"{bar(len(layouts), ser)} — heuristic: distinct `Game::X`/`Mars::X` in `##`–`####` headings of " - f"`struct-recovery.md` + `schema-gaps-resolved.md`", + f"- Classes with recovered member layouts: **{len(layouts)}** / {n(cg + cm)} named classes " + f"{bar(len(layouts), cg + cm)} — `objects/layouts.json` (serializer recovery) plus classes " + f"recovered by hand in `struct-recovery.md` + `schema-gaps-resolved.md`. Note {ser} types are " + f"*serializable*; the recovery also reaches non-serializable ones, so this is not a subset of that", f"- Functions: **{n(funcs)}** ({fsrc} from `01-fingerprint.md`); named/annotated in the **address contract** " f"(`ghidra/addresses.json`, not Ghidra's full rename count): **{a_total}**, verified **{a_ver}** " f"{bar(a_ver, a_total)}", ""]