Files
BFM-decomp/cookbook/C0186.md
T

3.0 KiB

§176a — THE VERIFICATION-LAYER LAWS (P31 overnight, 2026-08-15). What each check can and cannot prove.

These are not matching idioms; they are the rules for believing a matching result. Every one was paid for in gate cycles this session.

1. match_one verifies INSTRUCTION SHAPE, not SYMBOL IDENTITY. It masks jal/HI16/LO16 relocations, so a draft that calls the WRONG FUNCTION or loads/stores the WRONG GLOBAL reports a clean MATCH. Byte-witnessed: func_8002A234 (14 ins) stored v1→D_80078EE8 and 0→D_80078EE4 where the target does the exact reverse; match_one said MATCH, the whole-binary build differed in 2 bytes, and it cost five gate attempts. Same root cause as the invented PsyQ names in waves F/G (S80131E00→Square0, Blk20_…→RotMatrixY, SRM_…→RotTransSV). Rule: after MATCH, re-read the target .s relocation lines and check every symbol you wrote — each callee, each data symbol, and which symbol each load/store touches. Shape ≠ correctness.

2. A detector is ADVISORY; the whole-binary gate is the ARBITER. aprop_symfix flags STALE/AMBIGUOUS on LOCAL identifiers (typedef names, inline-asm macro names) and its draft-symbol extraction misses some extern forms, so a name the draft does declare can still report asm-only. Three wave-G drafts were withheld on such flags; all three banked unchanged when finally gated. Never withhold a standalone-MATCH draft on a flag alone — gate it and let the bytes decide (R39: a refusal check that silently discards good work is worse than one that lets a few failures through).

3. A verifier that can pass WITHOUT BUILDING is worse than no verifier. gate_main read the output binary's SHA after make build; when the build FAILED on a compile error the previous binary was still on disk, so it returned the good hash and reported BYTE-IDENTICAL for a build that never ran ("43 banked" on a TU that did not compile). The clean-fleet R22 caught it. Rule: delete the artifact before building, and treat a non-zero build exit as no-hash/never-pass.

4. An all-zeros result is a NULL, not a finding. gate_lane swallows gate_stage's stderr, so an unhandled corpus.CorpusError surfaced as banked 0, near 0, failed 0 — indistinguishable from an honest "nothing banked", twice. A real gate always classifies its drafts. If every bucket is zero, the tool did not run; run gate_stage directly to see the exception.

5. Before believing a measurement, run the control that would make it FAIL. The night's largest false conclusion — "main is blocked by a linker defect", complete with three hypotheses — died to one control: build with NO draft substituted at all. The "defect" reproduced with zero drafts, proving it was the build path, not the code. A null input, a known-answer population, or an independent oracle. R35 says fix the instrument first; this is the sharper form — confirm the instrument can answer, and that it answers correctly on a case whose answer you already know.