mlx-bun 0.0.10 → 0.0.12

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 (216) hide show
  1. package/README.md +107 -32
  2. package/THIRD_PARTY_LICENSES.md +10 -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/colibri-glm52-port.md +1479 -0
  10. package/docs/design/compat-cli-surface-design.md +4 -3
  11. package/docs/design/decode-speed-program.md +172 -0
  12. package/docs/design/docs-reorg-plan.md +1 -1
  13. package/docs/design/dspark-serving-program.md +371 -0
  14. package/docs/design/dspark-speculative-decoding.md +167 -5
  15. package/docs/design/faithful-l1-consolidation.md +310 -0
  16. package/docs/design/generic-model-support.md +8 -5
  17. package/docs/design/grammar-spec-batching-integration.md +301 -0
  18. package/docs/design/minicpm5-decode-megakernel.md +16 -11
  19. package/docs/design/mlx-lm-tool-parity-plan.md +11 -1
  20. package/docs/design/omlx-adoption-map.md +14 -6
  21. package/docs/design/orpo-training.md +34 -30
  22. package/docs/design/paged-kv-cache.md +214 -0
  23. package/docs/design/parallel-slots.md +82 -49
  24. package/docs/design/parity-tier-dag.md +43 -33
  25. package/docs/design/pre-colibri-stabilization.md +329 -0
  26. package/docs/design/repo-cleanup-plan.md +189 -0
  27. package/docs/design/runtime-isolation.md +19 -3
  28. package/docs/design/segmented-backward-training.md +3 -2
  29. package/docs/design/ssd-kv-cold-tier.md +130 -0
  30. package/docs/design/structured-output.md +484 -0
  31. package/docs/design/the-dreaming-master-plan.md +2 -3
  32. package/docs/design/turboquant-kv.md +235 -0
  33. package/docs/design/unified-engine-frontier-plan.md +774 -0
  34. package/docs/design/web-chat-beat-matrix.md +315 -0
  35. package/docs/design/web-chat-redesign.md +1112 -0
  36. package/docs/investigations/colibri-oracle-pin.md +1284 -0
  37. package/docs/investigations/dspark-handoff.md +182 -16
  38. package/docs/investigations/indexshare-performance-spike.md +357 -0
  39. package/docs/investigations/lab-build-journal.md +2 -2
  40. package/docs/investigations/openwiki-evaluation.md +249 -0
  41. package/docs/planning/IDEAS.md +14 -0
  42. package/docs/planning/PRODUCT_ROADMAP.md +2 -3
  43. package/docs/planning/chat-ui-vision-memory-first.md +199 -0
  44. package/docs/planning/chat-ui-vision.md +50 -0
  45. package/docs/planning/memory-docs-and-dag-plan.md +15 -6
  46. package/docs/planning/release-notes-v0.0.11.md +239 -0
  47. package/docs/planning/release-notes-v0.0.12.md +80 -0
  48. package/docs/reference/cli.md +63 -18
  49. package/docs/reference/distribution.md +37 -6
  50. package/docs/reference/embedding.md +40 -16
  51. package/docs/reference/features-matrix.md +99 -0
  52. package/docs/reference/library-api.md +69 -20
  53. package/docs/reference/memory.md +26 -6
  54. package/docs/reference/models.md +60 -3
  55. package/docs/reference/orpo-quickstart.md +3 -4
  56. package/docs/reference/server-api.md +717 -24
  57. package/docs/reference/server-config.md +265 -129
  58. package/docs/reference/training.md +24 -16
  59. package/package.json +5 -3
  60. package/src/anthropic.ts +67 -44
  61. package/src/audio/conformer.ts +647 -0
  62. package/src/audio/decode.ts +133 -0
  63. package/src/audio/features.ts +202 -0
  64. package/src/audio/transcode.ts +67 -0
  65. package/src/chat-template.ts +155 -10
  66. package/src/cli.ts +483 -113
  67. package/src/config.ts +116 -24
  68. package/src/diffusion/diffusion-generate.ts +14 -8
  69. package/src/download.ts +143 -12
  70. package/src/eval/README.md +7 -6
  71. package/src/eval/ifeval.ts +63 -120
  72. package/src/eval/kl.ts +59 -3
  73. package/src/eval/quality-db.ts +2 -2
  74. package/src/eval/runner.ts +17 -8
  75. package/src/eval/tasks/ifeval.ts +167 -31
  76. package/src/expert-io.ts +391 -0
  77. package/src/expert-offload.ts +15 -2
  78. package/src/expert-residency.ts +1134 -0
  79. package/src/expert-usage.ts +415 -0
  80. package/src/flags.ts +13 -0
  81. package/src/generate.ts +438 -35
  82. package/src/grammar.ts +585 -0
  83. package/src/hf-push.ts +1 -1
  84. package/src/hub-rest.ts +242 -0
  85. package/src/index.ts +14 -2
  86. package/src/jobs/runner.ts +37 -18
  87. package/src/kv-store.ts +586 -93
  88. package/src/lora.ts +85 -14
  89. package/src/md-modules.d.ts +42 -0
  90. package/src/media-fetch.ts +426 -0
  91. package/src/memory/rest.ts +245 -0
  92. package/src/memory/vault.ts +74 -0
  93. package/src/mlx/array.ts +149 -19
  94. package/src/mlx/autograd.ts +53 -16
  95. package/src/mlx/checkpoint.ts +46 -13
  96. package/src/mlx/custom-vjp.ts +83 -35
  97. package/src/mlx/ffi.ts +27 -3
  98. package/src/mlx/metal-kernel.ts +7 -4
  99. package/src/mlx/ops.ts +153 -23
  100. package/src/mlx/turboquant-ops.ts +831 -0
  101. package/src/mlx/turboquant-tables.ts +237 -0
  102. package/src/model/batched-mask.ts +62 -1
  103. package/src/model/batched-quant.ts +206 -0
  104. package/src/model/batched-rotating-quant.ts +346 -0
  105. package/src/model/batched-rotating.ts +46 -1
  106. package/src/model/compiled-decode.ts +4 -15
  107. package/src/model/factory.ts +103 -6
  108. package/src/model/gemma4-base.ts +395 -39
  109. package/src/model/gemma4.ts +122 -33
  110. package/src/model/generated/gemma4-12b.ts +9 -13
  111. package/src/model/generated/gemma4-26b.ts +10 -5
  112. package/src/model/generated/gemma4-e4b.ts +14 -9
  113. package/src/model/glm52-atlas.ts +511 -0
  114. package/src/model/glm52-cache.ts +706 -0
  115. package/src/model/glm52-config.ts +262 -0
  116. package/src/model/glm52-container.ts +305 -0
  117. package/src/model/glm52-coupling.ts +388 -0
  118. package/src/model/glm52-dsa.ts +602 -0
  119. package/src/model/glm52-expert-layout.ts +253 -0
  120. package/src/model/glm52-layout.ts +152 -0
  121. package/src/model/glm52-memory.ts +473 -0
  122. package/src/model/glm52-mla.ts +786 -0
  123. package/src/model/glm52-moe.ts +223 -0
  124. package/src/model/glm52-pilot.ts +537 -0
  125. package/src/model/glm52-reference.ts +445 -0
  126. package/src/model/glm52-residency.ts +557 -0
  127. package/src/model/glm52-resident-weights.ts +135 -0
  128. package/src/model/glm52-streamed-experts.ts +453 -0
  129. package/src/model/glm52-streamed-metal.ts +610 -0
  130. package/src/model/glm52-weights.ts +503 -0
  131. package/src/model/glm52.ts +1153 -0
  132. package/src/model/minicpm5.ts +58 -102
  133. package/src/model/paged-kv.ts +253 -0
  134. package/src/model/qwen3-delta.ts +76 -15
  135. package/src/model/qwen3-moe.ts +404 -0
  136. package/src/model/qwen3.ts +9 -8
  137. package/src/model/qwen3_5.ts +88 -23
  138. package/src/model/support.ts +19 -1
  139. package/src/model/universal/dense.ts +40 -5
  140. package/src/model/universal/rope.ts +36 -21
  141. package/src/native/expert_io.c +523 -0
  142. package/src/native-pack.ts +6 -4
  143. package/src/pi-web.ts +1299 -82
  144. package/src/preflight.ts +27 -8
  145. package/src/prompt-cache.ts +293 -33
  146. package/src/quantize/quantizer.ts +16 -1
  147. package/src/registry.ts +57 -2
  148. package/src/responses.ts +62 -49
  149. package/src/serve/batch-scheduler.ts +1050 -78
  150. package/src/serve/generation-gateway.ts +252 -24
  151. package/src/serve/isolate.ts +417 -0
  152. package/src/serve/lane-registry.ts +44 -0
  153. package/src/serve/session-search.ts +326 -0
  154. package/src/server.ts +1701 -175
  155. package/src/spec/assistant-source.ts +150 -0
  156. package/src/spec/deepspec-source.ts +143 -0
  157. package/src/spec/dflash-source.ts +124 -0
  158. package/src/spec/dspark/ab-stats.ts +136 -0
  159. package/src/spec/dspark/calibration.ts +112 -0
  160. package/src/spec/dspark/deepspec-module.ts +801 -0
  161. package/src/spec/dspark/generate-dflash.ts +72 -20
  162. package/src/spec/dspark/loader.ts +23 -0
  163. package/src/spec/dspark/module-dflash.ts +278 -36
  164. package/src/spec/dspark/quantize-drafter.ts +91 -0
  165. package/src/spec/generate.ts +59 -25
  166. package/src/spec/glm52-mtp-source.ts +392 -0
  167. package/src/spec/ngram-source.ts +132 -0
  168. package/src/spec/serve-loop.ts +617 -0
  169. package/src/spec/source.ts +118 -0
  170. package/src/spec/two-model.ts +171 -0
  171. package/src/ssd-cache.ts +111 -35
  172. package/src/tokenizer.ts +288 -2
  173. package/src/tool-approvals.ts +131 -0
  174. package/src/tool-call.ts +438 -22
  175. package/src/train/flash-cce.ts +2 -2
  176. package/src/train/job.ts +0 -12
  177. package/src/train/lora-params.ts +17 -6
  178. package/src/train/trainer.ts +60 -30
  179. package/src/vision/prompt.ts +217 -48
  180. package/src/web/app.html +1197 -1791
  181. package/src/web/app.js +5694 -0
  182. package/src/web/icon.svg +13 -0
  183. package/src/web/manifest.webmanifest +24 -0
  184. package/src/web/src/adapters-panel.ts +288 -0
  185. package/src/web/src/api.ts +57 -0
  186. package/src/web/src/assistant.ts +485 -0
  187. package/src/web/src/chat.ts +1050 -0
  188. package/src/web/src/composer.ts +959 -0
  189. package/src/web/src/dataset.ts +132 -0
  190. package/src/web/src/finetune.ts +267 -0
  191. package/src/web/src/hub.ts +428 -0
  192. package/src/web/src/main.ts +98 -0
  193. package/src/web/src/markdown.ts +0 -0
  194. package/src/web/src/memory-panel.ts +572 -0
  195. package/src/web/src/model-picker.ts +159 -0
  196. package/src/web/src/palette.ts +346 -0
  197. package/src/web/src/protocol.ts +62 -0
  198. package/src/web/src/quantize.ts +167 -0
  199. package/src/web/src/rag.ts +274 -0
  200. package/src/web/src/sessions.ts +469 -0
  201. package/src/web/src/shell.ts +804 -0
  202. package/src/web/src/status.ts +300 -0
  203. package/src/web/src/ui-catalog.ts +92 -0
  204. package/src/web/sw.js +71 -0
  205. package/src/web/vendor/README.md +61 -0
  206. package/src/web/vendor/hljs-LICENSE +29 -0
  207. package/src/web/vendor/hljs-theme.css +59 -0
  208. package/src/web/vendor/hljs.js +26 -0
  209. package/src/web-tools.ts +54 -2
  210. package/src/weights.ts +82 -19
  211. package/src/model/fused-decode-kernel.ts +0 -312
  212. package/src/model/fused-geglu-kernel.ts +0 -198
  213. package/src/model/fused-mlp-kernel.ts +0 -169
  214. package/src/model/fused-swiglu-kernel.ts +0 -240
  215. package/src/model/steel-linear-kernel.ts +0 -96
  216. package/src/status-page.html +0 -497
@@ -122,9 +122,10 @@ export); quantize / training-data gate on the capability matrix.
122
122
  + `--quantized-kv-start` (keep `--kv-quant` as a deprecated alias).
123
123
  - `serve --prompt-cache` → mlx-lm's `--prompt-cache-size` (entries) +
124
124
  `--prompt-cache-bytes` (our byte-cap is already the right model).
125
- - Our runtime levers (`--compiled-decode`, `--perf-kernel`,
126
- `--fused-decode`, `--fused-sdpa`, `--force-wire`) have no mlx-lm
127
- equivalent → keep, these are stick-shift flags.
125
+ - Our runtime levers (`--compiled-decode`, `--compiled-activations`,
126
+ `--fused-sdpa`, `--force-wire`) have no mlx-lm equivalent → keep, these
127
+ are stick-shift flags. (`--perf-kernel` and `--fused-decode` were deleted
128
+ 2026-07-05 — docs/design/unified-engine-frontier-plan.md §6.)
128
129
  - Adopt mlx-lm's sampling names verbatim: `--temp`, `--top-p`,
129
130
  `--top-k`, `--min-p`, `--max-tokens/-m`, `--seed`, `--xtc-*`.
130
131
 
@@ -0,0 +1,172 @@
1
+ # Decode-speed program — the ranked path to faster tokens
2
+
3
+ Status: PLANNED (2026-07-04). The single-stream decode roadmap, written
4
+ down after the mode-matrix benchmark (scripts/bench-modes.ts) confirmed
5
+ what the roofline work predicted: **the baseline decode path has no waste
6
+ left** — e4b/cpm5/12B sit at the memory-bandwidth wall in every tier, so
7
+ tier levers can't buy short-context decode speed. Faster means one of
8
+ four physical moves. Each lever below has evidence, an expected win, and
9
+ a trigger; pick them up in order.
10
+
11
+ **LANDED 2026-07-04 (correcting "no waste left" for CPM5):** CPM5 decode
12
+ carried one real, non-physics waste — an UNFUSED swiglu (`sigmoid`+`mul`+`mul`
13
+ = 3 kernels/layer × 24). Porting mlx-lm's `@partial(mx.compile) swiglu`
14
+ (activations.py → `compiledSwiglu` in minicpm5.ts, M=1 decode, kill-switch
15
+ `MLX_BUN_COMPILED_SWIGLU=0`) fused it to one `CV2ISigmoid…` kernel and moved
16
+ CPM5 **250.5 → 264.4 tok/s (+5.5%), to parity/ahead of mlx-lm (260.3)** —
17
+ bit-identical tokens. The win was per-dispatch host+encode tax (48 ops/token),
18
+ NOT the ~0.9% elementwise GPU time the Xcode capture showed; that's also why
19
+ CompiledDecode was 0% (it replays the same unfused graph). See §4 and
20
+ [[cpm5-decode-parity-investigation]]. The bandwidth-wall framing holds for the
21
+ *matmul* path; small-elementwise fusion is the exception it missed.
22
+
23
+ The physics: decode tok/s ≈ bandwidth ÷ bytes-touched-per-token
24
+ (M4 Pro ~273 GB/s). To go faster: read the bytes ONCE for several tokens
25
+ (speculation, batching), read FEWER bytes (quantization), SKIP reads
26
+ (caching), or shave the non-GPU residue (host overhead). TTFT/throughput
27
+ are already leadership territory (caches 16×/8.7× second-turn TTFT,
28
+ batching 12× TTFT + 2.1× aggregate under load, 2–5× startup vs the
29
+ Python stacks) — this program is about the decode number itself.
30
+
31
+ ## The levers, ranked
32
+
33
+ ### 1. Speculative decoding — the only wall-breaker [expected 1.1–3×]
34
+
35
+ One target forward verifies γ drafts + a bonus: the weight read amortizes
36
+ over up-to-γ+1 tokens. The serve loop, verify math (L1: 48/48 vs mlx-lm),
37
+ grammar composition, and `usage.speculation` telemetry ALL landed
38
+ 2026-07-03; what remains is drafters worth running:
39
+
40
+ - **1a. AssistantSource (build next)** — wrap the optiq KV-borrowing
41
+ gemma assistant drafter (`src/spec/drafter.ts`, bit-exact vs optiq's
42
+ spec_generate) as a `DraftSource` behind the SAME `--draft-model` flag
43
+ (parity-plan §7.9 dispatch-by-artifact). Artifacts downloaded for e4b +
44
+ 12B. Measured today: **12B γ=1 ≈ 1.09×** with this deliberately heavy
45
+ drafter (docs/design/spec-decode-larger-targets.md). L2 oracle exists.
46
+ - **1b. Cheaper drafter head** — the 262k tied-embedding argmax every
47
+ draft step dominates γ≥2 cost; capping/approximating it extends the win
48
+ past γ=1 (same doc, "remaining levers").
49
+ - **1d. Grammar-aware speculation (Josh's insight, 2026-07-04)** — the
50
+ grammar mask collapses the drafting problem on structured output:
51
+ (i) **jump-forward** — when the mask admits exactly ONE token
52
+ (structural JSON runs), emit it with NO forward at all and fold it into
53
+ the next verify window as a pre-accepted draft (zero-cost tokens,
54
+ 20–40% of dense-schema output); (ii) **constrained drafting** — mask
55
+ the drafter too (matcher `rollBack` on rejection, the flagged
56
+ MLX_BUN_SPEC_GRAMMAR_DRAFT lever): our own matrix measured acceptance
57
+ DROP 51%→29% under grammar with a blind drafter — constrained drafting
58
+ flips that to ~1.0 on structural spans; (iii) **grammar-pruned draft
59
+ TREES** — a DSpark token tree's branching factor collapses under a
60
+ tight mask, concentrating the budget on content positions
61
+ (XGrammar-2's `traverse_draft_tree`, the U2 trigger in
62
+ structured-output.md). Agent output is majority-structural: this turns
63
+ speculation near-deterministic exactly where agents spend tokens.
64
+ - **1c. DSpark (THE goal drafter)** — DFlash KV-injection, architecture
65
+ verified (overfit τ=3.24, paper-range). Blockers are research, not
66
+ serve wiring: 27B/12B retarget (regen+train, Josh's shell), data scale
67
+ (thousands of on-distribution generations vs 160), draft-loop
68
+ tightening. At τ≈3 on the 27B agent workload: **~15 → ~35–45 tok/s**
69
+ (docs/investigations/dspark-handoff.md). Slots in as `DflashSource`,
70
+ no-oracle/KL-gated (a Lab item).
71
+
72
+ ### 2. mlx bump — narrow 26B / spec-decode wins, NOT a general decode lever [main-only, from-source]
73
+
74
+ Pinned at mlx 0.31.2 — still the newest *released* mlx (and newest on
75
+ PyPI). All the decode-relevant work is UNRELEASED on mlx `main`; there is
76
+ no nightly wheel, so a bump means building mlx + mlx-c from source AND
77
+ running the oracle (mlx-lm / optiq — no released version targets mlx >
78
+ 0.31.2) on an unvalidated combination. That oracle validity is the real
79
+ cost, NOT golden regen (regenerating goldens is the verification, not a
80
+ gate). Upstream refs below are all in `ml-explore/mlx`, full-URL'd — a bare
81
+ `#NNNN` in this repo resolves to our own tracker (a 404), not theirs.
82
+
83
+ What `main` actually has (confirmed against the PR pages):
84
+ - [ml-explore/mlx#3485](https://github.com/ml-explore/mlx/pull/3485)
85
+ `GatherQMM::output_shapes` — merged 2026-05-29, zero numeric change. THE
86
+ prize: resolves the `src/generate.ts` blocker that keeps 26B MoE decode
87
+ uncompiled → unblocks CompiledDecode over the MoE (attacks the 26B host
88
+ term).
89
+ - [ml-explore/mlx#3764](https://github.com/ml-explore/mlx/pull/3764)
90
+ `qmv_wide` — merged 2026-06-26. Small-M (≈2–8) quant matvec; helps
91
+ SPEC-DECODE verify + batching, NOT single-stream M=1 and NOT the gather /
92
+ MoE path. Becomes the default for M≈2–8 → re-baseline batched goldens.
93
+ This is why the bump pairs with lever 1, not standalone.
94
+
95
+ What `main` does NOT have (corrects the prior note):
96
+ [ml-explore/mlx#3553](https://github.com/ml-explore/mlx/issues/3553) is an
97
+ OPEN issue (the problem report), NOT a merged fix, and `qmv_wide` is not
98
+ wired into the gather path. There is NO `gather_qmm` M=1 qmv-class fast
99
+ path upstream — the 26B M=1 compute-bound gap (~4 ms/step) is ours to close
100
+ (the shelved moe-qmv-kernel; roofline #2). Probable no-op:
101
+ [ml-explore/mlx#3120](https://github.com/ml-explore/mlx/pull/3120) (split-K
102
+ small-M quant matmul) was in mlx HEAD 2026-03-21, before the 0.31.2 tag —
103
+ likely already ours; confirm before counting it.
104
+
105
+ Sequence: land this WITH lever 1 (speculation), where #3485 + #3764 both
106
+ pay off — not as a standalone "faster decode" move it isn't.
107
+
108
+ ### 3. Quantization quality-per-bit — fewer bytes IS more tok/s [expected ~10–15%]
109
+
110
+ Decode scales ~linearly with weight bytes. oQ-style sensitivity-driven
111
+ quant in `convert` (adoption map #4, eval-gated) and TurboQuant (Phase 13)
112
+ are decode-speed programs wearing quality clothes: 4.0 → ~3.5 effective
113
+ bpw at equal KL ≈ +10–15% decode with zero kernel work. Gate: perplexity
114
+ + 6-task eval vs the existing knapsack at equal bpw.
115
+
116
+ ### 4. Host-side residuals [expected 2–8% on affected models]
117
+
118
+ - **CPM5 unfused swiglu — LANDED (+5.5% decode, 2026-07-04).** `compiledSwiglu`
119
+ (minicpm5.ts) `mx.compile`s `silu(gate)*up` → one kernel at decode (M=1),
120
+ cutting 48 dispatches/token. Proof the ~5% residual was dispatch tax, not GPU
121
+ time. The exact-copy `FaithfulMiniCPM5` fuses it for ALL shapes like mlx-lm's
122
+ non-M-gated `@mx.compile` (autograd-safe, Vjp maxDiff==0), which makes its
123
+ per-token kernel SET byte-identical to mlx-lm's (24==24, verified via parseable
124
+ **xctrace shader-list diff**); the M=1 gate leaves prefill unfused (extra
125
+ `vn_Sigmoid`+`vvn_Multiply`, ~0.7% there). Whether to widen production's gate
126
+ was deferred until the FaithfulMiniCPM5 A/B landed a verdict (it did — see the
127
+ retired note below). The same
128
+ standalone `sigmoid/silu + mul` pattern likely costs ~5% on other models' MLPs
129
+ (gemma/qwen) — audit and give each an `mx.compile` helper.
130
+ - **e4b ~5% per-step host overhead** (Phase 7 residual; dispatch count) —
131
+ now a prime suspect for the SAME unfused-elementwise cause; try the
132
+ compiledSwiglu treatment on its MLP.
133
+ - **FaithfulMiniCPM5 exact-copy backend — BUILT (2026-07-04; retired
134
+ 2026-07-05 — the default IS the faithful path; file deleted along with
135
+ `MLX_BUN_CPM5_FAITHFUL`, unified-engine-frontier-plan.md §6).** Was
136
+ `src/model/minicpm5-faithful.ts`: an op-for-op copy of mlx-lm's llama.py +
137
+ activations.py, swappable against our optimized `MiniCPM5Model` through the
138
+ same generate()/eval path. Bit-exact to the golden, dispatched-kernel set
139
+ verified identical to mlx-lm's. The A/B reference — prove identical, THEN
140
+ compare performance, THEN optimize. Debranching alone buys ~0% (host JS
141
+ conditionals hidden by the GPU-bound pipeline); its analysis produced the
142
+ matmul-fusion non-lever below.
143
+ - **P4 device-side step chaining** — depth-k chained step graphs, one
144
+ host sync per k tokens (batching-perf-path P4; the oMLX burst-decode
145
+ REFUTATION does not apply — this attacks OUR FFI/readback cost, a
146
+ different mechanism).
147
+ - ~~**Perf-kernel default flip**~~ — OBSOLETE: the perf kernel was DELETED
148
+ 2026-07-05 (regressed e4b 0.62–0.93× in the paired A/B; only win 12B@16k
149
+ +6% with a KL WARN — unified-engine-frontier-plan.md §6).
150
+
151
+ ### Non-levers (measured, don't re-litigate without new evidence)
152
+
153
+ Megakernels at M=1 (bandwidth floor: ceiling ~1.78× CPM5, achieved 0.94×
154
+ — multi-token amortization is required, which is levers 1/batching);
155
+ oMLX-style burst decode (refuted for Bun); fused-decode activeN (refuted
156
+ end-to-end on 12B, reverted); host graph-build overlap (already hidden by
157
+ the pipelined loop — spin-injection proof in decode-roofline-lookagain);
158
+ **QKV / gate-up matmul fusion at M=1** (CPM5 2026-07-04: q/k/v blocked by
159
+ OptiQ mixed precision — v_proj 8-bit vs q/k 4-bit; gate/up fusible in 18/24
160
+ layers but the speed-ceiling version is ~2% SLOWER — matmul dispatches are
161
+ bandwidth-hidden, and a concatenated `[2I]` qmv is less efficient at M=1
162
+ than two `[I]`. Contrast the swiglu win: that fuses ELEMENTWISE ops, which
163
+ are dispatch-bound. Don't re-litigate matmul fusion for single-stream).
164
+
165
+ ## Sequence
166
+
167
+ **1a AssistantSource → 2 mlx bump + re-bench → 3 oQ spike → 1b cheaper
168
+ head → 1c DSpark** (as its training milestones land, Josh-gated runs),
169
+ with §4 host residuals as filler between blocks. Re-run
170
+ `scripts/bench-modes.ts` (e4b + 12B, short/2k/4k) after each lever lands —
171
+ it is this program's scoreboard; promote clean-machine rows to
172
+ benchmarks/RESULTS.md.
@@ -12,7 +12,7 @@ and need a test run, so deferred to their own pass). Authored 2026-06-14.
12
12
  - P1 #5 — **DONE**: `STATUS.md` exists at root.
13
13
  - P1 #6 — **DONE**: `docs/planning/` exists with `IDEAS.md`, `PRODUCT_ROADMAP.md`, `ResearchTopics.md`, `journal.md`.
14
14
  - P1 #7 — open (optimization_plan.md still lives in docs/design/ as a standalone).
15
- - P1 #8 — **RESOLVED (2026-07-01)**: the duplicate `archive/pi-builtin-investigation.html` is already gone; `archive/` deliberately keeps 2 small demo artifacts (`mlx-bun-lab-report.html`, `wwdc-mlx-bun.html`, 76K total), tracked.
15
+ - P1 #8 — **RESOLVED (2026-07-01)**: the duplicate `archive/pi-builtin-investigation.html` is already gone; `docs/archive/` deliberately keeps 2 small demo artifacts (`mlx-bun-lab-report.html`, `wwdc-mlx-bun.html`, 76K total), tracked.
16
16
  - P2 #9 — **NOT DONE**: `harness-pi.ts`, `pi-provider.ts`, `pi-terminal.ts`, `pi-web.ts` still live flat in `src/` root (no `src/pi/` subdirectory).
17
17
  - P2 #10 — **PARTIALLY DONE**: ~57 one-off research/debug scripts moved to `scripts/experiments/`; production + tooling + bench-* + regen-* + parity-* + gen-model + eval/serve remain at `scripts/` root (the full bench/regen/build split was not applied).
18
18
  - P2 #11 — **DONE**: CLI vocabulary reconciled.
@@ -0,0 +1,371 @@
1
+ # DSpark serving program — flip spec wall-clock positive + design the product surface
2
+
3
+ Status: **PLANNED** (2026-07-07). Trigger for Josh's pickup: TurboQuant merge —
4
+ but only Phase 5 actually depends on it; Phases 0–4 are start-anytime.
5
+ Owner: decode / spec. Predecessors: PR #19 (DSpark paper code-complete +
6
+ serve-integrated + DeepSpec oracle), the first-live-run findings in
7
+ [dspark-handoff.md](../investigations/dspark-handoff.md) "FIRST LIVE RUN".
8
+
9
+ ## The problem, in numbers (2026-07-07 first live run, loaded machine)
10
+
11
+ `gemma-4-12B-it-OptiQ-4bit` + `deepseek-ai/dspark_gemma4_12b_block7` (γ=7):
12
+
13
+ - **τ ≈ 2.8** committed tokens per target forward (2.7× fewer target calls) —
14
+ the algorithm WORKS, at 26–33% per-token acceptance (the predicted
15
+ OptiQ-4bit-target degradation vs their bf16 training).
16
+ - **Wall-clock LOST ~3.4×** (spec 14.6 vs serial 49.8 agg tok/s). The tax is
17
+ the drafter itself: 6.9 GB bf16 → ~3 GB of weight reads per draft round
18
+ (2 GB untied lm_head + 7 × 134 MB markov_w2 + layers), ~14 host syncs per
19
+ round in the transcription-faithful `draftBlock`, tapped verifies falling
20
+ back to the unoptimized monolith, and ~14 GB of two-model weights
21
+ squeezing the 24 GB box.
22
+ - The 20 s bench TTFT was conc-4 queueing on the serial-forced spec lane
23
+ (documented routing), not prefill — but per-request slowdown is real.
24
+
25
+ **Goal:** spec ≥ 1.3× serial decode on 12B at the best config, clean-machine
26
+ paired A/B — then (and only then) design when it's on by default. Every phase
27
+ has its own gate; the drafter-quantization thesis
28
+ ([[drafter-quantization-thesis]]) makes the risky-looking phase (aggressive
29
+ quant) the SAFEST: drafter numerics only move acceptance, never correctness,
30
+ so the entire quality gate is one acceptance A/B.
31
+
32
+ **THE OBJECTIVE, stated right (Josh, 2026-07-07): 26–33% acceptance with the
33
+ bf16 drafter is the WORKABLE BASELINE — τ≈2.8 already pays. The question
34
+ quantization must answer is PRESERVATION: do we still get that number at
35
+ 4-bit?** The point of quantizing (and of mixed precision specifically) is
36
+ never compression for its own sake — it's cutting the drafter's cost 4×
37
+ WITHOUT making it terrible at predicting tokens.
38
+
39
+ **Sensitivity is MEASURED, never guessed — and THE TOOL ALREADY EXISTS
40
+ (Josh): optiq's sensitivity method** (mlx-optiq.com/docs/sensitivity —
41
+ per-group simulate-quant → KL vs reference on a curated calibration mix →
42
+ greedy knapsack allocation to target BPW, emitted as a quant predicate).
43
+ Phase 1e adopts it for the drafter rather than inventing anything; the
44
+ drafter's target-hiddens-in forward even makes it cheaper (cached hiddens →
45
+ no 12B in the sweep loop). Allocation comes from the measured table;
46
+ hypotheses (lm_head argmax-proximal, chain compounding, buffered layers)
47
+ are recorded for falsification only. Phase 1.5 (fine-tune) is an OPTIONAL
48
+ UPSIDE lever on top of the workable baseline, not a premise that acceptance
49
+ is broken.
50
+
51
+ **And TurboQuant's role, stated right (Josh): TQ is the better-quant-at-
52
+ EQUAL-bits instrument — the preservation tool, not a compression ladder.**
53
+ Mechanism: distribute the weight vectors Gaussian, then place the
54
+ quantization levels non-uniformly with FINER step sizes around the peak
55
+ where the mass lives — affine 4-bit spends half its levels on tails TQ
56
+ barely pays for. Expected: TQ-4bit preserves acceptance better than
57
+ affine-4bit at the same size; the Phase-5 headline comparison is TQ-4 vs
58
+ affine-4 AT EQUAL BPW, with lower-bpw rungs only after that's won.
59
+
60
+ ## Phase 0 — baseline + attribution (no code; Josh's shell for GPU runs)
61
+
62
+ The scoreboard everything else is measured against. All directional runs
63
+ fine on a loaded box; the FINAL pre/post pair must be clean-machine.
64
+
65
+ - [ ] **0a. Oracle gate the bf16 port ONCE** (before any optimization, so
66
+ later acceptance deltas are attributable to quant/tuning, not port bugs):
67
+ torch venv + DeepSpec install (header of
68
+ `scripts/oracle-dspark-deepspec.py` documents setup), dump the temp-0
69
+ trace (threshold 0 and 0.5 arms), run
70
+ `scripts/dspark-deepspec-compare.ts` — target vs their reference on the
71
+ **bf16 HF 12B** (`--target` the bf16 snapshot; the OptiQ-4bit arm is
72
+ `--acceptance-only`, a measurement not a bit gate). Fixture per
73
+ goldens/README.md conventions. Exit: round-for-round PASS, or a filed
74
+ divergence with the first-divergent round attached.
75
+ - [ ] **0b. γ sweep, conc 1** (no code): serve with `--num-draft-tokens`
76
+ ∈ {2, 3, 5, 7} × `bench-feature-matrix.ts --concurrency 1 --cells
77
+ serial,spec`. Record per-request tok/s + acceptance + TTFT per γ. Exit: a
78
+ table; the best-γ config is the Phase 1 comparison base.
79
+ - [ ] **0c. Time attribution**: xctrace (or coarse: MLX_BUN_LANE_DEBUG +
80
+ timers around `source.draft` / verify forward / commit in a scratch
81
+ branch) on ~20 rounds. Split: draft-block GPU, draft host syncs, verify
82
+ forward, tap/commit overhead. Plus the paging check: RSS + `vm_stat`
83
+ pageouts during a run (is the 14 GB pairing thrashing?). Exit: a
84
+ percentage breakdown — it ORDERS phases 1–3 by measured value, replacing
85
+ the estimates below.
86
+
87
+ ## Phase 1 — quantize the drafter (uniform 4-bit baseline; NOT TurboQuant yet)
88
+
89
+ The expected big lever: 6.9 GB → ~1.8 GB kills bandwidth AND memory pressure.
90
+ mlx-native group quantization, no research dependency.
91
+
92
+ - [x] **1a. `scripts/dspark-quantize-drafter.ts`** *(DONE 2026-07-07 — thin
93
+ driver over the house quantizer: `src/spec/dspark/quantize-drafter.ts`
94
+ adds a `quantizePredicate` overlay (confidence_head kept bf16, recorded
95
+ as the mlx `false` convention); load smoke = DeepspecDrafter reload +
96
+ one quantized fc forward)* — reads a DeepSpec-format
97
+ checkpoint dir, writes a quantized sibling (same config.json + arch stamp,
98
+ plus the house `quantization: {bits, group_size}` block). Per-tensor
99
+ policy:
100
+ - QUANTIZE (group-64, 4-bit default, flag-tunable): every 2-D matmul
101
+ weight — `layers.*.self_attn.{q,k,o}_proj`, `layers.*.mlp.{gate,up,
102
+ down}_proj`, `fc`, `lm_head`, `markov_head.markov_w2`, and
103
+ `embed_tokens` + `markov_head.markov_w1` as quantized-gather lookups
104
+ (mlx QuantizedEmbedding semantics — mlx-lm quantizes embeddings; ours
105
+ must match its gather-dequant numerics).
106
+ - KEEP bf16: all norms, `layers.*.layer_scalar`, `confidence_head.proj.*`
107
+ (tiny), biases.
108
+ - Output verified loadable before exit (load smoke).
109
+ - [x] **1b. Quantized forward in `src/spec/dspark/deepspec-module.ts`**
110
+ *(DONE 2026-07-07 — MatW/TableW weight representation: `.scales` sibling
111
+ detection, quantized_matmul transpose=true for every Linear, mlx
112
+ QuantizedEmbedding gather-dequant for embed_tokens/markov_w1; bf16 path
113
+ keeps the lazy transpose views untouched; synthetic quantized-checkpoint
114
+ describe block added — 8-bit tracks bf16 token-for-token)*:
115
+ detect quantization at load (scales/biases sibling keys — the house
116
+ detection pattern), route matmuls through `quantized_matmul` and lookups
117
+ through the quantized-gather path; bf16 path untouched (both formats load
118
+ through the same class — kind detection unchanged). Synthetic-format test
119
+ extended with a tiny quantized checkpoint (same file, new describe block).
120
+ - [x] **1c. The acceptance A/B harness** *(DONE 2026-07-07 — runner drives
121
+ `specServeRun` per arm (target loaded once, drafters sequential); spec
122
+ telemetry extended with per-position draftedByPos/acceptedByPos; the
123
+ stats/verdict math is model-free in `src/spec/dspark/ab-stats.ts`,
124
+ unit-tested in tests/dspark-ab-stats.test.ts; chat-template rendering
125
+ pins enable_thinking OFF)* — `scripts/dspark-drafter-ab.ts`:
126
+ same target, same prompt set (≥32 prompts, temp 0), drafter A vs drafter
127
+ B; reports per-position acceptance, τ, tok/s, and a paired verdict. This
128
+ is THE gate for every quantization experiment from here on (Phase 5
129
+ reuses it unchanged). Model-free logic unit-tested; the run itself is
130
+ GPU/Josh.
131
+ - [ ] **1d. Run it**: 4-bit vs bf16 drafter at best-γ from 0b. Exit criteria:
132
+ acceptance drop ≤ 3 points absolute AND wall-clock strictly improves;
133
+ record both. If 4-bit alone flips spec past serial — say so loudly in the
134
+ handoff, that's the headline.
135
+ - [ ] **1e. The MEASURED sensitivity map — ADOPT OPTIQ'S METHOD, don't
136
+ invent** (mlx-optiq.com/docs/sensitivity; source readable in the oracle
137
+ venv's `site-packages/optiq`). Their loop, verbatim: reference logits on
138
+ calibration data → simulate-quantize ONE group (quantize→dequantize
139
+ round-trip) → KL divergence on the full output distribution → restore →
140
+ next (group, bits); then their greedy knapsack (max KL-reduction per
141
+ extra bit) allocates to a target BPW. **Applied to the drafter it gets
142
+ CHEAPER than optiq's own runs**: the drafter isn't mlx_lm-loadable
143
+ (its forward needs target hiddens), so `optiq convert` can't run on it
144
+ directly — but that same fact removes the 12B from the loop entirely.
145
+ Cache tapped hiddens ONCE over an optiq-style 6-domain calibration mix
146
+ (our PATH-B shard format stores exactly this), then the whole sweep is
147
+ drafter-forward-only KL: 3B × ~10 groups × candidate bits = minutes.
148
+ The knapsack's output config gets ONE end-to-end acceptance A/B (1c
149
+ harness) as confirmation — KL is the search signal, acceptance is the
150
+ ship gate. Their protected-tensor defaults (lm_head/embed highest bits,
151
+ first/last blocks) are noted as the static prior their own exact method
152
+ exists to replace — we run the exact method; the drafter is small.
153
+
154
+ ## Phase 1.5 — acceptance upside (OPTIONAL; the baseline is already workable)
155
+
156
+ 26–33% (τ≈2.8) with the bf16 drafter pays as-is — this phase is the
157
+ opportunistic numerator work on top, NOT a precondition: the gap vs their
158
+ bf16-target numbers is distribution shift with a known mechanism, and every
159
+ point recovered multiplies the cost work. Run it if Phase 1's preserved
160
+ acceptance + cost win lands short of the ≥1.3× goal, or whenever the upside
161
+ is worth the run.
162
+
163
+ - [ ] **1.5a. Adaptation fine-tune on OUR target's hiddens** — their
164
+ checkpoint as init (the expensive pretraining is done; this is short
165
+ distribution adaptation, not their 10-epoch/38 TB run). The data comes
166
+ from OUR stack by construction: tap `[5,17,29,41,46]` hidden dumps from
167
+ the OptiQ-4bit 12B over on-distribution generations (the PATH-B regen
168
+ tooling extracts exactly this). OPEN DESIGN (resolve first): where the
169
+ gradient runs — (a) export our tapped-hidden shards into DeepSpec's
170
+ torch trainer format and fine-tune with THEIR code (MIT; least new code,
171
+ needs a torch box/venv and a shard-format bridge), vs (b) a training
172
+ path for the deepspec module in our stack (autograd through the port —
173
+ more work, fully local). Gate: acceptance A/B (1c harness) pre/post
174
+ fine-tune on held-out prompts; expected: a large chunk of the
175
+ bf16-vs-OptiQ gap closes.
176
+ - [ ] **1.5b. Confidence-threshold activation** — at 26% acceptance the
177
+ scheduler is finally in its design regime: sweep the raw threshold
178
+ (their knob) and/or our calibrated thresholds; objective =
179
+ tokens-per-second, not τ (pruning doomed positions trades a little τ
180
+ for less wasted draft+verify work). Free: no code, the machinery
181
+ shipped in PR #19.
182
+ - [ ] **1.5c. Quantify the target-fidelity axis** (measurement, not a
183
+ build): acceptance vs target quant — e.g. the same drafter against a
184
+ higher-fidelity 12B variant if one fits (mixed/8-bit tap-layer
185
+ experiments), to split "drafter needs adapting" from "target hiddens
186
+ are too degraded". Informs whether 1.5a alone suffices.
187
+
188
+ ## Phase 2 — tighten `draftBlock` (replay the proven tightening)
189
+
190
+ `Gemma4DSparkModel.draftBlock` kept the reference's per-position structure
191
+ (transcription fidelity): 7 argmax `itemUint32` syncs + 7 confidence
192
+ `toFloat32` syncs per round. We already solved this exact shape once
193
+ (module-dflash.ts, 2026-07-06 — bit-identity-pinned).
194
+
195
+ - [ ] **2a. On-device token chaining**: the sequential Markov loop keeps the
196
+ argmax result as a device `[1]` index feeding the next position's
197
+ `takeAxis(markov_w1, ·)`; ONE concat + ONE host read after the loop.
198
+ (Post-1b the bias matmul is quantized too.)
199
+ - [ ] **2b. Confidence: compute NOTHING at threshold ≤ 0** (their default —
200
+ today we compute + sync 7 confidences that are then unused); when a
201
+ threshold IS set, per-position reads are inherent to the truncation
202
+ decision (same reasoning as the dflash loop — document it).
203
+ - [ ] **2c. `collectLogits`-style skip** for `baseLogits` if the serve path
204
+ still materializes anything it discards (audit `DeepspecSource.draft` —
205
+ it disposes `block.baseLogits` unread; make the module skip building it,
206
+ mirroring `DflashDraftOpts.collectLogits`).
207
+ - [ ] **Gate**: bit-identity — pinned tokens on the deterministic synthetic
208
+ checkpoint pre/post rewrite (the tests/dspark-infer-loop.test.ts
209
+ pattern), plus real-checkpoint determinism (same prompt twice → same
210
+ drafts). Tightening is a pure optimization; any token change is a bug.
211
+
212
+ ## Phase 3 — `captureLayer` in the generated gemma forwards
213
+
214
+ Tapped spec forwards (prefill + EVERY verify round) currently fall back to
215
+ the monolith (the 2026-07-07 guard fix — correct-but-unspecialized). The
216
+ verify forward is the spec path's biggest single GPU cost; give it back the
217
+ generated fast path. Independent of Phases 1–2; parallelizable.
218
+
219
+ - [ ] **3a. `scripts/gen-model.ts`**: emit the tap exactly as the monolith
220
+ does — `captureLayer(i, h)` after each layer's residual
221
+ (gemma4.ts:970 semantics) + the post-finalNorm sentinel
222
+ (index = nLayers, gemma4.ts:989) — and REMOVE `this.hiddenTap !== null`
223
+ from the emitted fallback guard.
224
+ - [ ] **3b. Regenerate all three** (12b / 26b / e4b; regen commands in each
225
+ file's header) — the hand-patched guards are superseded by generator
226
+ output.
227
+ - [ ] **Gate**: (i) generated-parity suite green (untapped behavior
228
+ untouched); (ii) NEW tap-parity check — tapped rows bit-equal
229
+ monolith-vs-generated on the same input (model-gated test, e4b);
230
+ (iii) spec bench pre/post for the verify-round win.
231
+
232
+ ## Phase 4 — serving UX: flags, defaults, composition (the product pass)
233
+
234
+ Decisions, made on Phase 0–3 DATA, then implemented. The doctrine rails:
235
+ drop-in-replacement (a lone request must never get slower because a flag
236
+ exists), dont-delete-optionality (losing configs stay documented
237
+ default-off), spec = opt-in until it WINS a clean-machine paired A/B.
238
+
239
+ - [ ] **4a. γ default per drafter kind**: the checkpoint's trained γ is the
240
+ cap, not necessarily the default — if 0b says γ=3 beats γ=7 at our
241
+ acceptance, `numDraftTokens` defaults to the measured-best (per-kind
242
+ table in code, overridable), and the handoff documents why.
243
+ - [ ] **4b. Quantized-drafter UX**: `mlx-bun get` the bf16 →
244
+ `scripts/dspark-quantize-drafter.ts` is a manual step today; decide
245
+ whether `serve --draft-model <bf16-dir>` should offer/auto-run the
246
+ 4-bit conversion (like the starter-model flow) or just print the
247
+ command. Also: registry listing shows drafters distinctly (they are not
248
+ servable models — `/v1/models` must NOT list them; verify).
249
+ - [ ] **4c. Spec × prompt-cache composition** (parity-plan §7.6 — currently
250
+ BYPASSED on the spec path, v1 note in serve-loop.ts): multi-turn agents
251
+ re-prefill every turn when a draft is mounted. Scope it here or
252
+ explicitly re-defer with the measured cost (12B multi-turn TTFT with
253
+ spec on). At minimum: the boundary-snapshot machinery must not regress
254
+ when spec is mounted.
255
+ - [ ] **4d. Surface audit**: `--draft-kind` (keep), `--num-draft-tokens`
256
+ semantics doc, `/stats` shows mounted drafter + kind + γ, `usage.
257
+ speculation` already ships. server-config.md gets a "speculative
258
+ serving" section consolidating the four kinds + the when-does-it-pay
259
+ table (from Phase 0/1 data). features-matrix + cli.md + README rows.
260
+ - [ ] **4e. The default decision, written down**: for each (model, drafter)
261
+ pair measured, spec default on/off per the clean-machine A/B — recorded
262
+ in RESULTS.md "composition" + the features matrix. (Prediction to test:
263
+ ON for 12B+quantized drafter, OFF for e4b anything.)
264
+
265
+ ## Phase 5 — TurboQuant application (the trigger event)
266
+
267
+ Trigger, precisely: **TQ WEIGHT quantization** in the convert tooling — PR
268
+ #20 (2026-07-07) landed TurboQuant for KV; the rotation-based weight-side
269
+ is the noted follow-up (turboquant-kv.md), and it's what the drafter
270
+ needs. The drafter is its lowest-risk first customer — one-number gate,
271
+ no KL/eval battery. **TQ's HYPOTHESIS here is PRESERVATION AT EQUAL BITS**
272
+ (Gaussian-distributed vectors + finer step sizes around the peak, where
273
+ affine spends half its levels on tails) — a hypothesis 5b MEASURES, never
274
+ a default: TurboQuant earns its place the same way everything in this
275
+ repo does.
276
+
277
+ - [ ] **5a.** Extend `dspark-quantize-drafter.ts` with the TurboQuant
278
+ scheme.
279
+ - [ ] **5b. THE HEADLINE: TQ-4bit vs affine-4bit AT EQUAL BPW** through the
280
+ Phase-1c harness — same size, whose acceptance is closer to bf16? If
281
+ affine-4 bled in 1d, this is the expected fix; if affine-4 held, TQ's
282
+ margin becomes headroom for lower rungs. Only AFTER TQ-4 ≥ affine-4:
283
+ the lower-bpw ladder (3.5 / 3.0 / 2.5) → acceptance-per-byte curve,
284
+ knee adopted (dont-delete-optionality for the rest).
285
+ - [ ] **5b″. TQ IS HOW, SENSITIVITY-AWARE IS WHAT (Josh)** — two orthogonal
286
+ axes, never mixed: TQ vs affine is a METHOD question (decided by 5b's
287
+ equal-bpw measurement — a method that wins at equal bits wins for every
288
+ tensor, so there is no reason to mix METHODS within an artifact);
289
+ sensitivity is the bit ALLOCATION question (the 1e knapsack, whatever
290
+ the method — candidate set {method-2, method-3, method-4, method-8} per
291
+ group, measured KL cost each, greedy to target BPW). IF 5b confirms TQ,
292
+ the artifact is TQ-uniform at knapsack-allocated bits; if it doesn't,
293
+ affine-uniform at knapsack-allocated bits — either way the OUTCOME of
294
+ the measurement, not a premise. Nothing here is "on by default";
295
+ defaults are Phase 4e/6 decisions made on clean-machine numbers.
296
+ - [ ] **5b′. PER-TENSOR MIXED PRECISION rungs** (Josh, 2026-07-07). The
297
+ intuition "only 5 layers = few knobs" undercounts: the drafter's mass is
298
+ ~10 TENSOR GROUPS, and two of them are 58% of the bytes — `lm_head`
299
+ (~2 GB, read FULLY every round: the bandwidth whale) and `embed_tokens`
300
+ (~2 GB, gather-only: ~free to read, pure resident-memory win), plus
301
+ `markov_w2` (134 MB read ×7/round ≈ 940 MB). Both whales only shape the
302
+ DRAFT distribution → acceptance-only risk. Mixed rungs to sweep: 3-bit
303
+ lm_head + 3-bit markov_w2 + 4-bit layers; 2-bit embed variants; k_proj
304
+ (2 M params) 8-bit for free. The ~10-group space is small enough for a
305
+ near-exhaustive sweep through the SAME one-number harness — unlike
306
+ sensitivity search on a full model. **Allocation objective (the point):
307
+ PRESERVE the bf16 acceptance at the smallest cost, not minimize bytes —
308
+ and allocation follows the MEASURED sensitivity table from 1e, never a
309
+ prior.** The mixed rungs are built FROM the table: give the measured-
310
+ sensitive groups more bits (or TQ them, post-Phase-5), squeeze the
311
+ measured-insensitive ones. The "only 5 layers" worry dissolves either
312
+ way — the tensor-group space (~10 knobs, two of them 58% of the bytes)
313
+ is the real granularity, and 1e prices every knob empirically.
314
+ - [ ] **5c.** If TurboQuant wins: fold the recipe into 4b's UX (the
315
+ conversion the server offers is the TurboQuant one).
316
+
317
+ ## Phase 6 — quotable results + promotion
318
+
319
+ - [ ] Clean-machine protocol (reboot + `sudo purge`), then: bench-feature-
320
+ matrix conc-1 AND agg×4, serial vs best-spec-config, on 12B (and e4b as
321
+ the expected-negative control). Promote to `benchmarks/RESULTS.md`
322
+ "composition"; update decode-speed-program.md (lever 1c: measured),
323
+ STATUS, and the handoff's FIRST LIVE RUN section with the closing
324
+ numbers.
325
+ - [ ] Loose ends carried from PR #19, closed or re-deferred with reasons:
326
+ the paper-faithful gated RNN head (seqHead:"gru", currently variant-
327
+ documented), paper-Alg-1/STS survival-product scheduler (Lab item),
328
+ per-slot spec under batching (unified-engine frontier row — NOT this
329
+ program).
330
+
331
+ ## Dependency graph + who does what
332
+
333
+ ```
334
+ 0a (oracle gate, Josh: venv+GPU) ──┐
335
+ 0b (γ sweep, Josh: GPU) ──────────┼─→ 1a→1b→1c→1d (quant baseline; 1d Josh GPU)
336
+ 0c (attribution, Josh: GPU) ───────┘ │
337
+ ├─→ 1.5 (OPTIONAL acceptance
338
+ │ upside: fine-tune on our
339
+ │ target's hiddens + threshold
340
+ │ sweep; run if 1's preserved
341
+ │ acceptance falls short)
342
+ ├─→ 2a-2c (tightening; CPU-gated)
343
+ 3a-3b (generator tap; parallel anytime) ────┤
344
+ └─→ 4 (UX, needs 0–3 data)
345
+ TurboQuant merge ─→ 5a-5c (reuses 1c harness) ─→ 6 (clean machine, Josh)
346
+ ```
347
+ Priority note: Phase 1's PRESERVATION question ("do we keep 26–33% at
348
+ 4-bit?") is the program's hinge — one A/B answers it; if affine-4 bleeds,
349
+ the answers are MEASURED (1e sensitivity table → mixed allocation) and
350
+ ENGINEERED (Phase 5: TQ-4 at equal bpw — the better quant is the designed
351
+ fix, not a fallback).
352
+
353
+ Code phases (1a/1b/2/3/4-impl) are agent-runnable sessions; every GPU
354
+ measurement is Josh's shell. Nothing except Phase 5 waits on TurboQuant —
355
+ Phases 0–4 can land first so TurboQuant drops into a finished harness.
356
+
357
+ ## Exit criteria for the whole program
358
+
359
+ 1. Spec wall-clock ≥ 1.3× serial on 12B at the recommended config,
360
+ clean-machine paired — or a written verdict that it can't be reached and
361
+ why (with the config left as a characterized default-off lever).
362
+ 2. **Acceptance PRESERVED and understood**: the quantized drafter holds the
363
+ bf16 baseline's 26–33% (or the mixed rung that does is adopted, or the
364
+ loss is measured and accepted with numbers); the optional 1.5 upside
365
+ levers are run-or-explicitly-deferred — the drafter predicts tokens;
366
+ small was never the goal.
367
+ 3. The acceptance-per-byte curve exists (TurboQuant vs 4-bit vs mixed vs
368
+ bf16), with acceptance-at-fixed-budget allocation swept (5b′).
369
+ 4. The serving surface is documented, defaults decided per-pair and
370
+ recorded, and a fresh `mlx-bun get` → serve → speedup flow works without
371
+ reading any design doc.