Files
BFM-decomp/cookbook/C0184.md
T

5.3 KiB
Raw Blame History

§174 — THE ADAPT-CARD WAVE RECIPE (P31 waves A/B, 2026-08-14): prevention beats recovery

The pipeline (measured): adapt cards (§169 emitter) → one cheap drafter per card (haiku ≤50 ins, sonnet ≤120; model routing on the card) → each agent reads the TARGET .s in full, edits the proven seed body per the card's diff sites, and SELF-VERIFIES with match_one (≤6 iterations, parallel-safe) → symfix-first audit → gate_lane (single-writer, spread by destination TU) → targeted propagation. Wave A: 24 cards → 75% standalone → 12 banked (67% gate). Wave B (+1 prompt lesson): 48 → 79% standalone → 35/37 banked (95% gate), ~64–88k tok/bank e2e.

Law 1 — put §171 IN the drafter prompt, not just in the recovery path. "NEVER carry the seed's per-location symbols; spell every symbol from the TARGET .s's own relocations" produced 0 stale seed-symbols across 61 drafts — the class that was 24/24 of S49's gate failures and half of T6's, eliminated at the source for free. Prevention in the prompt beats symfix after.

Law 2 — the decl-matching lesson (67%→95% gate conversion in one wave). Wave A's six gate failures were ALL integration (3× a data extern typed by access width where the destination TU already declared it differently, 1 callee arity, 2 TU-context). The fix, verbatim in the wave-B prompt: before declaring a data extern or callee, grep the destination TU for an existing declaration and MATCH it exactly; only if absent, type by access width. One sentence, −28 points of gate failure.

Law 3 — bank the lesson between waves (the calibrate-then-scale cadence). 24-card wave A priced the lane and named the failure class; 48-card wave B applied it. The lesson transfer is the yield lever — never fire wave N+1 before reading wave N's failure verdicts (R38).

Law 4 — the DEF-side prototype is a wave-prompt law too (P31 wave-C probe, 2026-08-14). Law 2 tells the drafter to match the TU's decls for callees and data. The probe proved the same constraint binds the function being defined, and it silently costs the whole bank: func_80181724 reached standalone MATCH (13 ins) and gated 0/1. Cause: the destination TU already carried extern void func_80181724(s32, s32); (the §8b carried decl layer), while the matching definition wanted s16 a0 to emit the entry sll/sra pair — a conflicting prototype, so the whole-binary build fails while match_one (which compiles the draft ALONE) is blind to it. This is §20's DEF-side wall arriving through the wave path, and it has a mechanical, byte-identical answer — keep the TU's canonical signature and narrow at the USE site:

/* WRONG — conflicts with the TU's extern; match_one MATCHes, the gate refuses */
void func_80181724(s16 a0, s32 a1) { func_800291A0(D_80183A40[a0], a1 & 0xFF); }
/* RIGHT — same 13 bytes, no conflict: the cast emits the identical sll/sra */
void func_80181724(s32 a0, s32 a1) { func_800291A0(D_80183A40[(s16)a0], a1 & 0xFF); }

Re-gated 1/1. So the prompt clause is: before choosing your parameter types, grep the destination TU for an existing func_<YOURADDR> declaration; if one exists adopt it verbatim and move the narrowing into the body. Prevention again beats recovery — canon_sig_reconcile v3.2 performs this same rewrite after the fact, but a prompt sentence costs nothing and never re-perturbs a correct draft (the §19 "sig_unify REGRESSES canonical drafts" trap). Corollary for triage: a standalone MATCH that gates 0 is a declaration fact, never a codegen fact — look at the TU's decls before touching the body.

Lane economics measured (P31 wave C, so future waves price correctly). The tell lane (§172b LEN+N cards — functions that already survived one drafting attempt) runs ≈100k tok/card at a 33% standalone rate (1/3 probe), and its misses are genuine compiler residuals (register-ROLE swap, schedule cascade) that belong to the permuter, not to more agent effort. The adapt lane runs ~64–88k tok/bank at 75–79% standalone / 95% gate. Tell cards are worth running once because each bank is real, but budget the adapt pile first — and route every tell-lane NEAR straight to the grinder with its named class (a count-exact near like func_8017DAEC 113=113 with a pure v0/v1 role swap is prime permuter fuel). Also from the probe: the card's tell is a hint about the SHAPE of the gap, not a diagnosis — in 2 of 3 the real residual was something else (one draft had omitted an entire array lookup; one had the promotion already satisfied and was actually a §2 cross-call address-cache hoist-vs-remat). Re-diagnose from the live match_one diff; do not trust the tell attribution.

Ops notes: the drafter workflow makes NO tree writes (drafts land in .run/wave_*/; match_one is per-fn isolated) so drafting runs concurrently with the grinder — but the GATE is single-writer: stop the grinder (STOP sentinel) or wait, and note a finished --once grinder can leave a lingering wrapper process that fools a bare pgrep (check the log's "once done" line). The safety classifier can rate-limit under ~48-agent bursts — one agent lost its match_one run to that (report it unverified; the gate arbitrates anyway). Wave NEARs carry named classes — enqueue close≤3 drafts straight to the grinder queue as warmstart records.