Skip to content

Commit 6f418cb

Browse files
committed
docs(v6): 25-gap epic — UI wiring + real-math for V5 placeholders (cppmega-mlx-te6)
V5 audit found 14/25 closed gaps were backend-only or analytical proxies. v6 closes backend-ready→UI-wired→real-math triangle. 5 groups: A. UI wiring of V5 backends (H01-H10): Sharding/precision/Cancel/ warm-start/checkpoint UI controls + long-run e2e + DimensionsTab dispatch + probe textarea + Save/Load roundtrip + SideChannelsTab B. obs→math upgrade (H11-H14): memory parity, WS reconnect recovery, other-stages UI walk, AblationsTab expand C. real-math for V5 proxies (H15-H19): per-rank shard sim, dtype switching, real cross-doc mask, MoE forward hook, strict identical-loss-continuation D. advanced (H20-H24): fake-rank distributed, resume drift, concurrent trains, fp16 dtype, 100+ step UI walk w/ checkpoint E. meta-honesty (H25): audit script labels every V3+V4+V5 test 🟢/🟡/🔴 — target 0 🔴 ~190 sub-tickets total. Target 0 backend-without-UI tests. Design VisualBuilderPlan-v6.md (340 lines), spec VisualBuilderSpec-v6.md (310 lines). Epic cppmega-mlx-te6 created.
1 parent bc88316 commit 6f418cb

2 files changed

Lines changed: 736 additions & 0 deletions

File tree

VisualBuilderPlan-v6.md

Lines changed: 388 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
# VisualBuilderPlan-v6 — UI Wiring + Real Math-Effect for V5 Placeholders
2+
3+
**Status**: planned 2026-05-22 (epic id TBD)
4+
**Driver**: V5 closure audit found that out of 25 closed tickets, only
5+
~11 are real math-effect. The remaining 14 split between:
6+
- **"backend ready + UI not wired"** — 10 gaps where pytest passes but
7+
pressing nothing through UI can reproduce the effect (G05/G07/G09/G10/
8+
G12/G17/G19/G20/G15/G11)
9+
- **observation-only** — 4 gaps where extras report numbers but UI parity
10+
/ forward effect / lifecycle recovery never asserted (G06/G08/G18/G21)
11+
- **analytical proxies** — 3 gaps where extras reports `λ×something_norm`
12+
instead of running actual kernel (G07/G17/G25)
13+
14+
v6 closes the **backend-ready → UI-wired → real-math-effect** triangle.
15+
16+
## 1. Why v6 exists
17+
18+
V5 honest audit produced this taxonomy (25 gaps):
19+
20+
### A. UI wiring of V5 backends (10 gaps)
21+
Backend contracts shipped, no UI surface or App dispatch route. Through
22+
UI a user cannot reproduce what pytest covers.
23+
24+
| Gap | V5 origin | UI work needed |
25+
|------|-----------|-----------------------------------------------------|
26+
| H01 | G05 | Sharding ShardingTab → axis assignments → train |
27+
| H02 | G07 | TopBar fp8/mixed_precision toggles → train |
28+
| H03 | G09 | TopBar Cancel button + WS abort RPC + cancelled status row |
29+
| H04 | G10 | TopBar train-warm-start checkbox → opts.continue_from_run_id |
30+
| H05 | G12 | TopBar checkpoint-save/load path inputs → opts |
31+
| H06 | G15 | UI Train N=100 walk + e2e on real-corpus |
32+
| H07 | G19 | App dispatches DimensionsTab Apply → re-verify loop |
33+
| H08 | G20 | TopBar train-probe-text textarea → opts.inference_probe_text |
34+
| H09 | G11 | Save→Load roundtrip Train → extras parity e2e |
35+
| H10 | (V4-10) | SideChannelsTab (proper) replacing dropdown checkboxes|
36+
37+
### B. Observation → math-effect upgrade (4 gaps)
38+
Extras report numbers but no parity / recovery / forward-effect assertion.
39+
40+
| Gap | V5 origin | Math claim to assert |
41+
|------|-----------|-----------------------------------------------------|
42+
| H11 | G06 | memory_peak_bytes within 30% of UI MemoryBar estimate|
43+
| H12 | G08 | mid-train WS drop → reconnect → state preserved e2e |
44+
| H13 | G21 | UI walk through smoke pipeline asserting each stage extras |
45+
| H14 | G18 | AblationsTab table shows full extras per row (not just final) |
46+
47+
### C. Real math-effect for V5 analytical proxies (5 gaps)
48+
V5 returned `λ×proxy_norm` instead of running the kernel.
49+
50+
| Gap | V5 origin | Real-math upgrade |
51+
|------|-----------|-----------------------------------------------------|
52+
| H15 | G05 | stage_train simulates per-rank shard (loss parity) |
53+
| H16 | G07 | mlx dtype switching actually casts params |
54+
| H17 | G17 | doc_ids → real attention mask in attention forward |
55+
| H18 | G25 | MoE forward hook → routing entropy + load balance |
56+
| H19 | G12 | Strict identical-loss-continuation: save→load→losses[0]==saved[-1] within 1e-5 |
57+
58+
### D. Multi-device / advanced (5 gaps)
59+
| Gap | Description |
60+
|------|------------------------------------------------------|
61+
| H20 | Multi-device distributed train smoke (2 fake ranks) |
62+
| H21 | Inference-after-resume generation drift bounded |
63+
| H22 | Concurrent Train clicks → second click queued or rejected |
64+
| H23 | fp16 dtype option alongside bf16/fp8 |
65+
| H24 | 100+ step UI walk → checkpoint → resume → continuation|
66+
67+
### E. Meta-honesty (1 gap)
68+
| Gap | Description |
69+
|------|------------------------------------------------------|
70+
| H25 | Honest categorisation audit across V3+V4+V5 tests — every test labelled 🟢 math-effect / 🟡 propagation / 🔴 decorative. Target 0 🔴, no propagation in math-claimed surfaces. |
71+
72+
## 2. Goal
73+
74+
After v6: every V5 backend with a math claim has a UI surface that lets
75+
the user trigger it AND an e2e test that walks UI→backend→numerical
76+
assertion. Zero "backend pytest works but UI can't reach it" tests.
77+
78+
## 3. Stages — 25 H-gaps × 6-10 sub-tasks ≈ 200 sub-tickets
79+
80+
```
81+
cppmega-mlx-v6 (epic)
82+
├── H01-H10 UI wiring of V5 (10 × 6-8)
83+
├── H11-H14 obs→math upgrade (4 × 7-9)
84+
├── H15-H19 real-math for proxies (5 × 8-10)
85+
├── H20-H24 advanced (5 × 8-10)
86+
└── H25 meta-honesty (1 × 6)
87+
88+
Total: ~190 sub-tickets
89+
```
90+
91+
## 4. Per-gap acceptance + sub-task breakdown
92+
93+
### H01 — Sharding ShardingTab → train (UI wiring V5-G05, 8)
94+
- H01.1 ShardingTab accept proposal updates spec.sharding.axis_assignments
95+
- H01.2 App.handleRunPipeline forwards via stage_options (verify already wired)
96+
- H01.3 Topology-axis degree mismatch UI warning
97+
- H01.4 Train button respects sharding-incompatible config (disabled+reason)
98+
- H01.5 e2e: accept proposal → Train → extras.sharding_applied matches selection
99+
- H01.6 e2e: change topology mid-canvas → re-verify → new axes shown
100+
- H01.7 vitest ShardingTab dispatch
101+
- H01.8 Closure regression: G05 e2e tightened (compile_mode propagation)
102+
103+
### H02 — fp8/mixed_precision UI → train (UI wiring V5-G07, 7)
104+
- H02.1 TopBar `top-bar-mixed-precision` checkbox dispatches optim mutation
105+
- H02.2 TopBar `top-bar-fp8-enabled` checkbox dispatches sharding mutation
106+
- H02.3 App wire to spec.optim.mixed_precision / spec.sharding.fp8_enabled
107+
- H02.4 e2e: toggle mixed_precision → extras.master_dtype changes
108+
- H02.5 e2e: toggle fp8 → extras.fp8_active=true
109+
- H02.6 vitest TopBar checkboxes
110+
- H02.7 Honest doc: G07 propagation → UI-driven
111+
112+
### H03 — Cancel button + WS abort RPC (UI wiring V5-G09, 8)
113+
- H03.1 TopBar Cancel button (`run-pipeline-cancel`) visible while train running
114+
- H03.2 App tracks running run_id; click → rpc.call("pipeline.abort", {run_id})
115+
- H03.3 Backend JSON-RPC method `pipeline.abort` → request_abort(token)
116+
- H03.4 Modal shows "cancelled" status row with partial losses
117+
- H03.5 e2e: start N=64 → click Cancel → modal shows partial losses
118+
- H03.6 e2e: Cancel disabled when no train running
119+
- H03.7 vitest Cancel button
120+
- H03.8 Closure: G09 backend → UI-driven cancel
121+
122+
### H04 — train-warm-start checkbox (UI wiring V5-G10, 6)
123+
- H04.1 TopBar `train-warm-start` checkbox + previousRunId state
124+
- H04.2 App forwards continue_from_run_id when checked
125+
- H04.3 e2e: enable warm-start, run twice, second extras.opt_state_carried=true
126+
- H04.4 e2e: second-run losses[0] < first-run losses[0]
127+
- H04.5 vitest TopBar checkbox
128+
- H04.6 Honest doc: G10 backend → UI-driven
129+
130+
### H05 — checkpoint save/load path inputs (UI wiring V5-G12, 8)
131+
- H05.1 TopBar `train-checkpoint-save-path` text input
132+
- H05.2 TopBar `train-checkpoint-load-path` text input
133+
- H05.3 App forwards via stage_options.train
134+
- H05.4 e2e: set save-path → Train → extras.checkpoint.saved_path matches
135+
- H05.5 e2e: set load-path → Train → extras.checkpoint.loaded_path matches
136+
- H05.6 e2e: round-trip save → fresh page → load → Train → losses[0] within 1e-3 of saved[-1]
137+
- H05.7 vitest TopBar inputs
138+
- H05.8 Closure: G12 + H19 strict continuation
139+
140+
### H06 — UI N=100 real-corpus walk (UI wiring V5-G15, 7)
141+
- H06.1 e2e: load real parquet+tokenizer, set N=100, Train (90s budget)
142+
- H06.2 Assert extras.num_steps==100
143+
- H06.3 Assert losses_smoothed monotone-window
144+
- H06.4 Assert weight_delta_norm > 0.01
145+
- H06.5 Assert inference_probe.l2_diff > 0.1 (100 steps significant)
146+
- H06.6 Perf gate: completes within 120s on Apple Silicon
147+
- H06.7 Mark long-running tag for CI selective run
148+
149+
### H07 — DimensionsTab Apply dispatches spec mutation (UI wiring V5-G19, 7)
150+
- H07.1 App.tsx onApply handler maps entry → spec action
151+
- H07.2 Dispatch sets brick param to suggested value
152+
- H07.3 Re-verify removes the now-fulfilled suggestion
153+
- H07.4 e2e: load preset → Apply → row disappears
154+
- H07.5 e2e: applied suggestion reaches Train via extras.model_summary
155+
- H07.6 vitest App.onApply mapping
156+
- H07.7 Honest doc: G19 button → full feedback loop
157+
158+
### H08 — train-probe-text textarea (UI wiring V5-G20, 6)
159+
- H08.1 TopBar `train-probe-text` textarea in train dropdown
160+
- H08.2 App forwards via stage_options.train.inference_probe_text
161+
- H08.3 e2e: provide "hello world", Train → extras.inference_probe.real_tokens=true
162+
- H08.4 e2e: extras.inference_probe.text_len > 0
163+
- H08.5 e2e: top1_token_drift >= 0
164+
- H08.6 vitest TopBar textarea
165+
166+
### H09 — Save/Load roundtrip extras parity (UI wiring V5-G11, 8)
167+
- H09.1 e2e: build spec → Save → capture Blob
168+
- H09.2 Refresh page → Load same Blob
169+
- H09.3 Train → capture extras_after_load
170+
- H09.4 Build identical spec from scratch → Train → extras_baseline
171+
- H09.5 Assert model_summary equal between two
172+
- H09.6 Assert losses match within 1e-4
173+
- H09.7 vitest serialiser + parser
174+
- H09.8 Closure: G11 → identical-extras round-trip proven
175+
176+
### H10 — Proper SideChannelsTab (UI wiring V4-10/V5-G17, 9)
177+
- H10.1 New sidebar tab `sidebar-tab-side-channels`
178+
- H10.2 Per-family toggle + per-family data preview
179+
- H10.3 Replace train-dropdown checkboxes
180+
- H10.4 App wires via stage_options.train.side_channels
181+
- H10.5 e2e: enable doc_ids in SideChannelsTab → Train → forward_effect populated
182+
- H10.6 e2e: SideChannelsTab persists across tab switches
183+
- H10.7 vitest SideChannelsTab toggles
184+
- H10.8 Honest doc: V4-10 minimum-viable → proper tab
185+
- H10.9 Closure: replace 24_side_channels.spec.ts with tab-driven version
186+
187+
### H11 — Memory peak vs estimate parity (obs→math V5-G06, 8)
188+
- H11.1 verify RPC returns `estimated_peak_bytes` (separate from per-rank)
189+
- H11.2 UI MemoryBar shows both estimate + actual after Train
190+
- H11.3 vitest MemoryBar dual display
191+
- H11.4 e2e: Train → assert |actual - estimate| / estimate < 0.5 (within 50%)
192+
- H11.5 pytest: estimate accuracy on llama3_8b (tighter 30%)
193+
- H11.6 testid `memory-bar-actual`, `memory-bar-estimate`
194+
- H11.7 honest categorisation
195+
- H11.8 closure regression
196+
197+
### H12 — WS reconnect mid-train recovery (obs→math V5-G08, 9)
198+
- H12.1 e2e: start N=32 train
199+
- H12.2 page.context().setOffline(true) mid-train
200+
- H12.3 Wait 2s → setOffline(false)
201+
- H12.4 Assert BottomStrip shows reconnecting then connected
202+
- H12.5 Assert modal eventually shows completed train
203+
- H12.6 Assert no spurious error modal
204+
- H12.7 Backend WS heartbeat per-step
205+
- H12.8 vitest reconnect UI states
206+
- H12.9 Closure: G08 testid → recovery e2e
207+
208+
### H13 — Other-stages extras through UI (obs→math V5-G21, 7)
209+
- H13.1 e2e: run Smoke pipeline (12 stages)
210+
- H13.2 Expand each stage row in modal
211+
- H13.3 Assert dry_forward extras present (batch, seq_len, hidden, num_nodes)
212+
- H13.4 Assert loss_smoke extras (loss_value, loss_finite)
213+
- H13.5 Assert optimizer_smoke extras (optimizer_kind, num_groups)
214+
- H13.6 Negative: skipped stages don't crash on expand
215+
- H13.7 Closure: G21 pytest → UI-walk e2e
216+
217+
### H14 — AblationsTab full extras display (obs→math V5-G18, 8)
218+
- H14.1 AblationsTab row shows expand for full extras (like RunResultModal)
219+
- H14.2 vitest expand renders extras subtree
220+
- H14.3 e2e: run ablation → expand a variant row → full extras visible
221+
- H14.4 Assert per-row losses array displayed
222+
- H14.5 Assert per-row model_summary displayed
223+
- H14.6 Strict parity: sequential UI Train vs ablation variant — same extras shape
224+
- H14.7 Honest doc: G18 structural → user-visible
225+
- H14.8 Closure regression
226+
227+
### H15 — Real per-rank shard simulation (real-math V5-G05, 9)
228+
- H15.1 Backend: when sharding_applied has FSDP axis, simulate per-rank
229+
param shard for memory accounting
230+
- H15.2 extras.sharding_applied gets `per_rank_param_bytes`
231+
- H15.3 pytest: 8-way FSDP halves per-rank vs unsharded
232+
- H15.4 pytest: loss bit-identical between sharded and unsharded
233+
- H15.5 Strict tolerance < 1e-6
234+
- H15.6 e2e: UI accept fsdp2 → per_rank_param_bytes populated
235+
- H15.7 e2e: change to fsdp1 → per_rank_param_bytes differs
236+
- H15.8 Honest doc: G05 propagation → real simulation
237+
- H15.9 Closure regression
238+
239+
### H16 — Real mlx dtype switching (real-math V5-G07, 10)
240+
- H16.1 Backend: when master_dtype="fp32", instantiate params in fp32
241+
- H16.2 Backend: when train_dtype="fp16", cast forward inputs
242+
- H16.3 Backend: when fp8_active=true, attempt fp8 (may fallback w/ reason)
243+
- H16.4 extras.dtype_actual reports what mlx actually used
244+
- H16.5 pytest: fp32 master + bf16 train produces different param norms vs all-bf16
245+
- H16.6 pytest: fp16 train loss within tolerance of bf16
246+
- H16.7 e2e: toggle precision → extras.dtype_actual reflects
247+
- H16.8 Fallback path: fp8 unsupported → graceful + reason
248+
- H16.9 Honest doc: G07 string-echo → real cast
249+
- H16.10 Closure regression
250+
251+
### H17 — Real cross-doc attention mask (real-math V5-G17, 10)
252+
- H17.1 Backend: _build_attention accepts doc_attention_mask param
253+
- H17.2 stage_train.forward injects mask when doc_ids in side_channels
254+
- H17.3 Mask: positions with different doc_ids → -inf in attention scores
255+
- H17.4 extras.side_channels_forward_effect.doc_mask_applied = bool
256+
- H17.5 pytest: with doc_ids vs without → losses differ > 1e-4
257+
- H17.6 pytest: single-doc (all doc_ids equal) reduces to no-mask
258+
- H17.7 e2e: UI enable doc_ids → losses differ from disabled
259+
- H17.8 Token_ids: real conditional embed addition
260+
- H17.9 Honest doc: G17 proxy → real kernel
261+
- H17.10 Closure regression
262+
263+
### H18 — MoE forward hook (real-math V5-G25, 10)
264+
- H18.1 Backend: when MoE brick present, wrap forward with routing hook
265+
- H18.2 Capture routing weights per token (top-k softmax)
266+
- H18.3 Compute routing_entropy = mean(-Σ p log p) over tokens
267+
- H18.4 Compute load_balance_loss = Σ (expert_load - 1/num_experts)²
268+
- H18.5 Compute dropped_token_ratio when capacity factor < 1
269+
- H18.6 extras.moe populated with real values (not nulls)
270+
- H18.7 pytest: gpt_oss_20b → routing_entropy ∈ (0, log(num_experts))
271+
- H18.8 pytest: top_k=1 → routing_entropy < top_k=2
272+
- H18.9 e2e: change top_k via brick context → routing_entropy changes
273+
- H18.10 Honest doc: G25 propagation → real measurement
274+
275+
### H19 — Identical-loss-continuation strict (real-math V5-G12, 9)
276+
- H19.1 pytest: save checkpoint after N=4 → load → run 1 more step
277+
- H19.2 Assert losses[0] of resumed == losses[3] of original within 1e-5
278+
- H19.3 pytest: same with optimizer state carried (Adam moments)
279+
- H19.4 e2e: full UI walk Train N=4 save → fresh page Load Train N=1
280+
- H19.5 Assert losses[0] match (within 1e-3 since UI noise)
281+
- H19.6 Backend: opt.state also saved separately (state.safetensors)
282+
- H19.7 extras.checkpoint.opt_state_path
283+
- H19.8 Negative: missing opt-state file → warning + cold restart
284+
- H19.9 Closure regression: G12 smoke → strict math
285+
286+
### H20 — Multi-device distributed smoke (advanced, 10)
287+
- H20.1 Backend: stage_train accepts opts.fake_ranks=N
288+
- H20.2 Forward + backward simulated on N fake ranks
289+
- H20.3 Reduce gradients via mean
290+
- H20.4 extras.fake_ranks + extras.gradient_reduce_ms
291+
- H20.5 pytest: fake_ranks=2 vs 1 loss within 1e-6
292+
- H20.6 pytest: fake_ranks=4 weight_delta similar magnitude
293+
- H20.7 e2e: UI selects topology with degree=2 → fake_ranks=2 in extras
294+
- H20.8 Sharding axis assignment respected per fake rank
295+
- H20.9 Performance gate: fake_ranks=8 within 2× wall-clock vs 1
296+
- H20.10 Closure: real distributed proxy
297+
298+
### H21 — Inference-after-resume bounded drift (advanced, 8)
299+
- H21.1 Backend: extras.inference_probe.post_resume_drift = float
300+
- H21.2 Save checkpoint after train; load fresh model; resume train 1 step
301+
- H21.3 Compare inference probe between {train→save→reload} and {train continuous}
302+
- H21.4 Assert drift < 1e-3 (proves checkpoint round-trip preserves weights bit-exact)
303+
- H21.5 pytest end-to-end
304+
- H21.6 e2e: UI save→fresh→resume→probe text → drift bounded
305+
- H21.7 Negative: corrupt checkpoint → clear error not crash
306+
- H21.8 Closure regression
307+
308+
### H22 — Concurrent Train clicks (advanced, 8)
309+
- H22.1 App tracks trainInFlight state (one at a time)
310+
- H22.2 Second click while running → queued indicator or rejected
311+
- H22.3 TopBar Train button shows "Training..." while in-flight
312+
- H22.4 e2e: click Train twice quickly → only one modal opens
313+
- H22.5 e2e: second click after first completes → fresh run
314+
- H22.6 vitest Train button state machine
315+
- H22.7 Honest doc: lifecycle robust
316+
- H22.8 Closure regression
317+
318+
### H23 — fp16 dtype option (advanced, 8)
319+
- H23.1 Schema accepts train_dtype="fp16"
320+
- H23.2 UI TopBar precision dropdown {bf16, fp16, fp32}
321+
- H23.3 Backend casts forward inputs to fp16 when selected
322+
- H23.4 extras.train_dtype reports
323+
- H23.5 pytest: fp16 loss finite + within 1e-3 of bf16
324+
- H23.6 pytest: fp16 weight_delta < fp32 weight_delta (less precision)
325+
- H23.7 e2e: UI select fp16 → extras.train_dtype=="fp16"
326+
- H23.8 Closure regression
327+
328+
### H24 — 100+ step UI walk with checkpoint (advanced, 9)
329+
- H24.1 e2e: load preset + real corpus
330+
- H24.2 Set N=100, set save-path
331+
- H24.3 Train → modal completes
332+
- H24.4 Fresh page → set load-path → set N=20
333+
- H24.5 Train → assert losses[0] starts where saved[-1] ended
334+
- H24.6 Assert continuation within 1e-3
335+
- H24.7 Closure of G15 + G12 + H19 chain
336+
- H24.8 Perf gate < 200s wall-clock
337+
- H24.9 Mark long-running tag
338+
339+
### H25 — Honest categorisation audit (meta, 7)
340+
- H25.1 Script audit_v3_v4_v5.py walks all tests/v3+v4+v5/ + vbgui/e2e/scenarios/
341+
- H25.2 For each test extracts category (math-effect / propagation /
342+
decorative) from assertion patterns
343+
- H25.3 Markdown report: per-test label + assertion summary
344+
- H25.4 Target: 0 🔴 decorative, 🟡 propagation only on genuinely-config
345+
fields (e.g. project name)
346+
- H25.5 vbgui-side counterpart for vitest
347+
- H25.6 Closure report `tests/fixtures/honesty_audit_v6.md`
348+
- H25.7 v7 follow-up list
349+
350+
## 5. Workflow per gap (same as v5)
351+
352+
1. `bd update <id> --claim`
353+
2. Implement (each sub-task atomic)
354+
3. Code review (self if <100 LOC, gsd-code-reviewer otherwise)
355+
4. Perf check (no >5% regression on affected suite)
356+
5. Regression (pytest + vitest + e2e affected)
357+
6. `git add` specific files → commit → push → verify on origin/main
358+
7. `bd close <id>` only after push verified
359+
360+
## 6. Acceptance counters
361+
362+
| Surface | Before v6 | Target v6 |
363+
|----------------------------------------|-----------|-----------|
364+
| pytest | ~2400 | ≥2500 |
365+
| vitest | 189 | ≥220 |
366+
| Playwright deep e2e cells | 120+ | ≥200 |
367+
| extras keys | 30 | ≥38 |
368+
| backend-without-UI tests | ~10 | 0 |
369+
| 🟢 math-effect tests | ~25 | ≥55 |
370+
| 🟡 propagation tests on math claims | ~10 | 0 |
371+
| 🔴 decorative tests | 0 | 0 |
372+
373+
## 7. Out of scope (defer to v7+)
374+
375+
- Real multi-mac distributed training (network protocol, gradient
376+
all-reduce, FSDP2 sharded forward)
377+
- Production-scale (1B+ param) training matrix
378+
- A/B hyperparameter search UI
379+
- Anomaly detection on loss curves (NaN traps, gradient explosion)
380+
- Live inference serving from trained checkpoint
381+
- Model weight quantisation post-train
382+
- LoRA / PEFT adapter wiring
383+
384+
## 8. Done definition
385+
386+
All 25 H-gaps closed. Zero "backend pytest but no UI walk" tests.
387+
Closure report at `tests/fixtures/e2e_matrix_v6_report.md`. Honest
388+
categorisation audit at `tests/fixtures/honesty_audit_v6.md`.

0 commit comments

Comments
 (0)