mlx-bun 0.0.10 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +66 -21
  2. package/THIRD_PARTY_LICENSES.md +1 -0
  3. package/docs/archive/mlx-bun-lab-report.html +275 -0
  4. package/docs/archive/wwdc-mlx-bun.html +848 -0
  5. package/docs/design/audio-input-plan.md +356 -0
  6. package/docs/design/batching-perf-path.md +58 -35
  7. package/docs/design/batching-v2-plan.md +23 -9
  8. package/docs/design/bucketing-stage.md +2 -1
  9. package/docs/design/compat-cli-surface-design.md +4 -3
  10. package/docs/design/decode-speed-program.md +172 -0
  11. package/docs/design/docs-reorg-plan.md +1 -1
  12. package/docs/design/dspark-serving-program.md +371 -0
  13. package/docs/design/dspark-speculative-decoding.md +167 -5
  14. package/docs/design/faithful-l1-consolidation.md +310 -0
  15. package/docs/design/generic-model-support.md +8 -5
  16. package/docs/design/grammar-spec-batching-integration.md +301 -0
  17. package/docs/design/minicpm5-decode-megakernel.md +16 -11
  18. package/docs/design/mlx-lm-tool-parity-plan.md +11 -1
  19. package/docs/design/omlx-adoption-map.md +14 -6
  20. package/docs/design/orpo-training.md +34 -30
  21. package/docs/design/paged-kv-cache.md +214 -0
  22. package/docs/design/parallel-slots.md +82 -49
  23. package/docs/design/parity-tier-dag.md +43 -33
  24. package/docs/design/repo-cleanup-plan.md +189 -0
  25. package/docs/design/runtime-isolation.md +19 -3
  26. package/docs/design/segmented-backward-training.md +3 -2
  27. package/docs/design/ssd-kv-cold-tier.md +130 -0
  28. package/docs/design/structured-output.md +484 -0
  29. package/docs/design/the-dreaming-master-plan.md +2 -3
  30. package/docs/design/turboquant-kv.md +235 -0
  31. package/docs/design/unified-engine-frontier-plan.md +774 -0
  32. package/docs/design/web-chat-beat-matrix.md +315 -0
  33. package/docs/design/web-chat-redesign.md +1112 -0
  34. package/docs/investigations/dspark-handoff.md +182 -16
  35. package/docs/investigations/lab-build-journal.md +2 -2
  36. package/docs/investigations/openwiki-evaluation.md +249 -0
  37. package/docs/planning/IDEAS.md +14 -0
  38. package/docs/planning/PRODUCT_ROADMAP.md +2 -3
  39. package/docs/planning/chat-ui-vision-memory-first.md +199 -0
  40. package/docs/planning/chat-ui-vision.md +50 -0
  41. package/docs/planning/memory-docs-and-dag-plan.md +15 -6
  42. package/docs/planning/release-notes-v0.0.11.md +239 -0
  43. package/docs/reference/cli.md +35 -16
  44. package/docs/reference/distribution.md +13 -6
  45. package/docs/reference/embedding.md +35 -14
  46. package/docs/reference/features-matrix.md +97 -0
  47. package/docs/reference/library-api.md +30 -9
  48. package/docs/reference/memory.md +26 -6
  49. package/docs/reference/models.md +11 -1
  50. package/docs/reference/orpo-quickstart.md +3 -4
  51. package/docs/reference/server-api.md +634 -22
  52. package/docs/reference/server-config.md +250 -121
  53. package/docs/reference/training.md +14 -15
  54. package/package.json +5 -3
  55. package/src/anthropic.ts +13 -0
  56. package/src/audio/conformer.ts +647 -0
  57. package/src/audio/decode.ts +133 -0
  58. package/src/audio/features.ts +202 -0
  59. package/src/audio/transcode.ts +67 -0
  60. package/src/cli.ts +356 -105
  61. package/src/config.ts +49 -2
  62. package/src/download.ts +40 -0
  63. package/src/eval/README.md +7 -6
  64. package/src/eval/kl.ts +59 -3
  65. package/src/eval/quality-db.ts +2 -2
  66. package/src/eval/runner.ts +3 -3
  67. package/src/expert-offload.ts +15 -2
  68. package/src/flags.ts +13 -0
  69. package/src/generate.ts +330 -24
  70. package/src/grammar.ts +585 -0
  71. package/src/hub-rest.ts +242 -0
  72. package/src/kv-store.ts +442 -88
  73. package/src/lora.ts +8 -0
  74. package/src/md-modules.d.ts +42 -0
  75. package/src/media-fetch.ts +284 -0
  76. package/src/memory/rest.ts +245 -0
  77. package/src/memory/vault.ts +74 -0
  78. package/src/mlx/array.ts +106 -19
  79. package/src/mlx/autograd.ts +2 -2
  80. package/src/mlx/ffi.ts +27 -3
  81. package/src/mlx/metal-kernel.ts +7 -4
  82. package/src/mlx/ops.ts +153 -23
  83. package/src/mlx/turboquant-ops.ts +831 -0
  84. package/src/mlx/turboquant-tables.ts +237 -0
  85. package/src/model/batched-mask.ts +62 -1
  86. package/src/model/batched-quant.ts +206 -0
  87. package/src/model/batched-rotating-quant.ts +346 -0
  88. package/src/model/batched-rotating.ts +46 -1
  89. package/src/model/compiled-decode.ts +4 -15
  90. package/src/model/factory.ts +10 -4
  91. package/src/model/gemma4-base.ts +363 -39
  92. package/src/model/gemma4.ts +122 -33
  93. package/src/model/generated/gemma4-12b.ts +9 -13
  94. package/src/model/generated/gemma4-26b.ts +10 -5
  95. package/src/model/generated/gemma4-e4b.ts +14 -9
  96. package/src/model/minicpm5.ts +58 -102
  97. package/src/model/paged-kv.ts +253 -0
  98. package/src/model/qwen3-delta.ts +76 -15
  99. package/src/model/qwen3-moe.ts +404 -0
  100. package/src/model/qwen3.ts +9 -8
  101. package/src/model/qwen3_5.ts +88 -23
  102. package/src/model/support.ts +12 -1
  103. package/src/model/universal/dense.ts +40 -5
  104. package/src/model/universal/rope.ts +36 -21
  105. package/src/pi-web.ts +1299 -82
  106. package/src/preflight.ts +27 -8
  107. package/src/prompt-cache.ts +293 -33
  108. package/src/quantize/quantizer.ts +16 -1
  109. package/src/registry.ts +57 -2
  110. package/src/serve/batch-scheduler.ts +965 -72
  111. package/src/serve/generation-gateway.ts +214 -11
  112. package/src/serve/isolate.ts +417 -0
  113. package/src/serve/lane-registry.ts +44 -0
  114. package/src/serve/session-search.ts +326 -0
  115. package/src/server.ts +1380 -135
  116. package/src/spec/assistant-source.ts +150 -0
  117. package/src/spec/deepspec-source.ts +143 -0
  118. package/src/spec/dflash-source.ts +124 -0
  119. package/src/spec/dspark/ab-stats.ts +136 -0
  120. package/src/spec/dspark/calibration.ts +112 -0
  121. package/src/spec/dspark/deepspec-module.ts +801 -0
  122. package/src/spec/dspark/generate-dflash.ts +72 -20
  123. package/src/spec/dspark/loader.ts +23 -0
  124. package/src/spec/dspark/module-dflash.ts +278 -36
  125. package/src/spec/dspark/quantize-drafter.ts +91 -0
  126. package/src/spec/generate.ts +59 -25
  127. package/src/spec/ngram-source.ts +132 -0
  128. package/src/spec/serve-loop.ts +536 -0
  129. package/src/spec/source.ts +93 -0
  130. package/src/spec/two-model.ts +171 -0
  131. package/src/ssd-cache.ts +107 -35
  132. package/src/tokenizer.ts +288 -2
  133. package/src/tool-approvals.ts +131 -0
  134. package/src/tool-call.ts +408 -20
  135. package/src/train/flash-cce.ts +2 -2
  136. package/src/train/job.ts +0 -2
  137. package/src/train/trainer.ts +1 -4
  138. package/src/vision/prompt.ts +217 -48
  139. package/src/web/app.html +1184 -1789
  140. package/src/web/app.js +5667 -0
  141. package/src/web/icon.svg +13 -0
  142. package/src/web/manifest.webmanifest +24 -0
  143. package/src/web/src/adapters-panel.ts +288 -0
  144. package/src/web/src/api.ts +57 -0
  145. package/src/web/src/assistant.ts +485 -0
  146. package/src/web/src/chat.ts +1050 -0
  147. package/src/web/src/composer.ts +959 -0
  148. package/src/web/src/dataset.ts +132 -0
  149. package/src/web/src/finetune.ts +267 -0
  150. package/src/web/src/hub.ts +428 -0
  151. package/src/web/src/main.ts +98 -0
  152. package/src/web/src/markdown.ts +0 -0
  153. package/src/web/src/memory-panel.ts +572 -0
  154. package/src/web/src/model-picker.ts +159 -0
  155. package/src/web/src/palette.ts +346 -0
  156. package/src/web/src/protocol.ts +62 -0
  157. package/src/web/src/quantize.ts +167 -0
  158. package/src/web/src/rag.ts +274 -0
  159. package/src/web/src/sessions.ts +469 -0
  160. package/src/web/src/shell.ts +804 -0
  161. package/src/web/src/status.ts +252 -0
  162. package/src/web/src/ui-catalog.ts +92 -0
  163. package/src/web/sw.js +71 -0
  164. package/src/web/vendor/README.md +61 -0
  165. package/src/web/vendor/hljs-LICENSE +29 -0
  166. package/src/web/vendor/hljs-theme.css +59 -0
  167. package/src/web/vendor/hljs.js +26 -0
  168. package/src/model/fused-decode-kernel.ts +0 -312
  169. package/src/model/fused-geglu-kernel.ts +0 -198
  170. package/src/model/fused-mlp-kernel.ts +0 -169
  171. package/src/model/fused-swiglu-kernel.ts +0 -240
  172. package/src/model/steel-linear-kernel.ts +0 -96
  173. package/src/status-page.html +0 -497
@@ -78,7 +78,55 @@ Also confirmed: width is NOT the ceiling (`dDraft=1024` and `2560` both plateau
78
78
  ~0.17 pre-scale); the parity-safe `hiddenTap` doesn't change target numerics;
79
79
  long-context spec rollback works via the `trim(n, bypass)` physical-slice.
80
80
 
81
+ ## Serve integration (Phase 1, 2026-07-06)
82
+
83
+ DSpark is now serve-loadable behind the same `--draft-model` seam as every
84
+ other drafter — one verify/accept executor, no lane routing. The seam
85
+ (`src/spec/source.ts`) was extended from its two-model-only shape to carry the
86
+ target state KV-borrowing drafters need (it previously could NOT host these,
87
+ despite the header claiming so):
88
+
89
+ - `DraftProvider.open({sampler, target:{model,caches}})` — the source gets the
90
+ target model + its live caches.
91
+ - `DraftSource.tapLayers?` — when set (DSpark), the serve loop taps the
92
+ target's prefill + every verify forward and hands the captured `[1,L,m*H]`
93
+ context to `prefill(ids, ctxML)` / `commit(n, kAccept, vCtxML)` (ownership
94
+ transfers). `draft(feed, n, stepBase, anchorHidden?)` — the assistant borrows
95
+ the target anchor hidden; DSpark drafts from its growing `H_ctx`.
96
+
97
+ Provider selection is by artifact kind (`src/server.ts` `detectDraftKind`):
98
+ `dspark.json` → **DflashProvider** (`src/spec/dflash-source.ts`), a `*_assistant`
99
+ config → **AssistantProvider** (`src/spec/assistant-source.ts`, the optiq
100
+ KV-borrowing Gemma drafter — ships a real ~1.09× γ=1 decode win with no
101
+ training), else **TwoModelProvider**. `--draft-kind` overrides. The server pins
102
+ `--num-draft-tokens` to the DSpark checkpoint's trained block width
103
+ (`cfg.gamma`) so the serve loop never asks for more positions than were trained.
104
+
105
+ **Unified-engine alignment (per-slot readiness).** This is the
106
+ `DraftSource` seam the unified-engine plan names as the substrate for its
107
+ frontier "per-slot drafting behind the `DraftSource` seam" row
108
+ ([docs/design/unified-engine-frontier-plan.md](unified-engine-frontier-plan.md)).
109
+ Spec still forces the serial lane today (upstream `is_batchable = draft is
110
+ None`); Phase 1 did NOT change that — it made the seam *carry per-source target
111
+ state* (each source binds its own `TargetView{model,caches}`, anchor hidden,
112
+ and tapped H_ctx), which is exactly what a batched executor would need to open
113
+ one `DraftSource` per row. So the seam is forward-compatible with per-slot
114
+ drafting; the hard part that remains is the executor, not the seam — variable
115
+ accept-length per slot breaks a uniform-B step (see
116
+ [spec-decode-larger-targets.md](spec-decode-larger-targets.md) caveats), so
117
+ per-slot spec is a real batched-scheduler project, not free composition.
118
+
119
+ **v1 acceptance caveat:** the serve loop verifies with TOKEN-MATCH acceptance
120
+ (mlx-lm/optiq style, lossless at any temperature), NOT the paper's
121
+ distribution-level rejection-sampling verify. Greedy is identical; temp>0 is
122
+ still lossless but lower-acceptance. The richer rejection-sampling verify stays
123
+ in the standalone `generate-dflash.ts` (the measure script). See
124
+ [[dspark-seam-kv-borrowing]].
125
+
81
126
  ## Files
127
+ - `src/spec/source.ts` — the extended DraftSource/DraftProvider/TargetView seam.
128
+ - `src/spec/assistant-source.ts` / `dflash-source.ts` — the two KV-borrowing providers.
129
+ - `src/spec/serve-loop.ts` — the shared verify/accept executor (now tap-aware).
82
130
  - `src/model/gemma4.ts` — `hiddenTap`/`captureLayer` (parity-safe multi-layer tap).
83
131
  - `src/model/gemma4-base.ts` — `trim(n, bypass)` on rotating caches (spec rollback past the window).
84
132
  - `src/spec/dspark/module-dflash.ts` — faithful DFlash+Markov+confidence module.
@@ -96,11 +144,125 @@ bun scripts/dspark-train-dflash.ts --data <data> --out <ckpt> --iters 8000 --bat
96
144
  bun scripts/dspark-measure-dflash.ts --drafter <ckpt> --data <prompts.jsonl> # τ + tok/s vs vanilla
97
145
  ```
98
146
 
147
+ ## Paper components — Phase 2 (2026-07-06): the paper is CODE-COMPLETE
148
+
149
+ All remaining components landed the same day (multi-agent build, adversarially
150
+ reviewed; smoke 21/21, dspark test files 17/17, real-weights serve gate 3/3):
151
+
152
+ - **Alg 1, single-user form — confidence-scheduled draft-length pruning.**
153
+ `forwardInfer` drops position k (and truncates the block) when
154
+ `c_k < thresholds[k] ?? minConf`; position 0 always survives. Activation is
155
+ checkpoint-driven: STS thresholds in `dspark.json` (`cfg.sts`) or the
156
+ `MLX_BUN_DSPARK_MINCONF` env override — **uncalibrated checkpoints draft
157
+ fixed-γ exactly as before** (zero behavior change). Losslessness invariant:
158
+ pruning changes how many positions the target verifies, never what's emitted
159
+ (gated: truncation-never-redraw, prefix-identical to the unpruned block).
160
+ - **Variable-length draft contract.** `DraftSource.draft()` returns 1..n —
161
+ the RETURN LENGTH is authoritative; the serve loop and `dflashGenerate`
162
+ verify/trim/re-feed over `d = drafts.length`. Two-model/assistant return
163
+ exactly n (unchanged).
164
+ - **STS calibration (§3.2.1).** `src/spec/dspark/calibration.ts`
165
+ (`fitStsThresholds`: per position, smallest τ whose Laplace-smoothed
166
+ `P(accepted | conf ≥ τ)` ≥ target; pos 0 → 0, under-sampled → 0 (don't
167
+ prune on thin evidence), unreachable target → 1.0) +
168
+ `scripts/dspark-calibrate.ts` (GPU: greedy unpruned rounds via
169
+ `dflashGenerate`'s `onRound` hook → fit → patch `config.sts` into
170
+ dspark.json in place). ⚠ estimator shape is our reading — paper PDF absent.
171
+ - **RNN sequential head (Eq 6 — ⚠ design-doc-faithful shape, paper PDF
172
+ absent, flagged in code).** `cfg.seqHead: "markov"|"rnn"`; Elman recurrence
173
+ `s_k = tanh(s_{k-1}·wH + E[x_{k-1}] + bH)`, `B_k = s_k·wO`, `wO` zero-init
174
+ (starts as pure DFlash, like markov.w2); shares `markov.w1` as the token
175
+ embedding (also feeds the confidence head). Same loss, autograd-gated;
176
+ init-equivalence gate: rnn ≡ markov token-for-token at init.
177
+ Train with `scripts/dspark-train-dflash.ts --seq-head rnn`.
178
+ - **Draft loop TIGHTENED** (handoff item 3): greedy tokens chain ON-DEVICE
179
+ (one concat + one host read after the loop, was γ syncs); confidence reads
180
+ deferred+batched when pruning is inactive (per-position only when Alg 1
181
+ needs the answer mid-loop — inherent); `collectLogits: false` on the serve
182
+ path skips the per-position draft-logits materialization entirely
183
+ (`draftLogits` now optional). Gate: bit-identical to the pre-tightening
184
+ loop (pinned reference tokens, tests/dspark-infer-loop.test.ts).
185
+ - **Rename + central loader.** Canonical variant is now `"dspark"`
186
+ (`save()` stamps it; `load()` accepts legacy `"dflash"`);
187
+ `src/spec/dspark/loader.ts` `loadDsparkDrafter()` dispatches by variant and
188
+ refuses v1 single-vector checkpoints with a pointer at the v2 trainer. The
189
+ serve path and the bench (`bench-feature-matrix.ts` via the server's
190
+ exported `detectDraftKind`) both go through it.
191
+
192
+ ## DeepSpec ground truth (2026-07-06 audit — paper arXiv:2607.05147 + source)
193
+
194
+ DeepSeek open-sourced the reference implementation (**DeepSpec**, MIT,
195
+ github.com/deepseek-ai/DeepSpec) + trained drafters incl.
196
+ **`deepseek-ai/dspark_gemma4_12b_block7`** (for google/gemma-4-12B-it — our
197
+ primary target; 6.86 GB bf16 single safetensors, γ=7). Two audit agents
198
+ verified our build against the paper AND the source. Verdicts:
199
+
200
+ - **MATCH the paper exactly:** Eq 2-3 (context + KV injection), the
201
+ anchor++masks block, Eq 5 Markov (r=256), Eq 7 confidence (we add a
202
+ zero-init bias — superset), **Eq 8-12 loss to the letter** (0.1/0.9/1.0,
203
+ w_k=exp(−(k−1)/γ), analytic c*). Our defaults = their production config
204
+ (γ=5, 5 layers, Markov head).
205
+ - **Eq 6 RNN differs:** paper is a gated cell over
206
+ `z_k=[s_{k−1}; W₁[x_{k−1}]; h_k]`, fused (2r+d)→3r projection,
207
+ `B_k=W₂ᵀtanh(W_o z_k)` (conditioned on the backbone hidden). Ours is
208
+ ungated Elman without h_k — kept as variant `"rnn"` (paper calls the gains
209
+ "marginal"; production uses Markov); DeepSpec also ships an RNNHead of the
210
+ gated form if we ever want the faithful one.
211
+ - **§3.2.1 STS + Alg 1: the paper's machinery is NOT in the released code.**
212
+ STS = Sequential Temperature Scaling (per-position temperatures by ECE grid
213
+ search on cumulative survival products); Alg 1 = expected-throughput
214
+ maximization over survival products with a profiled SPS(B) cost table —
215
+ both live in their UNRELEASED production serving layer. The released
216
+ reference ships **per-position threshold truncation on sigmoid confidence
217
+ (default 0 = off), ℓ=0 allowed** — i.e. the released reference is shaped
218
+ like OUR scheduler. Our threshold fit (`calibration.ts`) is therefore a
219
+ reasonable calibrator for the reference-shaped scheduler, NOT the paper's
220
+ STS; renamed framing in code comments. Paper-faithful STS/Alg-1 = a future
221
+ Lab item if the survival-product scheduler ever earns a default.
222
+ - **The reference drafter is a DIFFERENT module than ours:** a full
223
+ Gemma4-shaped 5-layer transformer — hidden 3840, 16 Q heads × head_dim 512,
224
+ **1 KV head with K≡V (`attention_k_eq_v`, no v_proj tensor; scale-less
225
+ v_norm)**, attention scale 1.0 (QK-norm), RoPE partial_rotary 0.25 θ=1e6,
226
+ per-layer learned `layer_scalar`, scaled embed (×√3840, not stored), UNTIED
227
+ lm_head, final softcap 30, and an **incremental context-KV cache** (stores
228
+ projected target-context K/V; crops the block's noise K/V each round).
229
+ Sequential Markov sampling + confidence head (input [h; w1[prev]] = 4096)
230
+ as in the paper. Checkpoint: 74 tensors, bf16, **no key prefix**;
231
+ config.json `architectures: ["Gemma4DSparkModel"]` (loader detection key).
232
+ **12B tap layers: `target_layer_ids [5,17,29,41,46]`** (their layer_id+1
233
+ output convention == our tapLayers index; no final-norm sentinel).
234
+ - **THE ORACLE:** their verify is leaky rejection sampling that at
235
+ temperature 0 **degenerates to exact argmax token-match — RNG-free,
236
+ token-for-token deterministic**. Their eval emits per-round
237
+ proposal/accepted/next-token traces. Protocol: run their eval at temp 0
238
+ (+ a threshold arm) on fixed prompts → JSONL round fixtures → our port
239
+ must match round-for-round. Same discipline as the D2 diffusion oracle,
240
+ minus RNG parity entirely.
241
+
242
+ **Consequence:** DSpark is no longer oracle-less L3. The port of their module
243
+ (the `deepspec` variant) + their checkpoint gives a bit-exact-gateable path to
244
+ a REAL 12B speedup with zero training. Our own trainable module remains the
245
+ research path for custom targets. Training recipe reference: open-perfectblend
246
+ regenerated by the target, 10 epochs, batch 512, lr 6e-4, position decay
247
+ exp(−pos/4), ~38 TB cached hiddens — i.e. our "thousands of generations" data
248
+ estimate was directionally right and their scale is far beyond a laptop run.
249
+
99
250
  ## Open items / next
100
- - **Real data scale** (thousands of on-distribution generations, not 160) to lift generalizing τ toward the overfit ~0.75.
101
- - **Retarget to a slow model (27B/12b)** where the τ≈3 architecture actually nets a speedup — the whole point. Needs the drafter sized to that model's H/layers + regen+train there; 27B on 32GB is memory-tight (17.75GB weights + KV budget).
102
- - **Tighten the draft inference loop** (per-position host syncs, double 262K LM-head) so τ translates to wall-clock.
103
- - **Remaining paper components:** RNN head (Eq 6), STS calibration (§3.2.1), hardware-aware prefix scheduler (Alg 1 — single-user form = confidence-scheduled draft-length pruning).
104
- - **Rename** `dflash`→`dspark` (the faithful module IS DSpark; v1 is the legacy single-vector variant).
251
+
252
+ - [x] Serve integration (Phase 1) · [x] Alg-1-shaped scheduler (matches the
253
+ RELEASED reference; paper Alg-1 unreleased) · [x] threshold calibration ·
254
+ [x] RNN head (Elman variant; paper's gated cell differs — documented above)
255
+ · [x] loop tightening · [x] `dspark` rename + loader —
256
+ **everything buildable without a GPU is DONE.**
257
+ - [ ] **DeepSpec-variant port** (`Gemma4DSparkModel`) + loader detection +
258
+ oracle scripts — the no-training path to the 12B speedup (in progress).
259
+ - [ ] **Josh-gated GPU (the payoff):** real data scale (thousands of
260
+ on-distribution generations) + **retarget to 12B** (`--model` +
261
+ `--tap-layers 5,17,29,41,46` (DeepSeek's trained taps); 27B memory-infeasible to train on 24 GB, kept
262
+ dim-generic) + train (`--seq-head` A/B optional) + **calibrate**
263
+ (`scripts/dspark-calibrate.ts`) + live-τ measure. Recipe:
264
+ [docs/investigations/dspark-handoff.md](../investigations/dspark-handoff.md).
265
+ - [ ] Verify the Eq 6 / §3.2.1 shapes against the actual paper when the PDF
266
+ is available (both flagged in code).
105
267
 
106
268
  Full session handoff: `docs/investigations/dspark-handoff.md`.
@@ -0,0 +1,310 @@
1
+ # Faithful → L1: consolidating the "match mlx-lm" path
2
+
3
+ > **2026-07-05 addendum:** the Phase-1 deletion pass executed —
4
+ > fused-gelu/fused-swiglu/perf-kernel/fused-decode/CPM5_FAITHFUL deleted, `--l3`
5
+ > removed ([unified-engine-frontier-plan.md §6](unified-engine-frontier-plan.md)).
6
+ > The flag tables below are the historical decision record.
7
+
8
+ **Status:** PLANNED (2026-07-04). Written after the 2026-07-04 audit of the
9
+ `MLX_BUN_FAITHFUL` work (commit `fd8ff5f`). The faithful kernels are correct and
10
+ proven byte-identical to mlx-lm — but they were wired as a *parallel base-preset
11
+ axis* rather than folded into the L1 parity regime, which (a) abandons the
12
+ model-specific optimized files and (b) leaves the preset incomplete. This doc is
13
+ the contract for fixing that. Companion memory:
14
+ `faithful-is-the-l1-implementation`. Framing: [parity-tier-dag.md](parity-tier-dag.md),
15
+ `three-level-fidelity-tree-model` (memory).
16
+
17
+ ## The governing framing (two orthogonal axes)
18
+
19
+ 1. **Parity** — a *guarantee about which oracle you reproduce bit-for-bit*, full
20
+ logprobs (not just greedy argmax). **L1 = mlx-lm, L2 = mlx-optiq, L3 = no
21
+ oracle.** A config *is* L1 by definition iff its output matches mlx-lm bit-for-bit.
22
+ Nothing about parity concerns speed or methodology.
23
+ 2. **Performance / implementation** — orthogonal. Within a regime there are many
24
+ valid implementations (faster/slower, more/less memory). For a given regime we
25
+ **default-serve the fastest implementation that still holds the guarantee**; a
26
+ user overrides individual kernels with extra flags (`--l1 --fused-geglu`).
27
+
28
+ **Where "faithful" fits:** it is *the fastest known implementation of L1* — it runs
29
+ mlx-lm's own exact kernels, so it's bit-exact, and per the 2026-07-03 h2h it is
30
+ faster than our previous L1 path (the old "perf" path was net-SLOWER than mlx-lm:
31
+ 12B 0.88×, cpm5 0.95×, perf-kernel HURT e4b 0.62×). So faithful should become
32
+ **`--l1`'s default implementation**, not a new naked default and not a third
33
+ selection system.
34
+
35
+ **Naked `mlx-bun` (no flags) is unchanged:** best experience for local AI on a Mac —
36
+ fastest / most-intelligent / most-memory-efficient, no parity worries. The user who
37
+ needs the mlx-lm drop-in guarantee reads the docs and passes `--l1`.
38
+
39
+ > **SUPERSEDED 2026-07-05 — naked default is now `--l1`.** The full h2h pass
40
+ > (benchmarks-h2h-2026-07-05, M1 Max 32GB) showed the L1 faithful kernel set at
41
+ > exact speed parity with mlx-lm (1.00×) on cpm5/e4b/12B while every
42
+ > output-changing lever failed to beat it in a paired A/B: fused-decode 1.00×,
43
+ > fused-gelu +0–1%, the perf arm 0.62–0.93× on e4b (its one win, 12B @16k +6%,
44
+ > carried a KL WARN), and quantized KV 5–20% slower decode than bf16 at ≤16k on
45
+ > BOTH stacks (mlx-lm's own kv8 trails its bf16). "Best experience" and "L1"
46
+ > turned out to be the same thing today. Decision: naked = the l1 preset
47
+ > (`applyDecodeRoute` defaults the tier), quantized KV and every non-faithful
48
+ > kernel are opt-in (`--l2`/`--l3`/per-fork flags), and the L1 baseline is the
49
+ > base all future optimization must beat in a paired A/B to earn a default.
50
+
51
+ **`--l1/--l2/--l3` are pure ALIASES, not modes.** Each expands to a fixed set of the
52
+ individual per-fork flags — [`applyDecodeRoute`, cli.ts:752](../../src/cli.ts) just
53
+ `set()`s env vars, and a per-fork flag overrides the alias. Two invariants follow,
54
+ and they are the real design bar:
55
+
56
+ - **A user can reproduce any tier byte-for-byte by passing the individual flags.**
57
+ `--l1` is nothing but "the fastest parity-holding value of each flag."
58
+ - **No preset may set anything that isn't also an individual flag.** There is exactly
59
+ one bundle per tier; nothing bypasses the flag layer.
60
+
61
+ `MLX_BUN_FAITHFUL` violates both — it is a *second bundle* that sets choices
62
+ (compiled-geglu, `compiledLogitSoftcap`) which are **not exposed as flags at all**, so
63
+ `--l1` can neither reproduce nor reach them. Corollary: bit-exact-to-mlx-lm actually
64
+ *wants* compiled activations (mlx-lm itself `@mx.compile`s geglu/swiglu), so the
65
+ current `--l1` "unfused" description is backwards — **compiled is the L1 default**
66
+ (bit-exact + faster); unfused is a slower, same-parity opt-in.
67
+
68
+ ## What is actually wrong today
69
+
70
+ Three overlapping "match mlx-lm" mechanisms exist and disagree:
71
+
72
+ - **Tier system** `--l1/--l2/--l3` ([`applyDecodeRoute`, cli.ts:752](../../src/cli.ts))
73
+ — the correct axis, but `--l1` keeps `compiled-decode` on, leaves
74
+ `MLX_BUN_FUSED_GELU` on (custom metal geglu, divergent), and does **not** route to
75
+ the faithful compiled-geglu. So today `--l1` is not actually bit-exact for gemma.
76
+ - **`MLX_BUN_FAITHFUL` preset** ([faithful.ts](../../src/faithful.ts)) — flips a
77
+ *different, smaller* set of defaults and **forces the gemma monolith**
78
+ ([factory.ts:51](../../src/model/factory.ts)), abandoning the generated
79
+ `gemma4-12b/e4b/26b` specialization. It also leaves `MLX_BUN_PERF_KERNEL` on
80
+ (fires on every quantized-KV/OptiQ model), leaves kv-quant on, and never sets
81
+ `MLX_BUN_CPM5_FAITHFUL` — so `=1` is neither a real L1 guarantee on OptiQ models
82
+ nor faithful for MiniCPM5.
83
+ - **`Faithful*` subclasses** — `FaithfulMiniCPM5` (wired), and `FaithfulGemma4` /
84
+ `FaithfulQwen3` / `FaithfulQwen35` / `FaithfulUniversalDense` (**unwired** — their
85
+ headers claim factory dispatch that does not exist; `MLX_BUN_UNIVERSAL_FAITHFUL`
86
+ lives only in a comment).
87
+
88
+ Two of these violate stated principles: **forcing the monolith** runs an optimized
89
+ model through the generic path (rejected), and the **preset is incomplete**.
90
+
91
+ Only `qwen3_moe` did it right: the production class **is** the faithful port, so it's
92
+ bit-exact-by-construction with no flag. That is the pattern to generalize.
93
+
94
+ ### Current-state flag reference
95
+
96
+ | Flag | Default | Diverges from mlx-lm? | Notes |
97
+ |---|---|---|---|
98
+ | `MLX_BUN_PERF_KERNEL` | **ON** | **yes** — original kernel, not bit-exact | fires only on quantized KV; output-changing → L3-only |
99
+ | kv-quant | **config** for OptiQ | uniform=mlx-lm has it; mixed=optiq-only | off (bf16)=L1; uniform 4\|8=L1 in the L1 set (unfused SDPA == mlx-lm base.py); config (mixed)=L2 |
100
+ | `MLX_BUN_COMPILED_DECODE` | ON | no (whole-decode fusion, bit-exact) | structural; off in faithful preset |
101
+ | `MLX_BUN_FUSED_GELU` | ON | **yes** — custom metal geglu ≠ compiled geglu | output-changing vs mlx-lm dispatch |
102
+ | `MLX_BUN_FUSED_DECODE` | off | yes | L>1 only |
103
+ | `MLX_BUN_NO_FUSED_SDPA` | fused **on** | no — `ops.sdpa` is mlx's own kernel | faithful-aligned |
104
+ | `MLX_BUN_COMPILED_SWIGLU` | ON | no — matches mlx-lm `@mx.compile` | faithful-aligned |
105
+
106
+ The genuinely output-changing, must-be-off-for-L1 levers: **perf-kernel, kv-quant,
107
+ custom fused-gelu, fused-decode.** Compiled activations and `ops.sdpa` are
108
+ faithful-aligned and should stay on.
109
+
110
+ **Fork coverage gap.** `applyDecodeRoute` exposes only four forks —
111
+ `--compiled-decode`, `--perf-kernel`, `--fused-decode`, `--fused-sdpa`. The
112
+ activation-fusion choice (compiled-geglu vs custom fused-gelu; compiled-swiglu /
113
+ `useCompiledActivation`) has **no CLI fork** and is not in the TIERS presets. So the
114
+ alias invariant is broken: `--l1` cannot select the faithful compiled activation, and
115
+ you cannot reproduce the faithful setup with individual flags — only the out-of-band
116
+ `MLX_BUN_FAITHFUL` reaches it. Closing this gap is the crux of the fix.
117
+
118
+ ## The decisive rule: "compiled" = L1, custom "fused" = L3
119
+
120
+ The whole flag pile collapses to one distinction, verified in-source 2026-07-04:
121
+
122
+ - **"compiled" kernels** (`@mx.compile` geglu/swiglu, `compiled-decode`) go through
123
+ the **same libmlx** as mlx-lm → same graph → same kernel → **bit-exact**. These are
124
+ the faithful kernels. L1-valid AND faster (they remove per-op host tax).
125
+ - **custom "fused" Metal kernels** are mlx-bun ORIGINALS, each with a proven residual
126
+ vs mlx-lm → **NOT bit-exact → L3 only**:
127
+ - `fused-gelu-kernel.ts` header: *"Still a perf kernel (not bit-exact)… the only
128
+ residual is the pow/tanh math-lib difference"* (bit-exact vs our OWN unfused
129
+ path, kl=0 — but not vs mlx-lm's math lib).
130
+ - `fused-swiglu-kernel.ts`: MATCH variant *"Only residual: the metal exp() vs MLX's
131
+ math lib"*; F32 variant *"NOT bit-exact → L3"*. Default off.
132
+ - `fused-decode-kernel.ts` (`PERF_KERNEL`): online-softmax, envelope-gated, quant-KV
133
+ only. Not bit-exact by construction.
134
+ - **kv-quant** is a cache-precision axis, not a kernel, and it is NOT a clean
135
+ L1↔L2 boundary. **off (bf16) → L1**. **uniform `4|8` → L1** in the L1 kernel set
136
+ (`--l1 --kv-quant 8` = fused-sdpa off + perf off): mlx-bun's `quantizedSdpaUnfused`
137
+ (gemma4-base.ts) is **op-for-op identical** to mlx-lm's
138
+ `quantized_scaled_dot_product_attention` (`mlx_lm/models/base.py` — same
139
+ `mx.quantized_matmul` ×2 + `mx.softmax(precise=True)` + `where(…, finfo.min)` +
140
+ `queries *= scale`), and mlx-lm exposes exactly this scheme via `--kv-bits`. So
141
+ uniform-quant is **bit-exact L1 by construction** (a uniform-kv logit golden would
142
+ confirm the measured number; `bench.ts --baseline-kv 8` runs the stock-mlx-lm side
143
+ for the perf comparison). The FUSED / N-tiled prefill path (fused-sdpa ON — the
144
+ naked default and `--l2`) is the **optiq-aligned** one, not mlx-lm. **config
145
+ (per-layer mixed-precision) → L2** (optiq-only; mlx-lm has no per-layer analog).
146
+
147
+ **Decision table (serve decode flags):**
148
+
149
+ | Flag | Breaks mlx-lm parity? | L1 | L3 / naked | Relation to faithful |
150
+ |---|---|---|---|---|
151
+ | `MLX_BUN_COMPILED_SWIGLU` | no | ON | on | **is** faithful swiglu |
152
+ | compiled-geglu *(needs a fork)* | no | ON | on | **is** faithful geglu |
153
+ | `MLX_BUN_COMPILED_DECODE` | no (on==off) | ON | on | orthogonal free speed |
154
+ | `MLX_BUN_FUSED_SDPA` | no (`ops.sdpa`=mlx kernel) | on (bf16 no-op) | on | faithful-aligned; L2 lever |
155
+ | `MLX_BUN_FORCE_WIRE` / `EXPERT_OFFLOAD` | no (memory only) | user | user | orthogonal |
156
+ | `MLX_BUN_FUSED_GELU` | **yes** (pow/tanh) | **OFF** | if faster, KL-gated | competes w/ faithful geglu |
157
+ | `MLX_BUN_FUSED_SWIGLU`(`_F32`) | **yes** (exp/f32) | **OFF** | off (unproven) | competes w/ faithful swiglu |
158
+ | `MLX_BUN_PERF_KERNEL` | **yes** (online softmax) | **OFF** | on w/ kv-quant | unrelated original |
159
+ | `MLX_BUN_FUSED_DECODE` | **yes** | **OFF** | off | unrelated experiment |
160
+ | `--kv-quant` | off=L1; uniform `4\|8`=L1 in the L1 set (unfused SDPA==mlx-lm); config=L2 | **off (bf16)**, uniform ok | config | cache axis, not kernel (see kv note above) |
161
+ | `MLX_BUN_FAITHFUL` | — | **delete** | delete | illegal 2nd bundle → flags |
162
+ | `MLX_BUN_CPM5_FAITHFUL` | no | fold into `--l1` | — | A/B tool; likely retire |
163
+
164
+ So **L1 = all "compiled" flags on, all custom-"fused" flags off, kv-quant off** —
165
+ which is exactly what the `--l1` alias should expand to. After Phase 1 gives
166
+ compiled-geglu a fork, "faithful" stops being a separate concept: it's just the L1
167
+ flag values.
168
+
169
+ ## Per-model target (the 13 supported models)
170
+
171
+ "Targeted" = a dedicated optimized file exists and MUST be used in every regime.
172
+ "Generic" = universal-dense fallback.
173
+
174
+ | Model | Dedicated file | Faithful-L1 today | Gap |
175
+ |---|---|---|---|
176
+ | MiniCPM5-1B-OptiQ | `minicpm5.ts` (+`-faithful`, `CPM5_FAITHFUL`) | ~yes (compiled-swiglu default) | `--l1` ⇒ kv off + perf off; confirm default == faithful (may retire `-faithful`) |
177
+ | Qwen3-Embedding-4B | `qwen3.ts` | ❌ unfused swiglu | make compiled-swiglu default; retire `FaithfulQwen3` |
178
+ | Qwen3.5-4B / 9B-OptiQ | `qwen3_5.ts` | ❌ `useCompiledActivation=false` | default it true; retire `FaithfulQwen35` |
179
+ | Qwen3.6-27B-OptiQ | `qwen3_5.ts` | ❌ same | same |
180
+ | Qwen3-30B-A3B | `qwen3-moe.ts` | ✅ by default | gate perf/kv by regime only |
181
+ | gemma-4-e4b/12B/26B-OptiQ | `generated/gemma4-*.ts` | ❌ faithful forces monolith | drop force-monolith; make compiled-geglu/softcap default |
182
+ | Qwen2.5-0.5B, Llama-3.2-1B/3B | `universal/dense.ts` | ❌ unfused swiglu | default compiled-swiglu; retire `FaithfulUniversalDense` |
183
+ | gemma-2-2b-it | `universal/dense.ts` | ✅ (gemma2 geglu not `@mx.compile`d upstream) | none |
184
+
185
+ ## Plan
186
+
187
+ ### Phase 1 — Global bit-exact wins (no output change) ✅ LANDED 2026-07-04
188
+
189
+ Landed on branch `faithful-decode-parity` (M1 Max, 32 GB). Changes: qwen3-dense
190
+ (`qwen3.ts`) and universal-dense (`universal/dense.ts` `swigluFn` default) now use
191
+ the shared `compiledSwiglu`; gemma `faithfulGegluActive` defaults ON via
192
+ `MLX_BUN_COMPILED_GEGLU` (kill-switch `=0`); custom `MLX_BUN_FUSED_GELU` metal geglu
193
+ demoted to explicit opt-in (`fusedGeluEnabled` default off); factory force-monolith
194
+ branch removed (gemma keeps its generated specialization). qwen3.5 MLP already
195
+ compiled (`useCompiledActivation` fields were vestigial). **Verified bit-exact
196
+ (`maxDiff===0`) vs mlx-lm:** `universal-parity` (Qwen2.5-0.5B, Llama-3.2-1B/3B,
197
+ gemma-2-2b) 6/6; `generated-parity` (12B + e4b generated vs monolith) 5/5;
198
+ `parity.test` (gemma-4-12B monolith greedy+logits vs mlx-lm goldens) 1/1. tsc 0,
199
+ hygiene green. qwen3-dense (Qwen3-Embedding) not on this box → verified by analogy
200
+ to the identical qwen3.5 MLP pattern. The already-global micro-fixes (native
201
+ `mlx_arange`, host-side bf16 scalars) need no change — they were never
202
+ `faithfulMode`-gated.
203
+
204
+ These change *dispatched kernels only*, not output, so they apply in **every**
205
+ regime (naked default included) and need no regime gate. Each is bit-exact + faster.
206
+
207
+ 1. **Compiled activation default-on** for the dedicated classes that still run it
208
+ unfused: `qwen3.ts`, `qwen3_5.ts` (`useCompiledActivation` → default true), and
209
+ `universal/dense.ts` swiglu kinds. (qwen3_moe, minicpm5 already default-on.)
210
+ 2. **Decouple compiled-geglu + `compiledLogitSoftcap` from `faithfulMode()`** in
211
+ `gemma4.ts` (`faithfulGegluActive`) → make them the default; retire the custom
212
+ `MLX_BUN_FUSED_GELU` metal geglu to explicit opt-in. This makes gemma bit-exact on
213
+ the activation axis under `--l1` *and* naked default, in the generated files (which
214
+ inherit the base MLP) without regenerating them.
215
+ 3. **Drop the "force monolith under faithfulMode" branch** in `factory.ts` — the
216
+ generated `gemma4-*` specialization keeps running; it already gates
217
+ perf-kernel/fused-decode on flags, so `--l1` (perf/fused off) yields the L1 path
218
+ while staying model-specific.
219
+ 4. Confirm the already-global bit-exact micro-fixes (native `mlx_arange`, host-side
220
+ bf16 scalars) apply on all paths.
221
+
222
+ **Verify per model:** bit-exact tokens **and** logprobs vs the mlx-lm oracle venv
223
+ (`/Users/joshrossi/Code/mlx-lm/.venv`), plus an xctrace shader-list diff where a
224
+ kernel-set claim is made (`.gputrace` isn't parseable — use the shader-list method).
225
+
226
+ ### Phase 2 — Make `--l1` a pure alias over complete forks ✅ LANDED 2026-07-04
227
+
228
+ Landed: added `--compiled-activations on|off` (drives `MLX_BUN_COMPILED_GEGLU` +
229
+ `MLX_BUN_COMPILED_SWIGLU`) and `--fused-gelu on|off` (`MLX_BUN_FUSED_GELU`, the
230
+ custom L3 kernel) as per-fork flags in `applyDecodeRoute`; the `TIERS` presets now
231
+ set `compiledAct: true, fusedGelu: false` for l1/l2/l3, so `--l1` = { bf16 KV,
232
+ perf-kernel off, fused-decode off, **compiled activations on, custom fused-gelu
233
+ off** } and is byte-reproducible from individual flags. Fixed the `--l1` help text
234
+ (compiled activations, not "unfused"); documented both forks in `serve --help`, the
235
+ ready-card lever summary, `server-config.md`, and `cli.md`. Verified: tsc 0, help
236
+ renders the flags, and gemma `parity.test` is bit-exact vs mlx-lm under BOTH
237
+ `MLX_BUN_COMPILED_GEGLU=1` (default) and `=0` (uncompiled) — proving
238
+ `--compiled-activations off` is a valid same-parity opt-in and the env plumbing name
239
+ is honored. (qwen3/qwen3.5/universal compile swiglu unconditionally, so
240
+ `--compiled-activations off` only reverts gemma geglu + MiniCPM5 swiglu — documented.)
241
+
242
+ Original plan for reference:
243
+
244
+ First **close the fork-coverage gap**: expose the activation-fusion choice as an
245
+ individual per-fork flag (e.g. `--geglu compiled|fused|unfused` and the swiglu
246
+ equivalent, or booleans `--compiled-geglu` / `--fused-gelu` / `--compiled-swiglu`),
247
+ reading the same env vars the model files already check. Then wire it into the
248
+ `TIERS` presets in `applyDecodeRoute` so the `l1` alias sets it to the compiled
249
+ (bit-exact-fastest) value.
250
+
251
+ Result: `--l1` ⇒ { kv-quant off (bf16), perf-kernel off, fused-decode off,
252
+ custom-fused-gelu off, **compiled activations on**, model-specific file retained } —
253
+ and every one of those is an individual flag, so `--l1` is byte-reproducible by hand
254
+ and any single choice is overridable (`--l1 --fused-gelu on`). For MiniCPM5, `--l1`
255
+ implies whatever makes its decode bit-exact (its default may already qualify once
256
+ kv/perf are off — decide whether `FaithfulMiniCPM5` is still needed or retire it).
257
+
258
+ Fix the `--l1` help text: it is compiled activations + bf16 KV (not "unfused").
259
+
260
+ ### Phase 3 — Retire the parallel axis ✅ LANDED 2026-07-04
261
+
262
+ Deleted `MLX_BUN_FAITHFUL`/`faithfulMode`: `src/faithful.ts` → `src/flags.ts` (keeps
263
+ only the generic `flagOn` helper); `generate.ts` compiled-decode default is now a
264
+ plain `flagOn(…, true)`. Deleted the four unwired subclasses (`gemma4-faithful`,
265
+ `qwen3-faithful`, `qwen3_5-faithful`, `universal/dense-faithful`) and their exclusive
266
+ hooks: `setFaithfulGeglu`/`_faithfulGeglu` gone (`faithfulGegluActive` → the simpler
267
+ `compiledGegluActive` = `flagOn("MLX_BUN_COMPILED_GEGLU", true)`), the dead
268
+ `useCompiledActivation` fields gone (qwen3.5 already compiles all three activation
269
+ sites unconditionally). **Kept `FaithfulMiniCPM5`** (`MLX_BUN_CPM5_FAITHFUL`) as the
270
+ one wired, tested, complete op-for-op A/B reference. Verified: tsc 0, repo-wide sweep
271
+ for the deleted concepts is clean, gemma `parity.test` + `minicpm5-faithful-parity`
272
+ green. Net: "faithful" is no longer a concept in the code — it's just the default
273
+ kernels, selected (or not) by the individual flags.
274
+
275
+ ### Phase 4 — Docs + benchmark ✅ LANDED 2026-07-04 (numbers Josh-gated)
276
+
277
+ Docs updated: `features-matrix.md` (new `--compiled-activations` + `--fused-gelu`
278
+ rows), `server-config.md` + `cli.md` (Phase 2). Reworked `scripts/bench-faithful-matrix.ts`
279
+ + `benchmark.sh` comparison 0 for the new model — since faithful is now the default,
280
+ the matrix measures what **removing** each faithful kernel costs and what the L3
281
+ custom kernels add on top (`− compiled-decode`, `− compiled activations`,
282
+ `+ custom fused-gelu`), all vs mlx-lm; tsc 0. **The actual numbers are Josh-gated**
283
+ (clean/rebooted machine — `./benchmark.sh --redo`, or
284
+ `bun scripts/bench-faithful-matrix.ts`); loaded-machine absolutes are noise. Promote
285
+ quotable rows to `benchmarks/RESULTS.md` after the clean run.
286
+
287
+ ## Status: consolidation complete
288
+
289
+ Phases 1–4 landed on `faithful-decode-parity`. The three overlapping "match mlx-lm"
290
+ mechanisms are now one: the individual flags, with `--l1/l2/l3` as pure aliases.
291
+ Faithful kernels are the default; `--l1` is the bit-exact-mlx-lm implementation;
292
+ naked default is best-experience. Only follow-ups: (a) live qwen3-dense parity on a
293
+ box that has Qwen3-Embedding; (b) the clean-machine bench numbers.
294
+
295
+ ## Open decisions
296
+
297
+ - **`FaithfulMiniCPM5`**: retire (if `MiniCPM5Model` default is already bit-exact
298
+ mlx-lm under `--l1`) or keep as the A/B harness?
299
+ - **Keep one A/B backend** per family for regression proofing, or delete all and
300
+ lean on the oracle parity tests?
301
+ - **`MLX_BUN_FAITHFUL`**: delete, or alias to `--l1`?
302
+
303
+ ## Risks
304
+
305
+ - Compiled activation must be **autograd-safe** on the training path (mlx-lm's
306
+ `@mx.compile` threads the VJP; verified for cpm5/dense earlier — re-verify per
307
+ family before defaulting on).
308
+ - Making compiled-geglu the naked default changes gemma's *default* dispatched
309
+ kernels; confirm no downstream (vision, batching, generated specializations)
310
+ assumes the unfused/custom path.
@@ -22,10 +22,10 @@ This doc generalizes that shape to the whole mlx-lm zoo:
22
22
  - **Tier-0 GENERIC** — one config-driven "universal" module per architecture
23
23
  *family* (dense llama-family first, MoE second). Runs any covered checkpoint
24
24
  at **L1 parity** (bit-exact vs mlx-lm on this machine's GPU). Monolith path
25
- only: no perf kernels, no compiled decode, no kv-quant. Slow, never broken.
25
+ only: no compiled decode, no kv-quant. Slow, never broken.
26
26
  - **Tier-1 TARGETED** — a model *graduates*: dedicated/generated forward,
27
- optiq-quant **L2** parity, **L3** original kernels, benchmark entries.
28
- Exactly what gemma4 / minicpm5 / qwen3_5 already are.
27
+ optiq-quant **L2** parity, Lab experiments where warranted, benchmark
28
+ entries. Exactly what gemma4 / minicpm5 / qwen3_5 already are.
29
29
 
30
30
  The fidelity contract is unchanged (three-level tree): **L1 = mlx-lm
31
31
  bit-exact is the gate for ANY new model**, generic or targeted. A model the
@@ -337,8 +337,11 @@ vs a dedicated path that's worth the maintenance (number in
337
337
  `Gemma4Model` specifically — generalizing it to emit from
338
338
  `UniversalDense` is Phase 4 work, and *optional*: hand ports are proven
339
339
  and cheap ("faithful port took ~1 hr, not the huge effort claimed").
340
- 3. **L3 kernels**: fused decode / compiled decode / kv-quant / flash paths,
341
- each behind its existing flag, each frozen-oracle-gated.
340
+ 3. **Perf levers**: compiled decode / kv-quant / fused-sdpa, each behind its
341
+ existing flag. (The former "L3 kernels" step — fused decode / flash paths
342
+ with frozen-oracle gates — was deleted 2026-07-05; output-changing
343
+ experiments are now Lab items, see
344
+ [unified-engine-frontier-plan.md §6](unified-engine-frontier-plan.md).)
342
345
  4. RESULTS.md + docs entries; `mlx-bun models` label flips to `targeted`.
343
346
 
344
347
  Nothing about graduation removes the generic path — it stays as the