mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-04 16:28:11 -04:00
feat(draw): route the model tier off the prior RESIDUAL CLASS, not nins
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.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Generated by `tools/cookbook_index.py` — do not hand-edit** (R33). Regenerate after adding a cookbook section.
|
||||
>
|
||||
> `docs/matching-cookbook.md` is ~716 KB / 1080 sections. Grepping it blind is how three P30 wave-1 agents each "discovered" an idiom that was already written down. **Start here, then read the section.** A section appears under every symptom it addresses.
|
||||
> `docs/matching-cookbook.md` is ~716 KB / 1081 sections. Grepping it blind is how three P30 wave-1 agents each "discovered" an idiom that was already written down. **Start here, then read the section.** A section appears under every symptom it addresses.
|
||||
|
||||
**How to use:** name what you SEE in the diff (a stolen delay slot, an extra `la`, a swapped register pair, a `conflicting types` error), find that symptom below, read those sections first. If nothing fits, THEN grind — and add a section when you win.
|
||||
|
||||
@@ -1336,7 +1336,7 @@
|
||||
- **§408** — ★★★ — §406 REFUTED AS A SWEEP: THE SHAPE IS THE FAMILY, THE DISAGREEMENT IS THE DEFECT (P31 S71; 0 MATCH / 14 applied, 0 / 210) <sub>L33209</sub>
|
||||
- **§411** — ★★★ — THE PACK MUST CARRY THAT FUNCTION'S OWN HISTORY (P31 S71; measured 38/39 vs 124/131) <sub>L33386</sub>
|
||||
|
||||
### (unbucketed — title matched no symptom vocabulary) (323)
|
||||
### (unbucketed — title matched no symptom vocabulary) (324)
|
||||
|
||||
- **§3-How** — to use this <sub>L30</sub>
|
||||
- **§1** — Idiom catalog (asm pattern → C that produces it) <sub>L39</sub>
|
||||
@@ -1661,6 +1661,7 @@
|
||||
- **§407** — ★★ — LATE-WAVE ADDENDA TO §405 (the last agents in) <sub>L33180</sub>
|
||||
- **§409** — ★★★ — THE S71 JOURNAL-FUELLED WAVE: 100% FIRST-PASS MATCH, AND THE NINE LAWS IT BROUGHT BACK (P31 S71) <sub>L33261</sub>
|
||||
- **§3-The** — nine laws this wave produced <sub>L33288</sub>
|
||||
- **§413** — ★★★ — DIFFICULTY IS THE RESIDUAL CLASS, NOT `nins` — ROUTE THE MODEL TIER OFF HISTORY (P31 S71, Drew) <sub>L33473</sub>
|
||||
|
||||
|
||||
## All sections, in order
|
||||
@@ -2745,6 +2746,7 @@
|
||||
- **§410** — ★★★ — COPY THEN ACCUMULATE ON THE COPY: resolving the birthing-boost vs register-allocation dilemma (P31 S71; byte-proven `ov_SC04_015/func_8017EB78`, 98 ins) <sub>L33353</sub>
|
||||
- **§411** — ★★★ — THE PACK MUST CARRY THAT FUNCTION'S OWN HISTORY (P31 S71; measured 38/39 vs 124/131) <sub>L33386</sub>
|
||||
- **§412** — ★★★ — §323 CARVE BLOCKER 2 WAS A REGEX THAT COULD NOT SEE PAST `__attribute__` (P31 S71) <sub>L33424</sub>
|
||||
- **§413** — ★★★ — DIFFICULTY IS THE RESIDUAL CLASS, NOT `nins` — ROUTE THE MODEL TIER OFF HISTORY (P31 S71, Drew) <sub>L33473</sub>
|
||||
|
||||
|
||||
---
|
||||
@@ -3837,3 +3839,4 @@ Notes routinely quote that as a section id. This table resolves it. Grep bait: `
|
||||
| L33353 | §410 | ★★★ — COPY THEN ACCUMULATE ON THE COPY: resolving the birthing-boost vs register-allocatio |
|
||||
| L33386 | §411 | ★★★ — THE PACK MUST CARRY THAT FUNCTION'S OWN HISTORY (P31 S71; measured 38/39 vs 124/131) |
|
||||
| L33424 | §412 | ★★★ — §323 CARVE BLOCKER 2 WAS A REGEX THAT COULD NOT SEE PAST `__attribute__` (P31 S71) |
|
||||
| L33473 | §413 | ★★★ — DIFFICULTY IS THE RESIDUAL CLASS, NOT `nins` — ROUTE THE MODEL TIER OFF HISTORY (P31 |
|
||||
|
||||
@@ -33469,3 +33469,40 @@ position relative to a brace or a keyword must strip `__attribute__((…))` firs
|
||||
appear between `}` and the name, after the name, and after the parameter list. This is the §134 class
|
||||
(a scanner that cannot start where the C grammar actually puts things), and it is now the seventh tool
|
||||
in this project to hit it.
|
||||
|
||||
## §413 ★★★ — DIFFICULTY IS THE RESIDUAL CLASS, NOT `nins` — ROUTE THE MODEL TIER OFF HISTORY (P31 S71, Drew)
|
||||
|
||||
**The observation.** S71's wave ran one agent per function and logged wall-clock and tool-call counts.
|
||||
Duration tracks **iteration count**, and iteration count tracks the **residual class** — not size:
|
||||
|
||||
| function | ins | arm | wall | tool calls | what it was |
|
||||
|---|---|---|---|---|---|
|
||||
| `func_80181294` | **26** | opus | 18 min | 31 | REGALLOC-PERM, finished NEAR/2 |
|
||||
| `func_80185D44` | **47** | opus | 21 min | 48 | LUID contradiction (read cc1's `-dS` trace) |
|
||||
| `func_80185F4C` | **60** | opus | 22 min | 33 | sched1 birthing-boost tie |
|
||||
| `func_800D24D0` | 141 | opus | 33 min | 51 | `memrefs_conflict_p` PLUS-vs-LO_SUM |
|
||||
| `func_8017DB98` | 122 | opus | **80 s** | 10 | body recovered from a prior attempt |
|
||||
| `func_80182CBC` | 28 | opus | 115 s | 12 | §193-A twin remap |
|
||||
|
||||
A 26-instruction function took 18 minutes; a 122-instruction one took 80 seconds. **Size predicted
|
||||
neither.** What separates the two groups is whether the residual is a compiler-internal one
|
||||
(scheduling ties, birthing boost, register colouring, LUID order) where every hypothesis costs a
|
||||
compile-and-measure cycle — or an ordinary body question that a twin or a neighbour answers at once.
|
||||
|
||||
**Why they never escalated.** `draw_waves.arm_for` keys the model tier on `nins` alone, so a
|
||||
47-instruction regalloc wall is STRUCTURALLY unable to be drawn at the higher tier, and nothing
|
||||
escalates mid-run: each agent runs to its own budget and no watcher re-hands the target.
|
||||
|
||||
**The fix, available only because the packs now carry history (§411).** `arm_from_history()` reads the
|
||||
function's own journal notes at draw time and returns `fable` when they name a compiler-internal
|
||||
residual; it never downgrades what the size ladder chose. Negative-controlled over all 3,147 functions
|
||||
with history × 3 size bands = 9,441 decisions: **4,020 upgrades (43%), 0 downgrades.**
|
||||
|
||||
**A defect the control caught, worth as much as the lever.** The first negative control iterated only
|
||||
journal keys carrying a binary and reported `0 decisions / 0 downgrades` — a clean pass over an EMPTY
|
||||
set. The agent verdict schema never had a `binary` field, so **every historical note is name-keyed**,
|
||||
and the same name is a different function in another overlay (§238). Two consequences: the S71
|
||||
past-attempt fuel can serve one overlay's history to another's target, and any future join on these
|
||||
rows inherits it. `claude_wave_draft.js`'s `VERDICT` schema now requires `binary`, so new rows are
|
||||
exact; the historical corpus stays name-keyed and should be read with that caveat.
|
||||
`check-against-a-known-true-case`, again: the instrument passed because it measured nothing.
|
||||
|
||||
+47
-1
@@ -25,6 +25,7 @@ Then, per wave: t5_cards.py -> claude_wave_packs.py -> wave_args.py, and launch
|
||||
tools/workflows/claude_wave_draft.js with the args wave_args.py printed (never hand-typed).
|
||||
"""
|
||||
import argparse, collections, glob, json, os, sys
|
||||
import re
|
||||
|
||||
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
os.chdir(REPO)
|
||||
@@ -69,6 +70,51 @@ def arm_for(n):
|
||||
return 'opus' if n <= 150 else 'fable'
|
||||
|
||||
|
||||
# THE RESIDUAL CLASS PREDICTS DIFFICULTY BETTER THAN `nins` DOES (Drew, 2026-09-02, S71).
|
||||
# Measured over the S71 wave's own agent runs: wall-clock and iteration count track the RESIDUAL,
|
||||
# not the size. A 26-instruction function took 18 minutes and 31 tool calls (`func_80181294`,
|
||||
# still NEAR); a 122-instruction one took 80 seconds and 10 (`func_8017DB98`). The 20-30 minute
|
||||
# runs were all compiler-internal residuals — scheduling ties, birthing boost, register colouring —
|
||||
# where each hypothesis costs a compile-and-measure cycle:
|
||||
#
|
||||
# func_80185D44 47 ins opus 21 min 48 tool calls (LUID contradiction, read cc1 -dS)
|
||||
# func_80185F4C 60 ins opus 22 min 33
|
||||
# func_800D24D0 141 ins opus 33 min 51
|
||||
#
|
||||
# `arm_for` keys on size alone, so a 47-instruction regalloc wall was STRUCTURALLY unable to be
|
||||
# drawn at the higher tier, and nothing escalates mid-run. Now that every pack carries the
|
||||
# function's own history (§411), the prior residual class is known AT DRAW TIME — and 1,352 of the
|
||||
# 3,147 functions with history (43%) have a note naming one of these classes.
|
||||
_WALL_RE = re.compile(
|
||||
r'permuter|regalloc|register (?:alloc|colou?ring|pressure)|schedule[- ]reorder|SCHEDULE-'
|
||||
r'|birthing|LUID|sched1|sched2|scheduler-internal|cross_?jump|delay[- ]slot|colou?ring',
|
||||
re.I)
|
||||
|
||||
|
||||
def arm_from_history(binary, fn, n, _cache={}):
|
||||
"""`fable` when this function's own journal history names a compiler-internal residual.
|
||||
|
||||
Escalating SOONER is the standing finding (see arm_for); this applies it to the axis that
|
||||
actually predicts cost. Falls back to the size ladder when there is no history, and never
|
||||
DOWNGRADES what the size ladder chose."""
|
||||
if not _cache:
|
||||
try:
|
||||
sys.path.insert(0, os.path.join(REPO, 'tools'))
|
||||
import journal_notes
|
||||
_cache['idx'] = journal_notes.load()
|
||||
_cache['mod'] = journal_notes
|
||||
except Exception:
|
||||
_cache['idx'] = None
|
||||
base = arm_for(n)
|
||||
idx = _cache.get('idx')
|
||||
if not idx or base == 'fable':
|
||||
return base
|
||||
rows = _cache['mod'].notes_for(idx, binary, fn)
|
||||
if rows and _WALL_RE.search(" ".join(r.get('note') or '' for r in rows)):
|
||||
return 'fable'
|
||||
return base
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument('--prefix', required=True, help='wave dir prefix; waves are <prefix>1, <prefix>2, ...')
|
||||
@@ -128,7 +174,7 @@ def main():
|
||||
continue
|
||||
pool.append(dict(name=s.symbol, addr='0x%08x' % s.addr, nins=n, binary=b,
|
||||
sub=s.asm_dir, asm=s.asm_path, tu=s.path,
|
||||
cls='FRONTIER', arm=arm_for(n), **{'from': 'draw_waves'}))
|
||||
cls='FRONTIER', arm=arm_from_history(b, s.symbol, n), **{'from': 'draw_waves'}))
|
||||
pool.sort(key=lambda t: (t['nins'], t['binary'], t['name']))
|
||||
|
||||
print('population: %d open stub(s) in [%d,%d] ins, undrawn, over %d binaries (%d oracle refusals: %s)'
|
||||
|
||||
@@ -14,6 +14,11 @@ 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'] },
|
||||
@@ -21,7 +26,7 @@ const VERDICT = {
|
||||
draft_path: { type: 'string' },
|
||||
note: { type: 'string' },
|
||||
},
|
||||
required: ['fn', 'arm', 'status', 'closeness', 'compiles', 'draft_path', 'note'],
|
||||
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)`)
|
||||
@@ -41,10 +46,10 @@ CLI equivalents (run from ${REPO}):
|
||||
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, 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).`,
|
||||
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, arm: t.arm, status: 'NO-DRAFT', closeness: null, compiles: false, draft_path: '', note: 'agent returned null' })
|
||||
.catch(e => ({ fn: t.name, arm: t.arm, status: 'NO-DRAFT', closeness: null, compiles: false, draft_path: '', note: 'agent error: ' + String(e).slice(0, 120) }))))
|
||||
).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)) {
|
||||
|
||||
Reference in New Issue
Block a user