The metrics contract (roadmap §1) wants all three metrics WITH main in the denominators, and the
second, independent boundary oracle (R34) extended beyond the overlays. Both had landmines.
10a — main into the weighted metric, safely:
- weighted_metrics off the func_-only src_stubs regex onto corpus.stubs (R33). THE LANDMINE IS
REAL: src_stubs("SLUS_007.26") globs src/SLUS_007.26/*.c -> 0 files -> every row "matched" ->
main 100% + fleet % silently inflates. Routing through corpus.stubs is a PROVEN 0.000pp no-op on
the existing fleet (overlays are all func_) and closes the curated-name leak.
- a SEPARATE "MAIN game-code weighted" line (0.7%): main's Ghidra sig excludes the LINKED PsyQ
objects (Ghidra never analysed them), which is exactly right for a game-code metric (LINKED is
complete, counted in fn-count). Reported un-folded and caveated (month-stale sig, PROVISIONAL) —
folding a stale/incomplete value into the decomp.dev headline would mislead the flip checkpoint.
10b — the resident second oracle:
- make sig-resident: sig_image on the resident flat blob (byte-derived, not Ghidra). corpus.
sig_is_independent now covers resident -> audit-corpus checks its boundaries too. Probed clean
BEFORE wiring (144 fns, all 21 stubs present, 0 phantom), verified 0 phantom + 0 truncated.
- sig-overlays now derives its payload list from config/overlays.mk, not a 0.4.dec glob that
silently dropped the 4 SC07 index-1 overlays (the audit's own silent-skip class). tools-health
regenerates sig-overlays + sig-resident first so the audit never crashes on an absent sig.
10c — main's second oracle: docs/second-oracle.md. sig_image can't sign the PS-X EXE yet (0x800
header offset, interleaved data/linked islands, one text range); seeding from splat would destroy
independence for the PHANTOM class specifically. Honest deferral + scoped design, not a fake oracle.
- docs/progress.fleet.md regenerated: 140 binaries · fn-count 82.16% · instr-weighted 67.0%
(the honest post-T7 drop from 68.9%) · distinct 47.8% · MAIN game-code 0.7% (separate).
- SETUP §6.3 updated (R21).
4.8 KiB
The second, disagreeing oracle — coverage & the main-EXE deferral (Phase-27 T10)
R34: the whole-binary byte-gate is a perfect CORRECTNESS oracle and a NULL COVERAGE oracle — it is green at any decomp %, so it cannot see a function splat mis-sliced or invented (the
.spieces paste back verbatim, image byte-identical either way). Only a SECOND, INDEPENDENT boundary oracle can.make audit-corpus(tools/corpus.py --audit) is it: it cross-checks splat's function boundaries againstsig_image's, which are derived from the ORIGINAL bytes without splat or Ghidra.
Coverage (who the second oracle can see)
| binary | independent sig? | covered | why |
|---|---|---|---|
| 138 overlays | sig_image (byte-derived) |
✅ | validated 58,524/58,621 vs spimdisasm |
| resident | sig_image (Phase-27 T10) |
✅ | make sig-resident — 144 fns, all 21 stubs present, 0 phantom/truncated |
| main | — | ❌ deferred | sig_image cannot yet sign the EXE (below) |
corpus.sig_is_independent() gates the check to exactly this domain: a boundary cross-check applied
where the two oracles were never measuring the same thing is noise, not thoroughness (the audit's own
R14 lesson — over all 136 it reported 914 "slices"; in-domain, 193, all one real defect).
Resident (now covered). make sig-resident signs the resident flat blob with sig_image
(--vram-base 0x800CEDF8) instead of the Ghidra dumper, making its sig independent. Probed clean
(0 phantom + 0 truncated). ⚠️ The check is only valid while .run/sig.resident.jsonl IS the sig_image
sig — run make sig-resident before make audit-corpus (a stale Ghidra sig there resurrects the
"measuring Ghidra's limits" artefact). h_exact is raw-byte SHA1, so weighted_metrics is unaffected
by the swap (fleet % unchanged to the decimal).
Main EXE — why sig_image can't sign it yet (the deferral)
tools/sig_image.py assumes a flat blob whose file offset 0 IS its vram base, one contiguous code
region, and one [lo,hi) text range. The main EXE breaks all three — structurally, not with a flag:
- PS-X EXE header. The EXE has a 0x800 header before the code;
sig_imagemapsoff = start − vram_base, so file offset 0 must equal vram. Needs a--file-offset/--skip 0x800. - Interleaved data islands + linked PsyQ regions.
detect_code_endstops at the first sustained invalid run, andbootstrap_seedslinear-partitions from one entry — both assume ONE code prefix then data. main has rodata/data islands and linked libcd/libgs/… regions between code. Needs an island list, not a single range. - One
--text-lo/--text-hi. No multi-range support.
The trap in the cheap workaround. Seeding a main sig_image run from splat's symbols destroys
independence for the PHANTOM class specifically — a phantom IS a splat-invented address, so seeding
from splat makes every phantom look real. A seeded run still buys the TRUNCATED class (boundaries
re-derived by func_end), but it is a half-oracle, and calling it "the second oracle" would be the
exact self-deception R34 exists to prevent. So: deliver an honest deferral, not a fake oracle.
The scoped design (when it's built): teach sig_image (a) a --file-offset for the header, (b) a
segment list (from the splat yaml's [vram, type] rows — types, not function boundaries, so it stays
independent of splat's function slicing) so it signs each code segment and skips data/linked islands,
(c) multiple text ranges. Then sig_is_independent("main") → True and the EXE joins the audit.
Interim (Phase-27): main's boundary blind spot is DOCUMENTED, not silently green. A cheaper partial
that closes it sooner: a fresh Ghidra make sig-refresh (R23 — MCP stopped) at least refreshes the
month-stale main sig weighted_metrics reports from; it is not independent (Ghidra boundaries), so it
does not serve the audit, but it makes the MAIN game-code weighted number current.
The completion metrics (roadmap §1 "all three, with main in the denominators")
tools/progress.py --fleet reports:
- fn-count byte-ident — includes main (×134-inflated; one crack counts per overlay).
- instr-weighted — resident + 138 overlays (the decomp.dev-DISPLAY number). Main is NOT folded in.
- distinct-code — each h_exact class once.
- MAIN game-code weighted (new, T10) — reported SEPARATELY,
main_pct, from main's LINKED-excluding Ghidra sig. It is game-code-only (LINKED = complete, in fn-count) and PROVISIONAL (the sig is dated; seemain_sig_date). It is not folded into the decomp.dev-comparable fleet number because folding a stale/incomplete value would mislead the flip-timing checkpoint. A fresh/complete main sig (sig-refresh, or the sig_image-on-main oracle above) makes it authoritative and foldable — a roadmap decision for when the number is trustworthy.