Files
Drew T 00812fc62f docs(p31 s67): wave-playbook (the CURRENT pipeline) + seed_ref fix + harvest §333-§338
THE DOC GAP, and it cost tokens this session. `docs/automation-runbook.md` was titled "the
autonomous campaign, as it actually runs" while documenting the RETIRED OpenRouter/ox-alpha system
whose lanes are all deliberately DEAD. The current Claude-wave pipeline existed only as two dense
tooling-inventory rows in SETUP.md — reference, not procedure. Three of this session's costliest
mistakes were procedural and a playbook prevents each:
  * hand-typed a refill target -> invented func_80184F60 (2nd instruction of a matched function), 58k
  * hand-rolled a serial gate loop when parallel_gate existed -> ~1h for what took 103s
  * re-derived a function banked verbatim in ~20 overlays -> 102k

NEW docs/wave-playbook.md — start to finish, each guard paired with the MEASUREMENT that produced it
(that pairing is the part a generic decomp guide cannot have, and the seed of the future template).
automation-runbook.md retitled HISTORICAL with a pointer; SETUP.md §6.9 links the playbook.

NEW tools/seed_ref.py — the cross-TU banked twin, joined on corpus signature hashes (no atlas knn,
~2s fleet-wide), wired into t5_cards.py. FLEET: 87 open stubs have a banked twin; 41 of them sit in
twin_sweep's refusal ledger, invisible to BOTH tools at once. Documents twin_sweep's two holes:
load_sigs covers 141/213 binaries (main, resident, all md_MAIN_* absent), and one curated symbol
name silently disables an entire binary via a bare `except Exception: pass`.
Schema note: seed_ref's binary/fn are the EXEMPLAR's, because api_agent greps src/{binary} for {fn};
naming them after the target would send every agent grepping for itself — caught pre-ship.

HARVEST §333-§338 from the s67o2_1/pool_1 waves:
 §333 frame size is set by DECLARED aggregates, not used ones — an unreferenced trailing local is a
      dial (3 instances; one worth 30 of 32 residual rows)
 §334 a reload spill slot rounds to BIGGEST_ALIGNMENT for align AND size: one 4-byte pseudo grew a
      frame by 16 (82->53)
 §335 `extern u16 A[]` at a variable subscript allocates ~8B/access of dead stack temps that inflate
      the frame with ZERO extra instructions — invisible in a body diff (141->20)
 §336 the §5a barrier goes at the BOTTOM of the twin; find_cross_jump walks BACKWARD
 §337 the CC1-ONLY blocker class: blocker_probe's static oracle says "none" and cc1 still fails
 §338 _sltiu_bounds misreads a non-switch sltiu as a bounds check, over-spanning the table

gate_wave.py now STREAMS both lanes (R55) — it captured output and printed at the end, leaving a
zero-byte log indistinguishable from a hang.
2026-08-31 13:48:56 -06:00

71 lines
4.3 KiB
Python

#!/usr/bin/env python3
"""tools/t5_cards.py — BUILD the card fuel for a wave's own targets (P31 S63 T5).
Usage: t5_cards.py --wave .run/t5b [--out <wave>/cards.json]
WHY (byte-measured, P31 S63 wave t5a). `claude_wave_packs.py` looked cards up by BARE FUNCTION NAME
across `.run/wave_*_cards.json`; overlays share names at equal addresses, so 48 of 48 t5a targets
(and 15 of T4's 20) got ANOTHER binary's card — wrong banked twin, wrong TU neighbours, wrong
declarations. Nine agents independently reported discarding it. Keying by (binary, fn) fixes the
mis-attribution but leaves K-class ("never touched") targets with NO fuel at all, and `_fuel()`
then emits nothing: no `tu_ref`, no `decl_prior`. Yet t5a's agents cracked function after function
by finding an already-banked sibling in their own TU BY HAND ("in-TU twin ~150 lines earlier",
"structural twin in the SAME TU") — which is exactly what `tu_ref` is (§194-E, 62% of targets).
So this builds the two computable fields for the target ITSELF, from the same oracle
`build_wave_atlas` uses (`tools/wave_card_fuel.py`, R33):
tu_ref already-banked functions in the target's OWN TU, ranked by symbols shared with the
target's .s relocations
decl_prior the fleet's consensus signature for every symbol the .s references (§196) — kills
the two-arity A/B compile §195-A proved the asm cannot answer
`seed_ref` (the cross-TU banked twin) needs the atlas knn and is NOT built here: a card with no
seed_ref makes `_fuel` say "no banked twin — derive from the .s", which is TRUE, where the old
name-keyed card pointed confidently at an unrelated function. Coverage is printed (R32/R41)."""
import argparse, json, os, sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))); os.chdir(REPO)
sys.path.insert(0, 'tools')
import wave_card_fuel as F
import decl_prior as DP
import seed_ref as SR
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument('--wave', required=True)
ap.add_argument('--out', default='')
a = ap.parse_args()
targets = json.load(open(os.path.join(a.wave, 'targets.json')))
idx = DP.load()
cards, n_tu, n_dp, n_sr = [], 0, 0, 0
for t in targets:
tu = t.get('tu') or F.home_tu(t['binary'], t['name'])
tu_ref = F.tu_neighbours(t['binary'], t['name'], tu, t['asm'])
try:
dp = DP.for_asm(t['asm'], tu, idx=idx, binary=t['binary'])
except Exception as e:
print(' decl_prior failed for %s/%s: %s' % (t['binary'], t['name'], str(e)[:80])); dp = []
# seed_ref — THE CROSS-TU BANKED TWIN. Built from the signature hashes the corpus already
# computes (NOT the atlas knn, which is why this was skipped before). Measured cost of not
# having it: an opus agent spent 102,193 tokens re-deriving ov_SC03_107:func_8013DD68, whose
# body is banked verbatim at the same address in ov_MAIN_012 — because the card asserted
# "no banked twin". 87 open stubs fleet-wide have one; 41 of those sit in twin_sweep's
# refusal ledger, so they are invisible to BOTH tools at once.
try:
sr = SR.for_stub(t['binary'], t['name'])
except Exception as e:
print(' seed_ref failed for %s/%s: %s' % (t['binary'], t['name'], str(e)[:80])); sr = None
n_tu += bool(tu_ref); n_dp += bool(dp); n_sr += bool(sr)
cards.append({'fn': t['name'], 'binary': t['binary'], 'addr': t.get('addr'), 'nins': t['nins'],
'sub': t['sub'], 'tu_ref': tu_ref, 'decl_prior': dp, 'seed_ref': sr})
p = a.out or os.path.join(a.wave, 'cards.json')
json.dump(cards, open(p, 'w'), indent=1)
n_ref = sum(1 for c in cards if (c.get('seed_ref') or {}).get('mechanical_remap_refused'))
print('cards: %d built for %s (tu_ref %d/%d = %.0f%%; decl_prior %d/%d = %.0f%%; '
'seed_ref %d/%d = %.0f%%, of which %d were mechanically refused — copy the BODY, expect a '
'declaration blocker)'
% (len(cards), a.wave, n_tu, len(targets), 100.0 * n_tu / max(1, len(targets)),
n_dp, len(targets), 100.0 * n_dp / max(1, len(targets)),
n_sr, len(targets), 100.0 * n_sr / max(1, len(targets)), n_ref))
print('wrote %s' % p)
if __name__ == '__main__':
main()