Files
BFM-decomp/phase-ends
Drew T 610a13c21c fix(phase-29): T61/T62 — items 1-4; all three families converge on one root cause (the shared header)
0 banked. Four tool fixes, one byte-neutral header correction (committed separately), and the three
families resolve to a SINGLE root problem — plus a defect I introduced and caught by measuring.

FOUR TOOL FIXES, each verified by a verdict MOVING rather than by assertion:
 1. gather_externs prefers FILE-scope decls. Its contract says "file-scope extern decls" but
    `^[ \t]*extern` also matches an INDENTED one — a block-scope decl inside some OTHER function, not
    even in scope at the exemplar's own definition. Carried to file scope in the sibling,
    `extern void func_80155FF8(void *, u8);` (ov_SC01_077 L1213) landed above that sibling's
    DEFINE_func_80155FF8() macro and collided. ORDERED, not filtered — an indented decl stays the
    fallback it always was, so a symbol declared only block-scope is unaffected.
 2. reconcile_def_sig keeps the BODY's param names (the T60 fix, cookbook §109).
 3. §85 return-axis precondition — refuses when callers consume the return (reuses
    conform_decls.consumers, R33).
 4. Param-use guard — refuses to retype a parameter the body indexes/dereferences (func_8014D610's
    header says `void *a2` where the byte truth is `u16 *param_3` and the body does param_3[0]).

A DEFECT I INTRODUCED, CAUGHT BY MEASURING: func_8016163C read as a clean DIFF after T60 and I
reported it as "genuine codegen". It is not. match_one says SIZE-MISMATCH: draft 58 ins vs target 78
(delta -20, ratio 0.74, bucket redraft). Both overlays are 78 ins and extract_unit is fine —
--fix-def-sig demoted the return s32 -> void and gcc deleted the computation feeding it as dead. My
§85 check only asked whether CALLERS consume the return, never whether the BODY returns a value. The
tool manufactured a different-sized function and the verdict blamed the draft. Guard added. A "clean
DIFF" appearing right after a transform is a suspect, not a result.

THE CONVERGENCE: engine_core.h declares all three with types that contradict the byte truth —
func_80156044 int vs void (FIXED, byte-neutral, R22 140/140), func_8016163C void vs s32,
func_8014D610 void(s32,void*,void*) vs s32(s32,s32,u16*). Both remaining flips measure 0 §85
consumers. The fix is to correct the HEADER, not to bend the drafts.

AND ONE MORE LAYER: with its header fixed, func_80156044's verdict moved to `redefinition of
func_80155FF8` — extract_unit lifted a unit spanning TWO definitions and the sibling already defines
the wrapper via the shared macro. A unit-boundary defect, a fourth distinct cause. Three fixes peeled
three layers off one family.
2026-07-28 21:34:56 -06:00
..
2026-06-10 22:02:07 -06:00

phase-ends/ — the living record

  • PhaseEnd_Phase[N].md — written at each phase boundary (format defined in PROJECT_CONTEXT.md). Append-only history: build log, deviations, commit message, rules added, changelog. Never deleted.
  • CURRENT_PHASE.md — the in-phase autonomous log: approved phase plan, per-task checkboxes, current task pointer, blockers. Created at phase start, updated after every task, absorbed into the PhaseEnd file and deleted at phase close. This is the crash/compaction recovery point.
  • Sessions read PROJECT_CONTEXT.md, then every PhaseEnd_*.md in numeric order, then CURRENT_PHASE.md (if present) — in that order, every session.