mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 05:56:00 -04:00
9d07712205
- ×134 wave fuel walled (waves 13-16: +6 banked incl 476-ins func_80141CA4 ×134, fleet 60.93% -> 61.16%); grinder stopped (Drew). Pivot to reach-1 (overlay-unique) fuel. - reach-1 fuel BUILT: 298 uncached reach-1 prefetched (headless DecompileFunctions.java, 298 ok / 0 fail) -> all 420 reach-1 region-main draftable fns cached (median 47 ins). - tools/wave_targets.py: new reach1 pool, SMALLEST-FIRST sort (idiom-mining order). - tools/orchestrator.py: reach1 in POOLS; orch_state set to reach1 (prep --mode pool serves it). - docs/automation-runbook.md + CURRENT_PHASE.md: the reach-1 smallest-first harvest cycle (distill EVERY wave) for the fresh session Drew drives. x1 leverage (ov_SC01_077 only). - docs/matching-cookbook.md: marked the 2 floating §21 bullets CANDIDATE/unverified (near-miss func_801775E0, not byte-banked); drafter-cookbook-edit guard at commit:0219. - NOT started: no waves launched (Drew's instruction). Byte-gate unchanged (G3/P9).
108 lines
7.9 KiB
Markdown
108 lines
7.9 KiB
Markdown
# Phase-21 Automation Runbook (the unattended grind)
|
||
|
||
The automation manager: a **token-free grinder** (CPU permuter) + a **token-heavy worker** (LLM
|
||
agent waves), both banking through the **incorruptible whole-binary byte-gate** (G3/P9 — a wrong
|
||
match can NEVER bank) and logging every near-miss to a **ranked backlog** for hand-finishing.
|
||
|
||
## What is running right now (2026-06-22 — reach-1 pivot)
|
||
|
||
**Nothing running.** The reach-134 ×134 wave fuel hit its wall (waves 1–16 → fleet ≈61.16%, then confirmed
|
||
walls), and the grinder is STOPPED (`.run/auto/STOP` present). The project pivoted to the **reach-1 harvest**
|
||
(below) — fuel built + staged, NOT started; Drew drives it from a fresh session. Full state:
|
||
`phase-ends/CURRENT_PHASE.md` ★★ REACH-1 HANDOFF.
|
||
|
||
## ★ Reach-1 harvest — smallest-first idiom-mining (the Phase-21 active direction)
|
||
|
||
**Fuel (BUILT):** 420 reach-1 (overlay-unique) region-main draftable fns in `ov_SC01_077`, ALL Ghidra-C cached,
|
||
small (median 47 ins; 251 ≤60 ins). `tools/wave_targets.py --pool reach1` serves them **smallest-first**; the
|
||
orchestrator `POOLS` + `.run/auto/orch_state.json` are set to `reach1`, so **`prep --mode pool` serves it**.
|
||
Leverage is **×1** (overlay-unique → banks in `ov_SC01_077` only; `dedup_propagate` skips reach<2, no ×134) —
|
||
but `ov_SC01_077` is the largest overlay, so 420 small fns is a real fleet lever (plausibly +1–3%).
|
||
|
||
**The cycle (Drew controls pacing — `/loop` self-paced or hand-cycle):**
|
||
1. `.venv/bin/python tools/orchestrator.py prep --mode pool --n 24` → reach1 smallest-first batch → `.run/auto/wave_batch.json`.
|
||
2. Read the batch; launch `tools/workflows/worker_wave.js` with `args={draftDir:".run/drafts-wave", targets:<batch array PASTED VERBATIM>}`.
|
||
3. `.venv/bin/python tools/orchestrator.py finish --drafts .run/drafts-wave --commit` — **BACKGROUNDED + `dangerouslyDisableSandbox`** (foreground make build/git get sandbox-killed exit 144). Prints `{banked, near, verified, fleet_pct, …}`.
|
||
4. **Distill EVERY wave that has `verified` banks** — `tools/workflows/distill.js` `args={draftsDir:".run/drafts-wave", verified:<array>}`. This is an idiom-MINING pass (more aggressive than the conservative ×134 run): each new gcc quirk → cookbook → feeds forward to bigger reach-1 fns AND occasionally cracks a reach-134 **wall** (×134 bonus, like the Phase-18 pin idiom).
|
||
5. Loop (~17 waves for all 420). close-rate should stay HIGH (small fns) → no pool rotation; if it drops <0.15 ×2 and rotates off `reach1`, re-route by setting `"pool":"reach1"` in `.run/auto/orch_state.json`.
|
||
6. **Optional token-free grinder** (parallel — permutes reach-1 near-misses): `rm -f .run/auto/STOP && DRIVER=tools/grinder.py setsid nohup bash tools/auto_supervisor.sh --permute-secs 120 -j 14 >/dev/null 2>&1 &` (dangerouslyDisableSandbox).
|
||
|
||
The Monitor / STOP / grinder / safety sections below apply unchanged. (The original ×134 "Launch the WORKER
|
||
waves" cycle below is the same mechanics with a different pool — reach1 supersedes it for the active run.)
|
||
|
||
### Grinder (token-free, currently STOPPED)
|
||
|
||
`tools/grinder.py` under `tools/auto_supervisor.sh` — permutes the backlog's closest near-misses → byte-gate →
|
||
banks → ×134. Idles when drained; supervisor relaunches on crash. Heartbeat: `.run/auto/grinder_heartbeat.json`.
|
||
|
||
## Monitor (read-only, from anywhere)
|
||
|
||
```sh
|
||
bash tools/auto_status.sh # grinder + worker heartbeats, backlog size, recent commits
|
||
cat .run/auto/grinder_heartbeat.json # grinder: state/current/banked/fleet
|
||
.venv/bin/python tools/orchestrator.py status # worker ROI state (pool, waves, banked_total)
|
||
.venv/bin/python tools/backlog.py show -n 40 # the ranked near-miss backlog (docs/backlog.md)
|
||
git log --oneline -15 | grep phase-21 # what banked
|
||
make report BINARY=main # fleet % (+ dedup byte-honesty check, must be 0 failed)
|
||
```
|
||
|
||
## STOP everything (the kill switch)
|
||
|
||
```sh
|
||
bash tools/auto_stop.sh # touches .run/auto/STOP -> grinder finishes its current step, exits;
|
||
# the supervisor sees STOP and does not relaunch. Safe at any time.
|
||
rm .run/auto/STOP # to allow a relaunch later
|
||
```
|
||
|
||
## Launch the WORKER waves (token-heavy — the high-yield engine)
|
||
|
||
The worker drafts matching C with LLM agents (the §17–20 toolkit: register pins, array-of-struct
|
||
%lo-fold, call-site casts) — it cracks the hard tail the grinder can't. It needs a **Claude session**
|
||
(only a session can invoke the Workflow tool), so it runs as a self-paced **`/loop`**:
|
||
|
||
1. In a Claude Code session in this repo, run **`/loop`** with this cycle as the prompt:
|
||
> Run one Phase-21 orchestrator cycle, then stop if `.run/auto/STOP` exists:
|
||
> (a) `.venv/bin/python tools/orchestrator.py prep --n 24` — auto-picks a **class-focused** wave (re-attempt
|
||
> the backlog's biggest gcc-quirk class) or a **fresh pool** wave; it prints `{mode, sel, n, batch}`.
|
||
> (b) Read `.run/auto/wave_batch.json` and launch the **`tools/workflows/worker_wave.js`** Workflow with
|
||
> `args={draftDir:".run/drafts-wave", targets:<the batch array>}`. Wait for it.
|
||
> (c) `.venv/bin/python tools/orchestrator.py finish --drafts .run/drafts-wave --commit` — gates, banks,
|
||
> propagates ×134, logs near-misses; prints `{banked, propagated, near, verified, ...}`.
|
||
> (d) If `verified` is non-empty, launch the **`tools/workflows/distill.js`** Workflow with
|
||
> `args={draftsDir:".run/drafts-wave", verified:<the verified array>}` — it extracts any NEW byte-verified
|
||
> gcc idiom into the cookbook so the next wave inherits it (the learning flywheel).
|
||
> (e) Report the one-line result.
|
||
2. **The learning flywheel:** fresh waves classify their near-misses by gcc-quirk (the drafter stamps
|
||
`// @class`/`// @stuck`); once a class accrues ≥6 near-misses, `prep` fires a **class-focused re-attempt
|
||
wave** for it (drafters get the prior stuck-point + the live cookbook); `distill` turns each wave's banked
|
||
techniques into new cookbook idioms. This is the Phase-18 close-rate-rising loop, automated.
|
||
3. `/loop` self-paces (~15–20 min/wave, ~275k tokens/wave of ~24 agents). The grinder runs alongside,
|
||
draining near-misses. Cost-bounded by the ROI gate + `auto_stop.sh`. Inspect classes anytime:
|
||
`.venv/bin/python tools/wave_targets.py --list-classes`.
|
||
|
||
**Remote management (Drew has laptop + can remote into the dev box):** you don't need a bulletproof
|
||
keep-alive — if the worker `/loop` session dies, just remote in and re-run `/loop` (the grinder daemon
|
||
keeps running regardless, and every bank is already committed, so nothing is lost). Monitor with
|
||
`tools/auto_status.sh`; stop with `tools/auto_stop.sh`; resume by re-launching. The byte-gate guarantees
|
||
correctness while unattended, so the worst case of a crash is "it paused," never "it broke something."
|
||
|
||
## Re-prefetch fuel (only if adding fresh targets, needs Ghidra)
|
||
|
||
The run is **cache-based** (no live MCP needed). To add targets to the Ghidra-C cache later:
|
||
```sh
|
||
bash tools/ghidra_mcp_stop.sh # R23 (free the project lock)
|
||
.venv/bin/python tools/build_fuel_manifest.py --emit-prefetch .run/prefetch_addrs.txt
|
||
"$HOME/ghidra_12.1_PUBLIC/support/analyzeHeadless" "$HOME/bfm-decomp/ghidra" bfm \
|
||
-process ov_SC01_077 -noanalysis -readOnly -scriptPath tools/ghidra_scripts \
|
||
-postScript DecompileFunctions.java .run/prefetch_addrs.txt .run/ghidra_c
|
||
```
|
||
|
||
## Safety invariants (why this is safe to leave running)
|
||
|
||
- **Byte-gate is the sole arbiter (G3/P9):** every bank is whole-binary SHA1-verified; a wrong draft
|
||
is reverted, never banked. `make check-all` stays 136/136.
|
||
- **git is the crash-safe state machine:** every bank is a checkpoint commit (push is manual, R6 —
|
||
nothing leaves the machine on its own); `dedup_propagate --auto-from` is additive/resumable.
|
||
- **`auto_stop.sh`** halts both engines at the next safe boundary.
|
||
- **Honest measurement (P9):** only byte-matches bank; near-misses go to `docs/backlog.md`, ranked.
|