Commit Graph

10 Commits

Author SHA1 Message Date
Drew T 505a50a9b6 fix(draw_waves): --main was a no-op; every mixed draw saw ZERO main functions
bins is built from src/* DIRECTORIES, and main has no src/main/ — its TUs are
top-level src/*.c. So "main" was never in the list, and the filter that keeps
it could only ever preserve a "main" already present. --only-main worked
solely because it overwrote the list; --main contributed nothing, in every
mixed draw this project has ever run.

The tool meanwhile printed "main: refusing 49 LINKED subseg(s)" whenever
--main was passed, so it announced it was handling main while main was never
iterated. A flag that changes nothing is worse than a missing flag: it
answers the question you asked.

Measured: 0 -> 55 main stubs reach the pool. This is why S76y's 47 main
targets had to be assembled by hand from corpus.stubs — the draw could not
see the actual frontier. Coverage is now ASSERTED (R32): --main with zero
main stubs exits 4 and names itself a defect rather than reporting an empty
population as a fact.
2026-09-03 14:47:46 -06:00
Drew T 1778556b6d fix(draw_waves): a ledgered stub that is still OPEN is still work
After two S76 draws the tool reported 'population: 0 open stubs' with 51
open stubs on disk. True, and about a scope far narrower than the reader
believes — the session's dominant defect class. The draw ledger records what
was ATTEMPTED, not a property of the function, so a stub still open after
being drawn (the draft was never gated, or the blocker has since been fixed)
was filtered forever while the work remained.

This is the S72 exclude-list lesson in a second place, and the fix is the
same shape: --redraw-open includes them, and the population line now always
names how many were filtered for that reason alone, saying explicitly when
an empty pool means 'the ledger has seen them all', not 'the frontier is
empty' (R41 — a number ships with its denominator).
2026-09-03 14:26:57 -06:00
Drew T 57e5f970c8 docs(tools): four docstrings describing pre-session behaviour
* draw_waves Usage advertised [--no-main], which argparse never defined (the flags are
  --main / --only-main, and main is excluded by default), and omitted --exclude-file,
  which is now a PREREQUISITE that refuses a stale list.
* jr_isolate's STATUS block still declared the tool BLOCKED on split_src_region with the
  blocker unbuilt. Five defects were fixed this session and it runs the full chain to
  completion; what remains is a duplicate-definition class at assembly. Says so, and
  points at §431 as the cheaper route than finishing the item model.
* ld_interleave's layout diagram — the first thing anyone reads — showed the pre-S72
  three-piece island with 6324C.data.o. main's island is SEVEN pieces driven by --order;
  --front/--tail is the overlay form now.
* jtbl_rodata_pads described a stored-spec-only filter and advertised guards that no
  longer all exist; --derive serves main since S72.
2026-09-02 17:15:28 -06:00
Drew T e830e63be5 fix(draw_waves): normalise exclude rows to (binary, fn) — the list was excluding NOTHING
My own regression from the same session: exclude_audit.parse now returns 4-tuples (it
carries the WALL pin and each entry's note), and draw_waves built `skip` straight from
them, so every membership test against a 2-tuple missed and the exclude list had no
effect at all — while the run reported success.

Caught by MEASURING the pool rather than trusting the run: it came back 88 non-main + 45
main = the full frontier, when a 25-entry list should have reduced it. Now 69 and 41,
which reconciles exactly (88 - 16 carve-blocked - 3 non-main walls; 45 - 4 open main
walls, PopMatrix/PushMatrix being linked and already refused).

The silently-narrowed-scope shape again, and the third time this session that counting
the RESULT rather than trusting the REPORT is what caught it.
2026-09-02 14:55:15 -06:00
Drew T 983df054f2 feat(draw): audit the exclude list as a PREREQUISITE — a stale one is refused
An exclude list records what the TOOLING could not do, then gets treated as a property of
the FUNCTIONS. Nothing re-examined it, so every tool fix left behind a population that is
now tractable and still marked impossible — invisible, because the draw filters it out
before anything measures it.

MEASURED one day after .run/S71_exclude.txt was written: 88 of its 107 entries were
stale — 28 already banked, 14 linked PsyQ symbols that were never targets, and 46 whose
blocker had since been fixed. Those 46 are 12,750 instructions of open, drawable work
including main:SaveLoadRoutine (1,165), the largest function left in main.

* tools/exclude_audit.py (NEW) — classifies each entry by its CURRENT blocker
  (BANKED / LINKED / RE-PROBE / CARVE-BLOCKED / WALL), regenerates keeping only the
  still-valid classes, and --assert-fresh exits 3 on staleness.
* draw_waves --exclude-file — runs that audit and REFUSES to draw on a stale list, naming
  the counts and the regenerate command. --exclude-stale-ok still draws but prints what it
  ignores: skipping is possible, never silent. Also fixes the old --exclude parsing, which
  could not survive a '#' comment.
* .run/S72_exclude.txt — the regenerated list: 19 entries (16 CARVE-BLOCKED + 3 WALL),
  each carrying its reason, down from 107.

Verified in all three directions: stale refuses rc=1, fresh proceeds rc=0, override
proceeds and announces. The parser's own report-don't-drop design caught a bug I
introduced in it (comma-splitting before comment-stripping).
2026-09-02 14:38:39 -06:00
Drew T a2b0a6b98f tune(draw): opus up to 340 ins, not 150 - Fable for difficulty, not length (Drew)
Drew, 2026-09-02: "use opus mainly and only escalate the difficult ones to fable".
The S69 table that set the old 150 line actually puts opus's cliff at ~350:

    m1  opus  191-347 ins   10/15 MATCH   1,291 tok/matched-ins   <-- best measured
    m2  opus  347-670 ins    1/9  MATCH   7,158                   <-- the cliff

So 150 was handing opus's STRONGEST band to Fable. Fable is now reserved for >340
instructions and for arm_from_history's compiler-internal residual signal (§413) at any
size. On the current pool that moves three functions back to opus while two stay on
Fable because their history names a scheduling/regalloc residual - escalation by
difficulty rather than length, which is the point.

Retries default back to opus: one failure is not evidence of a wall, and the history
signal lifts a target on its own if the notes justify it.
2026-09-02 09:58:03 -06:00
Drew T bfea4affd8 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.
2026-09-02 01:29:59 -06:00
Drew T cee69c1bb5 feat(draw): NO SONNET — opus <=150 ins, fable >150 (Drew, 2026-09-01)
Measured over 129 drafting agents in one session, per MATCHED instruction (the
only cost that matters, since a failed agent is billed in full):

    sonnet  105 agents, 57 MATCH   4,289 tok/matched-ins  (flat ~47% above 30 ins)
    opus     24 agents, 11 MATCH   2,083                  (m1 191-347: 1,291, 67%)
    opus at 347-670:     1/9       7,158   <- the cliff, 2.92M tokens for ONE bank
    fable escalation:    3/4 closed at ~1/3 the cost of the attempt it rescued

Sonnet's per-agent price was never the cost that mattered; cost per BANK is, and
it lost on that by 2.1x. The m2 wave should have been fable from the start.

Escalating SOONER is the standing finding — higher models crack harder functions
in fewer tokens. Tested twice now (S68 A/B, S69 measurement); do not re-derive a
cheap-tier argument from per-agent price a third time.
2026-09-01 11:18:23 -06:00
Drew T d343892b4c chore: --only-main draw mode + progress/backlog refresh after the S66 gates
draw_waves.py gains --only-main (the main lane draws main and nothing else; implies --main so the
LINKED refusal still applies). Progress/backlog regenerated: fleet 99.2% instruction-weighted,
98.1% distinct.
2026-08-31 00:00:37 -06:00
Drew T 5c0b2969d7 tools(P31 S66): match_one --json answers on toolchain failure; packs render it; wave_args + draw_waves
- match_one.py: a CPP/CC1/MASPSX/AS failure printed bare text and exited 1 even under --json, so
  every programmatic caller got json.loads of a non-JSON line. claude_wave_packs swallowed 4 of 19
  prior drafts as 'residual not measured: Expecting value' — the most actionable datum a pack can
  carry (the draft does not COMPILE, here is the error) was the one it dropped. Negative control:
  a near draft still measures identically (closeness 2, same residual rows); human mode unchanged.
- claude_wave_packs.py: renders that verdict, pointing the agent at the card's decl_prior block.
  Coverage on wave r1 went 15/19 -> 19/19 packs carrying a measured verdict.
- wave_args.py (new): emits the claude_wave_draft.js args from <wave>/targets.json, asserting the
  .s exists, that sub is exactly its parent dir, and that the pack exists. Written because I
  hand-typed sub as 'ov_SC03_112/jr_80181D08' when the truth is
  'asm/ov_SC03_112/nonmatchings/ov_SC03_112_jr_801817E0' (a stub's asm dir is named for its jr-carve
  block, not itself) — all 19 agent oracles would have failed identically and read as a model
  failure. Negative control: that exact string is REFUSED.
- draw_waves.py (new): draws waves off corpus.stubs cheapest-first, ledger-filtered, with
  name-collision deferral (packs are name-keyed and refuse a duplicate). Re-measured the frontier:
  the S65 tier map's '~557 cheap singletons (3-17 ins)' conflated one-member FAMILIES with small
  functions — only 28 undrawn non-main stubs are <=17 ins; the bulk is 51-120 (258) and >120 (245).
2026-08-29 21:08:59 -06:00