|
| 1 | +# VisualBuilderPlan-v8 — Raschka-Gallery Full-Loop Closure |
| 2 | + |
| 3 | +**Status**: planned 2026-05-23 (epic `cppmega-mlx-v8`) |
| 4 | + |
| 5 | +**Driver**: post-v7 audit. v7 wired the operator UX (file picker, edge |
| 6 | +validation, checkpoint history, smoke_zero1). v8 closes the *gallery- |
| 7 | +to-train round trip* the user actually asks for: |
| 8 | + |
| 9 | +> "Из панели вытащить любую модель из galler.html, увидеть её |
| 10 | +> архитектурный спек и оригинальные training hyperparams, скейл-даунить |
| 11 | +> под мой devbox, увидеть memory matrix × precision × parallelism, |
| 12 | +> мутировать (residual / norm / MTP / MHC / Engram / n-gram side-channel), |
| 13 | +> увидеть что зафьюзится / материализуется / синхронизируется, поднять |
| 14 | +> данные с HF в стиле nanochat ИЛИ с GitHub через tree-sitter+clang, |
| 15 | +> прогнать N шагов трейна — и всё это покрыто Playwright e2e." |
| 16 | +
|
| 17 | +## bd ticket ID mapping (post-creation) |
| 18 | + |
| 19 | +| R# | scope | bd parent | priority | |
| 20 | +|------|----------------------------------------------------|----------------------|----------| |
| 21 | +| R01 | Per-preset training defaults (lr/bs/sched from paper) | new epic | P1 | |
| 22 | +| R02 | scale_down(preset, target_bytes) helper + UI slider | new epic | P1 | |
| 23 | +| R03 | Memory matrix UI (precision × parallelism) | new epic | P1 | |
| 24 | +| R04 | Auto-fit to detected devbox | new epic | P1 | |
| 25 | +| R05 | MXFP4 on Apple Metal (e2m1 block-scaled) | new epic | P2 | |
| 26 | +| R06 | Compile-trace panel (inductor / dlpack / fused) | new epic | P2 | |
| 27 | +| R07 | Z3 sync-necessity checker + advice | new epic | P2 | |
| 28 | +| R08 | Mid-canvas feature injection (MTP/MHC/Engram/n-gram) | new epic | P1 | |
| 29 | +| R09 | HF nanochat-style data quick-start | new epic | P1 | |
| 30 | +| R10 | Tree-sitter / clang GitHub-corpus side-stream | new epit | P2 | |
| 31 | +| R11 | N-step train end-to-end (full closure) | new epic | P0 | |
| 32 | +| R12 | Playwright e2e harness for the full loop | new epic | P0 | |
| 33 | + |
| 34 | +## 1. Why v8 exists |
| 35 | + |
| 36 | +The audit dialogue with the operator confirmed the gap is no longer |
| 37 | +"can we wire X" but "given any of the 57 presets, can you complete the |
| 38 | +*single-pane* journey: drag → see paper defaults → scale-down → see |
| 39 | +memory matrix → mutate (feature injection) → see compile/fuse trace → |
| 40 | +attach data → run N steps → see live loss". v7-Q delivered the UX |
| 41 | +plumbing; v8 delivers the journey *as one e2e flow*. |
| 42 | + |
| 43 | +We have: |
| 44 | +- 57 presets in `cppmega_v4/architectures/presets.py` (Raschka coverage) |
| 45 | +- `verify_and_estimate` per-brick memory at every mutation |
| 46 | +- 17 gotcha rules + `suggest_sharding` + 8 topology factories |
| 47 | +- `mx.distributed` real DP/TP/PP (V7-B-real) |
| 48 | +- bf16 / fp16 / fp32 / fp8 (e4m3, e8m0 blockscaled) precision toggles |
| 49 | +- Plasticity (FIRE/DASH/ReDo), 8-bit Adam/Muon, LossScaler |
| 50 | +- Live WS streaming (`/ws/train/{run_id}`, `/ws/gen/{job_id}`, |
| 51 | + `/ws/verify/{spec_hash}`) with sparkline + dead-man-switch |
| 52 | +- 62 existing Playwright e2e specs |
| 53 | + |
| 54 | +We *don't* have: |
| 55 | +- Per-preset paper hyperparameter table |
| 56 | +- Auto scale-down API |
| 57 | +- MXFP4 (Metal e2m1 path) |
| 58 | +- Compile-trace visualization |
| 59 | +- Z3 sync-necessity check |
| 60 | +- Tree-sitter/clang GitHub side-stream |
| 61 | +- HF nanochat-style quick-attach |
| 62 | +- The single integrated end-to-end e2e test |
| 63 | + |
| 64 | +## 2. Goal |
| 65 | + |
| 66 | +After v8 lands, an operator opens vbgui, picks any preset from the |
| 67 | +gallery, and reaches a finished N-step train in one continuous flow |
| 68 | +where every intermediate decision is informed by real backend data |
| 69 | +(paper defaults, memory matrix, fuse-trace, z3 advice). |
| 70 | + |
| 71 | +## 3. Stages — 12 R-epics × 2-5 sub-tasks ≈ 36 sub-tickets |
| 72 | + |
| 73 | +Workflow per epic stays identical to v5/v6/v7: |
| 74 | +discuss → plan → execute → verify, atomic commits per sub-task, |
| 75 | +real backend changes never silently degraded to mocks. |
| 76 | + |
| 77 | +## 4. Per-epic acceptance + sub-task breakdown |
| 78 | + |
| 79 | +### R01 — Per-preset training defaults (P1, 3 subs) |
| 80 | + |
| 81 | +**Why**: dragging llama3_8b currently inherits INITIAL_SPEC's |
| 82 | +generic `lr=1e-3 / adamw / constant`, not the paper's |
| 83 | +`lr=3e-4, wsd schedule, β=(0.9, 0.95)`. The operator must |
| 84 | +re-discover paper hyperparams from scratch. |
| 85 | + |
| 86 | +**Subs**: |
| 87 | +- R01.1 `cppmega_v4/architectures/preset_training_defaults.py`: |
| 88 | + table keyed by preset → {lr, batch, schedule, betas, gradient_clip, |
| 89 | + warmup_steps, mixed_precision, source_paper_url}. ≥ 30 presets covered. |
| 90 | +- R01.2 `build_preset_specs` RPC grows `defaults: TrainingDefaults` |
| 91 | + alongside `specs`; UI auto-fills LossTab / OptimTab / ScheduleEditor. |
| 92 | +- R01.3 Vitest + pytest parity check that defaults round-trip. |
| 93 | + |
| 94 | +**AC**: pick `llama3_8b` → OptimTab shows `lr=3e-4` automatically; pick |
| 95 | +`gpt_oss_120b` → schedule pre-populates with `cosine warmup=2000`. |
| 96 | + |
| 97 | +### R02 — scale_down(preset, target_bytes) helper + UI slider (P1, 4 subs) |
| 98 | + |
| 99 | +**Subs**: |
| 100 | +- R02.1 `cppmega_v4/architectures/scale_down.py:scale_down_preset(preset, target_bytes, *, min_dim=64) -> dict`: |
| 101 | + binary-search `hidden_size`, fall back to `num_layers` halving when |
| 102 | + hidden_size hits min_dim. Returns the resolved |
| 103 | + `(hidden_size, num_layers, est_bytes)`. |
| 104 | +- R02.2 New RPC `architectures.scale_down` (registry + dispatcher). |
| 105 | +- R02.3 UI: target-memory slider in GalleryTab → live `est_bytes` |
| 106 | + preview before drag-to-canvas. |
| 107 | +- R02.4 pytest: scale_down(llama3_8b, 1GB) ≤ 1GB and ≥ 0.5GB. |
| 108 | + |
| 109 | +### R03 — Memory matrix UI (P1, 3 subs) |
| 110 | + |
| 111 | +**Subs**: |
| 112 | +- R03.1 RPC `memory.matrix(spec, topologies, precisions) -> Matrix` |
| 113 | + returns 4 topologies × 4 precisions (fp32, bf16, fp16, fp8) per-rank |
| 114 | + worst-case bytes, plus a `fits` boolean per cell. |
| 115 | +- R03.2 UI: new `MemoryMatrix.tsx` (16 cells, color-coded fit/no-fit, |
| 116 | + hover for breakdown). Lives in sidebar/MemoryMatrixTab. |
| 117 | +- R03.3 Playwright: pick preset → matrix renders 16 cells → at least |
| 118 | + one cell green on `m3_ultra_solo`. |
| 119 | + |
| 120 | +### R04 — Auto-fit to detected devbox (P1, 2 subs) |
| 121 | + |
| 122 | +**Subs**: |
| 123 | +- R04.1 `platform.get_info` already returns the host's RAM/Metal info. |
| 124 | + New RPC `architectures.auto_fit(preset, host_info)` chains scale_down |
| 125 | + + suggest_sharding to pick the smallest valid (hidden, layers, axes) |
| 126 | + that fits. |
| 127 | +- R04.2 UI: "Auto-fit to this Mac" button next to GalleryTab preset → |
| 128 | + one-click drop to canvas with scaled spec. |
| 129 | + |
| 130 | +### R05 — MXFP4 on Apple Metal (e2m1 block-scaled) (P2, 4 subs) |
| 131 | + |
| 132 | +**Why**: user explicitly asked. NVIDIA NVFP4 is Hopper/Blackwell-only |
| 133 | +but Apple MLX 0.16+ exposes a `mx.metal` block-scaled e2m1 quant |
| 134 | +path the user nicknamed mx4. This is a real opportunity. |
| 135 | + |
| 136 | +**Subs**: |
| 137 | +- R05.1 `cppmega_mlx/quant/mxfp4_metal.py`: e2m1 block-scaled quant |
| 138 | + (16-elem blocks, fp8 e4m3 scales) using `mx.metal_kernel`. Round-trip |
| 139 | + RMSE ≤ bf16 → bf16 quant. |
| 140 | +- R05.2 SchemeRouter (`_quantize_8bit.py`) gets `QUANT_SCHEME_MXFP4`. |
| 141 | +- R05.3 Sharding spec gets `mxfp4_enabled` flag mirroring `fp8_enabled`. |
| 142 | +- R05.4 Parity test: e2m1 quant ↔ bf16 within ≤ 5% RMSE on a random |
| 143 | + tensor; smoke-train at mxfp4 reaches finite loss. |
| 144 | + |
| 145 | +### R06 — Compile-trace panel (P2, 4 subs) |
| 146 | + |
| 147 | +**Why**: user wants to see *what* the compiler fuses, what materializes, |
| 148 | +what passes through dlpack, and where MLX vs torch.compile vs TileLang |
| 149 | +takes over. |
| 150 | + |
| 151 | +**Subs**: |
| 152 | +- R06.1 RPC `compile.trace(spec, backend in {mlx, tilelang, torch_inductor})`: |
| 153 | + reuses `torch_compile_backend.inspect_fx_graph` for the torch path |
| 154 | + and `dispatch_lower(prim, return_msl=True)` for the TileLang path. |
| 155 | + Returns `{ops: [{name, fused, materialised, dlpack_boundary}], |
| 156 | + fused_groups: [...], compile_artifact_path}`. |
| 157 | +- R06.2 UI: `CompileTracePanel.tsx` — list ops with colored chips: |
| 158 | + green=fused, yellow=dlpack-crossing, red=materialised, gray=stays |
| 159 | + in source backend. |
| 160 | +- R06.3 Hook into BrickContextPanel — clicking a brick highlights its |
| 161 | + ops in the trace. |
| 162 | +- R06.4 pytest: trace on a small spec returns ≥ 1 fused group when the |
| 163 | + TileLang path is engaged. |
| 164 | + |
| 165 | +### R07 — Z3 sync-necessity checker (P2, 3 subs) |
| 166 | + |
| 167 | +**Why**: every dlpack boundary or `mx.eval` is a candidate sync point; |
| 168 | +many are unnecessary. Z3 can prove necessity from the data-flow graph. |
| 169 | + |
| 170 | +**Subs**: |
| 171 | +- R07.1 `cppmega_v4/spec/sync_checker.py`: build a small SSA-style |
| 172 | + graph from the spec's brick chain; encode constraints |
| 173 | + ("op B reads tensor X which op A writes; if A and B are on same |
| 174 | + backend, no sync needed"); solve with z3 (`z3-solver` dep). Returns |
| 175 | + `{necessary_syncs: [...], redundant_syncs: [{op, reason}], advice: [...]}`. |
| 176 | +- R07.2 RPC `sync.check(spec)`. |
| 177 | +- R07.3 UI: badge in CompileTracePanel — "3 redundant syncs, click |
| 178 | + for advice"; advice modal explains each candidate. |
| 179 | + |
| 180 | +### R08 — Mid-canvas feature injection (P1, 4 subs) |
| 181 | + |
| 182 | +**Subs**: |
| 183 | +- R08.1 `cppmega_v4/buildspec/rewriters.py` already has MTP / |
| 184 | + MHC / IFIM / Engram rewriters — confirm and surface in catalog. |
| 185 | +- R08.2 `catalog.list_options('feature_injectors')` enumerates |
| 186 | + available rewriters with their effect descriptions. |
| 187 | +- R08.3 UI: new `FeatureInjectionBar.tsx` between ParallelComposeBar |
| 188 | + and InsertIntoEdgeBar — dropdown of {MTP_WEIGHTED, MHC, IFIM, Engram, |
| 189 | + n_gram_side_channel} + Apply button. Mutation goes through existing |
| 190 | + rewriter pipeline. |
| 191 | +- R08.4 Playwright: apply MTP rewriter via the bar → run train → assert |
| 192 | + `extras.train.mtp.per_head_losses` populates (proves the rewriter |
| 193 | + actually fired, not just listed). |
| 194 | + |
| 195 | +### R09 — HF nanochat-style data quick-start (P1, 3 subs) |
| 196 | + |
| 197 | +**Subs**: |
| 198 | +- R09.1 `scripts/data/hf_quickstart.py`: takes |
| 199 | + `{dataset_id, split, tokenizer, n_tokens}`, streams from HF (datasets |
| 200 | + lib), tokenizes with the chosen tokenizer, writes a tmp parquet |
| 201 | + shard ready for stage_train. Matches the karpathy-nanochat "fineweb |
| 202 | + edu shuffle" pattern. |
| 203 | +- R09.2 RPC `data.hf_quickstart` (long-running, publishes progress on |
| 204 | + `/ws/data/{job_id}`). |
| 205 | +- R09.3 UI: button in DataInspector — "Quick-start from HF" → modal |
| 206 | + with dataset_id input + token-budget slider; on completion the |
| 207 | + parquet path auto-populates. |
| 208 | + |
| 209 | +### R10 — Tree-sitter / clang GitHub-corpus side-stream (P2, 4 subs) |
| 210 | + |
| 211 | +**Subs**: |
| 212 | +- R10.1 We already have `scripts/nanochat_data/clang_enriched_to_parquet.py` |
| 213 | + + `extract_git_history.py`. Wrap them into a single |
| 214 | + `scripts/data/github_corpus.py {repo_url, max_commits, max_tokens}`. |
| 215 | +- R10.2 RPC `data.github_corpus` parallel to `data.hf_quickstart`. |
| 216 | +- R10.3 UI: tabs in the HF-quickstart modal — "HF Hub" vs "GitHub |
| 217 | + repo (tree-sitter / clang)". |
| 218 | +- R10.4 Playwright: drive the GitHub branch against a small public |
| 219 | + repo; assert the resulting parquet has `source_doc_id` + clang |
| 220 | + side-channels populated. |
| 221 | + |
| 222 | +### R11 — N-step train end-to-end (P0, 3 subs) |
| 223 | + |
| 224 | +**Subs**: |
| 225 | +- R11.1 No new RPC — this is the *integration*: pick preset → R01/R02/R03 |
| 226 | + fire → R08 inject MTP → R09/R10 attach data → stage_train runs N |
| 227 | + steps with live WS sparkline (V7-L37..L41 already shipped). |
| 228 | +- R11.2 Document the canonical flow in a new |
| 229 | + `docs/raschka_full_loop.md` cookbook. |
| 230 | +- R11.3 Manual UAT script in `docs/uat/v8_raschka_full_loop.md`: |
| 231 | + 10-step user journey with expected screenshots. |
| 232 | + |
| 233 | +### R12 — Playwright e2e harness for the full loop (P0, 3 subs) |
| 234 | + |
| 235 | +**Subs**: |
| 236 | +- R12.1 `vbgui/e2e/scenarios/v8_raschka_full_loop.spec.ts`: a single |
| 237 | + end-to-end test that walks through R01 → R02 → R03 → R08 → R09 → R11 |
| 238 | + on a synthetic 64-token quick-data path. Total time ≤ 90s on dev box. |
| 239 | +- R12.2 Each intermediate assertion captures `page.on('console')` + |
| 240 | + `page.on('pageerror')` into `test.info().annotations` so flaky runs |
| 241 | + leave diagnosable traces (as the user requested earlier). |
| 242 | +- R12.3 A `--full-loop` flag on the e2e runner that picks 5 different |
| 243 | + presets in a matrix and validates the same 10-step journey on each. |
| 244 | + |
| 245 | +## 5. Workflow per epic (same as v5/v6/v7) |
| 246 | + |
| 247 | +For each R-epic: |
| 248 | +1. `bd update <id> --notes` capturing the audit context above. |
| 249 | +2. Discuss → plan → execute → verify per gsd-quick template. |
| 250 | +3. Atomic commit per sub-task with `feat(v8-rNN): ...` prefix. |
| 251 | +4. Acceptance gate: pytest + vitest + Playwright cover every sub-task. |
| 252 | + |
| 253 | +## 6. Acceptance counters |
| 254 | + |
| 255 | +- **R01..R12**: 12 epics; 36 sub-tickets total. |
| 256 | +- **Tests added**: ≈ 25 pytest, ≈ 10 vitest, ≈ 15 Playwright e2e. |
| 257 | +- **New RPCs**: `architectures.scale_down`, `architectures.auto_fit`, |
| 258 | + `memory.matrix`, `compile.trace`, `sync.check`, |
| 259 | + `data.hf_quickstart`, `data.github_corpus`. (+ 3 ws endpoints: |
| 260 | + `/ws/data/{job_id}`, others reuse v7.) |
| 261 | +- **Backend new files**: ≈ 9. |
| 262 | +- **UI new components**: `MemoryMatrix`, `CompileTracePanel`, |
| 263 | + `FeatureInjectionBar`, HF/GitHub quickstart modal, |
| 264 | + GalleryTab scale-down slider, auto-fit button. |
| 265 | + |
| 266 | +## 7. Out of scope (v9+) |
| 267 | + |
| 268 | +- NVFP4 (Hopper/Blackwell only). |
| 269 | +- Multi-host training launcher UI (CLI runbook only — `mlx.launch` / |
| 270 | + mpirun via `scripts/launch_multi.py` ships in v7-B-real). |
| 271 | +- Replacing existing `tiny_aya` / `zaya1` preset internals. |
| 272 | +- Cross-preset auto-arch-search (NAS). |
| 273 | + |
| 274 | +## 8. Done definition |
| 275 | + |
| 276 | +- All 12 R-epics closed in bd. |
| 277 | +- VisualBuilderPlan-v8.md + VisualBuilderSpec-v8.md present. |
| 278 | +- `v8_raschka_full_loop.spec.ts` green on llama3_8b, qwen3_dense_4b, |
| 279 | + gemma3_27b, deepseek_v3, gpt_oss_20b. |
| 280 | +- `docs/raschka_full_loop.md` + `docs/uat/v8_raschka_full_loop.md` |
| 281 | + written. |
0 commit comments