dashboard: count addresses.d fragments (was undercounting 615 vs 722), read layouts.json (22 -> 384), fix the layouts denominator and the 'objects' type row

This commit is contained in:
alex 2026-09-08 08:30:46 -04:00
parent e1f038e440
commit 648e9aae77
2 changed files with 34 additions and 21 deletions

View file

@ -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
<!-- dashboard-metrics {"verified": 93, "mapped_plus": 118, "targets": 139, "loc": 35208, "tests": 94, "checks": 3142, "addr_verified": 607, "addr_total": 615, "layouts": 22, "open_q": 26} -->
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
<!-- dashboard-metrics {"verified": 93, "mapped_plus": 118, "targets": 139, "loc": 35208, "tests": 94, "checks": 3142, "addr_verified": 703, "addr_total": 723, "layouts": 384, "open_q": 26} -->

View file

@ -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)}", ""]