Files
BFM-decomp/phase-ends/CURRENT_PHASE.md
T
Drew T d9f4dc86e4 docs(phase-23): lock the decided next-session sequence — A (reach≥2 with v3) then B (14B)
Drew's call: the fresh session starts with Path A (lora_grind --min-reach 2 + v3 → setter banks
propagate ×134 + dedup-collapse + flywheel), then Path B (corpus-v4 + a cloud Qwen2.5-Coder-14B
for the giant band). Rationale: A is $0/immediate and its banks enrich B's training corpus.
2026-06-30 10:08:51 -06:00

77 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CURRENT PHASE — Phase 23: The offline LLM matching tier (free local-model grinder + the data flywheel)
**Generation:** Gen2 (15th phase of the arc) · **Started:** 2026-06-29 · **Effort:** Max (synthesis/decisions/debug) ↔ xHigh (mechanical) · **Plan:** set up at PhaseEnd_Phase22 close (Drew); substance was already built across the 2026-06-29 session.
> Per-task crash-recovery log (P3). This file is the committable state a fresh session resumes from. Phase 22 (giants) closed → `PhaseEnd_Phase22.md` + `phase-ends/logs/Phase22.md`. The deep design + measured results of this phase live in `docs/gen2-mips-matching-model.md` (+ `docs/history/cheap-tier-ab-experiment.md`); the working-knowledge in the `cheap-tier-ab-validated` memory.
## Goal
Stand up a **free, local, fine-tuned matching model** that grinds the small/medium-function bulk and banks byte-verified matches for **$0** — with (a) a **data flywheel** (banks grow the corpus → retrain → the model improves *during* the run), (b) **escalation** (the permuter `grinder.py` closes the regalloc/schedule near-misses the model leaves; bigger models / your Opus take the tail), and (c) the **whole-binary byte-gate as the incorruptible sole arbiter** (a weak model is a *throughput* risk only, never correctness). **On-demand, NOT 24/7** (run only when Drew says go).
## Decisions (Drew)
- **On-demand runs**, bounded — never a standing 24/7 daemon. Queue a large batch, run a single shot, measure, scale the next.
- **Serve the model on the GPU via `tools/serve_local.py`** (Unsloth/torch, OpenAI endpoint at `http://127.0.0.1:1234/v1`) — **LM Studio was ejected 2026-06-30**; the prebuilt `llama-cpp-python` CUDA wheel SIGILLs on this no-AVX-512 CPU, so the Unsloth path is the reliable one (no build, loads in ~6 s).
- **Measure before investing** (the discipline that killed the v1 dead-end): every corpus/model change is **retested free on the 7B first**, and only scaled to a dense model if the cheap retest pays. *(Now satisfied for the 14B step: v3-on-7B paid.)*
- **Corpus-v3 DONE** (macro bodies + struct types; v3 banks the small/setter bulk, fleet→63.82%). The **14B gate is satisfied** → **corpus-v4 (struct-giant types) + a dense Qwen2.5-Coder-14B on cloud** for the >15-ins/giant band is the warranted next investment — Drew's call vs. path A (reach≥2 with v3).
## The operating loop (when running)
A. `lora_grind.py` (GPU) drafts open ≤N-ins stubs with the served fine-tuned model → banks via `gate_stage` → near-misses to the backlog.
B. `grinder.py` (CPU permuter) closes the backlog's regalloc/schedule near-misses → banks. **Run A+B concurrently** (GPU vs CPU; the histogram already shows 94 schedule/regalloc nears waiting).
C. Periodic propagate sweep (`dedup_propagate`) multiplies each bank fleet-wide.
D. Periodically: `export_pairs → format_finetune → train_lora → redeploy` (the model learns its own new wins); read the near-by-class histogram → pick the corpus-v3 / recovery-tooling target; raise `--max-nins` as the band lifts.
## Tasks
- [x] **T1 — Cheap-tier A/B** (Opus orchestrator + cheap agents) — Haiku 4.8× more matches/$ than Opus on the bulk; tools `tools/workflows/ab_match.js` + `tools/ab_score.py`. (`docs/history/cheap-tier-ab-experiment.md`.)
- [x] **T2 — Stock-local floor** — Qwen3.6-35B-A3B (LM Studio): structurally smart but **0 reliable byte-matches** (can't refine to byte-exact); format-robust; full-cookbook context made it *worse* (dilution). The floor to beat.
- [x] **T3 — The LoRA specialist pipeline** — `export_pairs.py` (mine banked asm↔C from build objects) → `format_finetune.py` (Qwen chat-template, compile-filtered) → `train_lora.py` (Unsloth QLoRA, Qwen2.5-Coder-7B, GPU on the 3080 Ti) → `eval_lora.py` (held-out gate-true). Corpus `datasets/match_pairs/` (gitignored).
- [x] **T4 — Corpus-v2 = the extern-block fix (THE unlock)** — capture the `extern <type> D_xxx;` block the src declares above each def (correct byte-verified types) → self-contained completions, compile 52%→92%, train 638→1111 (non-trivial 257→813). Same 7B retrained: held-out **6–15 ins 0%→85%**, non-trivial 0→26, meaningful(>15) 0→3. **Data was the bottleneck, confirmed.**
- [x] **T5 — First REAL banks on open stubs** — the v2 model drafted real open ov_SC01_077 stubs (LEAN, raw `.s` — model is format-robust, no bridge needed); whole-binary gate **banked 4** (func_80160B34, func_8015CC74, func_8016084C, func_801705C0; @commit:0320). Sample rate: 9/22 match_one proxy → **4/22 (18%) whole-binary** (the proxy→gate TU-plumbing gap).
- [x] **T6 — The mass-run driver** — `tools/lora_grind.py`: rotates every binary (config/check.*.sha), drafts the served model, banks via gate_stage (commit), defers/periodic-propagates, writes the **classified near-miss histogram** (the flywheel "missing idioms" signal). STOP/heartbeat/stats like grinder.py.
- [x] **T7 — Calibration run + the 0/222 puzzle — DEBUGGED + FIXED (2026-06-30).** Root cause = TWO independent harness bugs in `lora_grind`'s use of `gate_stage.run_gate` (R14, by reading the code + the run's backlog — which resolved a direct contradiction between two scout agents): **Bug A** — `good_sha()` passed the whole sha1sum line `"<sha> <name>"` vs harvest_verify's bare `sha1()` → **0 banks for EVERY binary incl. 077** (the "0/12" was a bug artifact, NOT an exhausted tail — the prior "`.sha` files carry the filename" note was the unfollowed thread); **Bug B** — the gate call left `src/asm/out` at the hardcoded ov_SC01_077 defaults → non-077 drafts dropped at the 077 stub-filter, **silently** (and the asm mis-resolution contaminated the backlog near-miss classes). Fix (`tools/gate_stage.py`): `run_gate` is binary-agnostic (resolve src/asm/out/good_sha from `binary`; good_sha bare-hash normalized) + a **loud negative-control guard**; `lora_grind.good_sha` fixed at source; byte-neutral (check-all 136/136). **Proof:** ov_SC01_000 spot-run banked **7/15 (47%) byte-identical** (was 0; @commit:0322). **ROI finding:** 6/7 banks are reach-1 (overlay-unique ×1) → broad rotation = high bank-RATE, low fleet-% ROI; the fleet lever is reach≥2 targeting (T9) + corpus-v3 (T8). Full write-up: `docs/gen2-mips-matching-model.md` → "T7 RESULT".
- [x] **T8 — Corpus-v3 — DONE (2026-06-30).** Mined the **1623 `engine_core.h` `DEFINE_func` macro bodies** (the shared setters/return-const the model was blind to — 96.6% of v2 was overlay-unique) + `format_finetune` inlines `engine_types.h` structs → corpus **1312→2891**, trainable **2534+291** (2.5× v2). **v3 trained** (loss 1.275→0.085), held-out eval **23/40 (57.5%)**; production batch banked ~352 fns + 45 shared groups → fleet **63.67→63.82%** ($0). The empty-leaf/setter class v2 couldn't draft is now banked. Struct-GIANT types deferred → **corpus-v4** (with a 14B). Details: `docs/gen2-mips-matching-model.md`.
- [x] **T9 — Operating loop — WIRED + RUN (2026-06-30).** Local GPU serving stood up (`tools/serve_local.py`, Unsloth — LM Studio ejected); the retrain cycle (`export_pairs → format_finetune → train_lora → serve_local`) + reach≥2 targeting (`--min-reach`) + propagate sweeps all run end-to-end. Grinder concurrent = 0 banks (permuter tail exhausted, Phase-22 reality — the LLM is the value now). **Refinements (NEXT):** `--min-reach 2` with v3 so setter banks propagate ×134; the dedup-collapse; raise `--max-nins` as v3 lifts the band.
- [ ] **T10 — Progress honesty + PhaseEnd** — track fleet % + bank-rate; PhaseEnd_Phase23 at a clean checkpoint (Tier-1 Max).
## ▶ RESUME HERE (fresh session)
**State:** Phase 23 in progress (NOT a phase end). **v3 is the current model** — `bfm-match-7b-v3` (Qwen2.5-Coder-7B QLoRA on **corpus-v3**), adapter at `models/bfm-match-7b-v3` (v2 kept as fallback at `models/bfm-match-7b`). **LM Studio is EJECTED** — serve via **`tools/serve_local.py`** (Unsloth GPU, OpenAI endpoint), NOT LM Studio. The **8-hour autonomous run (2026-06-30)** built local serving + the prompt fix + corpus-v3 + v3 + a production batch → **fleet 63.82%** (+502 byte-identical, $0), 136/136 byte-clean, 27 commits this session (local — **Drew pushes**, R6). Pipeline validated end-to-end: a free local model banks the small/setter bulk, including the empty-leaf class v2 couldn't. Corpus `datasets/match_pairs/` + `.venv-train` gitignored. (Phase 22 close `PhaseEnd_Phase22.md` is committed `commit:0325`.)
**NEXT TASK — DECIDED (Drew, 2026-06-30): do A first, THEN B.** (Sequencing rationale: A is $0 + immediate + realizes the ×134 lever this run set up, and A's new banks enrich the corpus that B trains on — so A-then-B compounds.)
**① START THE FRESH SESSION HERE — (A) more LLM harvesting with v3, the fleet-% lever.** Run **`lora_grind --min-reach 2` with v3**: now that v3 banks the shared setters, target the **shared (reach≥2)** ones so each bank propagates **×134** instead of re-banking inline per binary (what capped this run's % at +0.15). Pair with the **dedup-collapse** of the per-binary inline setters → shared `engine_core.h` macros, and run the **data flywheel** (the run's ~352 new banks grow the corpus → retrain a v3.1; raise `--max-nins` as the band lifts). Serve v3 via `serve_local.py` (command below). Cheapest, immediate, $0. Close A when its flywheel plateaus.
**② THEN — (B) train a 14B for the giant band.** The measure-before-investing gate is **SATISFIED** (v3-on-7B paid), so a dense **Qwen2.5-Coder-14B** for the **>15-ins / struct-giant** band is the warranted investment. Do **corpus-v4 first** (emit the struct-giant types the >40-ins fns need — the band v3 still compile-fails), then `train_lora.py --base unsloth/Qwen2.5-Coder-14B-Instruct-bnb-4bit --rank 32`. Needs ~16 GB → a **cloud A100/H100** (the 3080 Ti is 12 GB; 4-bit + offload locally works but slow). The byte-gate makes a wrong 14B a throughput risk only.
**Serve + run (when Drew says go):**
```
# 1) serve v3 on the GPU (replaces LM Studio):
LD_LIBRARY_PATH=$(ls -d .venv-train/lib/python3.12/site-packages/nvidia/*/lib | tr '\n' :) \
.venv-train/bin/python tools/serve_local.py --adapter models/bfm-match-7b-v3 --name bfm-match-7b-v3 --port 1234 &
# 2) bounded mass-run (reach≥2 targeting, the fleet-% lever):
API_BASE=http://127.0.0.1:1234/v1 MODEL=bfm-match-7b-v3 GATE_PHASE=phase-23 \
.venv/bin/python -u tools/lora_grind.py --min-reach 2 --max-nins 15 --batch 15 --iters 2 --max-batches N
# stop anytime: touch .run/auto/STOP ; retrain: export_pairs -> format_finetune -> train_lora --out models/bfm-match-7b-v4
```
## Verification invariant (every bank)
The **whole-binary byte-gate** (`gate_stage`/`harvest_verify`, G3/P9) is the sole arbiter — a wrong/weak draft can NEVER bank (it reverts to the stub). `make check-all` 136/136 byte-identical from a clean tree (R22); `dedup-check` 0 failed; the `db.*.gbf` churn is R23 restart-noise (do NOT stage). The fine-tuned model only affects *throughput*, never correctness.
## Reuse (no rewrites)
*New (this phase):* **`tools/serve_local.py`** (Unsloth GPU serving, OpenAI endpoint — **replaces LM Studio**) · `api_draft.py` (LEAN drafter + the "translate every instruction, never empty" prompt fix) · `ab_match.js`/`ab_score.py` (the cost A/B) · `export_pairs.py` (now mines the `engine_core.h` macro bodies — corpus-v3) / `format_finetune.py` (inlines `engine_types.h` structs) / `train_lora.py` / `eval_lora.py` (the LoRA pipeline) · `lora_grind.py` (the mass-run; `--min-reach`).
*Existing (now Phase-23 binary-agnostic):* `gate_stage` (`GATE_PHASE`-tagged + resolves src/asm/out/good_sha from `binary`) / `grinder.py` (per-binary + grouped gating) + `auto_supervisor.sh` / `dedup_propagate` / `backlog` (fleet-aware `load_best`) / `harvest_verify` / `match_one`; cookbook §17–§28.
## Guardrails
- **On-demand only** — never leave a 24/7 daemon running; bounded `--max-batches`, STOP-sentinel safe-exit.
- **Measure before investing** — retest every corpus/model change free on the 7B before any cloud spend.
- **Disk** — GGUF conversion writes ~30 GB intermediates onto the WSL vhdx (C:); `train_lora` now auto-cleans them; keep only the q4 GGUF. (A disk-full crash cost a session on 2026-06-29.)
- **Serve on GPU via `tools/serve_local.py`** (Unsloth/torch, the in-repo replacement for the ejected LM Studio) — the prebuilt llama-cpp-python CUDA wheel SIGILLs on this no-AVX-512 CPU; don't try it.
- Model size matches data size — no "massive" models on ~1–2k examples (overfit); dense > MoE for a limited-data LoRA.
## Blockers
- **(cleared) T7 — the 0/222 broad-rotation banking.** Root-caused to two harness bugs (good_sha sha1sum-format + the src/asm/out ov_SC01_077-default) and fixed + proven (ov_SC01_000 7/15 byte-identical, check-all 136/136). No blockers; a bounded mass-run is safe to size. Open levers (not blockers): corpus-v3 struct types (T8) + reach≥2 targeting (T9).
## Progress log
- 2026-06-29: **Phase opened at PhaseEnd_Phase22 close (Drew).** Built across this session: cheap-tier A/B (T1, Haiku 4.8×/$), stock-local floor (T2, 0), the LoRA pipeline (T3) + corpus-v2 extern-fix (T4, 6–15 ins 0%→85%), first 4 real open-stub banks (T5, @commit:0320), the `lora_grind` mass-run driver (T6). Calibration run (T7) launched (500 fns) — **18% on ov_SC01_077 but 0/222 broad rotation → #1 debug.** gate_stage commit tag made phase-agnostic. The whole arc + measured numbers: `docs/gen2-mips-matching-model.md`; memory `cheap-tier-ab-validated`. NEXT: T7 debug, then bounded mass-runs + corpus-v3.
- 2026-06-30: **T7 DEBUGGED + FIXED.** 3 Explore scouts (tooling / run-evidence / corpus) + a direct code read (R14 — which resolved a flat contradiction between two scouts) found **two independent bugs** in `lora_grind`'s gate path: **(A)** `good_sha()` passed `"<sha> <name>"` vs harvest_verify's bare `sha1()` → 0 banks for ALL binaries incl. 077 (so 077's "0/12" was a bug artifact); **(B)** `src/asm/out` defaulted to ov_SC01_077 → non-077 drafts dropped at the 077 stub-filter, silently. Fixed `gate_stage.run_gate` (binary-agnostic resolution + bare-hash normalize + a loud negative-control guard) + `lora_grind.good_sha`; byte-neutral (check-all 136/136). ov_SC01_000 spot-run **banked 7/15 (47%) byte-identical** (@commit:0322) → reach-2 `func_8017CE24` propagated ×2. **ROI:** 6/7 reach-1 → broad rotation is high bank-rate / low fleet-% ROI; the fleet lever is **reach≥2 targeting** + corpus-v3. Backlog now correctly classified (4× close=1 = grinder fuel). NEXT: **T8 corpus-v3** (struct types) + **T9 reach≥2 selection** + concurrent grinder.
- 2026-06-30 (cont.): **T9 reach≥2 targeting built + measured.** Added `lora_grind --min-reach N` (lazy sig-based reach oracle == `dedup_propagate`, validated 0-mismatch/60 + the func_8017CE24=2 ground truth; `--min-reach 2` ranks high-reach-first, naturally restricts to overlays). Bounded reach≥2 mass-run: ov_SC01_000's 15 reach≥2 (shared) stubs banked **0/15** (vs the reach-1 spot-run's 7/15) — the model is **weakest on shared code** (corpus skipped the `DEFINE_func` bodies + it's the regalloc/schedule tail). But **5/15 are close≤3 reach-134 near-misses** (3× close=1 = func_8012E27C/BF4C/AD64) → high-value permuter fuel (×134 each). **FINDING: reach≥2 model-only ≠ a fleet lever; the lever is reach≥2-draft → grinder-close (×134)**, which needs `grinder.py`'s per-binary fix (same class as T7) + a backlog `binary` field. (A foreground mass-run hit the 10-min Bash cap mid-2nd-batch; tree recovered clean via `git checkout`, check-all 136/136.) Details: `docs/gen2-mips-matching-model.md` "T9 RESULT". NEXT: the grinder per-binary fix (realize the reach-134 ×134 fuel), then corpus-v3.
- 2026-06-30 (cont.): **Grinder per-binary fix (5-layer) — built + validated; reach≥2 fuel re-characterized.** The grinder/backlog were ov_SC01_077-hardcoded **5 layers deep** — fixed all: `gate_stage` records `binary`, `backlog.FIELDS` keeps it, `backlog.load_best`/`_open_stubs` fleet-aware (stuck-local fns surface via their overlay record), `p16_permute.setup` takes asm-subdir, `grinder` per-binary resolution + grouped gating + None-`nins` allow. Validated: the 3 fresh reach-134 close=1 ov_SC01_000 fns now surface + resolve + gate per-000. **FINDINGS (byte-evidenced):** (a) the reach≥2 close=1 fuel is **MODEL semantic-misses** (7B drafts empty `void f(void){}` for trivial `return 1`/setters), NOT permuter fuel — a corrected draft banks them (**+3 byte-identical** via the fixed gate, @commit:0326); (b) ×reach is **propagation-capped** (inline-matched in `ov_SC01_077_a.c` → dedup "nothing to propagate" → banked ×1). So the reach-134 ×134 payoff routes through **corpus-v3** (leaf variety) + the **dedup-collapse**, NOT the permuter. check-all 136/136 throughout. Details: `docs/gen2-mips-matching-model.md` (grinder fix). NEXT: corpus-v3 + dedup-collapse.
- 2026-06-30 (**8-hour autonomous run, Drew away**): prompt-fix + local serving + corpus-v3 + v3 + big batch. **LM Studio ejected** → built **`tools/serve_local.py`** (Unsloth GPU serving as an OpenAI endpoint; the prebuilt llama-cpp-python CUDA wheel SIGILLs on this no-AVX-512 CPU, so the Unsloth/torch path is the reliable one). **PROMPT FIX** (`api_draft.LEAN_SYS` + `format_finetune.SYS`, synced): "translate EVERY instruction, never an empty body" — the v2 empty-leaf overfit, small-leaf band **0/3→2/3**, banked 3 on a fresh ov_SC01_001 batch. **CORPUS-V3**: `export_pairs` now mines the **1623 engine_core.h `DEFINE_func` macros** (the shared setters/return-const the model was blind to — 96.6% of v2 was overlay-unique) + `format_finetune` inlines `engine_types.h` structs → corpus **1312→2891**, trainable **2534+291** (2.5× v2). **v3 trained** (Qwen2.5-Coder-7B QLoRA, loss 1.275→0.085, ~2h), held-out gate-true eval **MATCH 23/40 (57.5%)** (generalizing; v2's mixed-set rate was lower). Grinder concurrent during training = 0 banks (permuter tail exhausted, Phase-22 reality). **Big batch** (v3, broad rotation, 25 binaries, propagate-every-3, **$0 LLM**): banked **~352 fns inline + 45 new shared groups** (1633→1678) → fleet **63.67% → 63.82%** (+502 byte-identical), **136/136 byte-clean**, 25 auto-commits. v3 repeatedly banked the **empty-leaf/setter class v2 couldn't** (func_8012E27C=return 1, 8012AD64/BF4C=setters). **Pipeline validated end-to-end:** a free local fine-tuned model harvests the small/setter bulk at $0, gated identically (G3/P9). Details: `docs/gen2-mips-matching-model.md` ("Corpus-v3 ... 8-hour autonomous run"). **NEXT (fresh session):** (1) **shared/reach≥2 targeting** (`lora_grind --min-reach 2` with v3 — so the setter banks propagate ×134 instead of re-banking inline per binary — the fleet-% lever); (2) **dedup-collapse** the per-binary inline setters; (3) **corpus-v4** = struct-giant types; raise `--max-nins` as the band lifts. Serve: `tools/serve_local.py --adapter models/bfm-match-7b-v3` (R21 / SETUP §Tooling inventory).