mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
bfea4affd8
Measured on S71's own wave: wall-clock tracks iteration count, and iteration count tracks the residual class, not size. A 26-instruction function took 18 min / 31 tool calls (regalloc, finished NEAR); a 122-instruction one took 80 s / 10. The 20-33 min runs were all compiler-internal residuals — scheduling ties, birthing boost, register colouring, LUID order — where every hypothesis costs a compile-and-measure cycle. arm_for keys on nins alone, so a 47-instruction regalloc wall could not be drawn at the higher tier and nothing escalates mid-run. arm_from_history() now reads the function's own journal notes at draw time and returns fable when they name one of those classes; it never downgrades the size ladder's choice. R39 control over 3,147 functions with history x 3 bands = 9,441 decisions: 4,020 upgrades (43%), 0 downgrades. The control's FIRST form passed over an empty set — it keyed on journal rows carrying a binary, and there are none: the agent verdict schema never had that field, so every historical note is name-keyed and the same name is a different function in another overlay (§238). claude_wave_draft.js's VERDICT now requires `binary`, so new rows are exact. Cookbook §413.
60 lines
4.4 KiB
JavaScript
60 lines
4.4 KiB
JavaScript
export const meta = {
|
|
name: 'claude-wave-draft',
|
|
description: 'Claude-subagent drafting wave (P31 S62 T5; born as the T4 model-ladder probe): one agent per target, model = target.arm, identical api_agent packs; drafts to <wave>/<arm>/<fn>.c',
|
|
phases: [
|
|
{ title: 'Draft', detail: 'one drafting agent per target (sonnet <=120 ins / opus >120 + residue), local match_one oracle only; tools/wave_judge.py is the judge' },
|
|
],
|
|
}
|
|
// args: { wave: '.run/t5a', targets: [{name, binary, nins, sub, arm}, ...] } — from tools/t5_targets.py
|
|
// (targets.json + SYS.md + packs/ live under <wave>, built by tools/claude_wave_packs.py)
|
|
const REPO = '/home/musashi/bfm-decomp'
|
|
const WAVE = args.wave
|
|
const TARGETS = args.targets
|
|
const VERDICT = {
|
|
type: 'object',
|
|
properties: {
|
|
fn: { type: 'string' },
|
|
// BINARY IS PART OF A FUNCTION'S IDENTITY (R48/§238). Without it every journal note is
|
|
// name-keyed, and the same name is a DIFFERENT function in another overlay — so the S71
|
|
// past-attempt fuel (§411) could serve one overlay's history to another's target. Every
|
|
// historical journal row lacks this; stamping it now makes future ones exact.
|
|
binary: { type: 'string' },
|
|
arm: { type: 'string' },
|
|
status: { type: 'string', enum: ['MATCH', 'NEAR', 'FAIL', 'NO-DRAFT'] },
|
|
closeness: { type: ['integer', 'null'] },
|
|
compiles: { type: 'boolean' },
|
|
draft_path: { type: 'string' },
|
|
note: { type: 'string' },
|
|
},
|
|
required: ['fn', 'binary', 'arm', 'status', 'closeness', 'compiles', 'draft_path', 'note'],
|
|
}
|
|
phase('Draft')
|
|
log(`wave ${WAVE}: ${TARGETS.length} targets (${TARGETS.filter(t => t.arm === 'sonnet').length} sonnet / ${TARGETS.filter(t => t.arm === 'opus').length} opus)`)
|
|
const results = await parallel(TARGETS.map(t => () => agent(
|
|
`You are one drafting agent of a Claude wave in the Brave Fencer Musashi decompilation repo at ${REPO}.
|
|
Byte-match ONE PlayStation function: ${t.name} (${t.nins} instructions, binary ${t.binary}).
|
|
|
|
FIRST read these two files in full — they are your complete brief and your ground truth pointers:
|
|
${REPO}/${WAVE}/SYS.md (the laws; it names tools read_file/grep/match_one/submit — use the CLI equivalents below)
|
|
${REPO}/${WAVE}/packs/${t.name}.md (the target pack: asm path, destination TU, card fuel, a warm-start body, any gate feedback)
|
|
|
|
CLI equivalents (run from ${REPO}):
|
|
read_file -> cat / sed -n
|
|
grep -> rg or grep -n (start at docs/cookbook-index.md; \`.venv/bin/python tools/cookbook_index.py --resolve N\` resolves a line number)
|
|
match_one -> .venv/bin/python tools/match_one.py ${t.name} --c <your_draft.c> --asm-subdir ${t.sub} --json
|
|
(prints JSON with status MATCH/near/... and closeness = masked mismatched instruction count; it is free — use it often)
|
|
submit -> write your FINAL draft to ${REPO}/${WAVE}/${t.arm}/${t.name}.c (mkdir -p the dir) and return the JSON verdict.
|
|
|
|
HARD RULES: never modify anything under src/, config/, include/, asm/, build/ or run make; never touch other agents' files; write only to ${WAVE}/${t.arm}/${t.name}.c and scratch under ${WAVE}/${t.arm}/scratch_${t.name}/. Budget: up to ~24 compile/match_one iterations, then stop honestly.
|
|
Your final answer is the JSON verdict only: fn, binary="${t.binary}", arm="${t.arm}", status (MATCH if match_one says MATCH; NEAR if it compiles with closeness>0; FAIL if it never compiled; NO-DRAFT if you wrote nothing), closeness (integer or null), compiles, draft_path, note (one line: what blocked you, or which cookbook § unlocked it).`,
|
|
{ label: `${t.arm}:${t.name}`, phase: 'Draft', model: t.arm, schema: VERDICT }
|
|
).then(v => v || { fn: t.name, binary: t.binary, arm: t.arm, status: 'NO-DRAFT', closeness: null, compiles: false, draft_path: '', note: 'agent returned null' })
|
|
.catch(e => ({ fn: t.name, binary: t.binary, arm: t.arm, status: 'NO-DRAFT', closeness: null, compiles: false, draft_path: '', note: 'agent error: ' + String(e).slice(0, 120) }))))
|
|
const byArm = {}
|
|
for (const r of results.filter(Boolean)) { (byArm[r.arm] = byArm[r.arm] || []).push(r) }
|
|
for (const arm of Object.keys(byArm)) {
|
|
const rs = byArm[arm]
|
|
log(`${arm}: self-reported MATCH ${rs.filter(r => r.status === 'MATCH').length} / NEAR ${rs.filter(r => r.status === 'NEAR').length} / FAIL ${rs.filter(r => r.status === 'FAIL').length} / NO-DRAFT ${rs.filter(r => r.status === 'NO-DRAFT').length} of ${rs.length} (the gate decides — R14)`)
|
|
}
|
|
return { wave: WAVE, results }
|