mlx-bun 0.2.0 → 0.4.0

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 (433) hide show
  1. package/README.md +71 -588
  2. package/docs/README.md +94 -0
  3. package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
  4. package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
  5. package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
  6. package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
  7. package/docs/archive/investigations/openjarvis-fit.md +198 -0
  8. package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
  9. package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
  10. package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
  11. package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
  12. package/docs/archive/planning/release-notes-v0.3.0.md +35 -0
  13. package/docs/design/batching.md +1841 -0
  14. package/docs/design/decode-speed-program.md +2346 -169
  15. package/docs/design/dreaming-nightly-pipeline.md +675 -282
  16. package/docs/design/generic-model-support.md +655 -334
  17. package/docs/design/kv-cache.md +824 -0
  18. package/docs/design/orpo-training.md +673 -1239
  19. package/docs/design/speculative-decoding.md +1990 -0
  20. package/docs/design/turboquant.md +2212 -0
  21. package/docs/design/unified-engine-frontier-plan.md +1833 -727
  22. package/docs/design/web-chat-redesign.md +666 -1083
  23. package/docs/planning/PRODUCT_ROADMAP.md +50 -1
  24. package/docs/planning/release-notes-v0.4.0.md +45 -0
  25. package/docs/planning/video-series.md +2 -2
  26. package/docs/reference/benchmarks.md +3613 -0
  27. package/docs/reference/cli.md +434 -160
  28. package/docs/reference/distribution.md +328 -182
  29. package/docs/reference/environment.md +398 -0
  30. package/docs/reference/glossary.md +39 -0
  31. package/docs/reference/library-api.md +566 -168
  32. package/docs/reference/memory.md +227 -64
  33. package/docs/reference/models.md +113 -6
  34. package/docs/reference/server-api.md +944 -429
  35. package/docs/reference/server-config.md +740 -338
  36. package/docs/reference/training.md +393 -254
  37. package/docs/reference/troubleshooting.md +158 -0
  38. package/package.json +9 -4
  39. package/src/audio/decode.ts +2 -2
  40. package/src/audio/features.ts +2 -2
  41. package/src/backends/mlx/assistant-target.ts +46 -0
  42. package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
  43. package/src/backends/mlx/autoregressive.ts +108 -0
  44. package/src/backends/mlx/batch-group.ts +1790 -0
  45. package/src/backends/mlx/cache-attachment.ts +67 -0
  46. package/src/backends/mlx/cache-layout.ts +31 -0
  47. package/src/backends/mlx/checkpoint-state.ts +53 -0
  48. package/src/backends/mlx/continuation-persistence.ts +106 -0
  49. package/src/backends/mlx/continuation-request.ts +43 -0
  50. package/src/backends/mlx/continuation.ts +77 -0
  51. package/src/backends/mlx/diffusion.ts +39 -0
  52. package/src/backends/mlx/draft-target.ts +32 -0
  53. package/src/backends/mlx/evaluation.ts +105 -0
  54. package/src/backends/mlx/fill-append.ts +45 -0
  55. package/src/backends/mlx/full-kv-row-append.ts +32 -0
  56. package/src/backends/mlx/full-kv-row-donor.ts +54 -0
  57. package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
  58. package/src/backends/mlx/gateway-binding.ts +130 -0
  59. package/src/backends/mlx/graph.ts +38 -0
  60. package/src/backends/mlx/kv-codec.ts +9 -0
  61. package/src/backends/mlx/kv-maintenance.ts +133 -0
  62. package/src/backends/mlx/kv-tensor-rows.ts +212 -0
  63. package/src/backends/mlx/memory-client.ts +189 -0
  64. package/src/backends/mlx/methods.ts +90 -0
  65. package/src/backends/mlx/model-prompt.ts +164 -0
  66. package/src/backends/mlx/model-serving.ts +79 -0
  67. package/src/backends/mlx/prefill-cohort.ts +89 -0
  68. package/src/backends/mlx/prefill-rows.ts +200 -0
  69. package/src/backends/mlx/prefill.ts +33 -0
  70. package/src/backends/mlx/projected-context-rows.ts +72 -0
  71. package/src/backends/mlx/request-policy.ts +20 -0
  72. package/src/backends/mlx/request-state-policy.ts +41 -0
  73. package/src/backends/mlx/rollback.ts +89 -0
  74. package/src/backends/mlx/row-sampling.ts +49 -0
  75. package/src/backends/mlx/serial-executor.ts +242 -0
  76. package/src/backends/mlx/speculative-group.ts +359 -0
  77. package/src/backends/mlx/speculative-round.ts +161 -0
  78. package/src/backends/mlx/speculative.ts +114 -0
  79. package/src/backends/mlx/state-rows.ts +104 -0
  80. package/src/backends/mlx/state-views.ts +46 -0
  81. package/src/backends/mlx/target-layout-capability.ts +35 -0
  82. package/src/backends/mlx/text-engine.ts +73 -0
  83. package/src/backends/mlx/transitioning-kv-rows.ts +165 -0
  84. package/src/cli.ts +66 -58
  85. package/src/client.ts +66 -0
  86. package/src/config.ts +21 -4
  87. package/src/contracts/completion.ts +16 -0
  88. package/src/contracts/execution.ts +60 -0
  89. package/src/contracts/generation.ts +103 -0
  90. package/src/contracts/host.ts +7 -0
  91. package/src/contracts/jobs.ts +66 -0
  92. package/src/contracts/pi-provider.ts +30 -0
  93. package/src/contracts/pi.ts +259 -0
  94. package/src/contracts/prefix-cache.ts +23 -0
  95. package/src/contracts/resources.ts +25 -0
  96. package/src/contracts/sampling.ts +23 -0
  97. package/src/contracts/scheduling.ts +45 -0
  98. package/src/contracts/tsconfig.json +14 -0
  99. package/src/diffusion/diffusion-generate.ts +218 -153
  100. package/src/embed.ts +1 -1
  101. package/src/engine/admission.ts +59 -0
  102. package/src/engine/cancellation.ts +38 -0
  103. package/src/engine/engine.ts +56 -0
  104. package/src/engine/execution-coordinator.ts +70 -0
  105. package/src/engine/execution-plan.ts +53 -0
  106. package/src/engine/resources.ts +53 -0
  107. package/src/engine/scheduler.ts +72 -0
  108. package/src/engine/session.ts +247 -0
  109. package/src/eval/README.md +44 -1
  110. package/src/eval/kl.ts +236 -3
  111. package/src/eval/reference-logits.ts +207 -0
  112. package/src/eval/runner.ts +8 -96
  113. package/src/fill/echo-index.ts +188 -0
  114. package/src/fill/fill-session.ts +452 -0
  115. package/src/fill/proposal.ts +68 -0
  116. package/src/fill/schema-rows.ts +506 -0
  117. package/src/fill/tool-boundary.ts +103 -0
  118. package/src/fit.ts +30 -16
  119. package/src/generate.ts +709 -417
  120. package/src/grammar.ts +12 -7
  121. package/src/harness-pi.ts +14 -33
  122. package/src/hub-rest.ts +4 -4
  123. package/src/index.ts +7 -0
  124. package/src/inference/denoising.ts +17 -0
  125. package/src/inference/draft-acceptance.ts +41 -0
  126. package/src/inference/draft-output.ts +49 -0
  127. package/src/inference/graph.ts +33 -0
  128. package/src/inference/prefill.ts +39 -0
  129. package/src/inference/rollback.ts +7 -0
  130. package/src/jobs/db.ts +4 -0
  131. package/src/jobs/index.ts +2 -0
  132. package/src/jobs/job-entry.ts +12 -12
  133. package/src/jobs/runner.ts +98 -13
  134. package/src/jobs/task-client.ts +19 -0
  135. package/src/jobs/types.ts +2 -58
  136. package/src/kv-scheme.ts +70 -12
  137. package/src/kv-store.ts +177 -63
  138. package/src/lab/README.md +18 -0
  139. package/src/{assets → lab/curve}/curve-designer.html +2 -2
  140. package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
  141. package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
  142. package/src/lab/paged-kv/paged-kv-rows.ts +83 -0
  143. package/src/{model → lab/paged-kv}/paged-kv.ts +32 -15
  144. package/src/library.ts +40 -0
  145. package/src/lora.ts +18 -0
  146. package/src/media-fetch.ts +1 -1
  147. package/src/memory/article.ts +1 -1
  148. package/src/memory/chunk.ts +1 -1
  149. package/src/memory/model.ts +30 -169
  150. package/src/memory/prompts.ts +1 -1
  151. package/src/memory/rest.ts +1 -1
  152. package/src/memory/schedule.ts +1 -1
  153. package/src/memory/stages.ts +4 -1
  154. package/src/memory/vault.ts +1 -3
  155. package/src/mlx/array.ts +11 -11
  156. package/src/mlx/compile.ts +12 -2
  157. package/src/mlx/ffi.ts +58 -11
  158. package/src/mlx/materialize.ts +23 -0
  159. package/src/mlx/metal-kernel.ts +10 -11
  160. package/src/mlx/ops.ts +33 -13
  161. package/src/mlx/quantized-rows.ts +36 -0
  162. package/src/mlx/token-bitmask.ts +52 -0
  163. package/src/mlx/turboquant-kv-decode.ts +82 -0
  164. package/src/mlx/turboquant-kv-inverse.ts +122 -0
  165. package/src/mlx/turboquant-ops.ts +33 -3
  166. package/src/mlx/turboquant-tables.ts +2 -2
  167. package/src/model/artifact-identity.ts +73 -0
  168. package/src/model/batched-kv.ts +235 -0
  169. package/src/model/batched-mask.ts +20 -6
  170. package/src/model/batched-quant.ts +9 -3
  171. package/src/model/batched-quantized-kv.ts +144 -0
  172. package/src/model/batched-rotating-quant.ts +78 -17
  173. package/src/model/batched-rotating-state.ts +76 -0
  174. package/src/model/batched-rotating.ts +65 -23
  175. package/src/model/batched-row-storage.ts +52 -0
  176. package/src/model/batched-ssm.ts +52 -0
  177. package/src/model/batched-turboquant-kv.ts +93 -0
  178. package/src/model/compiled-decode.ts +8 -2
  179. package/src/model/decoded-kv-donor.ts +12 -0
  180. package/src/model/delayed-quantized-kv.ts +72 -0
  181. package/src/model/delayed-rotating-quantized-kv.ts +93 -0
  182. package/src/model/delayed-turboquant-kv.ts +55 -0
  183. package/src/model/diffusion-gemma.ts +8 -8
  184. package/src/model/factory.ts +76 -33
  185. package/src/model/fingerprint.ts +1 -1
  186. package/src/model/flash-attention.ts +3 -3
  187. package/src/model/full-prefill-padding.ts +56 -0
  188. package/src/model/full-prefill-row.ts +105 -0
  189. package/src/model/gemma4-base.ts +179 -80
  190. package/src/model/gemma4.ts +40 -32
  191. package/src/model/generated/gemma4-12b.ts +6 -3
  192. package/src/model/generated/gemma4-26b.ts +6 -3
  193. package/src/model/generated/gemma4-e4b.ts +6 -3
  194. package/src/model/glm52-cache.ts +80 -4
  195. package/src/model/glm52-dsa.ts +13 -9
  196. package/src/model/glm52-memory.ts +1 -1
  197. package/src/model/glm52-mla.ts +12 -6
  198. package/src/model/glm52.ts +19 -40
  199. package/src/model/implementation.ts +69 -0
  200. package/src/model/kv-attention-view.ts +67 -0
  201. package/src/model/minicpm5.ts +17 -15
  202. package/src/model/profile.ts +12 -2
  203. package/src/model/qwen-append.ts +12 -0
  204. package/src/model/qwen-conv.ts +52 -0
  205. package/src/model/qwen3-delta-state.ts +74 -0
  206. package/src/model/qwen3-delta.ts +59 -36
  207. package/src/model/qwen3_5-checkpoint.ts +164 -0
  208. package/src/model/qwen3_5.ts +202 -78
  209. package/src/model/rotating-kv-donor.ts +35 -0
  210. package/src/model/rotating-kv-layout.ts +262 -0
  211. package/src/model/rotating-row-transaction.ts +35 -0
  212. package/src/model/speculative-rotating-kv.ts +101 -0
  213. package/src/model/ssm-prefill-padding.ts +39 -0
  214. package/src/model/support.ts +1 -1
  215. package/src/model/trellis-balanced-scatter.ts +51 -0
  216. package/src/model/trellis-linear.ts +672 -0
  217. package/src/model/trellis-shared-m.ts +151 -0
  218. package/src/model/trellis-shared-scatter.ts +144 -0
  219. package/src/model/trellis-splitk-prefill.ts +158 -0
  220. package/src/model/trellis-tiled-prefill.ts +136 -0
  221. package/src/model/trellis-vector-expand.ts +62 -0
  222. package/src/model/trellis-wide-prefill.ts +94 -0
  223. package/src/model/turboquant-codec.ts +83 -0
  224. package/src/model/universal/dense.ts +15 -14
  225. package/src/model/universal/modules.ts +8 -1
  226. package/src/native-pack.ts +6 -5
  227. package/src/pi-provider.ts +6 -64
  228. package/src/pi-web.ts +8 -315
  229. package/src/preflight.ts +31 -15
  230. package/src/prompt-cache.ts +110 -69
  231. package/src/quantize/allocator.ts +1 -1
  232. package/src/quantize/config-writer.ts +10 -3
  233. package/src/quantize/rotate.ts +4 -4
  234. package/src/quantize/trellis.ts +586 -0
  235. package/src/responses.ts +28 -1
  236. package/src/runtime-config.ts +18 -4
  237. package/src/sampler.ts +40 -29
  238. package/src/serve/batch-scheduler.ts +8 -1671
  239. package/src/serve/chat-request.ts +391 -0
  240. package/src/serve/chat-stage.ts +195 -0
  241. package/src/serve/checkpoint-identity.ts +64 -0
  242. package/src/serve/completion-executor.ts +15 -7
  243. package/src/serve/discovery-routes.ts +12 -13
  244. package/src/serve/generation-gateway.ts +183 -213
  245. package/src/serve/http.ts +159 -0
  246. package/src/serve/inference-request.ts +79 -0
  247. package/src/serve/isolate.ts +297 -105
  248. package/src/serve/lab-routes.ts +3 -0
  249. package/src/serve/lane-registry.ts +2 -1
  250. package/src/serve/model-admin-routes.ts +7 -5
  251. package/src/serve/model-binding.ts +43 -0
  252. package/src/serve/model-host.ts +511 -0
  253. package/src/serve/openai-wire.ts +103 -0
  254. package/src/serve/parent-application.ts +56 -0
  255. package/src/serve/pipeline.ts +33 -0
  256. package/src/serve/preparation.ts +33 -0
  257. package/src/serve/request-plan.ts +23 -18
  258. package/src/serve/request-prep.ts +344 -0
  259. package/src/serve/response-lease.ts +24 -0
  260. package/src/serve/responses-client.ts +69 -0
  261. package/src/serve/session-completion-engine.ts +110 -0
  262. package/src/serve/session-search.ts +1 -1
  263. package/src/serve/static-routes.ts +5 -5
  264. package/src/serve/text-completion-stage.ts +104 -0
  265. package/src/serve/token-streams.ts +519 -0
  266. package/src/server.ts +335 -2711
  267. package/src/spec/assistant-rows.ts +92 -0
  268. package/src/spec/assistant-source.ts +31 -55
  269. package/src/spec/cache-identity.ts +22 -0
  270. package/src/spec/deepspec-rows.ts +29 -0
  271. package/src/spec/deepspec-source.ts +16 -14
  272. package/src/spec/dflash-source.ts +36 -12
  273. package/src/spec/drafter.ts +153 -142
  274. package/src/spec/dspark/ab-stats.ts +3 -3
  275. package/src/spec/dspark/calibration.ts +2 -2
  276. package/src/spec/dspark/deepspec-module.ts +97 -122
  277. package/src/spec/dspark/generate-dflash.ts +2 -2
  278. package/src/spec/dspark/generate.ts +2 -2
  279. package/src/spec/dspark/loader.ts +1 -1
  280. package/src/spec/dspark/module-dflash.ts +129 -172
  281. package/src/spec/dspark/module.ts +3 -3
  282. package/src/spec/dspark/quantize-drafter.ts +1 -1
  283. package/src/spec/generate.ts +1 -1
  284. package/src/spec/glm52-mtp-graph.ts +29 -0
  285. package/src/spec/glm52-mtp-rows.ts +132 -0
  286. package/src/spec/glm52-mtp-source.ts +54 -137
  287. package/src/spec/glm52-mtp-state.ts +33 -0
  288. package/src/spec/ngram-source.ts +93 -21
  289. package/src/spec/projected-draft-rows.ts +73 -0
  290. package/src/spec/qwen-mtp-module.ts +171 -0
  291. package/src/spec/qwen-mtp-rows.ts +219 -0
  292. package/src/spec/qwen-mtp-source.ts +147 -195
  293. package/src/spec/qwen-mtp-state.ts +30 -0
  294. package/src/spec/serve-loop.ts +201 -201
  295. package/src/spec/source.ts +110 -13
  296. package/src/spec/two-model-rows.ts +61 -0
  297. package/src/spec/two-model.ts +44 -102
  298. package/src/ssd-cache.ts +99 -19
  299. package/src/ssd-durability.ts +36 -20
  300. package/src/tool-call.ts +51 -14
  301. package/src/train/flash-cce.ts +2 -2
  302. package/src/train/forward.ts +6 -0
  303. package/src/train/loss.ts +1 -1
  304. package/src/train/prefix-shared.ts +5 -1
  305. package/src/train/segmented.ts +5 -3
  306. package/src/train/trainer.ts +6 -6
  307. package/src/vision/prompt.ts +1 -1
  308. package/src/vision/qwen3vl-tower.ts +52 -10
  309. package/src/web/app.html +1 -1
  310. package/src/web/app.js +62 -60
  311. package/src/web/src/adapters-panel.ts +3 -3
  312. package/src/web/src/assistant.ts +2 -36
  313. package/src/web/src/chat.ts +4 -4
  314. package/src/web/src/composer.ts +4 -4
  315. package/src/web/src/hub.ts +1 -1
  316. package/src/web/src/markdown.ts +0 -0
  317. package/src/web/src/memory-panel.ts +1 -1
  318. package/src/web/src/model-picker.ts +1 -1
  319. package/src/web/src/palette.ts +1 -1
  320. package/src/web/src/protocol.ts +3 -39
  321. package/src/web/src/sessions.ts +1 -1
  322. package/src/web/src/ui-catalog.ts +4 -18
  323. package/src/web-assets.ts +47 -0
  324. package/src/weights.ts +55 -9
  325. package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
  326. package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
  327. package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
  328. package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
  329. package/docs/archive/investigations/hlg-ab.html +0 -34
  330. package/docs/archive/investigations/hlg-explorer.html +0 -238
  331. package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
  332. package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
  333. package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
  334. package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
  335. package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
  336. package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
  337. package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
  338. package/docs/archive/investigations/hlg-report.html +0 -262
  339. package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
  340. package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
  341. package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
  342. package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
  343. package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
  344. package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
  345. package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
  346. package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
  347. package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
  348. package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
  349. package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
  350. package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
  351. package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
  352. package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
  353. package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
  354. package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
  355. package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
  356. package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
  357. package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
  358. package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
  359. package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
  360. package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
  361. package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
  362. package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
  363. package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
  364. package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
  365. package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
  366. package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
  367. package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
  368. package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
  369. package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
  370. package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
  371. package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
  372. package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
  373. package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
  374. package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
  375. package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
  376. package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
  377. package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
  378. package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
  379. package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
  380. package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
  381. package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
  382. package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
  383. package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
  384. package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
  385. package/docs/archive/mlx-bun-lab-report.html +0 -275
  386. package/docs/archive/wwdc-mlx-bun.html +0 -848
  387. package/docs/design/adapters-end-to-end.md +0 -205
  388. package/docs/design/batching-perf-path.md +0 -255
  389. package/docs/design/batching-v2-plan.md +0 -529
  390. package/docs/design/bucketing-stage.md +0 -197
  391. package/docs/design/compat-cli-surface-design.md +0 -230
  392. package/docs/design/docs-reorg-plan.md +0 -165
  393. package/docs/design/dspark-serving-program.md +0 -371
  394. package/docs/design/dspark-speculative-decoding.md +0 -318
  395. package/docs/design/grammar-spec-batching-integration.md +0 -301
  396. package/docs/design/memory-inference-path.md +0 -422
  397. package/docs/design/memory-synthesis.md +0 -287
  398. package/docs/design/memory-system.md +0 -237
  399. package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
  400. package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
  401. package/docs/design/omlx-adoption-map.md +0 -98
  402. package/docs/design/orpo-dynamic-lambda.md +0 -220
  403. package/docs/design/orpo-future-enhancements.md +0 -340
  404. package/docs/design/paged-kv-cache.md +0 -214
  405. package/docs/design/parallel-slots.md +0 -567
  406. package/docs/design/parity-tier-dag.md +0 -161
  407. package/docs/design/repo-cleanup-plan.md +0 -231
  408. package/docs/design/runtime-isolation.md +0 -263
  409. package/docs/design/segmented-backward-training.md +0 -543
  410. package/docs/design/spec-decode-larger-targets.md +0 -229
  411. package/docs/design/ssd-kv-cold-tier.md +0 -415
  412. package/docs/design/structured-output.md +0 -484
  413. package/docs/design/tauri-desktop-app.md +0 -127
  414. package/docs/design/the-dreaming-master-plan.md +0 -445
  415. package/docs/design/turboquant-kv.md +0 -235
  416. package/docs/design/turboquant-weights.md +0 -467
  417. package/docs/design/w4a16-compute-precision-spike.md +0 -232
  418. package/docs/design/web-chat-beat-matrix.md +0 -315
  419. package/docs/design/write-pipeline-entity.md +0 -468
  420. package/docs/planning/IDEAS.md +0 -69
  421. package/docs/reference/embedding.md +0 -197
  422. package/docs/reference/features-matrix.md +0 -101
  423. package/docs/reference/orpo-quickstart.md +0 -135
  424. package/src/flags.ts +0 -11
  425. /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
  426. /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
  427. /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
  428. /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
  429. /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
  430. /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
  431. /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
  432. /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
  433. /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
@@ -0,0 +1,1841 @@
1
+ ---
2
+ status: active
3
+ axis: ON
4
+ canonical-for: batching
5
+ plan-anchor: "Phase 18 — Concurrent / batched serving (slots) + parallel load benchmark `[~]` (2026-06-13)"
6
+ last-verified: 2026-09-10
7
+ ---
8
+
9
+ # Batching — continuous scheduling for `--batch N`
10
+
11
+ The canonical design for concurrent serving: the scheduling seam
12
+ (`src/serve/generation-gateway.ts`), the continuous-batching engine
13
+ (`src/serve/batch-scheduler.ts`), the dynamic-B cache primitives
14
+ (`src/model/batched-*.ts`), admission, and the parity discipline that
15
+ gates all of it. Consolidates docs/design/batching.md, docs/design/batching.md,
16
+ docs/design/batching.md, the batching items of
17
+ docs/design/batching.md, and the scheduler tie-in from
18
+ docs/design/kv-cache.md. Status and dated progress live in PLAN.md (see the
19
+ plan anchor above); this file describes the mechanism as it exists and
20
+ what is still open.
21
+
22
+ Phase 6 now requires shared method/session execution at B=1 and B>1, full
23
+ batch feature parity, and configuration organized by owning interface. The
24
+ boundaries are defined in [engine architecture §12.5–12.7](unified-engine-frontier-plan.md#125-capability-negotiation-and-planning).
25
+ The separate serial mechanism described below is the current implementation;
26
+ retiring its duplicate lifecycle is open work. Backend specializations may
27
+ remain when they improve measured performance. R17 owns output checkpoint
28
+ publication to the shared RAM/SSD cache.
29
+
30
+ User-facing mirror: docs/reference/server-config.md ("Execution modes",
31
+ compatibility matrix, known limitations) and docs/reference/cli.md.
32
+
33
+ ## 1. Why batch, and why continuous
34
+
35
+ Decode on Apple Silicon is bandwidth-bound: every token streams the whole
36
+ weight set through the GPU to produce one token. Batch B sequences and
37
+ the weights stream once for B tokens — aggregate tok/s scales roughly
38
+ linearly until the compute roofline.
39
+
40
+ The motivating workload is one person's agent harness: a coding agent
41
+ fans out sub-agents against one local server. Serially, the Nth agent's
42
+ first token waits for N−1 full generations. Batched, 4–8 agents decode
43
+ together — each stream somewhat slower, nobody queued. "Single-user" in
44
+ this project means single user, many agents. Consequences that shape the
45
+ design: arrivals are uncoordinated (so scheduling is concurrency-driven),
46
+ sub-agents share long system prompts (so prefix reuse on the batch lane
47
+ matters), and agent fleets want long contexts (so quantized KV must
48
+ compose with batching).
49
+
50
+ The scheduler is **continuous** (iteration-level), never static: it
51
+ works per decode step, admitting a newly arrived request after a short
52
+ prefill and retiring finished rows between steps. A late request joins at
53
+ the next step instead of waiting for the in-flight batch to finish. By
54
+ Little's law requests overlap even at modest rates because generations
55
+ last seconds. When traffic never overlaps there is only ever one live row
56
+ and the engine runs its B=1 fast path (§4.4).
57
+
58
+ ## 2. Decisions and rationale
59
+
60
+ - **Concurrency IS the batch size.** `--batch N` is a concurrency cap
61
+ (mlx-lm's `--decode-concurrency` twin, accepted as an alias), not a
62
+ mode switch. Placement declares a *mechanism* per request; the
63
+ scheduler picks B=1 or B=N from its live row count. Default cap is 8
64
+ (`server.ts`: `serverOptions.batch ?? 8`), flipped from 1 on
65
+ 2026-07-05 once a lone scheduler request measured within 0.992–0.996
66
+ of the strict serial decode (paired A/B, byte-identical output). The
67
+ earlier "mode switch, auto-batching rejected for determinism" decision
68
+ was reversed with an answer to its objection: mlx_lm.server itself
69
+ batches by arrival, so drop-in parity *requires* it, and anyone who
70
+ needs arrival-independent numerics pins `--batch 1`.
71
+ - **`--batch 1` is the determinism pin.** It runs the strict serialized
72
+ single-queue path; a request's bits never depend on what else was in
73
+ flight. Use it for golden regeneration.
74
+ - **bf16 batching IS the mlx-lm-parity contract.** mlx-lm's batched
75
+ path is bf16 (`BatchRotatingKVCache.to_quantized` raises), so the
76
+ oracle for a batched row is **mlx-lm's own batch mode at the same B**,
77
+ bit-exact — never our own B=1 (batching changes the attention
78
+ kernel's reduction order, so B=N vs B=1 measures the wrong thing).
79
+ KV-quant unset ⇒ bf16 on both lanes.
80
+ - **Compositions inherit the scheme's oracle.** Batched per-layer
81
+ quantized KV (`--kv-quant config`) has no mlx-lm oracle; each row is
82
+ gated against the *serial* composition of the same scheme (unpadded
83
+ rows bit-exact / KL 0; padded rows within a calibrated bf16
84
+ reduction-order envelope). Lab-style KL/eval gates are for no-oracle
85
+ schemes only, never a substitute for an oracle that exists.
86
+ - **Compat mode: no parity-breaking levers on the batched step.** The
87
+ scheduler drives `forwardHidden`/`logitsFromHidden` directly. Only
88
+ bit-exact machinery engages: compiled decode at B=1 on adopted
89
+ serial-class caches (same kill switch as serial), the compiled
90
+ activations, the pipelined readback. `--fused-sdpa` and `--force-wire`
91
+ never reach the batched lane.
92
+ - **Never downgrade a composition to make it batchable.** Placement is a
93
+ support check only: it does not strip MTP/drafting, KV schemes,
94
+ TurboQuant, grammar, adapters, or sampling. A composition the scheduler
95
+ does not implement runs unchanged on the serial mechanism today. That
96
+ fallback preserves behavior during migration; it does not meet the new
97
+ batch feature-parity requirement.
98
+ - **Drain, never starve.** A serial-lane waiter pauses admission; running
99
+ rows finish; the lock hands over FIFO; admission resumes (mlx-lm
100
+ `drain_batch`). One `AsyncMutex` is the single exclusion domain for
101
+ the GPU and shared model state (`loraState`): serial generation, the
102
+ scheduler's whole active period, curve `/generate` + `/signal`, and
103
+ adapter mount/unmount all go through it.
104
+ - **Byte-budget admission is ours, not mlx-lm's.** mlx-lm caps counts
105
+ only; we project bytes (`--kv-budget`) because GPU OOM is uncatchable.
106
+ Budget accounting is total bytes, mirroring the byte-capped
107
+ `PromptCache` precedent. Queue-don't-OOM; never evict a running row.
108
+ - **Solo prefill, then join.** Joiners prefill alone (chunked,
109
+ interleaved with decode steps) and merge/extend into the running
110
+ batch. This is why SSM state needs no `ssm_mask` under left-padding:
111
+ state never sees a pad token, and the token-exact match vs mlx-lm's
112
+ masked-pad prefill proves the equivalence.
113
+ - **oMLX-style burst decode is refuted here.** Their `_step_burst` runs
114
+ K steps per event-loop hand-off to dodge Python's per-token GIL
115
+ ping-pong (~1 ms/token). Ported faithfully and measured 2026-07-02
116
+ (M1 Max 32 GB): cpm5 B=4 aggregate 345→289 tok/s, batch-lane B=1
117
+ 149→121, TTFT +~100 ms (SSE flushes wait out the budget). Bun's
118
+ `setImmediate` hop costs microseconds; there is nothing to dodge.
119
+ Reverted; the per-yield step is the measured optimum. Don't re-add
120
+ without new evidence.
121
+ - **Spec decode and scheduling are separate concerns.** A mounted
122
+ `--draft-model` selects speculation only for compatible requests. Ordinary
123
+ fallback requests can batch when their actual features permit it. Concurrent
124
+ speculation is an explicit feature gap in the shared-execution program;
125
+ accepted lengths and target/draft state belong to the decode method.
126
+
127
+ ## 3. Scheduling seam — `GenerationGateway`
128
+
129
+ `place(shape)` freezes one `GenerationPlacement { shape, mechanism }`
130
+ with `mechanism ∈ {"serial", "continuous"}`; `run()` rejects a placement
131
+ made for another shape. The predicate, exactly as implemented
132
+ (`#supportsContinuous`):
133
+
134
+ ```
135
+ continuous iff
136
+ model is not DiffusionGemmaModel (non-autoregressive)
137
+ AND batch > 1
138
+ AND #modelCachesBatchable() (cache-capability gate, below)
139
+ AND !hasVision (offset-0 prefill + bidirectional mask)
140
+ AND adapter state supports compatible groups (when adapters are selected)
141
+ AND KV scheme supports the loaded cache layouts
142
+ AND selected method has a grouped implementation
143
+ AND !(hasGrammar AND MLX_BUN_GRAMMAR_BATCH=0)
144
+ ```
145
+
146
+ `hasRepetitionPenalty` and `hasLogitsExtras` (min_p, XTC, logit_bias,
147
+ presence/frequency) are carried on `RequestShape` for stats only — they
148
+ do **not** gate. The load-bearing reason: Qwen3.5 ships a default
149
+ repetition penalty in `generation_config.json`, which once routed every
150
+ request serial. Per-row `StepSampler`s fold the processors over a per-row
151
+ device-side token history, so all of these batch.
152
+
153
+ **Cache-capability gate** (`#modelCachesBatchable`, mirrors mlx-lm
154
+ server.py's all-caches-have-`merge` check), memoized from a fresh
155
+ `makeCache()`:
156
+ - `UniversalDenseModel`: batchable iff `!maskArray` and no
157
+ `sliding_attention` layer type — plain full-attention Tier-0 archs
158
+ (Llama) only; gemma2-family (pad-blind causal mask in `forwardLayers`)
159
+ and sliding-window universal archs are unvalidated cells → serial.
160
+ - Otherwise every cache must be `KVCache`, `RotatingKVCache`, a
161
+ `BatchableCache` implementer (GLM-5.2's `MLACache`, which owns its own
162
+ merge/extract/filter and byte projection), or `SSMCache` (Qwen3.5's
163
+ gated-DeltaNet state; `MLX_BUN_BATCH_SSM=0` removes it → serial).
164
+
165
+ **KV-scheme capability:** the resolved `KvScheme` qualifies per-layer affine,
166
+ start-zero uniform affine, and ordinary TurboQuant with immediate or delayed
167
+ conversion through actual cache and layout support. The MLX layout binding
168
+ creates encoded rows; scheduling does not encode or decode KV. Qualified methods
169
+ share delayed affine/TurboQuant conversion and per-layer affine policies through
170
+ these row interfaces; TurboQuant sliding layers remain bf16.
171
+ Unsupported compositions retain the existing serial route until their shared implementation and acceptance are complete.
172
+
173
+ Paged Gemma4 bf16 requests compose through `PagedKvRows`; the request state
174
+ policy supplies block storage and prompt-cache bypass without changing scheduling.
175
+ GLM `--mtp on` (its default) mounts the MTP
176
+ drafter and therefore routes serial+spec — `--mtp off` exposes ordinary
177
+ GLM batching.
178
+
179
+ **Lanes and the lock.** Serial requests run under `runExclusive`, which
180
+ counts itself as a serial waiter; the scheduler's `admissionHeld()` reads
181
+ that count. The serial branch also wraps `onToken` in a ≥25 ms
182
+ rate-limited macrotask hop: serial decode is an unbroken microtask chain
183
+ and without the hop `/stats`, `/health`, and accepts stall for the whole
184
+ generation (measured 2.5 s on a 512-token cpm5 run, 2026-07-02; after:
185
+ 10–44 ms). The batch lane needs none — its drive loop yields per step.
186
+
187
+ `/stats.batch` exposes `{configured, mode, batched, active_rows,
188
+ pending_rows, submitted_rows, kv_bytes, kv_budget_bytes}`; `mode` is
189
+ `off | serial | batch` (cap ≤1 / model not batchable / batchable).
190
+ `MLX_BUN_LANE_DEBUG=1` logs each placement with its shape.
191
+
192
+ ## 4. The engine — `BatchScheduler`
193
+
194
+ One detached Bun-async driver loop (no threads) owns one running batch.
195
+ Per iteration: purge aborted pending rows → decide `hasWork` (running
196
+ rows, an in-flight prefill — finished even under drain — or admissible
197
+ pending) → sleep on `#wake` when idle, releasing the lock and the wired
198
+ limit → otherwise acquire both, start/advance at most one joiner prefill,
199
+ burst-admit further queued rows that fit before the next step, run one
200
+ decode step, yield.
201
+
202
+ ### 4.1 Admission
203
+
204
+ FIFO from `#pending`, head-of-line, never reordered. A joiner starts iff
205
+ no prefill is in flight, admission isn't held, `running < maxBatch`, and
206
+ `#kvAdmits` passes:
207
+ - context limit: `prompt + max_tokens > batchCacheMaxTokens` (GLM's
208
+ compressed caches declare one) rejects with `RangeError`;
209
+ - no `--kv-budget` ⇒ admit;
210
+ - a row whose projected bytes exceed the budget *alone* (empty batch, no
211
+ prefill) rejects with an actionable error — never deadlocks the queue;
212
+ - otherwise admit iff `projectedKvBytes + need ≤ budget`, else wait.
213
+
214
+ Projection is worst-case: `batchRowKvBytes(config, prompt, max_tokens,
215
+ scheme)` = the scheme's `bytesAt` (window-capped for rotating layers,
216
+ quantized bytes under a config scheme), or per-cache `projectedBytes()`
217
+ when every cache is a `BatchableCache`. `/stats.batch.kv_bytes` is the
218
+ running + mid-prefill sum.
219
+
220
+ ### 4.2 Joiner prefill (chunked, interleaved, cache-aware)
221
+
222
+ - **Prompt-cache take at admission** (`RowPromptCache.take`): the solo
223
+ prefill starts from the longest usable cached prefix (namespace `""`
224
+ — adapter requests never reach this lane); `cachedTokens` is real.
225
+ Serves are non-consuming zero-copy clones, so N agents sharing a
226
+ system prompt reuse one prefill. The SSD tier lives inside `take()`,
227
+ so restores reach batch joiners too.
228
+ - **Boundary snapshot**: for a cold-ish prefill ≥256 tokens, chunking
229
+ splits exactly at `min(snapshotAt ?? len, len−1)` and a trim-free
230
+ strict-prefix entry is cloned + put there — the same invariant as the
231
+ serial lane's `snapshotAt`. Without it the lane's only entry was the
232
+ untrimmable `[prompt+gen]` finish-time put, a total miss once rings
233
+ wrap (12B batched ctx-repeat 84 s vs serial 0.4 s, 2026-07-06).
234
+ - **One chunk per loop iteration** (`prefillChunkSize`, default 2048 =
235
+ the serial constant) with `evalAll(state)` + `#quantizeSolo` +
236
+ `clearCache()` at every chunk boundary, one decode step between
237
+ chunks. Running rows stall at most one chunk per joiner; the prefill
238
+ transient stays bounded.
239
+ - **Oracle tail convention** (`MLX_BUN_PREFILL_TAIL_SPLIT`, default on):
240
+ drain to `len−1`, then an L=1 forward of the last prompt token yields
241
+ step-0 logits — mlx-lm's `insert_segments` + `GenerationBatch._step`
242
+ shape, and what keeps `--batch 1 == unified == oracle` bit-exact.
243
+ - **Slice before the head**: the last hidden position is sliced before
244
+ `logitsFromHidden` (a whole-prompt `[1,Lp,V]` would be ~4.3 GB bf16 at
245
+ Gemma's V=262k and an 8k prompt).
246
+ - **Token 0 is sampled on the solo caches before merge**, so per-row
247
+ serial equivalence holds by construction. A row that stops at token 0
248
+ (EOS, stop sequence, 1-token grammar) never joins `#running`; its
249
+ prompt-only caches go back to the prompt cache.
250
+ - **Mixed-precision conversion** (`#quantizeSolo`): the scheduler-side
251
+ mirror of serial `maybeQuantizeKv` — same per-layer map, same skip
252
+ rules, same chunk-boundary placement (`KVCache.toQuantized` /
253
+ `RotatingKVCache.toQuantized`). That placement is what makes a row's
254
+ quantized bytes bit-exact vs serial `--kv-quant config`.
255
+
256
+ ### 4.3 Join: adopt, extend, or merge — by layer kind
257
+
258
+ `#kinds[layer] ∈ {full, rot, ssm, owned-batch}` from the prototype
259
+ caches. Before any merge the decode pipeline is flushed (read + emit) so
260
+ the row set is settled and the next step starts cold.
261
+
262
+ **Adopt-don't-copy.** A row joining an *empty* batch keeps its solo
263
+ caches as the inners (pointer handoff, zero bytes moved). The lone row's
264
+ caches stay serial-class (`KVCache` / `RotatingKVCache` /
265
+ `QuantizedKVCache` / `RotatingQuantizedKVCache`), which is what makes the
266
+ B=1 step literally the serial graph and lets compiled decode and
267
+ prompt-cache put/take apply to it.
268
+
269
+ When a second row joins, per layer:
270
+
271
+ | kind | running batch cache | join op | file |
272
+ | --- | --- | --- | --- |
273
+ | full (bf16) | `KVCache` `[B,H,S,D]` + shared `#fullLeftPad` | `extendKVRows` — one pad + one B-axis concat, running rows untouched, pads grow (mlx-lm `BatchKVCache.extend`); `MLX_BUN_BATCH_EXTEND=0` = `mergeKVRows` re-merge (per-row slices, pads re-normalized; O(B·S)) | `src/model/batched-mask.ts` |
274
+ | full (quantized) | `QuantizedKVCache` over (packed, scales, biases) | `extendQuantRows` / `mergeQuantRows` — the same surgery on all three triples | `src/model/batched-quant.ts` |
275
+ | rot (bf16) | `BatchedRotatingCache` (persistent; self-tracks per-row leftPad/offsetArr through ring wrap) | always re-merge: extract each running row's temporal view + the new row → `BatchedRotatingCache.merge(rows, offsets, maxSize)`; an adopted lone `RotatingKVCache` is the first row (pad 0) | `src/model/batched-rotating.ts` |
276
+ | rot (quantized) | `BatchedRotatingQuantCache` | same recipe over triples, `merge(rows, offsets, maxSize, groupSize, bits)` | `src/model/batched-rotating-quant.ts` |
277
+ | ssm | `SSMCache` `[B,…]` state, no temporal axis, no padding | `SSMCache.mergeRows(prev, solo)` — B-axis concat | `src/model/qwen3-delta.ts` |
278
+ | owned-batch | `BatchableCache` (GLM `MLACache`) | `solo.makeEmptyBatch().mergeRows([prev, solo])`; the cache owns leftPad/rowOffsets | `src/model/glm52-cache.ts` |
279
+
280
+ Routing among these branches is **by capability only** (`isRowBatchCache`,
281
+ `isBatchableCache`, `isQuantizedKvCache`, …). `BatchedRotatingCache` has
282
+ no `signature()` override, so any signature-based conjunct silently drops
283
+ the running batch from the merge — the 2026-08-22 agg×4 outage from
284
+ 443f333 (joiners built a B<max ring; the next full-B step crashed in the
285
+ grow-path concatenate; whole-batch drop). `tests/batch-rotating-join.test.ts`
286
+ pins this.
287
+
288
+ After a real merge every row is marked `merged`: its KV is interleaved in
289
+ batched buffers and must be *extracted*, not adopted, at finish.
290
+
291
+ ### 4.4 The decode step
292
+
293
+ Pipelined, mirroring mlx-lm `GenerationBatch._step`: (1) forward all rows
294
+ from the still-unread `[B]` token register (or, cold, from each row's
295
+ `current`), sample, `asyncEvalAll` the new register; (2) then read the
296
+ *previous* register with `toIntTokens()` (an eval + raw int read — never
297
+ `toFloat32()`, whose cast kernel queued behind the whole next step and
298
+ stalled every token), emit, evict. Rows that finish get one extra
299
+ harmless KV write from the already-built step; `filter` drops the row
300
+ (mlx-lm behaves identically). `MLX_BUN_BATCH_NO_PIPELINE=1` reads
301
+ synchronously (same math, slower).
302
+
303
+ **Unpadded fast path.** When every `#fullLeftPad` is 0 the full layers'
304
+ per-step `BatchedDecodeMaskCache` / `BatchedQuantDecodeMaskCache`
305
+ wrapper is skipped: `KVCache.makeMask(1)` is already the empty mask and
306
+ every row sits at the shared scalar offset, so the bare cache dispatches
307
+ the same per-step graph serial builds. The wrapper otherwise costs a
308
+ host mask build + ~8 device nodes per full layer per token. Rot,
309
+ owned-batch, and ssm caches always pass through unwrapped.
310
+
311
+ **Compiled decode at B=1.** With exactly one row, unpadded, a uint32
312
+ register, serial-class inners (`CompiledDecode.supports`; a
313
+ filtered-to-one batched rot-quant cache is excluded), and a Gemma4
314
+ dense model, the scheduler replays the serial engine's compiled step
315
+ (`MLX_BUN_COMPILED_DECODE` is the same kill switch). A failed step is
316
+ transactional: the scheduler permanently falls back to the graph path.
317
+
318
+ **Vectorized homogeneous sampling.** When every live row is plain greedy
319
+ (`plainGreedy` from the gateway: temperature 0, no logprob capture, no processors, no
320
+ grammar), one `toLogprobs` + `argmaxAxis` over `[B,V]` replaces B
321
+ slice/sample/concat graphs — per-row identical math, tie behavior
322
+ included. `MLX_BUN_BATCH_VEC_SAMPLE=0` forces the per-row closure path.
323
+ Length-doomed rows get a placeholder slot (never emitted; one KV write
324
+ on their own about-to-evict row).
325
+
326
+ **Sampling boundary.** `contracts/sampling.ts` defines `SamplingSession`:
327
+ borrow model logits, apply request-owned processing/selection, return the
328
+ chosen token and optional probability tensors. Device and host-token MLX
329
+ samplers implement the same generic contract. `createRowSampling` accepts a
330
+ sampler through that interface and binds its captures to output delivery.
331
+ The scheduler receives token tensors and a callback, with no probability
332
+ retention policy. Captures are evaluated with their token and retained by
333
+ request-local step until emission; cancellation and unconsumed lookahead
334
+ release them. Token-only requests retain the existing vectorized shortcut.
335
+ Explicit seeds no longer determine scheduler placement. The seed and local
336
+ sample index select the random key, independently of sibling requests.
337
+ Reproducibility is conditional on the same numerical execution composition,
338
+ not a promise of equal logits across different batch shapes. The preserved
339
+ `--batch 1` control supplies the earlier fixed-shape behavior.
340
+ Vectorized logprob capture and the broader sampling-kernel interface remain
341
+ optimization work; this change establishes no performance dominance.
342
+
343
+ **Grammar rows** (`#stepGrammar`): read the previous register *before*
344
+ building the graph (the matcher's `accept` needs JS numbers), `accept()`
345
+ per live grammar row (fires async bitmask fills that overlap the graph
346
+ build), `await ready()`, sample per row with the mask applied in the
347
+ closure, emit the values already read. While any grammar row is live the
348
+ batch effectively runs unpipelined — bounded by the ~0.1 ms readback +
349
+ 0.004–0.19 ms/row fills. Flushes must `accept()` the flushed tokens too,
350
+ or matchers run one token behind on every mid-decode join (found by the
351
+ conformance gate, 2026-07-03).
352
+
353
+ **Housekeeping.** `clearCache()` every 256 steps (serial's cadence;
354
+ mlx-lm batched uses 512), not per step. The per-step `setImmediate` hop
355
+ is rate-limited to 25 ms when exactly one row is running with an empty
356
+ queue and no drain — the 0.4–0.8% B=1 drive-loop tax, paired-measured
357
+ 2026-07-05. `MLX_BUN_BATCH_STEP_TRACE=1` prints per-step build/read/
358
+ emit/gap timings.
359
+
360
+ ### 4.5 Emit, evict, extract
361
+
362
+ `#emit` mirrors `generate()`: EOS terminates **without** an `onToken`
363
+ call; `onToken` returning `false` → `stop`; grammar terminated → `stop`
364
+ (the final token was delivered; never sample into an all-`-inf` mask);
365
+ `generated ≥ max_tokens` → `length`. Per-row `StopMatcher`, tool router,
366
+ and SSE fan-out live in each request's `onToken` closure.
367
+
368
+ **Failure containment.** A row's `onToken` throwing rejects and evicts
369
+ *that* row (mlx-lm `remove`); siblings continue. A forward/sampling error
370
+ cannot be attributed to a row: every row rejects and the batch KV is
371
+ dropped (`dropOnly`, never put). A prefill error rejects only the joiner.
372
+
373
+ **Eviction** (`#applyFilter(keep)`): `filterRows` on batched/owned
374
+ caches; `filterKVRows` / `filterQuantRows` (B-axis `takeAxis`) on full
375
+ layers; the pending register and `#pendingReal` flags are filtered in
376
+ lockstep. Full layers also remove the minimum left padding shared by all
377
+ survivors, matching `mlx-lm BatchKVCache.filter`. Buffer width and per-row
378
+ padding shrink together, preserving absolute RoPE positions. Quantized full
379
+ layers slice the token axis of packed/scales/biases together; rotating layers
380
+ retain their separate layout. Keeping those unused columns can change logits
381
+ through a different attention reduction shape.
382
+
383
+ **Finish-time prompt-cache put.** A lone never-merged row hands its
384
+ adopted serial-class caches back zero-copy, keyed by `promptIds + fed`
385
+ (`fed` = generated tokens whose KV actually entered the cache, per-row
386
+ exact via the register's real/placeholder flags; a placeholder that ever
387
+ fed taints the row and extraction refuses). A merged row with ≥256
388
+ prompt tokens is **extracted** per layer into fresh owned serial caches
389
+ before the filter mutates the batch (`#extractRowCaches`: `extractRow`,
390
+ `extractKVRow`, `extractQuantRow`; SSM only when the row's advance count
391
+ equals the key exactly — recurrent state is untrimmable), evaluated
392
+ asynchronously so the batched source buffers free once the copies land,
393
+ then put. Extract is a pure slice+copy of byte-preserved rows
394
+ (`tests/batched-extract.test.ts`), so an extracted row's bytes equal the
395
+ solo run's. All take/put happens inside the gateway's exclusion domain —
396
+ the scheduler is the sole owner of cache mutations on its lane.
397
+
398
+ ## 5. The one hard numerics problem: per-sequence position
399
+
400
+ Rows are different lengths at different positions, but a cache has one
401
+ `offset`, RoPE takes one offset, and the mask is built from it. mlx-lm's
402
+ solution, and ours: **left-padding + per-row mask + per-row RoPE**.
403
+
404
+ - Right-align every row so "the current token" is one column → one
405
+ advancing offset still works. Full layers share `#fullLeftPad`; rot
406
+ caches self-track per-row `leftPad`/`offsetArr` across the ring wrap.
407
+ - Mask padding with a real `[B,1,1,S]` boolean mask (`j >= leftPad[b]`,
408
+ `buildBatchedDecodeMask`) even at the N=1 decode step, and use the same
409
+ fused bool-mask `scaled_dot_product_attention` mlx-lm uses. An additive
410
+ mask would deviate.
411
+ - RoPE positions are a per-row array (`ropeOffsetArr`,
412
+ `ops.ropeDynamic`; `UniversalRope.applyDynamic` for Tier-0 archs).
413
+ Two traps every attention implementation must respect: capture the
414
+ offset array **once** per forward (reading it before and after
415
+ `updateAndFetch` gives K and Q different positions — the 2026-06-14
416
+ Gemma fix; the generator `scripts/gen-model.ts` carries it for
417
+ generated handlers), and a batched cache's `ropeOffsetArr` must be
418
+ **stable within a step** and refreshed only in `releaseRopeArr` (the
419
+ rope-array step-stability contract from milestone 2). MiniCPM5
420
+ (2026-06-14) and universal dense (2026-07-03, latent since v0.0.9)
421
+ each shipped a scalar-offset bug that only the batched oracle caught:
422
+ every model family wires the per-row path separately.
423
+ - Batched prefill numerics differ from B=1 prefill (GEMV vs GEMM
424
+ reduction order), and a padded row's attention sum accumulates in a
425
+ different order than its solo run. The unpadded row is bit-exact vs
426
+ solo; the padded row carries bounded bf16 noise (≤0.23 logit diff over
427
+ 8 steps, CPM, 2026-06-14). Calibrate the envelope per model; don't
428
+ chase it — mlx-lm B=2 diverges from its own B=1 the same way.
429
+
430
+ ## 6. KV memory model
431
+
432
+ Three rungs, in increasing fidelity of "who needs it most":
433
+
434
+ 1. **Static per-slot partition** — rejected, never built (wastes memory,
435
+ arbitrary per-slot context cap).
436
+ 2. **Dynamic shared budget, contiguous padded caches** — what ships.
437
+ Each layer holds one `[B,H,S,D]` buffer (growing in `KVCache.STEP`
438
+ = 256 granules) whose width tracks the *longest* live row; short rows
439
+ pay padding to that width. Admission is by projected total bytes
440
+ (§4.1). Quantized KV multiplies the budget (~4× at 4-bit), and
441
+ batching × quantized KV compounds.
442
+ 3. **Paged KV** — the density upgrade. `PagedKVCache`/`BlockPool`
443
+ (`src/lab/paged-kv/paged-kv.ts`, `--paged-kv`) shipped as a serial-only,
444
+ bf16, Gemma4 full-attention block manager, bit-exact vs the plain
445
+ path. The batching payoff is **not built**: paged caches into the
446
+ scheduler's `LayerInner` union, block-count admission replacing byte
447
+ projection, `#mergeJoiner`/`#applyFilter`/`#extractAndPut` allocating
448
+ and freeing blocks instead of pad+concat, then block-level CoW prefix
449
+ sharing and a fused paged-attention kernel. That is where the
450
+ padded-batch-waste win lives (a 50-token row cohabiting with a
451
+ 4000-token row pays ~4000 tokens of KV today).
452
+
453
+ The `--prompt-cache` budget is the *prefix* cache; live decode KV is a
454
+ separate pool. mlx-lm trims its prompt cache to `total − live batch
455
+ bytes`; sharing one ceiling between the LRU and the live batch is still
456
+ open (§8).
457
+
458
+ ## 7. Verification
459
+
460
+ Oracle discipline: every batched mechanism lands with a token-for-token
461
+ gate against whoever ships the same protocol, generated from the oracle
462
+ venv (`scripts/oracle/gen-batched-golden.py` static B=N;
463
+ `gen-batched-dynamic-golden.py` merge/extract/filter join+leave;
464
+ `gen-batched-extend-golden.py` — extend's pad layout differs from
465
+ re-merge, so it has its own golden; `gen-rotating-golden.py`
466
+ `BatchRotatingKVCache` model-free incl. ring wrap). Goldens are
467
+ machine-layered (`goldens/*.json`, per-GPU overrides under
468
+ `goldens/<machine>/`). The B=N-vs-our-B=1 KL harness is an internal
469
+ consistency check only, never the gate.
470
+
471
+ | Behavior | Test | Tier |
472
+ | --- | --- | --- |
473
+ | rotating merge/decode/mask/filter incl. ring wrap vs mlx-lm; generic row-storage state | `tests/batched-rotating.test.ts`, `batched-rotating-state.test.ts` | fast, oracle |
474
+ | static B=2 + dynamic join/leave + extend-join vs mlx-lm (CPM, Gemma 12B/e4b/26B, Llama 3B Tier-0) | `tests/batched-decode-parity.test.ts` (`MLX_BUN_TEST_BATCH_DECODE=1`) | gated, oracle |
475
+ | Qwen3.5 SSM batched vs mlx-lm B=2 | `tests/qwen35-batched-parity.test.ts` | gated, oracle |
476
+ | scheduler orchestration (admit/step/evict/join) | `tests/batch-scheduler.test.ts` | gated, exact live same-B protocol oracle (CPM and Gemma 12B) |
477
+ | per-row containment, drain, full-attention join routing | `tests/batch-containment.test.ts` | gated |
478
+ | rotating-layer join mid-decode (2026-08-22 regression) | `tests/batch-rotating-join.test.ts` | gated |
479
+ | KV-budget queueing + projection math | `tests/batch-kv-budget.test.ts`, `kv-budget-projection.test.ts` | gated / fast |
480
+ | vectorized sampling bit-equal A/B | `tests/batch-vec-sample.test.ts` | gated |
481
+ | grammar under batching (mixed masks, 4 schemas at B=4, churn, joiner, truncation) | `tests/batch-grammar.test.ts` | gated |
482
+ | quantized dynamic-B ops; batched mixed-KV per row vs serial composition | `tests/batched-quant.test.ts`, `batched-rotating-quant.test.ts`, `batched-kv-quant-parity.test.ts` | fast / gated |
483
+ | per-row extract == solo bytes | `tests/batched-extract.test.ts` | fast |
484
+ | live server: engage, `/stats`, SSE fan-out, serial coexistence | `tests/batch-serving.test.ts` | gated smoke |
485
+
486
+ Closeout recheck (2026-09-08, Apple M1 Max 32 GB, Bun 1.4.0,
487
+ MLX 0.32.2): the old Gemma solo-KL bound failed even though all 24 captured
488
+ simultaneous-admission vectors matched the same-B oracle exactly. Replaying
489
+ the mid-stream join exposed a separate mismatch in two later vectors: the
490
+ native full cache retained padding that `BatchKVCache.filter` removes.
491
+ The scheduler now performs that removal. The live protocol gate passes all
492
+ 48 full-vector hashes on each of MiniCPM5 and Gemma 12B, including row
493
+ retirement and a join triggered after two emitted tokens. The oracle receives
494
+ inputs and row/step identities, never the native expected hashes. It uses
495
+ mlx-lm merge/extend/filter operations and ordinary full-attention KV caches
496
+ when the reference state has no padding, matching serving's scalar-position
497
+ policy. No tolerance was increased and no golden was regenerated.
498
+
499
+ Production oracle: `scripts/oracle/batch-scheduler-reference.py`. Initial
500
+ capture and comparison: `reports/qwen38-closeout/gemma-protocol/`.
501
+ Before/after gates: `gemma-protocol-gate.log`,
502
+ `gemma-protocol-filter-trim-gate.log`, `batch-protocol-final-gate.log`.
503
+ The original unchanged-HEAD failure remains in
504
+ `memory-baseline-batch-scheduler.log`. Quantized slice and broader serving
505
+ checks accompany the filter change; M4 campaign acceptance remains separate.
506
+
507
+
508
+ Benchmark harness: `scripts/bench-matrix.ts features` (six-cell
509
+ composition matrix over live SSE: TTFT p50/p95 at the first SSE token,
510
+ wall-clock aggregate tok/s, schema conformance as a hard gate);
511
+ `scripts/bench-serving-load.ts` is the client-only stack-vs-stack tool.
512
+ Quotable numbers come from a clean machine only; SSE bursts inflate
513
+ naive tok/s — use wall-clock.
514
+
515
+ ## 8. Open items
516
+
517
+ ### Consolidation matrix
518
+
519
+ This is the implementation gap inventory, not a claim that all combinations
520
+ have passed acceptance. Phase 6 coordinates the work; Phase 18 tracks its
521
+ batch cells. Current placement rules are in `src/engine/execution-plan.ts`.
522
+
523
+ The window-crossing scheduler gate in `tests/parity/batch-scheduler.test.ts`
524
+ now passes on Gemma 12B and e4b on both development machines. Two rows cross
525
+ the sliding window during decode; a third begins beyond it and joins late.
526
+ The pinned oracle replays the same admissions, merges and retirements and
527
+ compares every sampled full float32 logit vector. Run with
528
+ `MLX_BUN_TEST_BATCH_RING_WRAP=1`; `MLX_BUN_TEST_BATCH_RING_MODEL` selects the
529
+ existing artifact. Evidence is recorded in benchmarks.md.
530
+
531
+ Josh revised the immediate objective to shared batching by default with
532
+ effectively equal measured single-request performance. Serving already defaults
533
+ to a concurrency cap of eight, with B1 execution for a lone eligible request.
534
+ Explicit `--batch 1` and compatibility routing retain serial. Removing that
535
+ executor is deferred; complete feature parity is follow-up work, not a blocker
536
+ for the current default. Supported ordinary requests use the shared method.
537
+ Compare single-request B=1 latency, cold/cached
538
+ prefill and sustained decode as well as concurrent throughput. Record memory,
539
+ cache reuse and actual admitted rows. Keep kernel settings fixed for the
540
+ refactor comparison; measure new kernel optimizations in separate experiments.
541
+ Keep measured single-request regressions and unsupported features explicit.
542
+ They do not require repeating completed checks or withholding the current default.
543
+
544
+ | Concern | Current implementation | Required completion |
545
+ |---|---|---|
546
+ | Scheduling and ordinary decode | Default cap eight uses shared execution at B1/B>1; explicit `--batch 1` retains serial | Keep the batched default using existing measurements; address specific regressions separately. Serial removal is deferred |
547
+ | Speculative decode | Qwen MTP, prompt lookup, standalone, assistant, DeepSpec and seeded DSpark/DFlash providers are integrated through shared B1/B>1 interfaces, with the native/HTTP/cache checks recorded below | Finish specific unsupported combinations and measured regressions; GLM artifact testing is deferred and no trained DSpark/DFlash checkpoint exists |
548
+ | Sampling and logprobs | Shared sampler contract; ordinary groups capture logprobs and accept explicit seeds | Extend same-B oracle/feature compositions and matched performance; preserve per-request RNG/history |
549
+ | Grammar and fill | Grammar batches where enabled; grammar jump and fill remain serial-only | Shared constraint/proposal contracts and qualified batch compositions |
550
+ | Adapters and media | Compatible adapter groups use shared execution; media remains serial | Adapter performance and broader compositions; media preparation and compatible state through shared execution |
551
+ | KV layout | Full and rotating affine/TurboQuant layouts, delayed/per-layer transitions, speculative donors and ordinary paged storage are integrated and tested below | Direct paged-attention kernel, block sharing and quantized/speculative paging remain separate extensions; do not repeat the completed row-layout gates |
552
+ | Prefix and output reuse | Ordinary execution and Qwen MTP prefill/completed decode use the shared RAM/SSD cache | Finish composed native, long Kanban and pressure/timing acceptance; identical retention semantics at B=1 and B>1 |
553
+ | Generation resume | Shared ordinary resume is integrated after both-machine native/HTTP compiled, quantized/delayed and mixed-grammar checks, combined suites and eight M4 timing arms. The packed-model fixture now owns fresh weights per server | Adapter resume is integrated through the same policy; native/HTTP composition checks accompany it. Reuse the completed ordinary resume evidence |
554
+ | Usage and cleanup | The gateway forwards timing/rates; the missing first-token timestamp in ordinary batch admission is now fixed | Consistent events/accounting, row cancellation, failure cleanup and persistence on shutdown |
555
+ | Configuration | Placement still chooses path-specific implementations | One resolved configuration per concern; no setting silently disappears when B changes |
556
+
557
+ Tests must exercise actual B>1 work, not requests routed back to serial.
558
+ Completed checks remain evidence for their recorded source and settings. A new
559
+ run needs a named changed behavior, uncovered case or reproduced failure;
560
+ an open umbrella row is not a reason to repeat all its passing checks.
561
+ The working tree now has a shared transaction lifecycle for request-owned
562
+ acceptance counts. Recurrent state and attention KV implement the same round
563
+ operations. Attention rollback changes logical coverage without moving KV;
564
+ subsequent appends overwrite rejected tips. Positions, masks, extraction,
565
+ empty draft prefixes, late joins and retirement belong to that storage layout.
566
+ A singleton preserves allocated prefill capacity and uses the existing scalar
567
+ RoPE and causal-mask operations. Removing the longest recurrent row now updates
568
+ the group's maximum coverage to its survivors.
569
+
570
+ `MlxStateRows` applies admission, filtering and extraction through layer-owned
571
+ row layouts. Attention KV and `BatchedSSMCache` implement the same port; the
572
+ collection contains no model-family branches or persistence policy. Admission
573
+ borrows prepared state and builds all layer replacements before releasing the
574
+ active collection. Unit checks cover reorder, extraction, removal of every row,
575
+ reuse, donor ownership and failed admission. Full-target MTP controls preserve
576
+ all retained state at B=1/2/4 on both Macs through this collection.
577
+
578
+ The affine row layout composes those same position/retention
579
+ operations for affine KV's packed values, scales and biases. It adds no
580
+ dequantization to joins, rollback or extraction. Qwen attention consumes a
581
+ quantized numerical port rather than selecting the concrete cache class.
582
+ Focused KV4/KV8 storage checks pass, including unequal acceptance, overwritten
583
+ rejected tips, joins, growth, removal and compact extraction. Full-target MTP
584
+ bf16/KV4/KV8 round checks pass on both Macs, including output-aligned stopping
585
+ and survivor continuation at smaller B. Both complete model-free suites and
586
+ typechecks pass. `BatchedQuantizedKVCache` and the numerical attention port are
587
+ adopted in the working tree. The tested source matches the adopted source;
588
+ native logs and the `batched-quantized-state-manifest.json` live in the
589
+ composition-baseline reports. Same-B external quantized-oracle coverage and
590
+ actual speculative serving acceptance remain open.
591
+
592
+ The row method also handles a zero-proposal round through the
593
+ same graph/commit interfaces. It processes the pending companion KV row without
594
+ sampling a draft or projecting draft logits, then retains the target hidden
595
+ row needed for later speculation. The old request producer skips this update;
596
+ the native control instead discards a depth-one proposal to obtain the same
597
+ processed state. The change is adopted after full-round bf16/KV4/KV8 checks at B=1/2/4 on
598
+ both Macs, companion checks that forbid draft sampling/head projection in the
599
+ zero round, both complete model-free suites and typechecks. The source manifest
600
+ and native logs live in the composition-baseline reports. This does not adopt
601
+ the separately measured request-producer replacement or B=1 hidden slice.
602
+
603
+ The provider exposes grouped companion state through the existing
604
+ checkpoint representation. `PreparedStateChange` lets target and draft owners
605
+ stage a join independently; `applyStateChanges` publishes both only after all
606
+ preparation succeeds, then releases their old state. A failed preparation
607
+ leaves active membership intact. Publication performs no graph construction or
608
+ validation, and adds no operation to the decode round. Focused checks cover
609
+ failed joins, cleanup errors and released allocations. Full bf16/KV4/KV8
610
+ rounds through coordinated admission and both complete suites pass on both
611
+ Macs. This interface is adopted; serving must now use it before exposing a new
612
+ request to scheduling. Source identity and checks live in the
613
+ composition-baseline `mtp-admission-manifest.json`. Companion ownership
614
+ details live in speculative-decoding §4.8.
615
+
616
+ The MTP numerical graph is separate from request lifecycle and sampling. It
617
+ uses the cache's positions for both singleton and batched forwards. Native
618
+ checks on both Macs preserve full-target state and subsequent output at B=2,
619
+ plus the companion graph's output and KV after unequal prefix retention.
620
+ Uniform acceptance uses the existing recurrent kernel; unequal acceptance uses
621
+ the row-length-aware state-only kernel. The separate uniform state-only replay
622
+ experiment did not establish a serving win, so it is not promoted.
623
+
624
+ The exact-token acceptance walk is now a portable request state machine.
625
+ It tracks accepted drafts, correction/bonus tokens, EOS, token budgets and
626
+ grammar termination. Sampling still owns score processing and request history.
627
+ A group can stop sampling a finished walk while advancing the others.
628
+
629
+ The MTP row method keeps draft-token arrays on device between steps and reads
630
+ one complete proposal block. Its sampler is injected; committed state can be
631
+ extracted, filtered and joined between rounds. Greedy and seeded sampling pass
632
+ at depths 1/2/4 on both Macs, including retirement, reorder, rejoin and reuse
633
+ after removing every row. B=1 preserves the existing producer's proposals and
634
+ state. At B=2, per-step host-read and device-chain controls match at the same B.
635
+ The M4's B=2 KV differs from independent B=1 controls; cross-B byte equality is
636
+ not claimed, and the initial result remains in the raw logs.
637
+
638
+ `advanceSpeculativeRows` composes injected drafting, target verification,
639
+ request samplers and state transactions into one committed round. It has no
640
+ admission, output delivery or persistence policy. Each sampler walks only its
641
+ accepted prefix, recording logprobs and respecting EOS, grammar and remaining
642
+ token budget. The existing request adapter and grouped method now use the same
643
+ `sampleSpeculativeRows` operation. Failure tests cover disposal of verification
644
+ tensors when sampling or target/draft retention fails; the caller discards the
645
+ failed method state.
646
+
647
+ Native complete-round tests pass on both Macs at B=1/2/4 and depths 1/2/4,
648
+ with seeded depth-two coverage. They compare proposals, target samples,
649
+ logprobs, every retained target layer and draft KV/hidden state across three
650
+ rounds. B=1 uses the prior request producer and manual accept loop as control;
651
+ B=2/4 use a manual same-B loop with host reads between draft steps. These are
652
+ internal refactor controls, not a replacement for the external numerical oracle.
653
+ The row method is adopted in the working tree but not yet selected by serving;
654
+ matched complete-request performance remains an acceptance gate.
655
+
656
+ The next isolated candidate replaces the request producer's draft/commit loop
657
+ with the shared row method at B=1. The frozen `mtp-shared-draft-reference`
658
+ checkout retains the previous producer for native continuation and paired
659
+ serving comparisons. A zero-proposal round must retain its pending companion
660
+ KV row before resuming speculation; the old producer skipped that state update.
661
+ Native continuation checks pass on both Macs. Both serving orders preserve
662
+ responses but do not establish equal-or-better performance; the candidate is
663
+ not adopted. Numbers and frozen source identities live in benchmarks.md.
664
+ The subsequent isolated change replaces the B=1 hidden gather with a slice of
665
+ the same verification context, retaining the shared method. Its first serving
666
+ block is essentially flat in decode, but the reverse-order block regresses;
667
+ neither the source replacement nor the slice specialization is adopted.
668
+
669
+ The isolated round interface also separates verification/sampling from state
670
+ commit. `advanceSpeculativeOutputs` composes that round with the portable
671
+ `deliverDraftOutputs` operation. Each consumer receives verified tokens through
672
+ `GenerationOutput`; its stop, failure or cancellation affects its own request.
673
+ Both target and draft state then retain the same published, processed prefix,
674
+ excluding unpublished EOS and unprocessed correction/bonus tokens. Failed or
675
+ cancelled state must be discarded. Continuing requests keep their complete
676
+ accepted prefix. Verification context is an opaque disposable resource,
677
+ allowing graph-specific hidden taps without putting model details into
678
+ scheduling. The output method is adopted in the working tree; it is not wired
679
+ to serving. Focused tests and the complete isolated model-free suite pass.
680
+ Final-source native controls pass on both Macs, including removal of stopped
681
+ requests and continued generation at B=4→2 and B=2→1. They retain the old
682
+ request producer as the independent B=1 control. Same-B survivor comparisons
683
+ check tokens, logprobs and every retained target/companion state. Logs and the
684
+ source manifest live in the composition-baseline reports.
685
+
686
+ These are method/state dependencies, not served batched MTP acceptance. Grouped
687
+ draft generation, verification and sampling still need to be integrated into
688
+ the shared executor, with actual concurrent serving, cancellation, persistence,
689
+ quantized-layout composition and matched performance checks.
690
+ Parallel tool execution in Pi only exercises inference batching if model
691
+ requests overlap; record admitted rows and arrival schedules in the evidence.
692
+ Josh's September 8 M1 Max and M4 Pro suites are the ordinary-execution
693
+ baseline, recorded in [benchmarks.md](../reference/benchmarks.md#direct-scheduler-b1-versus-serial--both-machines-2026-09-08).
694
+ Retain those results and target new measurements at changed implementations
695
+ and newly supported features. They justify consolidation, but do not prove
696
+ the missing feature combinations or removal of host overhead.
697
+
698
+ ### Remaining mechanisms
699
+
700
+ - **`--prompt-concurrency`**: not accepted by the CLI (mlx-lm's cap on
701
+ joiners per iteration; the drop-in alias slot). Today at most one
702
+ joiner prefills at a time.
703
+ - **Rotating-layer `extend`**: Gemma joins extend their full layers but
704
+ re-merge their sliding layers (`BatchedRotatingCache.merge` only).
705
+ - **Logprobs acceptance**: ordinary batch capture is implemented through
706
+ the sampler interface. Unit coverage checks interleaved pipelines,
707
+ processor-adjusted probabilities, top-only capture and abandoned outputs;
708
+ `tests/parity/batch-logprobs.test.ts` exercises real B=1/B=2 and retirement.
709
+ On M4 Pro, the packed Qwen campaign artifact passes native and HTTP
710
+ serial/B=1 identity, actual B=2 capture on/off token identity, row retirement,
711
+ selected/top-only wire shapes and SSE omission. This is an internal
712
+ composition check, not a same-B external oracle or performance comparison.
713
+ Explicit-seed gateway tests also reproduce serial/B=1 outputs and repeat
714
+ fixed B=2 and controlled B=3 joins on M1 Gemma e4b and M4 packed Qwen.
715
+ These keep the numerical composition fixed; they do not assert
716
+ arrival-independent output across different batch shapes.
717
+ Broader compositions and matched performance remain open.
718
+ Cached-request diagnostics found synchronous recurrent-boundary persistence
719
+ inside a RAM hit and a serial request-time durability flush. Both are
720
+ removed: the shared cache lends immutable state views, and SSD writes run
721
+ independently of requests, acquiring idle ownership without draining active
722
+ batches. Explicit flush and shutdown remain durability
723
+ boundaries. Native repeated-prefix and SSD-restart continuation checks pass
724
+ on M1 and M4, including the saved long-prefix size on M4. A stalled-write
725
+ regression verifies RAM reuse and snapshot lifetime through donor eviction.
726
+ Balanced Bun 1.4.2 serving preserves outputs and near-equal decode time,
727
+ but a small cached first-output gap remains. First admission now seeds the existing device-token register to overlap its readback
728
+ with the following forward. Same-B MiniCPM/Gemma oracle vectors, compiled
729
+ B=1, grammar, first-token stop/EOS/length, callback containment, seeded
730
+ sampling and native/HTTP logprobs pass. Both serving arm orders preserve
731
+ outputs and close most of the remaining gap; the change is adopted in the
732
+ working tree. Actual method selection now precedes scheduling: configuring
733
+ a draft does not force ordinary logprob or supported affine-KV fallbacks
734
+ onto the serial executor. Native B=2 fallback outputs match ordinary B=2
735
+ on M1 and M4; HTTP tests with a configured draft also reach B=2 without
736
+ opening the producer. HTTP correctness tests align request arrivals at the gateway
737
+ to require concurrent work; serving benchmarks retain natural arrivals.
738
+ Broader pressure acceptance remains open.
739
+ Measurements live in benchmarks.md.
740
+ The gateway forwards timing/rates, but native regression coverage found that
741
+ ordinary admission never set the first-token timestamp. That caused zero decode
742
+ time even for multi-token responses. The timestamp is now recorded at first-token
743
+ emission; repeated shared-prefix B=2 tests check positive decode time and rate.
744
+ Both execution lanes already use the same omitted-library-cap fallback. Broader
745
+ usage and stopping compositions remain in the matrix.
746
+ - **LoRA-group batching**: ordered adapter sets now select compatible groups
747
+ through a backend-owned execution context. The scheduling driver sees only
748
+ readiness. Context changes and prefix identity bind under the execution
749
+ lease; adapter replacement while queued cannot reuse stale KV. Gemma e4b
750
+ native B=1/B=2, mixed queues, callback failure, separate prefix reuse and
751
+ queued replacement pass on M1/M4. Both machines match the pinned same-B
752
+ oracle through retirement and late joins; KV4/logprobs composition also passes.
753
+ Both M4 serving arm orders preserve single-request output. A small cached
754
+ first-output gap keeps strict performance dominance open;
755
+ simultaneous different adapters within a single forward remain future work.
756
+ Shared adapter speculation is qualified by the grouped provider's
757
+ `supportsTargetAdapters` capability. Prompt lookup declares it because its
758
+ draft state contains token history, with no separate learned graph to adapt.
759
+ Target prefill and verification retain the existing group adapter context;
760
+ adapter revision namespaces protect both target and lookup checkpoints.
761
+ Unqualified combinations retain ordinary execution. Learned providers do not
762
+ declare this capability. The native `adapter-lookup.test.ts` gate covers
763
+ compatible rows, seeded logprobs, failure cleanup and adapter prefix isolation;
764
+ both-machine bf16/KV4/TurboQuant checks pass, including existing ordinary-adapter
765
+ regressions. HTTP tests verify actual generated token alignment, exact seeded
766
+ RAM/SSD restart choices and speculation metadata, adapter namespace isolation
767
+ and concurrent requests. Concatenating a second newline can change the rendered
768
+ token boundary; the negative tokenization control preserves that distinction.
769
+ Full suites/typechecks pass; eight M4 comparisons are complete. Ordinary source cost is
770
+ effectively flat. Enabled lookup has low draft acceptance and loses concurrent
771
+ throughput on this workload; stricter matching is under separate measurement.
772
+ The feature remains opt-in and does not qualify a new default.
773
+ Evidence: `reports/qwen38-closeout/composition-baseline/adapter-lookup/`.
774
+ - **Prompt-cache byte interplay**: mirror mlx-lm's
775
+ `trim_to(total − activeBatchBytes)` so the LRU and the live batch
776
+ share one ceiling. Derived default for `--kv-budget` (usable − weights
777
+ − prompt-cache cap − headroom) instead of unlimited-when-unset.
778
+ - **Long-context batched golden is complete** for the recorded Gemma 12B/e4b
779
+ same-B window-crossing, retirement and late-join cases on both machines.
780
+ See the ring-wrap gate above; it is not outstanding implementation work.
781
+ - **Unvalidated model cells** stay serial by the capability gate:
782
+ gemma2-family (`maskArray`) and sliding-window universal archs.
783
+ - **Paged KV**: ordinary shared storage is integrated and measured. Direct
784
+ paged attention, block sharing and quantized/speculative paging remain;
785
+ the state/cache interfaces own storage, independently of scheduling.
786
+ - **GLM MTP with concurrent requests**: the isolated shared provider passes
787
+ tiny-model and same-B Python state checks. Josh deferred final real-artifact
788
+ testing; see the GLM section below.
789
+ - **Grammar jump-forward** (`MLX_BUN_GRAMMAR_JUMP`) is serial-only;
790
+ `#stepGrammar` does not jump. Native XGrammar-2 via TVM-FFI has a
791
+ B≫8 trigger.
792
+ - **Device-side step chaining (depth-k pipelining)**: serial-decode
793
+ work, orthogonal to the scheduler; the matrix's serial baselines show
794
+ its headroom.
795
+ - **Batch-invariant kernels** (identical numerics regardless of B): Lab
796
+ research item; would restore determinism under load for free.
797
+ - **Qwen MTP with concurrent requests is integrated**: variable accepted
798
+ lengths, method-owned state alignment, shared sampling and generated RAM/SSD
799
+ reuse have passed the controls recorded below. Further work must identify a
800
+ specific unsupported composition or regression.
801
+ - **Stale source comments**: `batch-scheduler.ts` header still says
802
+ joins re-merge (extend landed 2026-07-04); `generation-gateway.ts`
803
+ header and server-config.md say merged rows are never re-put
804
+ (`#extractAndPut` now extracts merged rows ≥256 prompt tokens).
805
+
806
+ ## 9. Measured numbers (labels required)
807
+
808
+ - oMLX head-to-head, **M1 Max 32 GB, loaded machine, 2026-07-02**, 4
809
+ concurrent × 128 tok, median of 3, wall-clock aggregate tok/s, oMLX
810
+ 0.4.5.dev1 on the same OptiQ snapshots: MiniCPM5-1B 339.0 vs mlx-bun
811
+ `--batch 4` 345.4 (mean TTFT 397 vs 119 ms); gemma-4-e4b 89.8 vs 87.1;
812
+ Qwen3.5-4B 100.9 vs 99.5 (TTFT 848 vs 368 ms). Before P5/logits
813
+ processors landed the same day, oMLX led 1.46–1.89× and our TTFT
814
+ queue-inflated to 1.1–3.8 s.
815
+ - Batch-lane B=1 gap before Phase 2, **cpm5, M1 Max, 2026-07-05**: ~149
816
+ tok/s vs 267 serial (1.8×). mlx-lm's own `BatchGenerator` at B=1:
817
+ 256.5 vs 264.6 for `stream_generate` (3.3% tax) — the unified design
818
+ was proven achievable before the gap was closed.
819
+ - After Phase 3.2, **2026-07-05 paired A/B**: unified B=1 decode ratios
820
+ 0.992–0.996 vs strict serial, byte-identical output (the default-flip
821
+ evidence).
822
+ - Batched mixed-KV, **cpm5, 2026-07-05**: `--batch 2 --kv-quant config`
823
+ 400 tok @ 240 tok/s aggregate; B=1 bit-exact vs the optiq golden;
824
+ padded-row KL within the 5e-2 envelope (bf16 same-harness ~9e-3).
825
+ Gemma 12B rotating-quant B=2 join: unpadded row KL 0 every step, padded
826
+ ≤4e-3.
827
+
828
+ ## 10. History
829
+
830
+ - 2026-06-13 — Phase 18 opened; batched padded prefill found already
831
+ proven in the training path (`buildBatchedPadMask`, `BatchedMaskCache`).
832
+ - 2026-06-14 — `--batch N` flag (designed as `--slots`; `--decode-concurrency`
833
+ alias); `BatchedDecodeMaskCache` + decode-parity harness; dynamic-B
834
+ `mergeKVRows`/`filterKVRows` vs mlx-lm dynamic golden;
835
+ `BatchedRotatingCache`; `BatchScheduler` + `GenerationGateway` wired;
836
+ L1 row green — CPM, Gemma 12B, e4b, 26B bit-exact vs mlx-lm B=2.
837
+ - 2026-07-01 — v2 hotfix bundle: slice-before-head, cache-capability
838
+ gate, one lock domain (`runExclusive`); per-row containment +
839
+ drain-on-serial-waiter; engine hygiene (pipelined decode, clearCache/256,
840
+ chunked interleaved admit).
841
+ - 2026-07-02 — SSM batched path (Qwen3.5, token-exact vs mlx-lm B=2);
842
+ per-row logits processors; `MLX_BUN_LANE_DEBUG`; serial-lane macrotask
843
+ hop; oMLX burst port refuted; `--model` override honored by serve/bench.
844
+ - 2026-07-03 — grammar B1 per-row matchers + churn gates; `--kv-budget`
845
+ admission + `/stats.batch` fields; Tier-0 universal per-row RoPE fix
846
+ (Llama token-exact vs mlx-lm B=2); `bench-matrix.ts features`.
847
+ - 2026-07-04 — extend-join for full-attention layers (own oracle);
848
+ vectorized homogeneous greedy sampling (bit-equal A/B).
849
+ - 2026-07-05 — Phase 2: B=1 host tax closed (`toIntTokens`, unpadded
850
+ fast path); Phase 3.1: batched per-layer quantized KV (full layers);
851
+ Phase 3.2: adopt-don't-copy, compiled decode at B=1, prompt-cache
852
+ take/put on the batch lane; milestone 2: batched rotating-quant (every
853
+ shipped `kv_config` batches); `--batch` default 1→8; concurrency-is-the-
854
+ batch-size decision.
855
+ - 2026-07-06 — boundary-snapshot put on the batch lane (12B ctx repeat
856
+ 84 s → serial-class).
857
+ - 2026-07-07 — rot-quant join `temporalView` leak fixed.
858
+ - 2026-08-21 — Serving consolidation: immutable `GenerationPlacement`
859
+ (`serial` | `continuous`); resolved `KvScheme` authoritative through
860
+ placement, conversion, and projection; GLM `MLACache` as a
861
+ `BatchableCache`.
862
+ - 2026-08-22 — rotating-join regression from 443f333 fixed (capability-only
863
+ merge routing); `tests/batch-rotating-join.test.ts`.
864
+
865
+ ### Shared KV maintenance
866
+
867
+ `createKvMaintenance` resolves the KV policy once when composing an execution
868
+ session. The ordinary generator, speculative prefill and the MLX execution group call
869
+ this same backend operation at their populated-cache boundaries. Per-layer lookup
870
+ is no longer rebuilt on every serial decode step. Streaming conversion still
871
+ materializes and releases each layer before converting the next. The existing
872
+ single-call `maybeQuantizeKv` export remains a compatibility entry point.
873
+ Server uniform KV4/KV8 now uses the same operation and existing quantized
874
+ batch layouts. Capability discovery probes actual cache-owning layers, including
875
+ Gemma's shorter donor prefix; it does not infer a separate scheduler from the
876
+ quantization label. M1 Gemma e4b and M4 packed Qwen native tests cover seeded
877
+ sampling with logprobs, exact serial/B=1 output, and repeatable controlled
878
+ B=2/B=3 joins. Matched serving performance and same-B external oracle coverage
879
+ remain open. Start-zero TurboQuant now has encoded row-layout support for
880
+ ordinary and Qwen MTP methods. Positive ordinary TurboQuant thresholds now
881
+ have merged-row transitions; Qwen delayed affine conversion is also adopted.
882
+
883
+ Prefill policy is now shared too. The execution group captures its chunk
884
+ default once; ordinary and speculative preparation use it, and both honor a
885
+ library request override. The gateway forwards that override instead of
886
+ silently using the group default for ordinary requests. The scheduler does
887
+ not choose a numerical prefill chunk. M1 native seeded-sampling and MTP
888
+ RAM/SSD checks pass on both Macs. The first matched M4 short control preserves
889
+ all measured outputs with effectively unchanged timing; long-context checks
890
+ and balanced acceptance remain open.
891
+
892
+ Delayed ordinary TurboQuant conversion is now adopted after the mixed-phase,
893
+ prefix-reuse, fused/unfused and composed MTP/generated-output checks pass on
894
+ M4 as well as the original M1 family gates. The cache publishes its actual
895
+ irreversible conversion boundary through clone, extraction, RAM lookup and
896
+ SSD metadata; earlier plain donors remain usable. The first M4 hygiene run
897
+ found only a stale generated docs map. After regeneration, full hygiene passes
898
+ on both machines without changing tested code. The main full suite and typecheck
899
+ pass for ordinary conversion. Delayed shared MTP is also adopted after native
900
+ state/continuation, seeded B1/B3/late-admission, SSD and complete-suite gates on
901
+ both machines. Conversion follows retained state after rollback and preserves
902
+ physical row positions; the codec and kernels are unchanged. The main full suite, typecheck and hygiene pass. Qwen delayed affine is also adopted; these correctness gates establish
903
+ no new TQ default or performance gain.
904
+
905
+ ### Variable proposal lengths
906
+
907
+ The shared method accepts 0..depth proposals independently per
908
+ request. It sizes the rectangular target graph from the longest returned
909
+ proposal, right-pads shorter rows, and samples only through each row's own
910
+ bonus position. State commit retains the pending token and accepted inputs;
911
+ no padding enters a reusable checkpoint. If every proposal is empty, the
912
+ method runs one target step even when the configured draft depth is positive.
913
+ Scheduling does not choose proposal lengths or manage their acceptance.
914
+
915
+ The M1 Qwen control covers four rows with 0/1/2/3 proposals, greedy and seeded
916
+ sampling, independent retirement and re-admission. Both bf16 and KV4 preserve
917
+ tokens, logprobs, target state and companion state against explicit same-B
918
+ controls that use a different right-padding token. These are internal
919
+ composition checks, not an external oracle or a performance claim. Unit tests
920
+ also prove that padding consumes no sampler steps and terminal consumers
921
+ retain only delivered inputs. The complete candidate suite and typecheck pass.
922
+ M4 uniform/variable native controls and the complete suite also pass. The
923
+ first fixed-settings serving comparison was slower despite identical outputs
924
+ and speculation statistics. Alternating frozen-source controls do not reproduce
925
+ that slowdown and retain identical work. The three-file verifier patch is now
926
+ adopted; the main complete suite and typecheck pass. Provider migrations must retain their own
927
+ prefill/checkpoint conventions; this verifier alone does not change eligibility.
928
+ Evidence: `reports/qwen38-closeout/composition-baseline/variable-proposals/`.
929
+
930
+ ### Prompt lookup through the shared method
931
+
932
+ The shared executor now implements prompt lookup as a provider-owned draft
933
+ row group. Each row holds committed token history, proposes repeated spans,
934
+ and appends only inputs retained by verification. It uses the same variable
935
+ proposal verifier, sampler, target state, membership transactions and cache
936
+ publication as MTP. Scheduling has no prompt-lookup branch.
937
+
938
+ Preparation explicitly supplies processed-token coverage to the provider.
939
+ For tail-split lookup, the last prompt token becomes the first pending input;
940
+ completed-output keys include it exactly once. Integer history tensors use the
941
+ existing companion-checkpoint format and byte accounting. The generic tensor
942
+ reader now supports int32 for streamed SSD persistence; no separate prompt
943
+ lookup writer or storage queue is introduced.
944
+
945
+ M1 Qwen bf16/KV4/KV8/fused-TQ serving checks pass B1 controls, real B4 verification, independent
946
+ consumer stops/failures, cancellation, late joins, seeded logprobs and grammar.
947
+ All four schemes pass tool turns with thinking off/on, generated-prefix RAM reuse and fresh-PID
948
+ SSD restoration with identical responses, logprobs and acceptance. The initial
949
+ SSD flush failure exposed the missing int32 raw-byte reader; its regression
950
+ now passes. MTP3 serving regressions also pass in bf16/KV4/TQ after the shared
951
+ preparation changes. Unsplit-prefill serving and the complete model-free/SSD
952
+ suite and typecheck also pass. The same planned native/HTTP scheme matrix,
953
+ MTP regressions, unsplit-prefill and complete suite now pass on M4. An initial
954
+ M4 wrapper failure supplied CLI quantization value `0` instead of `off`; it
955
+ failed before inference, was retained, and the corrected wrapper resumed
956
+ without source changes. Fixed-MTP timing is effectively unchanged. Both lookup arm orders preserve
957
+ single-request outputs with small decode improvements and new cached-prefix
958
+ reuse. Concurrent outputs differ and observed shared throughput is lower, so
959
+ strict dominance is unproven. Default depth-10 KV4 serving checks now pass on
960
+ both Macs and exercise all ten proposal positions. Serving eligibility is
961
+ currently Qwen only. The seven-file patch is adopted; its concurrent
962
+ performance gap remains open, and serial deletion is not qualified.
963
+ Evidence: `reports/qwen38-closeout/composition-baseline/ngram-group/`.
964
+
965
+ A separate composed checkout combines the adopted variable verifier and ordinary
966
+ TQ cache with prompt lookup and delayed speculative TQ. The only production
967
+ hunk shared by the two candidate patches is the generic cache preparation hook
968
+ before state admission; the merged source is verified against both deltas.
969
+ M1 fused/unfused target/MTP and prompt-lookup native checks, threshold-crossing
970
+ SSD restart, full suite and typecheck pass. The lookup extension exercises the
971
+ same delayed-cache serving test by substituting its draft provider, including
972
+ mixed phases, seeded metadata, retirement and late joins. M4 composition now passes the same native/SSD gates, full suite, typecheck
973
+ and hygiene. Both production patches are now adopted in main, with exact
974
+ file identity against the tested composed source. Fixed-MTP composition
975
+ timing is effectively unchanged. Main full-suite, typecheck and hygiene checks pass.
976
+ Evidence: `reports/qwen38-closeout/composition-baseline/method-composed/`.
977
+
978
+ ### One-token prefill allocator maintenance candidate
979
+
980
+ A native first-output diagnostic found milliseconds of allocator cleanup after
981
+ a cached one-token target forward. A separate candidate retains allocator
982
+ buffers for single-token prefill work and still clears after larger chunks.
983
+ This is method-owned maintenance; the scheduler, sampler and cache-persistence
984
+ policy are unchanged. KV4 and delayed-TQ one-token suffix SSD continuations,
985
+ complete suites and typechecks pass on M1/M4. Alternating M4 fixed-MTP timing
986
+ preserves every request/output/usage record and consistently reduces cached
987
+ first-output latency. The first control has slower cold/decode timings, so
988
+ a broad throughput gain is not established. The 8k M1 allocation comparison
989
+ retains identical output/acceptance, nearly equal peak live allocation and a
990
+ larger final allocator pool. Matched serial/shared timing still shows a cached-latency regression; the
991
+ candidate remains isolated and long-context/SSD pressure remains open.
992
+ Evidence: `reports/qwen38-closeout/composition-baseline/single-token-maintenance/`.
993
+
994
+ ### Delayed affine conversion
995
+
996
+ The adopted delayed-affine implementation extracts `TransitioningKvRows` from the
997
+ existing TurboQuant adapter. It owns committed positions, conversion, physical
998
+ padding, merge/extract and speculative rollback. Codec-specific adapters retain
999
+ their attention arithmetic. The affine adapter uses the storage-owned
1000
+ `KvAttentionState` port for mixed-precision rows and the existing packed batch
1001
+ attention once every row converts. Model binding declares support once and
1002
+ passes that capability to request planning and executor construction.
1003
+
1004
+ Both Macs pass KV4/KV8 target rollback and continuation, ordinary serial/B1
1005
+ seeded token/logprob equality, MTP B1/B3 retirement and late admission, plus SSD
1006
+ restoration with conversion in prefill or decode. Fused/unfused TQ native and
1007
+ serving regressions pass. Final full suites and typechecks pass on both Macs.
1008
+ Positive affine thresholds and non-default group sizes have distinct cache
1009
+ identities; captured conversion floors survive extraction and SSD restoration.
1010
+ The original constructor-capability failure is retained in the raw evidence.
1011
+
1012
+ Alternating M4 fixed-settings comparisons preserve every request output, usage
1013
+ and finish reason with effectively unchanged timing. The reviewed implementation
1014
+ is adopted in the unreleased working tree. Eligibility currently covers Qwen;
1015
+ other model attention ports remain open. Main full-suite/typecheck/hygiene checks pass. Prompt lookup also passes KV4/KV8
1016
+ B1/B3 seeded continuation, actual proposals, retirement and late admission on
1017
+ both Macs without scheduler or sampler changes.
1018
+ No serial deletion or performance gain is established by these checks. Evidence is under
1019
+ `reports/qwen38-closeout/composition-baseline/delayed-affine/`.
1020
+
1021
+ The adopted attention port preserves Gemma's donor/shared-layer contract: a donor
1022
+ calls `Cache.attentionState.appendAndFetch` once, then every sharing layer calls
1023
+ `KvAttentionView.attend` on the same captured state. The view owns tensor handles
1024
+ and captured row alignment independently of later cache mutation or disposal.
1025
+ The model retains pre-write RoPE positions through all consumers and releases
1026
+ the view once at the end of the pass. Qwen and MiniCPM use one consumer per
1027
+ append. Existing training attention paths remain separate from these inference
1028
+ cache views. Scheduling and sampling do not manage attention-view lifetimes.
1029
+
1030
+
1031
+ The captured-view implementation exposes `appendAndFetch` and a
1032
+ reusable `KvAttentionView.attend`. Qwen consumes one view per append; Gemma can
1033
+ retain it for sharing layers. KV4/KV8 unit checks preserve two distinct queries
1034
+ after further appends, conversion, row reordering and disposal of the original
1035
+ cache. Full-model Gemma 12B logits and active cache state match the existing
1036
+ consumer path. The initial state test compared unused allocator capacity;
1037
+ the corrected check compares every processed value, and keeps the failed raw
1038
+ comparison. Gemma 12B/e4b and MiniCPM KV4/KV8 pass on both machines. The e4b checks
1039
+ exercise multiple reads per append. The M4 full suite passes. The initial M1
1040
+ suite used a fallback DeltaNet fixture because this worktree lacked the
1041
+ already-qualified machine golden; copying that unchanged fixture makes the
1042
+ focused test pass. Qwen KV4/KV8 rollback, MTP and prompt lookup regressions plus the corrected
1043
+ M1 suite pass. Both-machine typechecks and KV4/KV8 SSD continuations pass.
1044
+ Alternating M4 decode/cached/throughput timing is effectively unchanged. The
1045
+ final control end check flags a small CPU daemon; the raw result is retained
1046
+ and assessed under the campaign background-CPU policy. Startup remains less
1047
+ stable and is not qualified by this screen. The reviewed change is adopted;
1048
+ main integration suite, typecheck and hygiene pass.
1049
+ Gateway eligibility is unchanged until the broader storage combinations pass.
1050
+ Candidate evidence: `reports/qwen38-closeout/composition-baseline/kv-attention-view/`.
1051
+
1052
+
1053
+ Delayed rotating affine storage remains a separate missing composition. Its
1054
+ logical row positions and physical ring columns are different state: conversion
1055
+ or packing must preserve the mask/column relationship. Existing
1056
+ `BatchedRotatingQuantCache` has encoded row merging; the shared
1057
+ `BatchedRotatingState` owns ring positions. Reuse those interfaces rather than
1058
+ introducing a scheduler quantization branch. Qualify unequal offsets, conversion
1059
+ before/after wrap, retirement, late admission, shared Gemma consumers and
1060
+ persisted conversion floors before widening the gateway capability.
1061
+
1062
+ The first M4 benchmark wrapper stopped before inference because its parent
1063
+ shell inherited an older oracle path. The Qwen correctness child had already
1064
+ passed with the explicit pinned environment. Only the unstarted benchmark was
1065
+ resumed, with that same explicit environment; the completed four-arm comparison
1066
+ and failed launch log are retained.
1067
+
1068
+
1069
+ ### Multi-token rotating storage
1070
+
1071
+ The adopted rotating-block implementation removes the one-token-only restriction
1072
+ from both bf16 and affine batched rings. `RowStorage` supplies encoded tensor
1073
+ operations; `appendRotatingStorage` retains chronological history without
1074
+ unpacking affine tensors; `BatchedRotatingState.commitConcat` owns padding,
1075
+ logical positions and the physical write head. One-token updates retain their
1076
+ existing in-place implementation. The port follows the pinned mlx-lm
1077
+ `BatchRotatingKVCache._update_concat` for left-padded inputs.
1078
+
1079
+ A block temporarily holds up to window + block length - 1 columns so every
1080
+ query retains its full context. Extraction preserves that active block, while
1081
+ merge views select the newest window. Dropping the temporary suffix during
1082
+ extraction would corrupt subsequent continuation. No scheduler or sampler
1083
+ changes are needed for these storage operations.
1084
+
1085
+ The same-B live oracle gate covers B=1/2/3, unequal left padding, mixed
1086
+ one-token and block writes, repeated wrap, overshoot, row reordering,
1087
+ retirement, late admission and extracted state. bf16 values and masks match
1088
+ mlx-lm exactly on both Macs. KV4/KV8 share its geometry and preserve every
1089
+ extracted affine component against the native quantizer. Existing Gemma 12B
1090
+ ring-wrap serving still matches 48 full logit vectors per machine. Both-machine model-free/SSD suites and typechecks pass. The reviewed six-file
1091
+ change is adopted; main typecheck/hygiene pass. No performance or full
1092
+ batched-prefill claim is made.
1093
+ Right-padded prefill finalization and speculative rollback of overwritten
1094
+ ring entries remain part of the shared-execution work. Delayed rotating
1095
+ precision conversion is implemented below.
1096
+ Evidence: `reports/qwen38-closeout/composition-baseline/rotating-block/`.
1097
+
1098
+
1099
+ ### Delayed rotating affine conversion
1100
+
1101
+ The adopted implementation uses `TransitioningKvRows` for ordinary membership,
1102
+ retirement, checkpoints and precision changes. `SpeculativeTransitioningKvRows`
1103
+ adds rollback for layouts that implement it; ordinary rotating storage does not
1104
+ advertise speculative rollback. `TransitioningKvPositions` supplies geometry
1105
+ without a codec. Full-attention positions retain the existing `KvTensorRows`
1106
+ operations, and rotating positions use `BatchedRotatingState`.
1107
+
1108
+ An aligned row keeps the group's physical ring columns while exposing its own
1109
+ logical position to the shared affine-maintenance policy. Conversion quantizes
1110
+ the existing buffers without changing their physical layout, write head or
1111
+ padding. Once every row is converted, the encoded planes concatenate
1112
+ without reordering columns. `captureKvAttention` provides the same owned read
1113
+ view for either representation; Gemma's donor and sharing layers can keep
1114
+ reading it after the cache advances. No scheduling or sampling branch is added.
1115
+
1116
+ Membership changes normalize borrowed solo rows through owned clones. Empty
1117
+ plain and affine cache clones now remain valid empty caches, enabling a group
1118
+ to be assembled before its first input. Extraction preserves each row's
1119
+ conversion floor in the existing persistence schema. The implementation enables
1120
+ positive affine thresholds for ordinary Gemma execution; other speculative
1121
+ providers, rotating rollback and true batched-prefill scheduling remain open.
1122
+
1123
+ Gemma 12B and e4b KV4/KV8 pass exact hidden/logit and active-state checks before
1124
+ and after the real sliding window on both Macs. Serialized output snapshots
1125
+ preserve token IDs, state and conversion floors; restored snapshots continue
1126
+ identically to RAM snapshots in the same process. These are not fresh-process
1127
+ or long Kanban acceptance. Seeded gateway checks match serial at B1 and repeat
1128
+ B2/B3 admission schedules exactly on both machines. The model-free checks cover
1129
+ mixed precision, empty rows, conversion before/after wrap, block updates,
1130
+ retirement, late joins and attention reads after owner disposal. Final complete suites and typechecks pass on both Macs, as do Qwen affine
1131
+ MTP/lookup and fused/unfused TurboQuant regressions. Artifact-defined per-layer
1132
+ Gemma compression also passes both-machine hidden/logit/state and persistence
1133
+ checks. Four alternating M4 default Qwen benchmark arms preserve all responses
1134
+ and show effectively flat decode, cached latency and concurrent throughput;
1135
+ startup equivalence remains open. The reviewed fifteen-file implementation is
1136
+ adopted in the unreleased working tree. Main integration suite, typecheck and hygiene pass.
1137
+ Measurements and source identities live in benchmarks.md.
1138
+ Evidence: `reports/qwen38-closeout/composition-baseline/delayed-rotating/`.
1139
+
1140
+
1141
+ ### Singleton admission copy candidate
1142
+
1143
+ An isolated candidate replaces singleton `copyOf` calls with `contiguous` in
1144
+ attention and recurrent admission. The proposed GPU-copy saving is not
1145
+ supported: the M4 MLX 0.32.2 probe shows both operations share existing storage.
1146
+ This agrees with the existing explanation in `mlx/materialize.ts`; a method
1147
+ name is not evidence of a physical copy. The evaluated synthetic Qwen-shaped
1148
+ probe shows no additional GPU allocation for either version. Also, shared MTP
1149
+ publishes its first token before the singleton group merge, so that merge is
1150
+ not on its first-output critical path.
1151
+
1152
+ Both-machine ownership tests cover independent updates, donor immutability,
1153
+ self-merge, spare capacity and source/group disposal. Typechecks pass. The M4
1154
+ bf16/KV4/KV8 round matrix, fused/unfused TQ, delayed affine/TQ serving and full
1155
+ suite pass. M1 native/serving and the complete suite also pass. The alternating
1156
+ M4 HTTP comparison preserves all responses and records lower cached latency
1157
+ in both candidate arms. The physical-copy explanation is disproven, so further
1158
+ attribution/repetition remains before adoption. Startup equivalence and direct
1159
+ serial dominance are still unproven. Extraction and larger group concatenation
1160
+ are unchanged. Timing belongs in benchmarks.md.
1161
+ Evidence: `reports/qwen38-closeout/composition-baseline/singleton-state/`.
1162
+
1163
+
1164
+ ### Recurrent output-row retention
1165
+
1166
+ `SSMCache.extractRow` used `copyOf` with a comment promising compact storage.
1167
+ The current MLX implementation aliases that storage: after releasing a B>1
1168
+ source group, one retained row still owns the entire group's backing buffers.
1169
+ The M1 allocation probe reproduces this for B=2/4/8. This is separate from
1170
+ singleton admission, where sharing a complete allocation is appropriate.
1171
+
1172
+ The adopted extraction fix uses the existing `materializeCopy` port for
1173
+ a proper subset of a batch and retains a separate array handle for B1. Row
1174
+ values and logical offsets do not change. The allocation regression fails on
1175
+ the unchanged control and passes on the candidate, including exact values and
1176
+ disposal on both Macs. Both-machine KV4 MTP round matrices, bf16/KV4/TQ generated
1177
+ RAM/SSD continuations, complete suites and typechecks pass. Four alternating
1178
+ M4 HTTP arms preserve all responses and support integration without a broad
1179
+ speed claim. The source and regression test are adopted; main integration
1180
+ suite and typecheck pass. Long-context pressure acceptance remains open.
1181
+ No scheduler, sampler, persistence queue or quantization policy changes.
1182
+ Evidence: `reports/qwen38-closeout/composition-baseline/ssm-extraction/`.
1183
+
1184
+
1185
+ ### Padded prefill cache interfaces
1186
+
1187
+ The adopted cache-interface portion adds `PaddedPrefillCache`. Its frozen
1188
+ validation checkout is `padded-prefill-validation`.
1189
+ Preparation receives each prompt's lengths and left/right padding. Finalization
1190
+ removes trailing padding from logical coverage and aligns retained state before
1191
+ decode or checkpoint publication. The cache owns these operations; neither the
1192
+ scheduler nor a quantization codec chooses token positions.
1193
+
1194
+ `FullPrefillPadding` applies one row-roll index to arbitrary full-cache tensor
1195
+ planes. Plain KV, affine KV4/KV8 and TurboQuant reuse it without dequantizing or
1196
+ requantizing the stored values. Rotating storage instead moves completed rows'
1197
+ padding ahead of valid history before each block trims its window. Both plain
1198
+ and affine rings share that position logic and preserve physical alignment.
1199
+
1200
+ One-token padded chunks use the existing rotating concat operation and block
1201
+ mask. MLX-LM's public dispatch rejects this case before finalization. The gate
1202
+ therefore invokes its existing `_update_concat` and takes the first query from
1203
+ its two-token block mask for this extension. Ordinary decode retains the ring
1204
+ write. This is an extension checked against oracle operations, not a claim that
1205
+ the upstream public dispatcher supports the same call.
1206
+
1207
+ The live geometry gate checks B1/B2/B3, left/right padding, an existing prefix,
1208
+ window overflow, zero-length prefixes, one-token chunks, finalization, extraction
1209
+ and continuation.
1210
+ Plain tensors, masks and positions match the reference. Affine and TurboQuant
1211
+ retained bytes match their existing codecs applied to oracle-retained rows.
1212
+ MiniCPM5 and Gemma 12B/e4b also match full same-B logit vectors after padded
1213
+ prefill and three continuations with whole-prompt, 5/5/1 and singleton chunks
1214
+ on both Macs. Complete suites and typechecks pass on both machines. Qwen KV4
1215
+ MTP rounds, both TurboQuant decode settings and generated-prefix bf16/KV4/TQ
1216
+ RAM/SSD continuations also pass on both Macs. These regression runs use ordinary
1217
+ unpadded preparation; they do not qualify Qwen padded prefill.
1218
+
1219
+ The cache-interface changes are adopted; main integration suite and typecheck pass.
1220
+ Qwen recurrent-state masking, delayed rotating transitions and ordinary
1221
+ preparation cohorts are adopted below, including cancellation/checkpoints.
1222
+ MTP preparation and full-cache delayed padding still keep S1a open.
1223
+ No serving performance result or default change is claimed for this candidate.
1224
+ Evidence: `reports/qwen38-closeout/composition-baseline/padded-prefill/`.
1225
+
1226
+ Full-cache finalization now retains absolute valid ends, as rotating storage
1227
+ already does. It removes only padding actually processed. This lets a cohort
1228
+ finish early after a longer request leaves, or finalize a partial prefix before
1229
+ the original planned width. Ending before any right padding leaves tensors and
1230
+ positions untouched. Filtering and reordering requests also filters those ends.
1231
+ The unchanged control subtracts unprocessed padding and produces negative
1232
+ coverage in the shortened-cohort regression; its failure log is retained.
1233
+
1234
+ Both-machine geometry checks cover completed and shortened B1/B2/B3 prefills,
1235
+ cached prefixes, leading padding, request retirement/reordering before
1236
+ finalization, extraction and three subsequent decode steps. The reference full
1237
+ cache receives the effective padding of the processed prefix; Bun receives the
1238
+ original plan. Survivor checks select the corresponding oracle rows without
1239
+ changing their physical columns. Plain state/masks match exactly; affine and
1240
+ TurboQuant retained bytes match encoding those oracle rows. Host lifecycle
1241
+ tests also cover finalization before any work and preparation after a partial
1242
+ checkpoint. Model-free suites, SSD tests and typechecks pass on both Macs.
1243
+ This fixes cache preparation semantics; serving cohorts and their cancellation
1244
+ handling are still unwired. No speed or default change is claimed.
1245
+ Evidence: `reports/qwen38-closeout/composition-baseline/padded-partial/`.
1246
+
1247
+
1248
+ ### Recurrent-state padding
1249
+
1250
+ The adopted recurrent-padding change extends the same prefill port to
1251
+ Qwen's recurrent cache. `SsmPrefillPadding` tracks remaining real-token lengths
1252
+ and leading padding. Each cache advances coverage by real tokens only and
1253
+ filters that metadata with its rows. Finalization clears preparation state.
1254
+ Uniform prompts keep the unmasked kernel and ordinary convolution tail.
1255
+
1256
+ The model constructs one mask for all recurrent layers. DeltaNet zeroes padded
1257
+ QKV inputs before convolution, selects each row's convolution tail by its
1258
+ remaining length, and uses the pinned oracle's masked recurrence. Masked steps
1259
+ leave recurrent state unchanged. The existing kernel body is shared between
1260
+ masked and unmasked specializations; scheduling does not choose or manipulate
1261
+ these state operations. The convolution-tail selection already used by
1262
+ variable speculative rollback is reused here.
1263
+
1264
+ The primitive gate compares full output and state hashes with the pinned GPU
1265
+ oracle at the 27B head geometry. It includes B1/B3, fully masked rows, singleton
1266
+ steps and an unmasked continuation. The unchanged control fails this gate.
1267
+ The small Qwen model passes padded full-model checks on M1. Both Macs pass
1268
+ same-B 27B full-logit and convolution/recurrent-state checks for unequal prompts,
1269
+ batch-of-one and uniform B3, with whole-prompt, singleton-tail and all-singleton
1270
+ chunks. The machines use their locally installed affine 27B artifacts; these
1271
+ are per-machine oracle comparisons, not an engine or artifact speed comparison.
1272
+
1273
+ The isolated checkout initially retained three obsolete HEAD files and lacked
1274
+ its machine-specific golden blobs. Those setup problems were corrected against
1275
+ the validated base; no golden was regenerated. Initial scalar creation also
1276
+ needed the existing typed-array constructor. Failure logs remain with the
1277
+ candidate evidence. Both-machine MTP/TQ/generated RAM/SSD regressions, full
1278
+ suites and typechecks pass. Four alternating M4 HTTP arms preserve all responses
1279
+ with roughly flat decode/cached/throughput timing. Startup equivalence remains
1280
+ open. The change is adopted; main integration suite and typecheck pass.
1281
+ Delayed-codec preparation and actual serving cohorts remain open.
1282
+ Evidence: `reports/qwen38-closeout/composition-baseline/padded-ssm/`.
1283
+
1284
+ ### Delayed rotating compression during padded prefill
1285
+
1286
+ Delayed rotating KV4/KV8 now implements the same preparation/finalization port.
1287
+ Each physical row receives its own token lengths and the common batch layout.
1288
+ All rows use the padded block operation when any sibling has right padding,
1289
+ including a singleton chunk in a row without padding. An initial implementation
1290
+ lost alignment by letting that row switch to a ring write while its sibling
1291
+ continued block writes. The retained failing regression exposed incorrect cache
1292
+ bytes when those rows later packed together.
1293
+
1294
+ The shared position snapshot now combines every row's pending valid end when
1295
+ format conversion packs a group. Maintenance uses valid token counts, so a
1296
+ padded suffix cannot cross a precision threshold or inflate the earliest
1297
+ reusable prefix. Physical write positions remain separate. Retirement and
1298
+ reordering preserve pending preparation state before and after packing.
1299
+
1300
+ Both-machine geometry gates include B1/B2/B3/B4/B8, KV4/KV8, multiple transition
1301
+ thresholds, cached history, ring overflow, singleton chunks, early finalization
1302
+ and survivor continuation. Cache geometry comes from the pinned MLX-LM oracle.
1303
+ Encoded bytes match the existing codec applied to oracle-retained rows; exact
1304
+ attention checks use those oracle tensors and masks with the native operation
1305
+ at the same B as each mixed or packed representation. Gemma 12B/e4b also pass
1306
+ same-B padded model logits through the delayed layout before conversion.
1307
+ The latter is plain-KV model coverage, not a quantized padded full-model oracle.
1308
+ Ordinary Gemma conversion and RAM/SSD continuations, complete model-free suites
1309
+ and typechecks pass on both Macs.
1310
+
1311
+ The change is adopted in the unreleased working tree. Four alternating Gemma
1312
+ M4 benchmark arms preserve all responses; the small timing differences are
1313
+ recorded in benchmarks.md and establish no speed win or serial dominance.
1314
+ Full-attention delayed affine/TurboQuant padding remains open. Ordinary serving
1315
+ now uses the real-token cohort described below.
1316
+
1317
+ Ordinary serving uses a method-owned cohort of equal-length
1318
+ chunks of real tokens. It admits later arrivals between forwards. Each request
1319
+ retains its planned maintenance, checkpoint and tail boundaries when a peer
1320
+ causes an earlier split. Scheduling chooses membership; the preparation method
1321
+ runs shared forwards; cache layouts merge positions and retain precision policy.
1322
+ The same target driver now serves MTP and prompt lookup; their providers own
1323
+ companion prefill state through the separate method interface.
1324
+
1325
+ The first lifecycle runs exposed an empty-state native evaluation and missing
1326
+ initialization when a cold recurrent row joins populated state. Empty caches
1327
+ submit no evaluation. The recurrent layout initializes the new row's state
1328
+ with zeros, matching the pinned reference's ArraysCache.merge behavior.
1329
+ Qwen 27B on the M4 then exposed a logit mismatch immediately after retirement.
1330
+ Full-attention storage now removes padding shared by all survivors, matching
1331
+ BatchKVCache.filter. Affine planes inherit that operation; TurboQuant's tensor
1332
+ storage applies it too. These are cache membership operations, independent of
1333
+ scheduler, sampler and inference method.
1334
+
1335
+ Focused tests cover staggered B1→B2→B3 joins, real-token identity, native retained
1336
+ bytes with immediate/delayed affine and TurboQuant conversion, checkpoint
1337
+ boundaries, cancellation and completion callback failure. A scheduler-level test
1338
+ submits a request during the first forward and observes it in the next chunk.
1339
+ The model gate compares every full logit vector to the pinned same-B reference
1340
+ through joins and retirement, then compares each extracted row's continuation.
1341
+ It passes Qwen 27B and Gemma 12B on both Macs; the M1 also passes Qwen 0.8B.
1342
+ Padded cache geometry/encoded-state/attention checks pass on both machines.
1343
+ The padded oracle now performs retirement itself; stock filter omits pending
1344
+ prefill metadata, so the fixture selects its right-padding/length metadata with
1345
+ the same row indices before calling the existing finalize operation.
1346
+
1347
+ MTP delayed affine/TurboQuant regressions pass on both Macs. Four-arm M4
1348
+ timing and response differences are recorded in benchmarks.md.
1349
+ An added token-zero check then found that the M4 Qwen 27B sampler receives
1350
+ different logits when each completed row projects its hidden state separately.
1351
+ The shared forward's full vectors still match; the M=1 projection changes its
1352
+ arithmetic. A follow-up candidate projects the batch before selecting logits
1353
+ for each sampler. Its focused lifecycle checks and the M4 Qwen 27B/Gemma 12B full-vector,
1354
+ sampler-logit and continuation checks pass on both Macs. Full model-free suites
1355
+ and typechecks pass on both; all 829 nonignored source/test files match across
1356
+ the isolated copies. Fresh four-arm M4 timing is effectively flat. The corrected
1357
+ cohort is adopted in the unreleased working tree; main integration suite, typecheck and hygiene pass. Seeded delayed-TQ/affine serving and logprob/wire checks also pass on
1358
+ M1. Unsplit-prefill seeded serving passes M1 Qwen 0.8B and M4 Gemma 12B.
1359
+ Validated source: `/Users/joshrossi/.cache/mlx-bun/prefill-cohort-logits-validation`.
1360
+ A separate admission-control benchmark retains the measured code but disables
1361
+ additional cohort admission. That control restores every baseline response,
1362
+ attributing the changed concurrent text to shared prefill.
1363
+ Full delayed affine/TurboQuant padded preparation and MTP prefill cohorts remain
1364
+ open. The production HTTP benchmark now measures long aggregate prompts and
1365
+ staggered arrivals. Its four-arm M4 comparison exposes a first-output latency
1366
+ regression and no throughput win at the current chunk size. M1 traces show
1367
+ internal token zero ready before a long B3 preparation chunk delays the first
1368
+ response write. A matching smaller-chunk M4 screen still loses latency and
1369
+ throughput. Prefill work-unit sizing and admission policy need tuning through
1370
+ the shared scheduler/preparation interfaces. This is a scheduling decision
1371
+ within the shared executor; it must not introduce another serial method.
1372
+ See benchmarks.md for numbers and limits.
1373
+ Evidence: `reports/qwen38-closeout/composition-baseline/prefill-cohort/`.
1374
+
1375
+ An isolated follow-up exposes preparation work through the scheduling contract.
1376
+ The preparation owner reports each admitted request's initial uncached token
1377
+ count; the queue adapter reports the next prompt length as an upper bound before
1378
+ cache restoration. The scheduler extends a cohort only while the combined
1379
+ weight fits its internal preparation budget, currently 2,048 tokens. The first
1380
+ request always enters and can exceed that budget alone. No request is rejected
1381
+ or sent to another executor. Short late arrivals still join ongoing preparation.
1382
+ An earlier version reduced the weight as prefill progressed. That allowed a
1383
+ nearly completed request to attract another long request into its tail; the
1384
+ first M4 screen restored early latency but still lost total request time. The
1385
+ current candidate retains admission weight until the request leaves preparation.
1386
+ This policy changes grouping, not per-request chunk or precision boundaries.
1387
+ Cache lookup/restoration remains in preparation, not scheduling. The conservative
1388
+ queued estimate can postpone a cached long request until current preparation
1389
+ ends. A native Qwen/TurboQuant check with a long RAM prefix passes repeat output,
1390
+ retirement, immutable cache bytes and actual B2 decode on M1. The original late
1391
+ prefill join remains covered, alongside a group test that holds a nearly finished
1392
+ row's weight and then advances its queued sibling through the same executor.
1393
+ The seven-file fixed-weight policy is adopted in the unreleased working tree.
1394
+ Both complete suites/types and long-prefix ownership checks pass. The M4
1395
+ comparison restores the large first-output loss and all response identity,
1396
+ while small unfavorable timing differences remain in benchmarks.md. Main
1397
+ integration suite/typecheck pass; strict performance dominance remains open.
1398
+ Candidate: `/Users/joshrossi/.cache/mlx-bun/prefill-budget-validation`.
1399
+ Evidence: `reports/qwen38-closeout/composition-baseline/prefill-cohort/budget*`.
1400
+
1401
+ A separate two-file lifetime candidate releases input IDs after the forward and
1402
+ releases unused hidden output before cache evaluation/maintenance, matching the
1403
+ existing shared prefill primitive. The cohort previously retained those handles
1404
+ until the end of the work unit. Finished rows still retain hidden output through
1405
+ batched projection and sampling. Focused ownership checks pass on M1, and the Qwen 27B
1406
+ same-B model/sampler/continuation gates pass on both Macs. The M1 full suite and
1407
+ typecheck pass. The four-arm M4 comparison preserves all responses but shows no
1408
+ throughput or material measured-memory benefit. This candidate is not adopted;
1409
+ its M4 full suite/typecheck was not run. Timing and limits are in benchmarks.md.
1410
+ Candidate: `/Users/joshrossi/.cache/mlx-bun/prefill-drain-validation`.
1411
+
1412
+ The next isolated candidate exposes `DraftPrefillGroup` from the draft provider.
1413
+ It borrows each batch of new target tokens and context, and owns companion KV,
1414
+ the pending true hidden row, cold/restored membership and checkpoint capture.
1415
+ `QwenMtpRows` supplies preparation and draft/commit state at B1 and B>1;
1416
+ its preparation port requires no sampler. A restored request bridges its next
1417
+ token to the saved preceding hidden; cold members discard that bridge. The
1418
+ remaining chunk pairs each shifted token with its preceding target hidden.
1419
+ Preparation evaluates KV and the retained final hidden, leaving unused decoder
1420
+ outputs outside the evaluated graph. Membership and state extraction reuse the
1421
+ existing cache storage and checkpoint format. The scheduler is unchanged.
1422
+
1423
+ Pinned same-B checks pass companion KV and full decoder continuation through
1424
+ B1/B2/B3/B4, cold joins, restored prefixes, retirement and a long chunk boundary
1425
+ on both Macs. Those checks exposed a pre-existing unfused activation in the MTP
1426
+ MLP. Reusing the target's compiled SwiGLU matches the reference vectors. Existing
1427
+ MTP draft/commit checks and both complete suites/typechecks pass. Initial suite
1428
+ failures came from omitted benchmark fixtures and an old M1 DeltaNet golden in
1429
+ the isolated checkouts; restoring each machine's existing inputs fixes them
1430
+ without changing the implementation or regenerating goldens.
1431
+
1432
+ The seven-file provider port and numerical correction are adopted in the
1433
+ unreleased working tree. The M4 HTTP comparison preserves response text and
1434
+ prompt/output counts with effectively flat timing, while draft acceptance
1435
+ traces change. That comparison did not exercise batched MTP prefill because
1436
+ shared target preparation was not yet connected to the new port. Numbers and limits are in
1437
+ benchmarks.md. Main integration suite/typecheck pass.
1438
+
1439
+ The isolated `MlxPrefillRows` driver now owns target membership and chunk
1440
+ progression for ordinary, Qwen MTP and prompt-lookup preparation. Each method
1441
+ supplies initialization, chunk planning, checkpoint capture and completion.
1442
+ The draft provider owns companion prefill state and materializes restored
1443
+ companion copies before target membership releases an SSD backing lease.
1444
+ Final-step maintenance is explicit in the chunk plan. A fully restored
1445
+ pending-token prefix completes without a zero-length model forward.
1446
+ Prepared rows return to scheduling before joining active decode, preserving
1447
+ the first-output publication boundary.
1448
+
1449
+ The connected implementation passes full suites and typechecks on both Macs,
1450
+ and the target's same-B oracle checks through staggered prefill and continuation.
1451
+ The companion and Gemma 12B target oracles also pass on M1. Expanded native MTP serving checks on both
1452
+ Macs prove that a request submitted during the first target chunk joins the
1453
+ next chunk before any output. M4 passes this check with prompt lookup and
1454
+ TurboQuant as well. These checks also cover B1 producer parity, B4 execution,
1455
+ retirement, cache reuse and HTTP grammar/logprobs. Generated target/draft
1456
+ checkpoints preserve RAM/SSD continuation after restart on both Macs with TQ.
1457
+ An added M1 probe confirms four rows actually share prefill in that cache test,
1458
+ before the first output.
1459
+
1460
+ Added method lifecycle checks verify exact target/history coverage, zero-work
1461
+ restored prefixes and cancellation before and after preparation completes.
1462
+ The latter exposed a failure that rejected siblings before active admission.
1463
+ The method now rejects and releases only the cancelled ready row. A first-token
1464
+ consumer failure also leaves an unfinished sibling running. M1 delayed affine
1465
+ MTP/lookup and delayed TurboQuant MTP checks preserve seeded continuation,
1466
+ metadata and speculation through mixed precision, retirement and late joins.
1467
+ The common driver also passes M1 ordinary TQ ownership checks with a 4096-token
1468
+ RAM prefix through concurrent use, retirement and supersession.
1469
+ The balanced M4 comparison is complete. Shared MTP prefill reduces concurrent
1470
+ throughput. Two attribution runs retain the same shared engine but prepare
1471
+ requests individually; both reproduce control responses and restore throughput.
1472
+ This isolates the regression to enabling shared preparation, without requiring
1473
+ a separate serial executor. A narrower policy that finishes a resumed final
1474
+ prefill step before admitting new rows does not recover throughput and produces
1475
+ the same responses and speculation counters as the shared-prefill candidate.
1476
+ Neither scheduling variant is adopted. The common driver is adopted with the
1477
+ cache ownership fix described below.
1478
+ Phase/layer probes narrow the sustained gap to MLP evaluation despite identical
1479
+ Trellis routes, shapes, dtypes and call counts. Settling target state and clearing
1480
+ the temporary pool after preparation does not restore throughput and is not
1481
+ adopted. The snapshot-lifetime investigation below recovers this loss. Numbers
1482
+ and conditions are in benchmarks.md. Broader composed oracle/cache coverage and
1483
+ other draft providers remain open.
1484
+ Candidate: `/Users/joshrossi/.cache/mlx-bun/mtp-prefill-validation`.
1485
+ Evidence: `reports/qwen38-closeout/composition-baseline/prefill-cohort/mtp-prefill*`.
1486
+
1487
+ Common-driver candidate: `/Users/joshrossi/.cache/mlx-bun/mtp-cohort-validation`.
1488
+ Evidence: `reports/qwen38-closeout/composition-baseline/prefill-cohort/mtp-cohort*`.
1489
+ The integration patch and source hashes are recorded in
1490
+ `mtp-cohort-integration.patch` and `mtp-cohort-integration-manifest.json`
1491
+ under that evidence directory. Native logs identify which test expansion
1492
+ ran; earlier serving checks did not include arrival during target prefill.
1493
+
1494
+
1495
+ A deferred compact row snapshot retained its full parent batch until evaluated.
1496
+ Resolving target snapshots before RAM retention recovers the concurrent loss
1497
+ in two M4 runs while preserving every shared-prefill response and full usage
1498
+ record. Failed tile-size, input-evaluation and residency experiments are closed.
1499
+
1500
+ The adopted implementation moves snapshot materialization into the
1501
+ RAM prefix-cache implementation. A backend operation resolves target state and
1502
+ opaque companion tensors before storage accepts ownership. It performs no
1503
+ serialization or disk I/O; eventual SSD persistence keeps its existing queue.
1504
+ Ordinary decode, MTP and lookup publish through the same interface and need no
1505
+ method-local evaluation rule. Evaluation failure leaves ownership with the
1506
+ publisher. The regression test measures retained allocation before any cache
1507
+ read and covers both target and companion rows. Both-machine suites/typechecks, composed native checks and the matched M4
1508
+ comparison pass. Main integration suite, typechecks and hygiene pass. Strict B1/long-prefill
1509
+ acceptance and remaining feature combinations are still open. Numbers belong in
1510
+ benchmarks.md; source and validation records are in
1511
+ `mtp-cohort-cache-boundary-manifest.json` under the cohort evidence directory.
1512
+
1513
+
1514
+ ### Full-attention delayed compression during padded prefill
1515
+
1516
+ The integrated full-attention implementation provides the existing padded-prefill
1517
+ port for delayed affine and TurboQuant rows. Each row keeps physical write
1518
+ positions, valid token coverage and pending finalization separately. Conversion
1519
+ uses valid coverage; codec tensors retain their physical alignment. When all
1520
+ rows share an encoding, the packed layout inherits pending valid ends. Finalizing
1521
+ and retiring rows move encoded planes without re-encoding live values. The same
1522
+ row wrapper and transition lifecycle serve affine and TurboQuant; model methods
1523
+ and scheduling are unchanged.
1524
+
1525
+ The extended pinned cache oracle passes on both Macs with KV4/KV8 and TQ K8/V3
1526
+ and K4/V4, multiple conversion thresholds, cached history, shortened prefill,
1527
+ retirement/reorder, continuation, and leading padding across multiple chunks.
1528
+ It checks masks, positions, retained encoded bytes and same-B attention. Invalid
1529
+ padding bytes are not state: moving encoded zero padding can preserve a different
1530
+ signed zero than encoding it again, while retained values and attention remain
1531
+ exact. Existing unpadded row/cohort regressions pass on M1. The Gemma model gate
1532
+ exposes a before-conversion mismatch when affine attention runs uniform BF16
1533
+ rows separately. The candidate now assembles those tensors through the same
1534
+ helper as TurboQuant and runs attention at the model's B. The cache oracle
1535
+ passes after that change, as does the Gemma affine full-model gate. Both
1536
+ regression suites and typechecks pass. Affine MTP/lookup and delayed affine/TQ
1537
+ seeded serving pass on M1; TQ MTP serving passes on M4. Generated MTP3/B4
1538
+ RAM/SSD continuations pass on both Macs. All four matched M4 arms preserve responses and usage. The measured small
1539
+ throughput decrease is recorded in benchmarks.md; strict B1/long-prefill
1540
+ acceptance remains open. Main integration suite/typechecks pass. Candidate:
1541
+ `/Users/joshrossi/.cache/mlx-bun/full-delayed-prefill-validation`; evidence:
1542
+ `reports/qwen38-closeout/composition-baseline/full-delayed-prefill/`.
1543
+
1544
+
1545
+ ### Standalone autoregressive draft graph integration
1546
+
1547
+ The integrated implementation runs a standalone draft graph at the group's B and
1548
+ hands its log-probabilities to the existing row sampler. Immutable batch
1549
+ snapshots retain accepted recurrent/attention boundaries without changing
1550
+ scheduler policy. Full acceptance retains the last draft token as companion
1551
+ state and re-feeds it with the next target token in one forward, preserving
1552
+ the reference's two-token shape. Mixed one-/two-token feeds use the existing
1553
+ padding interface. Zero-proposal rounds preserve processed-token coverage.
1554
+
1555
+ The lifecycle test covers mixed acceptance, removal/reordering, zero proposals
1556
+ and mixed re-feed lengths. A native Qwen 0.8B gate on M1 checks exact B1
1557
+ attention/recurrent state and continuation at depths 0, 1, 3 and 4. The provider is integrated; its old single-request source now
1558
+ adapts the same draft engine at B1. The small Qwen target/drafter passes B1/B4
1559
+ serving, late joins, consumer failures, seeded sampling/logprobs and HTTP
1560
+ coverage. Companion state reuses backend cache codecs; asynchronous prefill
1561
+ completion now preserves target-context ownership. The 27B target with the 0.8B
1562
+ standalone drafter passes B1/B4 serving on both Macs (228 assertions each),
1563
+ and depth-three TurboQuant generation restores through RAM and cold SSD
1564
+ (33 assertions each). The M4 pinned Python oracle passes the Llama 3B/1B
1565
+ pair, including four templated cases at draft depths two and three, grammar
1566
+ and stopping. Full suites pass on M1/M4 (2,153/2,157 tests), as do all
1567
+ typechecks and the existing MTP/lookup native regressions. The first matched M4 comparison improves B1 decode and cache reuse but
1568
+ hits a GPU allocation failure in the four-request phase, including the
1569
+ benchmark’s retry. The allocation trace explains why short correctness
1570
+ coverage did not expose this workload failure. Replaying
1571
+ all prior requests reproduces the failure with 4,005 MiB of accounted RAM
1572
+ cache entries and a 17,987 MiB observed allocation peak. A fresh aggregate
1573
+ passes. The retained entries consume the remaining working-set headroom;
1574
+ the repeated comparison holds both arms at 2 GiB RAM plus SSD and completes
1575
+ all four arms. Decode/cached/aggregate gains and cold/startup costs are
1576
+ recorded in benchmarks.md. Main integration suite, typechecks and hygiene pass. The retained 4 GiB failure remains part of cache-settings acceptance.
1577
+ Validation source:
1578
+ `/Users/joshrossi/.cache/mlx-bun/two-model-batch-validation`; source hashes and
1579
+ checks: `reports/qwen38-closeout/composition-baseline/two-model-batch/`.
1580
+
1581
+ ### Full-attention target capability binding
1582
+
1583
+ The integrated implementation moves the shared target-layout factory behind
1584
+ one storage capability binding. Grouped speculation then depends on the
1585
+ model graph's batch support and the storage transaction factory, replacing
1586
+ the Qwen-only target selection. Scheduler and draft-provider policy are
1587
+ unchanged; quantized speculation retains its existing qualification while
1588
+ plain full-attention targets are tested. On M1, Llama 3B with the same model
1589
+ as drafter passes shared B1/B4 serving, late joins, retirement, seeded
1590
+ sampling/logprobs and grammar (224 assertions). Prompt lookup passes the
1591
+ same serving harness; generated RAM/SSD continuation passes at depth three.
1592
+ The pinned Llama model oracle is exact at B1/2/3/4/8 across padding, chunks
1593
+ and continuation (541 assertions), and mixed-prefix target rollback passes
1594
+ 1,352 state/continuation assertions. The complete M1 suite and typechecks pass.
1595
+ Both-machine Llama and MiniCPM serving/lookup/cache checks pass, as do the
1596
+ wide same-B oracle, Qwen MTP/TQ regressions, full suites and typechecks.
1597
+ The M4 shared B1 path also passes the pinned templated speculation oracle.
1598
+ Four matched M4 arms complete; gains, cold-prefill cost and the changed
1599
+ concurrent response are recorded in benchmarks.md. Main integration suite, all typechecks and full hygiene pass. Rotating target transactions remain the next storage gap.
1600
+ Validation source:
1601
+ `/Users/joshrossi/.cache/mlx-bun/full-kv-speculation-validation`;
1602
+ evidence: `reports/qwen38-closeout/composition-baseline/full-kv-speculation/`.
1603
+
1604
+
1605
+ ### Rotating target transactions
1606
+
1607
+ The rotating-storage adapter now exposes independent speculative acceptance
1608
+ through the existing target transaction interface. It reuses the existing
1609
+ ring kernels and keeps the verification rectangle: rejected suffixes move
1610
+ into masked left padding. Contracting the rectangle preserved KV columns
1611
+ but failed Gemma continuation equality; that candidate was replaced.
1612
+ Published rows contain the newest accepted window; ordinary ring extraction
1613
+ retains its pinned-oracle oversized-block behavior. No scheduler change.
1614
+
1615
+ Tagged storage windows, cloning, retirement and subsequent writes pass.
1616
+ Both-machine Gemma 12B lookup serving passes at B1/B4, including late joins,
1617
+ consumer failures, grammar, seeded sampling/logprobs and HTTP coverage.
1618
+ Both-machine Gemma target transactions pass 3,080 assertions across prompts
1619
+ straddling the actual 1,024-token window, unequal acceptance and continuation.
1620
+ Both-machine pinned padded-model oracle passes at B1/2/3/4/8, and
1621
+ standalone draft generation restores through RAM/cold SSD. The default
1622
+ lookup depth of ten exposed a singleton geometry difference; an unwrapped
1623
+ single row now retains its scalar prefix geometry without adding padding.
1624
+ The depth-ten serving gates now pass for Gemma 12B and e4b on both Macs.
1625
+ Both final suites/typechecks pass, alongside the Qwen MTP/TurboQuant regression.
1626
+ The final-source matched M4 comparison completes: concurrent throughput and
1627
+ cache reuse improve, while B1 decode is effectively equal. Startup variation
1628
+ and different concurrent trajectories are recorded in benchmarks.md.
1629
+ The adapter is integrated; main integration suite, all typechecks and full hygiene pass. Quantized rotating
1630
+ speculation and the remaining providers are still open.
1631
+ Validation source: `/Users/joshrossi/.cache/mlx-bun/rotating-speculation-validation`.
1632
+ Evidence: `reports/qwen38-closeout/composition-baseline/rotating-speculation/`.
1633
+
1634
+
1635
+ ### Assistant draft rows
1636
+
1637
+ The assistant provider now supplies the same prefill and decode row interfaces
1638
+ as standalone drafting. It retains only the last true target hidden and its
1639
+ processed-token count. The target layout supplies captured donor attention;
1640
+ full and rotating layouts own valid intervals and masks. The draft graph does
1641
+ not know the scheduler or cache geometry. Matching layers and draft steps reuse
1642
+ the same donor mask. All proposals remain on device until the chain completes.
1643
+ Generated checkpoints store the hidden companion alongside existing target KV,
1644
+ using the same RAM ownership and queued SSD persistence.
1645
+
1646
+ Both e4b centroid and 12B tied heads use one graph at B1/B2/B4. The pinned
1647
+ optiq loader assumes centroid presence from config; the 12B numerical test binds
1648
+ its actual tied embedding head by tensor presence while retaining upstream
1649
+ layers. The e4b end-to-end test uses unmodified optiq `spec_generate`. Both
1650
+ machines pass these checks, serving/retirement/late joins, grammar/logprobs and
1651
+ generated RAM/SSD continuations. Tagged donor columns independently verify
1652
+ padding, rejected suffixes, wrap and row removal. The old contiguous transposed
1653
+ weights caused a hidden-state mismatch on M4 even before this refactor. Keeping
1654
+ upstream's strided weight views restores exactness and avoids duplicate weights.
1655
+
1656
+ The candidate is integrated after both regression suites/types and the four-arm
1657
+ M4 comparison. Main integration suite, all typechecks and full hygiene pass. Throughput and cached
1658
+ latency improve; readiness and concurrent-trajectory differences remain recorded
1659
+ in benchmarks.md. DeepSpec follows through projected context rows.
1660
+ Evidence: `reports/qwen38-closeout/composition-baseline/assistant-batch/`.
1661
+
1662
+
1663
+ ### Shared DeepSpec context rows
1664
+
1665
+ DeepSpec's context projections and block graph now accept the active row count.
1666
+ The MLX projected-context owner reuses full-attention row storage, cache codecs
1667
+ and coordinated membership. Its attention port hides context geometry and
1668
+ validity from the block graph. The provider declares its target taps and stores
1669
+ projected context as the ordinary method companion in RAM and queued SSD.
1670
+ Accepted lengths advance each row independently; rejected context never survives
1671
+ in a published checkpoint. The sequential proposal head stays on device through
1672
+ the block, and serving skips unconsumed confidence values when truncation is off.
1673
+
1674
+ Synthetic tests cover bf16/quantized projections, confidence truncation through
1675
+ zero proposals, differing commit lengths, restoration and retirement. Real Gemma
1676
+ 12B serving/generated RAM/SSD checks pass with both DeepSpec bf16 and its affine
1677
+ 4-bit sibling on both Macs, including the trained seven-token block. Native B1
1678
+ full logits and confidence match the frozen implementation; B2/B4 repeat exactly
1679
+ with independent positions. These comparisons are regression controls, not a new
1680
+ external DeepSpec oracle: the existing Torch oracle requires a separate reference
1681
+ environment and bf16 target that are not currently installed on either laptop.
1682
+ Real-window checks confirm that shared speculation remains active across wrap
1683
+ while the previous serial path stops drafting there. Both-machine regression
1684
+ suites and all typechecks pass. Four matched M4 arms improve aggregate throughput
1685
+ and cached latency; startup cost and one changed concurrent response remain
1686
+ recorded in benchmarks.md. The candidate is integrated; the main regression
1687
+ suite, all typechecks and full hygiene pass. DSpark/DFlash is next and shares the projected-context lifecycle.
1688
+ Evidence: `reports/qwen38-closeout/composition-baseline/deepspec-batch/`.
1689
+
1690
+
1691
+ ### Shared DSpark/DFlash provider
1692
+
1693
+ The draft graph accepts B1 and B>1 through one backbone and sequential proposal
1694
+ head. Markov and RNN heads retain their existing numerical behavior. Confidence
1695
+ can retire proposals independently for each row, and sampled proposal streams
1696
+ remain independent. Serving avoids confidence and float-logit outputs it does
1697
+ not consume. Scheduling does not select a draft implementation.
1698
+
1699
+ DSpark and DeepSpec use the same projected-context lifecycle. The provider binds
1700
+ projection and proposal operations; the shared owner handles prefill, accepted
1701
+ lengths, row membership, capture and restoration. The MLX context store applies
1702
+ padding and attention validity. Generated context enters the existing RAM cache
1703
+ and queued SSD persistence as a method companion.
1704
+
1705
+ Pinned graph outputs and both-machine Markov/RNN real-Gemma serving/generated
1706
+ RAM/SSD tests pass. Those tests use seeded draft weights, so they establish
1707
+ execution and state contracts, not trained acceptance or quality. Josh confirms
1708
+ that no DSpark/DFlash checkpoint has been trained yet. DeepSpec's real trained provider passes again
1709
+ after the extraction. Full suites and all typechecks pass on both Macs. Four M4
1710
+ arms measure the extraction using the same trained DeepSpec checkpoint and
1711
+ preserve all responses and usage. The measured throughput cost is recorded in
1712
+ benchmarks.md. The provider is integrated; the main suite, all typechecks
1713
+ and full hygiene pass. Trained DSpark performance qualification remains open. Evidence:
1714
+ `reports/qwen38-closeout/composition-baseline/dflash-batch/`.
1715
+
1716
+
1717
+ ### Affine rotating speculation and encoded assistant donors
1718
+
1719
+ Uniform start-zero KV4/KV8 target layouts now compose with grouped draft
1720
+ providers on full-attention and rotating targets. Acceptance bookkeeping is
1721
+ shared by plain and encoded rings. Packed values, scales and biases use the
1722
+ same row operations; rollback, retirement and snapshots never dequantize.
1723
+ Ordinary rotating extraction retains the oracle's oversized-block geometry;
1724
+ committed speculative snapshots retain only the newest valid window.
1725
+ Unpadded verification keeps the existing fused attention mask dispatch.
1726
+ Standard universal attention and MiniCPM now consume encoded cache ports
1727
+ instead of assuming plain arrays or a concrete affine cache class. The
1728
+ universal softcap branch retains its existing plain-only qualification.
1729
+
1730
+ Assistant drafting consumes an opaque donor-attention interface. Storage owns
1731
+ the encoded planes and validity ranges; the assistant only supplies queries.
1732
+ Its compatibility source now adapts the same row graph at B1. The redundant
1733
+ plain-only AssistantTarget interface is removed. Additive donor masks follow
1734
+ the pinned affine attention operation, independently of boolean cache masks.
1735
+
1736
+ Native continuation controls must hold both batch size and physical attention
1737
+ width fixed. The pinned Python oracle reproduces the captured different-width
1738
+ full-attention results byte for byte: one bf16 output differs despite identical
1739
+ queries and valid KV. The transaction test pads only the reference's fetched
1740
+ views to the compared width, retaining exact state and hidden checks.
1741
+ The old executor disables speculation near the rotating boundary, so its KV4
1742
+ text can differ from active shared speculation. Live-window tests record that
1743
+ comparison and check repeated active execution; they do not relabel ordinary
1744
+ fallback as an exact speculative oracle.
1745
+
1746
+ Both-machine KV4/KV8 serving and generated RAM/SSD checks pass for DeepSpec,
1747
+ assistant, standalone and lookup providers; seeded DSpark heads pass the same
1748
+ state contracts. Real-window transactions, same-width target controls, pinned
1749
+ regressions, full suites and all typechecks pass. Six M4 benchmark arms preserve
1750
+ bf16 responses and usage with effectively unchanged throughput. KV4 improves
1751
+ aggregate throughput on this workload, with different generated text and more
1752
+ prefill time; the comparison is recorded in benchmarks.md. The change is
1753
+ integrated; the main suite, all typechecks and full hygiene pass.
1754
+ Evidence: reports/qwen38-closeout/composition-baseline/rotating-spec-quant/.
1755
+ Strict performance dominance and removal of the serial control remain open.
1756
+
1757
+
1758
+ ### TurboQuant donor attention and grouped draft composition
1759
+
1760
+ Solo, packed and delayed TurboQuant storage expose the same owned donor-attention
1761
+ interface as plain and affine caches. Captures preserve physical columns, row
1762
+ validity and right-prefill padding without appending tokens, converting precision
1763
+ or advancing membership. Assistant attention consumes eagerly decoded values,
1764
+ preserving its existing operation order. MiniCPM retains its explicit deferred
1765
+ value rotation. Storage chooses the existing codec and fused decode kernels;
1766
+ scheduling does not identify the encoding or draft provider.
1767
+
1768
+ The gateway advertises grouped TurboQuant from the bound target and provider
1769
+ capabilities. Positive conversion thresholds use the existing row-local
1770
+ transition lifecycle; sliding layers remain bf16. Per-layer affine speculative
1771
+ configuration composes through the same bound row capabilities. Group binding now snapshots per-layer and
1772
+ TQ options so caller mutation cannot change a live graph or its persisted
1773
+ identity. Explicit per-layer schemes get distinct group/cache keys, with
1774
+ canonical layer ordering; absent/empty schemes preserve existing keys exactly.
1775
+
1776
+ The isolated candidate passes independent K8/V3 donor codec plus pinned assistant
1777
+ graph controls at B1/B2/B4 with fused decoding on/off for both Gemma assistant
1778
+ artifacts on the M4, alongside the unchanged bf16 oracle. M1 assistant and
1779
+ MiniCPM lookup pass start-zero serving/generated RAM/SSD and delayed conversion,
1780
+ retirement and late admission. Both-machine assistant, DeepSpec, standalone and lookup serving/generated
1781
+ RAM/SSD, delayed conversion and late admission pass, along with seeded DSpark
1782
+ heads, Qwen regressions, final suites and typechecks. Twelve M4 timing arms
1783
+ record both source-only costs and TQ configuration tradeoffs in benchmarks.md.
1784
+ The source is integrated; main suite, all typechecks and full hygiene pass.
1785
+ Evidence: `reports/qwen38-closeout/composition-baseline/turboquant-donor/`.
1786
+
1787
+ The independent assistant control also exposed an existing centroid-head
1788
+ numerical shortcut on M1: matrix multiplication did not preserve the oracle's
1789
+ bf16 multiplication followed by reduction. On identical B2 hidden state, Python
1790
+ reproduced the old candidate token with matmul and the oracle token with explicit
1791
+ multiply/sum. The shared head now preserves that operation order at every B and
1792
+ selects the lowest vocabulary ID among tied candidate scores, matching sparse
1793
+ vocabulary argmax without materializing a full-vocabulary logits buffer. The
1794
+ independent codec, hidden-state and token assertions remain exact. The M1 e4b
1795
+ control and unmodified end-to-end optiq generation pass after the correction;
1796
+ final both-machine regressions pass. The source-only and TQ timing comparisons
1797
+ are recorded in benchmarks.md. Attribution repeats recover the prior decode rate on the unchanged candidate, so they do not establish a code regression. Captured diagnostics live
1798
+ with the composition evidence.
1799
+
1800
+
1801
+ ### Delayed and per-layer affine rotating speculation
1802
+
1803
+ Rotating storage owns an accepted-column transaction shared by plain and affine rings. The generic transition interface invokes that transaction while rows have different precisions; after conversion it packs them into the existing affine layout. Conversion sees only committed tokens. Scheduling and draft providers do not inspect the quantization scheme.
1804
+
1805
+ Full and rotating caches expose owned donor-attention views. Mixed rows retain their physical columns, valid offsets and padding; assistants submit queries and masks without materializing a common decoded cache. Pending right-prefill padding is excluded before finalization, including when a physical block exceeds the sliding window. Captures survive cache conversion, row reordering and disposal.
1806
+
1807
+ The gateway now advertises supported affine configurations from the target/provider binding, including model-defined per-layer schemes and positive library thresholds. The served CLI schemes retain start zero. Independent attention fixtures cover padding and ownership; exact target hidden/logit controls cross the real sliding boundary at verification widths one and eight. Native checks cover continuation, precision transitions, unequal retirement, late admission, grammar/logprobs and generated RAM/SSD companions. Legacy serving rejects some of these combinations; repeated shared execution is not relabeled as a legacy speculative oracle.
1808
+
1809
+ Both-machine provider/native/suite/typechecks and six matched M4 benchmark arms are complete. Bf16 responses and usage remain exact; model-defined per-layer KV trades higher single-request decode for lower aggregate throughput on the measured workload. The change is integrated; main suite, types and full hygiene pass. Evidence: `reports/qwen38-closeout/composition-baseline/delayed-rotating-spec/`. Defaults and the serial retirement criterion remain unchanged.
1810
+
1811
+
1812
+ ### GLM compressed target and native MTP rows
1813
+
1814
+ The integrated implementation adds independent accepted-column transactions and cold-row membership to compressed MLA/DSA storage, without reconstructing full K/V. Native MTP exposes a shared numerical graph and a grouped provider. Its prompt prefill retains the target anchor while leaving MTP cache empty; committed accepted positions are rebuilt from true target hidden states. Target token coverage and the shorter draft-cache offset remain separate checkpoint metadata. The existing attachment interface persists both through the generic RAM/SSD cache.
1815
+
1816
+ Both-machine tiny-model controls cover exact B1 proposals/absorption, zero-proposal rounds, streamed expert calls, B4 membership with DSA enabled, late admission, retirement, generated-prefix identity and identical RAM/SSD next drafts. DSA verification retains every request row and shares each logical query selection across FULL/SHARED layers. Compressed multi-token attention now masks physical left padding, including native MTP absorption. Empty DSA rows survive filtering and resumed writes. Six two-layer dense/sparse boundary cases per machine match independent pinned Python/MLX hidden and every cache plane byte-for-byte. Python uses the uncompiled sigmoid/multiply activation that this model graph calls. Different-B controls retain greedy IDs but can differ in small f32 values because physical widths select absorbed/reconstructed attention and different matrix kernels; they are not the exact numerical oracle. Both full model-free suites, the SSD suite and all typechecks pass. The tested implementation is integrated in the PR. Josh has deferred final GLM-5.2 Colibri artifact testing. This does not establish real-artifact numerical or performance acceptance. Evidence: `reports/qwen38-closeout/composition-baseline/mla-transaction/`.
1817
+
1818
+
1819
+ ### Paged storage through shared row and request-state interfaces
1820
+
1821
+ `PagedKvRows` owns per-request block pools, immutable snapshots, gathering, masks, offsets and row membership. The existing executor binds it through the cache-layout interface. `MlxRequestStatePolicy` supplies compatible state construction and a prompt-cache port; paging selects no prefix store. Switching back to ordinary requests restores their cache port. The same paging constructor serves the compatibility entry point and shared prefill. B1 keeps the existing paged numerical graph; B>1 gathers padded attention inputs while retaining separate persistent pools. Gemma12 full sampled logits and HTTP choices match the ordinary same-B controls, including seeded sampling, logprobs and grammar. Both-machine model-free, SSD and typechecks pass. The native storage comparison fixes cohort membership and snapshot boundaries on both sides, covering small blocks and the default block boundary; otherwise cache publication changes the numerical prefill graph. The matched M4 source comparison is effectively flat, and shared paging improves concurrent throughput versus serial while retaining effectively equal B1 decode. Concurrent text can differ across batch geometries; the exact storage oracle compares the same geometry. Timing and limitations live in benchmarks.md; evidence is `reports/qwen38-closeout/composition-baseline/paged-rows/`. Paged attention kernels, block-level prefix sharing, quantized paging and speculative paging remain separate work.
1822
+
1823
+ The sampler now exposes an optional stateless operation for compatible independent score rows. Ordinary decode and speculative verification share normalized-score argmax. Verification can sample a rectangle in one device operation and readback; acceptance still consumes only each request's valid positions. Grammar, processors, stochastic sampling, logprob capture and custom sampler functions retain their request-local operation. Same-shape numerical tests include rounding-created ties and large vocabularies; both-machine native/generated RAM/SSD and suites pass. Twelve matched M4 arms preserve ordinary throughput and improve lookup aggregate throughput; startup and trajectory limits are recorded in benchmarks.md. Lookup defaults remain unchanged. Evidence: `reports/qwen38-closeout/composition-baseline/independent-sampling/`.
1824
+
1825
+
1826
+ ### Campaign checkout consolidation
1827
+
1828
+ The PR incorporates the accepted implementations from the M1 and M4 campaign
1829
+ checkouts, including ordinary resume, adapter-aware resume and GLM shared MTP
1830
+ state. Older TQ, provider, cache and prefill candidates are superseded by the
1831
+ integrated compositions above. Benchmark baselines and rejected wider-Trellis,
1832
+ option-capture, state-only replay and cohort scheduling variants remain
1833
+ experiment records; copying them over the selected implementation would undo
1834
+ later work. The singleton `copyOf` to `contiguous` candidate remains unpromoted
1835
+ because its attribution and startup result are unresolved.
1836
+
1837
+ The 80 secondary Git worktrees and 99 campaign source copies were removed
1838
+ after their source files and reports were verified in a content-addressed local archive under
1839
+ `reports/pr-closeout/checkout-archive/`, with per-checkout manifests, SHA-256
1840
+ objects and the recorded base commit. The PR retains the implementation and
1841
+ findings; bulky raw experiment data stays local under the repository policy.