mlx-bun 0.4.0 → 1.0.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 (601) hide show
  1. package/README.md +1453 -88
  2. package/THIRD_PARTY_NOTICES.md +882 -0
  3. package/bin/mlx-bun.convert.mjs +4 -0
  4. package/bin/mlx-bun.fuse.mjs +4 -0
  5. package/bin/mlx-bun.generate.mjs +4 -0
  6. package/bin/mlx-bun.lora.mjs +4 -0
  7. package/bin/mlx-bun.mjs +13 -21
  8. package/bin/mlx-bun.server.mjs +4 -0
  9. package/bin/mlx-bun.upload.mjs +4 -0
  10. package/dist/web/app.js +9584 -0
  11. package/package.json +50 -53
  12. package/src/cli/ane-install.ts +76 -0
  13. package/src/cli/args.ts +179 -0
  14. package/src/cli/engine-entry.ts +68 -0
  15. package/src/cli/inference.ts +214 -0
  16. package/src/cli/job-entry.ts +97 -0
  17. package/src/cli/library-host.ts +117 -0
  18. package/src/cli/main.ts +57 -0
  19. package/src/cli/memory-engine.ts +163 -0
  20. package/src/cli/memory.ts +36 -0
  21. package/src/cli/mlx-lm-aliases.ts +309 -0
  22. package/src/cli/model-choice.ts +33 -0
  23. package/src/cli/model-selection.ts +84 -0
  24. package/src/cli/module-host.ts +11 -0
  25. package/src/cli/module-verbs.ts +50 -0
  26. package/src/cli/numerical-policy.ts +12 -0
  27. package/src/cli/perplexity.ts +89 -0
  28. package/src/cli/serve-host.ts +423 -0
  29. package/src/cli/serve-isolated.ts +217 -0
  30. package/src/cli/serve-options.ts +106 -0
  31. package/src/cli/serve-state.ts +205 -0
  32. package/src/cli/serve.ts +500 -0
  33. package/src/cli/served-model-host.ts +102 -0
  34. package/src/cli/server-entry.ts +148 -0
  35. package/src/cli/serving-unit.ts +177 -0
  36. package/src/{tui.ts → cli/terminal.ts} +13 -8
  37. package/src/cli/worker-adapters.ts +34 -0
  38. package/src/cli/worker-entry.ts +272 -0
  39. package/src/cli/worker-unit.ts +179 -0
  40. package/src/engine/adapter-operation.ts +26 -0
  41. package/src/engine/cache-services.ts +114 -0
  42. package/src/engine/completion.ts +39 -0
  43. package/src/engine/generation-gateway.ts +358 -0
  44. package/src/engine/index.ts +59 -0
  45. package/src/engine/kv-budget.ts +120 -0
  46. package/src/engine/media-preparation.ts +232 -0
  47. package/src/engine/model-binding.ts +24 -0
  48. package/src/engine/model-host.ts +513 -0
  49. package/src/engine/model-serving.ts +89 -0
  50. package/src/{serve → engine}/preparation.ts +10 -8
  51. package/src/{serve → engine}/session-completion-engine.ts +6 -6
  52. package/src/engine/telemetry.ts +109 -0
  53. package/src/hub/downloads.ts +116 -0
  54. package/src/jobs/db.ts +16 -7
  55. package/src/jobs/events.ts +29 -0
  56. package/src/jobs/executable.ts +5 -0
  57. package/src/jobs/host.ts +51 -0
  58. package/src/jobs/in-process.ts +55 -0
  59. package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
  60. package/src/jobs/runner.ts +150 -231
  61. package/src/jobs/service.ts +75 -0
  62. package/src/jobs/sse.ts +59 -23
  63. package/src/jobs/worker-memory.ts +46 -0
  64. package/src/jobs/worker-process.ts +163 -0
  65. package/src/jobs/worker-supervisor.ts +258 -0
  66. package/src/modules.ts +58 -0
  67. package/src/publishing/catalog-hub.ts +18 -0
  68. package/src/publishing/credentials.ts +31 -0
  69. package/src/publishing/upload.ts +37 -0
  70. package/src/residency/leased-adapters.ts +21 -0
  71. package/src/residency/local-records.ts +16 -0
  72. package/src/residency/model-residency.ts +405 -0
  73. package/src/residency/record-index.ts +28 -0
  74. package/src/residency/resident-estimate.ts +50 -0
  75. package/src/{anthropic.ts → server/anthropic.ts} +3 -81
  76. package/src/server/cache-routes.ts +36 -0
  77. package/src/{serve → server}/chat-request.ts +12 -10
  78. package/src/{serve → server}/chat-stage.ts +27 -22
  79. package/src/{client.ts → server/client.ts} +28 -2
  80. package/src/{serve → server}/completion-executor.ts +42 -42
  81. package/src/server/decision-body.ts +55 -0
  82. package/src/server/decision-routes.ts +134 -0
  83. package/src/server/decision-wire.ts +228 -0
  84. package/src/{serve → server}/discovery-routes.ts +42 -83
  85. package/src/server/generated-token-history.ts +57 -0
  86. package/src/{serve → server}/http.ts +25 -13
  87. package/src/{serve → server}/inference-request.ts +5 -5
  88. package/src/server/job-routes.ts +20 -0
  89. package/src/server/media-prompt.ts +63 -0
  90. package/src/server/memory-completion-client.ts +46 -0
  91. package/src/server/model-routes.ts +192 -0
  92. package/src/{serve → server}/openai-wire.ts +3 -2
  93. package/src/{serve → server}/pipeline.ts +1 -1
  94. package/src/server/prompt-builder.ts +13 -0
  95. package/src/server/prompt-contracts.ts +17 -0
  96. package/src/server/proxy-routes.ts +187 -0
  97. package/src/server/publishing-routes.ts +41 -0
  98. package/src/{serve → server}/request-plan.ts +8 -8
  99. package/src/{serve → server}/request-prep.ts +82 -68
  100. package/src/{serve → server}/responses-client.ts +30 -13
  101. package/src/{responses.ts → server/responses.ts} +12 -93
  102. package/src/server/routes.ts +205 -0
  103. package/src/server/start.ts +72 -0
  104. package/src/server/status-routes.ts +108 -0
  105. package/src/{serve → server}/text-completion-stage.ts +25 -4
  106. package/src/server/text-prompt.ts +12 -0
  107. package/src/{serve → server}/token-streams.ts +36 -38
  108. package/src/server/worker-routes.ts +311 -0
  109. package/src/storage/paths.ts +61 -0
  110. package/src/web/assets.ts +37 -0
  111. package/src/web/{src → browser}/api.ts +5 -23
  112. package/src/web/browser/chat-host.ts +17 -0
  113. package/src/web/browser/installed-panels.ts +6 -0
  114. package/src/web/browser/main.ts +55 -0
  115. package/src/web/browser/markdown.ts +279 -0
  116. package/src/web/browser/memory-panel.ts +9 -0
  117. package/src/web/browser/palette.ts +64 -0
  118. package/src/web/browser/protocol.ts +13 -0
  119. package/src/web/browser/shell.ts +467 -0
  120. package/src/web/{src → browser}/status.ts +32 -36
  121. package/src/web/build.ts +51 -0
  122. package/src/web/public/app.html +1054 -0
  123. package/src/web/{sw.js → public/sw.js} +4 -3
  124. package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
  125. package/src/web/{vendor → public/vendor}/hljs.js +2 -2
  126. package/THIRD_PARTY_LICENSES.md +0 -51
  127. package/docs/README.md +0 -94
  128. package/docs/archive/hlg-sampling.md +0 -350
  129. package/docs/archive/investigations/audio-input-plan.md +0 -356
  130. package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
  131. package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
  132. package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
  133. package/docs/archive/investigations/curve-bisector-routing.md +0 -123
  134. package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
  135. package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
  136. package/docs/archive/investigations/dspark-handoff.md +0 -224
  137. package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
  138. package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
  139. package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
  140. package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
  141. package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
  142. package/docs/archive/investigations/lab-build-journal.md +0 -157
  143. package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
  144. package/docs/archive/investigations/openjarvis-fit.md +0 -198
  145. package/docs/archive/investigations/openwiki-evaluation.md +0 -249
  146. package/docs/archive/investigations/optimization_plan.md +0 -346
  147. package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
  148. package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
  149. package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
  150. package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
  151. package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
  152. package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
  153. package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
  154. package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
  155. package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
  156. package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
  157. package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
  158. package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
  159. package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
  160. package/docs/archive/planning/chat-ui-vision.md +0 -50
  161. package/docs/archive/planning/chunk-task-roadmap.md +0 -77
  162. package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
  163. package/docs/archive/planning/journal.md +0 -316
  164. package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
  165. package/docs/archive/planning/pi-integration-review.md +0 -178
  166. package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
  167. package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
  168. package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
  169. package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
  170. package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
  171. package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
  172. package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
  173. package/docs/archive/planning/web-ui-pass-plan.md +0 -213
  174. package/docs/archive/planning/website-readme-pass-plan.md +0 -224
  175. package/docs/design/batching.md +0 -1841
  176. package/docs/design/decode-speed-program.md +0 -2349
  177. package/docs/design/dreaming-nightly-pipeline.md +0 -684
  178. package/docs/design/generic-model-support.md +0 -705
  179. package/docs/design/kv-cache.md +0 -824
  180. package/docs/design/orpo-training.md +0 -680
  181. package/docs/design/speculative-decoding.md +0 -1990
  182. package/docs/design/turboquant.md +0 -2212
  183. package/docs/design/unified-engine-frontier-plan.md +0 -1880
  184. package/docs/design/web-chat-redesign.md +0 -695
  185. package/docs/planning/PRODUCT_ROADMAP.md +0 -594
  186. package/docs/planning/ResearchTopics.md +0 -119
  187. package/docs/planning/release-notes-v0.4.0.md +0 -45
  188. package/docs/planning/video-series.md +0 -421
  189. package/docs/reference/benchmarks.md +0 -3613
  190. package/docs/reference/cli.md +0 -585
  191. package/docs/reference/distribution.md +0 -355
  192. package/docs/reference/environment.md +0 -398
  193. package/docs/reference/glossary.md +0 -39
  194. package/docs/reference/library-api.md +0 -674
  195. package/docs/reference/memory.md +0 -260
  196. package/docs/reference/models.md +0 -334
  197. package/docs/reference/server-api.md +0 -1775
  198. package/docs/reference/server-config.md +0 -846
  199. package/docs/reference/training.md +0 -462
  200. package/docs/reference/troubleshooting.md +0 -158
  201. package/src/admission-plan.ts +0 -18
  202. package/src/audio/conformer.ts +0 -647
  203. package/src/audio/decode.ts +0 -133
  204. package/src/audio/features.ts +0 -202
  205. package/src/audio/transcode.ts +0 -67
  206. package/src/backends/mlx/assistant-target.ts +0 -46
  207. package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
  208. package/src/backends/mlx/autoregressive.ts +0 -108
  209. package/src/backends/mlx/batch-group.ts +0 -1790
  210. package/src/backends/mlx/cache-attachment.ts +0 -67
  211. package/src/backends/mlx/cache-layout.ts +0 -31
  212. package/src/backends/mlx/checkpoint-state.ts +0 -53
  213. package/src/backends/mlx/continuation-persistence.ts +0 -106
  214. package/src/backends/mlx/continuation-request.ts +0 -43
  215. package/src/backends/mlx/continuation.ts +0 -77
  216. package/src/backends/mlx/diffusion.ts +0 -39
  217. package/src/backends/mlx/draft-target.ts +0 -32
  218. package/src/backends/mlx/evaluation.ts +0 -105
  219. package/src/backends/mlx/fill-append.ts +0 -45
  220. package/src/backends/mlx/full-kv-row-append.ts +0 -32
  221. package/src/backends/mlx/full-kv-row-donor.ts +0 -54
  222. package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
  223. package/src/backends/mlx/gateway-binding.ts +0 -130
  224. package/src/backends/mlx/graph.ts +0 -38
  225. package/src/backends/mlx/kv-codec.ts +0 -9
  226. package/src/backends/mlx/kv-maintenance.ts +0 -133
  227. package/src/backends/mlx/kv-tensor-rows.ts +0 -212
  228. package/src/backends/mlx/memory-client.ts +0 -189
  229. package/src/backends/mlx/methods.ts +0 -90
  230. package/src/backends/mlx/model-prompt.ts +0 -164
  231. package/src/backends/mlx/model-serving.ts +0 -79
  232. package/src/backends/mlx/prefill-cohort.ts +0 -89
  233. package/src/backends/mlx/prefill-rows.ts +0 -200
  234. package/src/backends/mlx/prefill.ts +0 -33
  235. package/src/backends/mlx/projected-context-rows.ts +0 -72
  236. package/src/backends/mlx/request-policy.ts +0 -20
  237. package/src/backends/mlx/request-state-policy.ts +0 -41
  238. package/src/backends/mlx/rollback.ts +0 -89
  239. package/src/backends/mlx/row-sampling.ts +0 -49
  240. package/src/backends/mlx/serial-executor.ts +0 -242
  241. package/src/backends/mlx/speculative-group.ts +0 -359
  242. package/src/backends/mlx/speculative-round.ts +0 -161
  243. package/src/backends/mlx/speculative.ts +0 -114
  244. package/src/backends/mlx/state-rows.ts +0 -104
  245. package/src/backends/mlx/state-views.ts +0 -46
  246. package/src/backends/mlx/target-layout-capability.ts +0 -35
  247. package/src/backends/mlx/text-engine.ts +0 -73
  248. package/src/backends/mlx/transitioning-kv-rows.ts +0 -165
  249. package/src/chat-template.ts +0 -382
  250. package/src/cli.ts +0 -3168
  251. package/src/config.ts +0 -399
  252. package/src/contracts/completion.ts +0 -16
  253. package/src/contracts/execution.ts +0 -60
  254. package/src/contracts/generation.ts +0 -103
  255. package/src/contracts/host.ts +0 -7
  256. package/src/contracts/pi-provider.ts +0 -30
  257. package/src/contracts/pi.ts +0 -259
  258. package/src/contracts/prefix-cache.ts +0 -23
  259. package/src/contracts/resources.ts +0 -25
  260. package/src/contracts/sampling.ts +0 -23
  261. package/src/contracts/scheduling.ts +0 -45
  262. package/src/contracts/tsconfig.json +0 -14
  263. package/src/dataset/generators.ts +0 -1087
  264. package/src/dataset/index.ts +0 -12
  265. package/src/dataset/job.ts +0 -81
  266. package/src/dataset/llm.ts +0 -71
  267. package/src/dataset/registry.ts +0 -528
  268. package/src/diffusion/diffusion-generate.ts +0 -507
  269. package/src/download.ts +0 -505
  270. package/src/embed.ts +0 -77
  271. package/src/engine/admission.ts +0 -59
  272. package/src/engine/cancellation.ts +0 -38
  273. package/src/engine/engine.ts +0 -56
  274. package/src/engine/execution-coordinator.ts +0 -70
  275. package/src/engine/execution-plan.ts +0 -53
  276. package/src/engine/resources.ts +0 -53
  277. package/src/engine/scheduler.ts +0 -72
  278. package/src/engine/session.ts +0 -247
  279. package/src/eval/README.md +0 -130
  280. package/src/eval/ifeval.ts +0 -81
  281. package/src/eval/kl-prompts.ts +0 -29
  282. package/src/eval/kl.ts +0 -590
  283. package/src/eval/perplexity.ts +0 -164
  284. package/src/eval/quality-db.ts +0 -92
  285. package/src/eval/reference-logits.ts +0 -207
  286. package/src/eval/runner.ts +0 -124
  287. package/src/eval/score.ts +0 -49
  288. package/src/eval/tasks/bfcl.ts +0 -535
  289. package/src/eval/tasks/gsm8k.ts +0 -98
  290. package/src/eval/tasks/hashhop.ts +0 -139
  291. package/src/eval/tasks/humaneval.ts +0 -275
  292. package/src/eval/tasks/ifeval.ts +0 -525
  293. package/src/eval/tasks/mmlu.ts +0 -157
  294. package/src/eval/ultrafeedback.ts +0 -60
  295. package/src/evaldb.ts +0 -199
  296. package/src/expert-io.ts +0 -393
  297. package/src/expert-offload-build.ts +0 -77
  298. package/src/expert-offload.ts +0 -76
  299. package/src/expert-residency.ts +0 -1134
  300. package/src/expert-usage.ts +0 -415
  301. package/src/fill/echo-index.ts +0 -188
  302. package/src/fill/fill-session.ts +0 -452
  303. package/src/fill/proposal.ts +0 -68
  304. package/src/fill/schema-rows.ts +0 -506
  305. package/src/fill/tool-boundary.ts +0 -103
  306. package/src/fit.ts +0 -249
  307. package/src/generate.ts +0 -1346
  308. package/src/grammar.ts +0 -591
  309. package/src/harness-pi.ts +0 -184
  310. package/src/hf-push.ts +0 -628
  311. package/src/hub-rest.ts +0 -242
  312. package/src/index.ts +0 -63
  313. package/src/inference/denoising.ts +0 -17
  314. package/src/inference/draft-acceptance.ts +0 -41
  315. package/src/inference/draft-output.ts +0 -49
  316. package/src/inference/graph.ts +0 -33
  317. package/src/inference/prefill.ts +0 -39
  318. package/src/inference/rollback.ts +0 -7
  319. package/src/jobs/index.ts +0 -31
  320. package/src/jobs/job-entry.ts +0 -107
  321. package/src/jobs/task-client.ts +0 -19
  322. package/src/jobs/types.ts +0 -2
  323. package/src/kv-scheme.ts +0 -257
  324. package/src/kv-store.ts +0 -1099
  325. package/src/lab/README.md +0 -18
  326. package/src/lab/curve/curve-designer.html +0 -303
  327. package/src/lab/curve/curve-sampler.ts +0 -122
  328. package/src/lab/expert-trace/expert-trace.ts +0 -69
  329. package/src/lab/paged-kv/paged-kv-rows.ts +0 -83
  330. package/src/lab/paged-kv/paged-kv.ts +0 -270
  331. package/src/library.ts +0 -40
  332. package/src/lora.ts +0 -408
  333. package/src/md-modules.d.ts +0 -49
  334. package/src/media-fetch.ts +0 -442
  335. package/src/memory/article.ts +0 -558
  336. package/src/memory/chunk-validation.ts +0 -150
  337. package/src/memory/chunk.ts +0 -318
  338. package/src/memory/cluster.ts +0 -351
  339. package/src/memory/coverage.ts +0 -134
  340. package/src/memory/crosslink.ts +0 -0
  341. package/src/memory/db.ts +0 -253
  342. package/src/memory/entity.ts +0 -203
  343. package/src/memory/gate.ts +0 -187
  344. package/src/memory/history.ts +0 -152
  345. package/src/memory/ingest.ts +0 -335
  346. package/src/memory/model.ts +0 -183
  347. package/src/memory/normalize.ts +0 -671
  348. package/src/memory/parse.ts +0 -22
  349. package/src/memory/pipeline.ts +0 -275
  350. package/src/memory/prompts.ts +0 -65
  351. package/src/memory/query.ts +0 -334
  352. package/src/memory/reconcile.ts +0 -734
  353. package/src/memory/redirect.ts +0 -108
  354. package/src/memory/reindex.ts +0 -325
  355. package/src/memory/resolve.ts +0 -307
  356. package/src/memory/rest.ts +0 -245
  357. package/src/memory/route.ts +0 -272
  358. package/src/memory/schedule.ts +0 -165
  359. package/src/memory/stages.ts +0 -777
  360. package/src/memory/synthesize.ts +0 -1638
  361. package/src/memory/tools.ts +0 -749
  362. package/src/memory/vault.ts +0 -1194
  363. package/src/memory/wikify.ts +0 -1064
  364. package/src/mlx/array.ts +0 -388
  365. package/src/mlx/autograd.ts +0 -304
  366. package/src/mlx/checkpoint.ts +0 -149
  367. package/src/mlx/compile.ts +0 -168
  368. package/src/mlx/custom-vjp.ts +0 -188
  369. package/src/mlx/ffi.ts +0 -512
  370. package/src/mlx/materialize.ts +0 -23
  371. package/src/mlx/metal-kernel.ts +0 -159
  372. package/src/mlx/ops.ts +0 -1021
  373. package/src/mlx/quantized-rows.ts +0 -36
  374. package/src/mlx/token-bitmask.ts +0 -52
  375. package/src/mlx/turboquant-kv-decode.ts +0 -82
  376. package/src/mlx/turboquant-kv-inverse.ts +0 -122
  377. package/src/mlx/turboquant-ops.ts +0 -861
  378. package/src/mlx/turboquant-tables.ts +0 -237
  379. package/src/mmap.ts +0 -90
  380. package/src/model/artifact-identity.ts +0 -73
  381. package/src/model/batched-kv.ts +0 -235
  382. package/src/model/batched-mask.ts +0 -279
  383. package/src/model/batched-quant.ts +0 -212
  384. package/src/model/batched-quantized-kv.ts +0 -144
  385. package/src/model/batched-rotating-quant.ts +0 -353
  386. package/src/model/batched-rotating-state.ts +0 -149
  387. package/src/model/batched-rotating.ts +0 -338
  388. package/src/model/batched-row-storage.ts +0 -179
  389. package/src/model/batched-ssm.ts +0 -52
  390. package/src/model/batched-turboquant-kv.ts +0 -93
  391. package/src/model/compiled-decode.ts +0 -712
  392. package/src/model/decoded-kv-donor.ts +0 -12
  393. package/src/model/delayed-quantized-kv.ts +0 -72
  394. package/src/model/delayed-rotating-quantized-kv.ts +0 -93
  395. package/src/model/delayed-turboquant-kv.ts +0 -55
  396. package/src/model/diffusion-gemma.ts +0 -1026
  397. package/src/model/factory.ts +0 -189
  398. package/src/model/fingerprint.ts +0 -33
  399. package/src/model/flash-attention.ts +0 -694
  400. package/src/model/full-prefill-padding.ts +0 -56
  401. package/src/model/full-prefill-row.ts +0 -105
  402. package/src/model/gemma4-base.ts +0 -2351
  403. package/src/model/gemma4.ts +0 -1315
  404. package/src/model/generated/gemma4-12b.ts +0 -429
  405. package/src/model/generated/gemma4-26b.ts +0 -448
  406. package/src/model/generated/gemma4-e4b.ts +0 -717
  407. package/src/model/generated/index.ts +0 -18
  408. package/src/model/glm52-atlas.ts +0 -511
  409. package/src/model/glm52-cache.ts +0 -792
  410. package/src/model/glm52-config.ts +0 -262
  411. package/src/model/glm52-container.ts +0 -305
  412. package/src/model/glm52-coupling.ts +0 -388
  413. package/src/model/glm52-dsa.ts +0 -606
  414. package/src/model/glm52-expert-layout.ts +0 -253
  415. package/src/model/glm52-layout.ts +0 -152
  416. package/src/model/glm52-memory.ts +0 -500
  417. package/src/model/glm52-mla.ts +0 -792
  418. package/src/model/glm52-moe.ts +0 -223
  419. package/src/model/glm52-pilot.ts +0 -537
  420. package/src/model/glm52-reference.ts +0 -445
  421. package/src/model/glm52-residency.ts +0 -557
  422. package/src/model/glm52-resident-weights.ts +0 -135
  423. package/src/model/glm52-streamed-experts.ts +0 -453
  424. package/src/model/glm52-streamed-metal.ts +0 -610
  425. package/src/model/glm52-weights.ts +0 -503
  426. package/src/model/glm52.ts +0 -1132
  427. package/src/model/implementation.ts +0 -69
  428. package/src/model/kv-attention-view.ts +0 -67
  429. package/src/model/minicpm5.ts +0 -376
  430. package/src/model/profile.ts +0 -443
  431. package/src/model/qwen-append.ts +0 -12
  432. package/src/model/qwen-conv.ts +0 -52
  433. package/src/model/qwen3-delta-state.ts +0 -74
  434. package/src/model/qwen3-delta.ts +0 -451
  435. package/src/model/qwen3-moe.ts +0 -404
  436. package/src/model/qwen3-mrope.ts +0 -252
  437. package/src/model/qwen3.ts +0 -277
  438. package/src/model/qwen3_5-checkpoint.ts +0 -164
  439. package/src/model/qwen3_5.ts +0 -819
  440. package/src/model/rotating-kv-donor.ts +0 -35
  441. package/src/model/rotating-kv-layout.ts +0 -262
  442. package/src/model/rotating-row-transaction.ts +0 -35
  443. package/src/model/speculative-rotating-kv.ts +0 -101
  444. package/src/model/ssm-prefill-padding.ts +0 -39
  445. package/src/model/support.ts +0 -102
  446. package/src/model/trellis-balanced-scatter.ts +0 -51
  447. package/src/model/trellis-linear.ts +0 -672
  448. package/src/model/trellis-shared-m.ts +0 -151
  449. package/src/model/trellis-shared-scatter.ts +0 -144
  450. package/src/model/trellis-splitk-prefill.ts +0 -158
  451. package/src/model/trellis-tiled-prefill.ts +0 -136
  452. package/src/model/trellis-vector-expand.ts +0 -62
  453. package/src/model/trellis-wide-prefill.ts +0 -94
  454. package/src/model/turboquant-codec.ts +0 -83
  455. package/src/model/universal/archs.ts +0 -332
  456. package/src/model/universal/dense.ts +0 -612
  457. package/src/model/universal/modules.ts +0 -170
  458. package/src/model/universal/rope.ts +0 -381
  459. package/src/native/expert_io.c +0 -523
  460. package/src/native/frame_extract.swift +0 -140
  461. package/src/native-pack.ts +0 -162
  462. package/src/pi-provider.ts +0 -53
  463. package/src/pi-session.ts +0 -81
  464. package/src/pi-terminal.ts +0 -274
  465. package/src/pi-web.ts +0 -1932
  466. package/src/preflight.ts +0 -158
  467. package/src/prompt-cache.ts +0 -460
  468. package/src/quantize/allocator.ts +0 -627
  469. package/src/quantize/atomic-output.ts +0 -38
  470. package/src/quantize/calibration.ts +0 -181
  471. package/src/quantize/config-writer.ts +0 -145
  472. package/src/quantize/index.ts +0 -84
  473. package/src/quantize/job.ts +0 -162
  474. package/src/quantize/quantizer.ts +0 -666
  475. package/src/quantize/rotate.ts +0 -603
  476. package/src/quantize/safetensors-writer.ts +0 -249
  477. package/src/quantize/sensitivity.ts +0 -216
  478. package/src/quantize/trellis.ts +0 -586
  479. package/src/quantize/weight-transform.ts +0 -292
  480. package/src/registry.ts +0 -602
  481. package/src/runtime-config.ts +0 -81
  482. package/src/safetensors.ts +0 -158
  483. package/src/sampler.ts +0 -1011
  484. package/src/serve/admin-routes.ts +0 -184
  485. package/src/serve/assets/curve-terrain.html +0 -105
  486. package/src/serve/assets/training-inference-map.html +0 -575
  487. package/src/serve/aux-routes.ts +0 -132
  488. package/src/serve/batch-scheduler.ts +0 -8
  489. package/src/serve/checkpoint-identity.ts +0 -64
  490. package/src/serve/generation-gateway.ts +0 -496
  491. package/src/serve/isolate.ts +0 -609
  492. package/src/serve/kv-budget.ts +0 -14
  493. package/src/serve/lab-routes.ts +0 -341
  494. package/src/serve/lane-registry.ts +0 -45
  495. package/src/serve/model-admin-routes.ts +0 -155
  496. package/src/serve/model-binding.ts +0 -43
  497. package/src/serve/model-host.ts +0 -511
  498. package/src/serve/parent-application.ts +0 -56
  499. package/src/serve/prompt-response-trace.ts +0 -147
  500. package/src/serve/response-lease.ts +0 -24
  501. package/src/serve/session-search.ts +0 -326
  502. package/src/serve/static-routes.ts +0 -101
  503. package/src/server.ts +0 -1314
  504. package/src/spec/assistant-rows.ts +0 -92
  505. package/src/spec/assistant-source.ts +0 -126
  506. package/src/spec/cache-identity.ts +0 -22
  507. package/src/spec/deepspec-rows.ts +0 -29
  508. package/src/spec/deepspec-source.ts +0 -145
  509. package/src/spec/dflash-source.ts +0 -149
  510. package/src/spec/drafter.ts +0 -304
  511. package/src/spec/dspark/ab-stats.ts +0 -136
  512. package/src/spec/dspark/calibration.ts +0 -112
  513. package/src/spec/dspark/data-dflash.ts +0 -156
  514. package/src/spec/dspark/data.ts +0 -201
  515. package/src/spec/dspark/deepspec-module.ts +0 -776
  516. package/src/spec/dspark/generate-dflash.ts +0 -245
  517. package/src/spec/dspark/generate.ts +0 -290
  518. package/src/spec/dspark/loader.ts +0 -23
  519. package/src/spec/dspark/loss.ts +0 -213
  520. package/src/spec/dspark/module-dflash.ts +0 -560
  521. package/src/spec/dspark/module.ts +0 -540
  522. package/src/spec/dspark/quantize-drafter.ts +0 -91
  523. package/src/spec/dspark/sample.ts +0 -112
  524. package/src/spec/generate.ts +0 -276
  525. package/src/spec/glm52-mtp-graph.ts +0 -29
  526. package/src/spec/glm52-mtp-rows.ts +0 -132
  527. package/src/spec/glm52-mtp-source.ts +0 -317
  528. package/src/spec/glm52-mtp-state.ts +0 -33
  529. package/src/spec/ngram-source.ts +0 -204
  530. package/src/spec/projected-draft-rows.ts +0 -73
  531. package/src/spec/qwen-mtp-module.ts +0 -171
  532. package/src/spec/qwen-mtp-rows.ts +0 -219
  533. package/src/spec/qwen-mtp-source.ts +0 -383
  534. package/src/spec/qwen-mtp-state.ts +0 -30
  535. package/src/spec/serve-loop.ts +0 -585
  536. package/src/spec/source.ts +0 -215
  537. package/src/spec/two-model-rows.ts +0 -61
  538. package/src/spec/two-model.ts +0 -113
  539. package/src/ssd-cache.ts +0 -372
  540. package/src/ssd-durability.ts +0 -211
  541. package/src/tokenizer.ts +0 -335
  542. package/src/tool-approvals.ts +0 -131
  543. package/src/tool-call.ts +0 -695
  544. package/src/train/dataset.ts +0 -484
  545. package/src/train/diffusion-lora.ts +0 -216
  546. package/src/train/export.ts +0 -32
  547. package/src/train/flash-cce.ts +0 -1234
  548. package/src/train/forward.ts +0 -266
  549. package/src/train/fuse.ts +0 -243
  550. package/src/train/index.ts +0 -46
  551. package/src/train/job.ts +0 -205
  552. package/src/train/lora-params.ts +0 -254
  553. package/src/train/loss.ts +0 -1591
  554. package/src/train/merge.ts +0 -195
  555. package/src/train/optimizer.ts +0 -183
  556. package/src/train/prefix-shared.ts +0 -490
  557. package/src/train/rank.ts +0 -137
  558. package/src/train/segmented.ts +0 -1828
  559. package/src/train/steel-qmm-header.ts +0 -1461
  560. package/src/train/trainer.ts +0 -1221
  561. package/src/train/watch.ts +0 -542
  562. package/src/vision/diffusion-vision.ts +0 -620
  563. package/src/vision/embedder.ts +0 -168
  564. package/src/vision/preprocess.ts +0 -247
  565. package/src/vision/prompt.ts +0 -373
  566. package/src/vision/qwen3vl-preprocess.ts +0 -409
  567. package/src/vision/qwen3vl-prompt.ts +0 -196
  568. package/src/vision/qwen3vl-tower.ts +0 -555
  569. package/src/vision/siglip.ts +0 -599
  570. package/src/vision/video-frames.ts +0 -148
  571. package/src/web/app.html +0 -2143
  572. package/src/web/app.js +0 -5703
  573. package/src/web/skills/memory/SKILL.md +0 -120
  574. package/src/web/skills/web-research/SKILL.md +0 -46
  575. package/src/web/skills.ts +0 -97
  576. package/src/web/src/adapters-panel.ts +0 -288
  577. package/src/web/src/assistant.ts +0 -451
  578. package/src/web/src/chat.ts +0 -1057
  579. package/src/web/src/composer.ts +0 -959
  580. package/src/web/src/dataset.ts +0 -132
  581. package/src/web/src/finetune.ts +0 -267
  582. package/src/web/src/hub.ts +0 -428
  583. package/src/web/src/main.ts +0 -98
  584. package/src/web/src/markdown.ts +0 -0
  585. package/src/web/src/memory-panel.ts +0 -572
  586. package/src/web/src/model-picker.ts +0 -159
  587. package/src/web/src/palette.ts +0 -346
  588. package/src/web/src/protocol.ts +0 -26
  589. package/src/web/src/quantize.ts +0 -167
  590. package/src/web/src/rag.ts +0 -274
  591. package/src/web/src/sessions.ts +0 -469
  592. package/src/web/src/shell.ts +0 -804
  593. package/src/web/src/ui-catalog.ts +0 -78
  594. package/src/web/vendor/README.md +0 -61
  595. package/src/web-assets.ts +0 -47
  596. package/src/web-tools.ts +0 -478
  597. package/src/weights.ts +0 -255
  598. /package/src/{serve → server}/completion-sink.ts +0 -0
  599. /package/src/web/{icon.svg → public/icon.svg} +0 -0
  600. /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
  601. /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
@@ -1,1990 +0,0 @@
1
- ---
2
- status: landed
3
- axis: ON
4
- canonical-for: speculative-decoding
5
- plan-anchor: "Phase 14 — Qwen 3.x family bring-up `[~]`"
6
- last-verified: 2026-09-07
7
- ---
8
-
9
- # Speculative decoding — one verifier, every draft source
10
-
11
- Speculative decoding in mlx-bun is ONE verify/accept executor
12
- (`src/spec/serve-loop.ts`, `specServeRun`) behind ONE seam
13
- (`src/spec/source.ts`, `DraftProvider` / `DraftSource` / `TargetView`), with
14
- every drafter — a second model, a KV-borrowing head, a trained block
15
- drafter, a model-free lookup, a target-native MTP row — differing only in
16
- what fills the draft. Output is the target's own samples at every position;
17
- a drafter can only change decode SPEED (acceptance, tokens per target
18
- forward), never which tokens are emitted. This doc is canonical for the
19
- seam contract, the verifier/rollback contract, the source roster and each
20
- source's oracle, and the DSpark program (our trained drafter + DeepSeek's
21
- released one). Status and changelog prose live in PLAN.md; this doc keeps the
22
- design plus a short dated History.
23
-
24
- Consolidated 2026-08-23 from `docs/design/speculative-decoding.md` (base),
25
- `docs/design/speculative-decoding.md` (phases 0–6 + the wall-clock-negative problem
26
- statement) and `docs/design/speculative-decoding.md` (12B assistant γ=1 result).
27
- The DSpark handoff (first live run, recipe) is archived at
28
- [docs/archive/investigations/dspark-handoff.md](../archive/investigations/dspark-handoff.md).
29
-
30
- ## 1. The seam (`src/spec/source.ts`)
31
-
32
- ```
33
- DraftProvider server-lifetime: owns the loaded draft weights
34
- .open({ sampler, target: { model, caches } }) → DraftSource (per request)
35
- DraftSource per request: owns draft-side state
36
- prefillMode? "tail-split" (default, mlx-lm shape) | "full" (native MTP)
37
- pinTargetKernelFamily? GLM native MTP: one M=1 quantized family across the verify batch
38
- tapLayers? layer indices the loop must capture on prefill + every verify forward
39
- prefill(promptIds, ctxML?) seed draft state (ctxML iff tapLayers)
40
- draft(feed, n, stepBase, anchorHidden?) → 0..n RETURN LENGTH IS AUTHORITATIVE
41
- commit(d, kAccept, vCtxML?, verifiedHidden?, acceptedTokens?)
42
- weightsBytes admission accounting (0 when the provider owns the weights)
43
- ```
44
-
45
- - `target` is a read-only view of the target model + its LIVE caches (the
46
- same arrays the loop drives). Two-model ignores it; every KV-borrowing
47
- source reads donor K/V, embeddings, lm-head or hidden taps through it.
48
- - `sampler` is the REQUEST sampler (mlx-lm parity: drafts are sampled with
49
- the same sampler as the target; greedy drafting under a temperature>0
50
- request is not parity — two-model and both MTP sources honor this; the
51
- assistant and DSpark sources draft greedily, which is lossless but not
52
- parity at temp>0).
53
- - `feed` is mlx-lm's re-feed rule (generate.py:645-648): `[correction]` after
54
- a rejected round, `[lastDraft, bonus]` after an all-accept round.
55
- - `draft()` may return fewer than `n` (DSpark confidence pruning) or ZERO
56
- (DeepSpec ℓ=0 / ngram no-match): the loop degenerates to one plain target
57
- step, still tapped and committed for context-growing sources.
58
- - `commit()` ownership: `vCtxML` transfers to the source; `verifiedHidden`
59
- stays caller-owned.
60
- - Per-slot readiness: each source binds its own target state (donor views,
61
- anchor hidden, tapped context), which is what a batched executor would
62
- need to open one source per row. The executor is the hard part — variable
63
- accept length per slot breaks a uniform-B step — so per-slot spec is a
64
- batched-scheduler project tracked in
65
- [unified-engine-frontier-plan.md](unified-engine-frontier-plan.md), not
66
- free composition. Today a mounted draft forces the serial lane for every
67
- request (upstream `is_batchable = draft is None`).
68
-
69
- ## 2. The verifier / rollback contract (`src/spec/serve-loop.ts`)
70
-
71
- Faithful to mlx-lm `speculative_generate_step` (generate.py:473-654, read
72
- from the oracle venv). Per round:
73
-
74
- 1. **Draft** `d ≤ n` tokens (`n = min(γ, tokens left)`), with the anchor
75
- hidden offered to sources that borrow it.
76
- 2. **One target forward** over `[pending, ...drafts]` (d+1 positions),
77
- tapped when `tapLayers` is set. The lm-head runs ONCE over the whole
78
- window (batched — matches mlx-lm and optiq; NOT bit-exact to stock
79
- token-at-a-time decode at bf16 knife-edges, see §2.1).
80
- 3. **Accept walk**: sample the target at each position in order through the
81
- shared `StepSampler` (processors, grammar mask, history — identical
82
- ordering to the serial and batched lanes); accept while the target's
83
- token equals the draft's (exact token-match acceptance, NOT
84
- distribution-level rejection sampling); the first mismatch emits the
85
- target's correction, an all-accept round emits the bonus. EOS is never
86
- emitted as content, even when it arrives as an accepted draft.
87
- 4. **Emit** the round's tokens through `onToken` one at a time, in order
88
- (bursts of ≤ d+1), so stop-sequence matching and detokenization see the
89
- stream `generate()` would produce.
90
- 5. **Roll back + commit**: the verify window kept positions `0..kAccept`.
91
- Trimmable caches `trim(d − kAccept)`; recurrent caches
92
- `specRoundRollback(kAccept + 1)`; on full accept `specRoundCommit()`.
93
- Then `source.commit(d, kAccept, vCtxML, vHidden, drafts[0..kAccept))`;
94
- the next anchor hidden is the verify hidden at the emitted position.
95
- 6. **Chain** via the re-feed rule; `pending = emit`.
96
-
97
- **Cache rollback contract** (`src/model/gemma4-base.ts` `Cache`):
98
- `trim(n, bypass?)` for trimmable caches; the optional spec-round trio
99
- `specRoundBegin()` / `specRoundCommit()` / `specRoundRollback(keep)` for
100
- non-trimmable recurrent state. `SSMCache` (`src/model/qwen3-delta.ts`,
101
- gated-DeltaNet) implements the trio: the snapshot is free (MLX arrays are
102
- immutable — the layer hands its replaced conv/recurrent refs to the round),
103
- and rollback restores the snapshot then REPLAYS the kept `keep` window tokens
104
- through conv1d + the delta kernel from recorded position-local inputs
105
- (`qkv`/`a`/`b`, retained not copied). Replay is bit-exact by construction:
106
- the kernel's per-thread loop is serial, so the prefix arithmetic is identical
107
- whether or not the rejected tail was processed (gated in
108
- tests/qwen-ssm-specround.test.ts). The loop arms every cache before the
109
- verify forward; a cache that is neither trimmable nor round-capable ends
110
- speculation for the request.
111
-
112
- **Deliberate deviations from upstream**
113
-
114
- - Rotating-cache ring wrap: upstream RAISES when a `RotatingKVCache` stops
115
- being trimmable mid-generation (generate.py:529-533); a serve endpoint
116
- must not 500 mid-stream, so the loop checks `offset + n + 1 < maxSize`
117
- before each round and, if the window is about to wrap, STOPS SPECULATING
118
- and finishes with plain single-token decode (bit-equivalent — the
119
- target's own samples). The standalone `src/spec/generate.ts` loop keeps
120
- the reference behavior and throws.
121
- - Prompt-cache reuse is BYPASSED on the spec path (fresh caches per spec
122
- request, `cachedTokens = 0`). mlx-lm composes spec with its LRU prompt
123
- cache; wiring ours through `PromptCache` + the SSD tier is the open item
124
- in §8.
125
- - Grammar × spec (novel — no runtime serves both): the drafter runs FREE;
126
- the grammar mask rides the accept walk in `samplePos` (mask before
127
- sample; the matcher advances on emitted tokens only, so rejected drafts
128
- never touch grammar state and no matcher rollback exists). Grammar
129
- termination mid-burst truncates the round. Gates: greedy grammar+spec ≡
130
- greedy grammar-only long-prefix + 100% schema validity
131
- (tests/spec-serve.test.ts).
132
-
133
- Telemetry (`usage.speculation`): drafted / accepted / rejected / targetCalls /
134
- rounds / acceptanceLengths / tokensPerForward / forwardsSaved, plus
135
- per-position `draftedByPos` / `acceptedByPos` (the acceptance-A/B report).
136
- `MLX_BUN_SPEC_TRACE=1` prints per-round feed + drafts.
137
-
138
- ### 2.1 Prefill convention and what "lossless" means here
139
-
140
- - **Oracle prefill shape** (`MLX_BUN_PREFILL_TAIL_SPLIT`, default on): mlx-lm's
141
- speculative path drains BOTH models to len−1 and has NO separate step-0 —
142
- the un-drained last prompt token HEADS the first verify window, and the
143
- first emitted token is that window's position-0 sample. A separate L=1
144
- step-0 is ulp-different from the (1+γ)-window GEMM and flips near-ties.
145
- Gated live 4/4 token-for-token vs the oracle venv (γ∈{2,3} × 2 prompts,
146
- including a knife-edge cell). Sources with `prefillMode: "full"` (Qwen
147
- MTP, GLM MTP) take the full-prompt shape their references use.
148
- - **Batched verify vs stock decode**: both oracles (mlx-lm, optiq) batch the
149
- verify lm-head, so greedy spec is bit-exact to THE SPEC ORACLE and agrees
150
- with stock decode only on tie-free prompts. An earlier port verified
151
- per-position to match stock — a target no real implementation has, at a
152
- γ× lm-head read tax — and was replaced by `picksBatched`
153
- (`src/spec/generate.ts`, 2026-06-14). Losslessness gates therefore use
154
- TIE-FREE prompts (tests/spec-decode.test.ts, spec-serve-assistant,
155
- qwen38-mtp) or long-prefix agreement (spec-decode-12b): a flip on a
156
- tie-free prompt is an accept/reject/rollback bug, never rounding.
157
- - **"Lossless" is a claim about the verifier, not the artifact.** The
158
- Qwen3.8-27B TQ artifact's serve gauntlet recorded MTP at 76% acceptance /
159
- 2.53 tokens per forward WITH an output-divergence flag vs plain greedy
160
- (consistent with verify-width reduction-order near-ties, the same class as
161
- the 12B step-0 convention; margin analysis queued) — it is NOT claimed
162
- lossless on the model card. The 27B bf16-target pairing gate, by
163
- contrast, was token-identical on its tie-free prompt (PLAN 14g).
164
-
165
- ## 3. Draft-source roster
166
-
167
- `--draft-model <dir>` mounts a drafter; the artifact's KIND selects the
168
- provider (`src/server.ts` `detectDraftKind`; `--draft-kind` overrides).
169
- Detection order: `dspark.json` → `dspark`; `config.json` `architectures[0]
170
- === "Gemma4DSparkModel"` → `deepspec`; `model_type` containing `assistant` →
171
- `assistant`; `model_type` ending `_mtp` → `mtp`; else `two-model`. `ngram`
172
- is never detected (no artifact) and refuses a `--draft-model`; any other
173
- kind without one is refused. `--draft-kind mtp` with no `--draft-model`
174
- resolves the companion bundled at `<model>/mtp/` (single-repo artifacts) and
175
- refuses otherwise. GLM-5.2's checkpoint-native MTP row mounts by default
176
- (`--mtp on|off`) and is mutually exclusive with an explicit drafter.
177
-
178
- | kind | source | what fills the draft | target state used | draft sampler | default γ | oracle / gate |
179
- |---|---|---|---|---|---|---|
180
- | `two-model` | `src/spec/two-model.ts` | a full second model, autoregressive, own KV | none | request sampler | 3 (mlx_lm.server) | **L1**: mlx_lm.server `--draft-model`, same pair, greedy, token-for-token (spec-vs-spec). Shared B1/B>1 autoregressive draft state retains independent attention/recurrent boundaries and the pending final-token lag; paired RAM/SSD checkpoints use backend cache codecs. Tokenizer-family probe at startup refuses mismatches (upstream silently accepts ~0%). |
181
- | `assistant` | `src/spec/assistant-source.ts` (+ `drafter.ts`) | Gemma "-assistant" Q-only 4-layer head | donor K/V (last sliding + last full layer) + anchor hidden, borrowed each step | greedy (own head) | 3 | **L2**: optiq `spec_generate` — e4b γ=2 identical 48-token output AND identical accept/reject trace (drafted 60 / accepted 17 / target calls 31), `scripts/oracle/oracle-spec.py`. Head picked by TENSOR PRESENCE (centroid vs tied-embedding) — optiq's config-based detection loads the 12B/26B unified drafters with zero-init centroids → 0% acceptance; ours 29% on the same artifact. |
182
- | `dspark` | `src/spec/dflash-source.ts` (+ `dspark/module-dflash.ts`) | our trained DSpark block drafter (KV injection + Markov + confidence) | multi-layer hidden tap (`tapLayers`, e4b {20,31,41,42}) grown into H_ctx | greedy | pinned to `cfg.gamma` | **Lab** (no external oracle for our checkpoints): CPU smoke, infer-loop bit-identity pins, real-weights serve gate. See §4. |
183
- | `deepspec` | `src/spec/deepspec-source.ts` (+ `dspark/deepspec-module.ts`) | DeepSeek's released `Gemma4DSparkModel` drafters (e.g. `dspark_gemma4_12b_block7`, γ=7) | tap `[5,17,29,41,46]` on the 12B, projected into an incremental context-KV cache; accepted rows only | argmax (temp-0 reference) | pinned to `block_size` | **DeepSpec reference** at temperature 0: its leaky rejection sampling degenerates to exact argmax token-match, RNG-free — round-for-round trace fixtures (`scripts/oracle/oracle-dspark-deepspec.py`; the compare script was deleted 2026-08-23, git history). |
184
- | `ngram` | `src/spec/ngram-source.ts` | model-free prompt lookup: longest trailing k-gram (max..min) matched at its FIRST earlier occurrence; the continuation is the draft | token history rebuilt from prompt + feed/commit | none | 10 (Saxena `num_pred_tokens`); `--ngram-max 3` / `--ngram-min 1` | Ported from Prompt Lookup Decoding (Saxena) + vLLM `ngram`. Lossless by verify — gated token-identical to non-spec greedy (tests/spec-ngram.test.ts on e4b; tests/qwen35-spec-ngram.test.ts on the 0.8B hybrid, proving real accepts AND real DeltaNet rollbacks). |
185
- | `mtp` | `src/spec/qwen-mtp-source.ts` | Qwen-trained MTP head (`qwen3_5_mtp`, one full-attention layer + `fc` merge; `mlx-community/Qwen3.8-27B-MTP-*`) | target `embed_tokens` + `lm_head` (not standalone) + PRE-final-norm last-layer hidden via `tapLayers = [nLayers−1]`; `prefillMode: "full"` | request sampler | `block_size − 1` (=2) | Reference: mlx-vlm `qwen3_5_mtp` drafter. Draft-logit parity vs mlx-vlm 0.6.14: draft tokens exact through the chained block, worst \|Δlogprob\| 1.9e-1 = bf16-vs-f32 floor. Losslessness: token-identical on the tie-free 27B pairing gate (tests/qwen38-mtp.test.ts, opt-in `MLX_BUN_TEST_QWEN38_MTP=1`). Target rollback = the SSMCache spec-round contract (§2). |
186
- | GLM native | `src/spec/glm52-mtp-source.ts` | GLM-5.2's checkpoint-native MTP row (`enorm`/`hnorm`/`eh_proj` + one decoder layer, int8 routed experts) | target embeddings + output head + anchor hidden; own `MLACache` role `mtp`; `prefillMode: "full"`, `pinTargetKernelFamily` | request sampler | `glmMemoryPlan.mtpDraftTokens` | Direct Colibri (SPEC_PIN contract; "niente prefill" — the first draft opens a decode-only window from the anchor). Commit rebuilds accepted rows from the target's VERIFIED hidden window (only the first speculative row was conditioned on a true hidden). Measured in docs/reference/benchmarks.md "Colibri G4 serial native MTP". |
187
-
188
- The `source.ts` header roster lists five of these (two-model, assistant,
189
- dflash, ngram, GLM MTP); `deepspec` and `mtp` are wired in `server.ts` and
190
- the CLI and belong in the same list — this table is the complete roster.
191
-
192
- **Row conventions worth knowing.** Qwen MTP is predict-2-ahead: drafter KV
193
- row at position p is built from `(embed(token_{p+1}), hidden_p)`; `draft()`
194
- opens by building the pending token's row from the target's TRUE hidden at
195
- the emitted position (held over from the verify tap), then chains its own
196
- output for the rest of the block; `commit()` trims rejected rows and, on
197
- all-accept, appends the missing accepted row from the verify hidden. The
198
- sampler contract is `[1, V]` — the module output `[1,1,H]` must be
199
- reshaped before sampling or 2-D-slicing samplers (top-k) throw (the
200
- 2026-08-20 serve-lane MTP 500; both MTP sources carry the fix).
201
-
202
- ## 4. DSpark — the trained block drafter
203
-
204
- DSpark (DeepSeek, arXiv:2607.05147, "Confidence-Scheduled Speculative
205
- Decoding with Semi-Autoregressive Generation") = a **DFlash** parallel
206
- backbone (KV-injected, all γ draft tokens in one pass) + a lightweight
207
- **sequential head** (Markov, Eq 5; RNN, Eq 6) adding intra-block dependency
208
- + a **confidence head → calibration → draft-length scheduler**. DFlash alone
209
- suffers suffix decay; the sequential head is what makes it DSpark.
210
-
211
- ### 4.1 Mechanism (our module, `src/spec/dspark/module-dflash.ts`, paper Eq 2–3)
212
-
213
- - **Eq 2 (context):** tap the target's hidden states at layers `{l1…lm}`
214
- (e4b default {20,31,41,42}, the last = post-finalNorm sentinel at index
215
- nLayers), concat on the feature axis, project to draft width:
216
- `H_ctx ∈ [L_ctx, d]`. Extraction is the parity-safe `model.hiddenTap`
217
- (no-op when off) in `gemma4.ts` `forwardLayers`; `qwen3_5.ts` carries the
218
- same contract.
219
- - **Eq 3 (injection):** every draft layer forms
220
- `K_i = [W_i^K H_ctx; W_i^K H_d]`, `V_i` likewise, `Q_i = W_i^Q H_d`; the
221
- γ-block queries attend bidirectionally over `[context ++ block]`. The
222
- draft projects the target's HIDDEN STATES with its OWN Wk/Wv (not the
223
- target's K/V — that is the assistant drafter).
224
- - **Markov head (Eq 5):** `B_k = W1[x_{k−1}]·W2`, r=256, W2 zero-init →
225
- starts as pure DFlash, τ climbs as intra-block dependency is learned.
226
- - **Confidence head (Eq 7):** `c_k = σ(wᵀ[h_k; W1[x_{k−1}]])` (we add a
227
- zero-init bias — superset).
228
- - **Loss (Eq 8–12):** `0.1·CE + 0.9·TV + 1.0·BCE`, position weights
229
- `w_k = exp(−(k−1)/γ)`, analytic c*. Audit verdict: matches the paper to
230
- the letter; our defaults (γ=5, 5 layers, Markov) = their production
231
- config.
232
- - **RNN head (`cfg.seqHead: "rnn"`)**: ungated Elman
233
- `s_k = tanh(s_{k−1}·wH + E[x_{k−1}] + bH)`, `B_k = s_k·wO` (wO zero-init;
234
- init-equivalent to Markov). The paper's Eq 6 is a gated cell over
235
- `[s_{k−1}; W₁[x_{k−1}]; h_k]` — ours differs, kept as a variant (paper
236
- calls the RNN gains marginal; DeepSpec ships the gated form if ever
237
- wanted).
238
- - **Confidence-scheduled pruning (Alg 1, single-user form)**: `forwardInfer`
239
- drops position k (and truncates the block) when
240
- `c_k < thresholds[k] ?? minConf`; position 0 always survives. Activation is
241
- checkpoint-driven (`cfg.sts` thresholds) or `MLX_BUN_DSPARK_MINCONF`;
242
- uncalibrated checkpoints draft fixed-γ. Invariant: pruning changes how
243
- many positions the target verifies, never what is emitted (gated:
244
- truncation-never-redraw, prefix-identical to the unpruned block).
245
- - **Calibration (`src/spec/dspark/calibration.ts`)**: per position, the
246
- smallest threshold whose Laplace-smoothed `P(accepted | conf ≥ τ)` meets
247
- the target; pos 0 → 0; under-sampled → 0; unreachable → 1.0. This fits the
248
- RELEASED reference's scheduler shape (per-position threshold truncation on
249
- sigmoid confidence, default 0 = off, ℓ=0 allowed). The PAPER's §3.2.1 STS
250
- (per-position temperatures by ECE grid search on survival products) and
251
- Alg 1's throughput maximization over a profiled SPS(B) table live in
252
- DeepSeek's UNRELEASED serving layer — paper-faithful STS/Alg-1 is a Lab
253
- item only if the survival-product scheduler ever earns a default.
254
- - **Verify**: the serve loop's token-match acceptance (§2). The standalone
255
- `generate-dflash.ts` keeps the paper's distribution-level verify
256
- (`min(1, p/q)` accept + residual `norm(relu(p−q))` resample) for the
257
- measure path; greedy is identical, temp>0 is lossless either way but
258
- token-match accepts less.
259
- - **Rollback past the sliding window**: `trim(n, bypass=true)` physically
260
- slices rejected tips on rotating caches (`gemma4-base.ts`).
261
- - **Checkpoint format**: `dspark.json` stamps `variant: "dspark"` (legacy
262
- `"dflash"` accepted); `src/spec/dspark/loader.ts` `loadDsparkDrafter()`
263
- dispatches by variant and REFUSES v1 single-vector checkpoints
264
- (`module.ts`/`data.ts`/`generate.ts` — the superseded baseline that
265
- dropped KV injection) with a pointer at the trainer.
266
-
267
- ### 4.2 The v1 → v2 finding (why "faithful" matters)
268
-
269
- v1 fused a single final-hidden vector as one token — it removed the paper's
270
- load-bearing mechanism and never tested it. v2 is faithful. Getting v2 to
271
- paper-range required an off-by-one fix in the TV-loss target alignment
272
- (block position k predicts `x_{t+1+k}` whose target is
273
- `softmax(LM_head(h_{t+k}))`; the data gathered `t+k+1`) — present in both
274
- v1 and v2, fixed in `data-dflash.ts` (`blockIdx = g+k`). Overfit on 3
275
- articles: per-position acceptance ~0.75, τ 3.24 (paper range 0.6–0.9 / τ
276
- 3–4) — the architecture is correct. 160 articles generalize to ~0.17 and
277
- plateau; width is not the ceiling (`dDraft` 1024 and 2560 both plateau);
278
- the paper trains on 1.3M samples × 10 epochs. (M1 Max 32 GB, e4b
279
- OptiQ-4bit, 2026-06-30.)
280
-
281
- ### 4.3 DeepSpec — the released reference, and why DSpark has an oracle
282
-
283
- DeepSeek open-sourced DeepSpec (MIT) + trained drafters incl.
284
- `deepseek-ai/dspark_gemma4_12b_block7` (6.86 GB bf16, γ=7, for
285
- gemma-4-12B-it). Their drafter is a DIFFERENT module from ours: a full
286
- Gemma4-shaped 5-layer transformer — hidden 3840, 16 Q heads × head_dim 512,
287
- 1 KV head with K≡V (`attention_k_eq_v`, no v_proj), scale 1.0 (QK-norm),
288
- partial RoPE 0.25 θ=1e6, per-layer `layer_scalar`, scaled embed (×√3840),
289
- UNTIED lm_head, final softcap 30, incremental context-KV cache. 74 bf16
290
- tensors, no key prefix, `architectures: ["Gemma4DSparkModel"]`. 12B tap
291
- layers `[5,17,29,41,46]` (their layer_id+1 output convention == our
292
- `tapLayers` index; no final-norm sentinel). Ported verbatim in
293
- `deepspec-module.ts` (transcribe their forward, then op-for-op), with
294
- context rows cached post-norm-post-rope (bit-equivalent to their in-round
295
- concat; argued in `projectContextKV`). Argmax is over the bf16 sum — an f32
296
- sum reorders near-ties vs torch.
297
-
298
- Consequence: DSpark is no longer oracle-less. The DeepSpec port + their
299
- checkpoint is the no-training path to a 12B speedup, gateable
300
- round-for-round at temp 0. Our trainable module remains the research path
301
- for custom targets. Their training recipe (for scale reference):
302
- open-perfectblend regenerated by the target, 10 epochs, batch 512, lr 6e-4,
303
- ~38 TB cached hiddens.
304
-
305
- The drafter also loads QUANTIZED: `scripts/dspark.ts quantize` writes an
306
- mlx-native affine sibling (`src/spec/dspark/quantize-drafter.ts`: every 2-D
307
- matmul weight + `embed_tokens`/`markov_w1` as quantized-gather tables;
308
- `confidence_head`, norms, `layer_scalar`, biases stay bf16 — the mlx
309
- `false` convention), and `deepspec-module.ts` detects `.scales` siblings and
310
- routes through `quantized_matmul` / QuantizedEmbedding gather-dequant
311
- (mlx-lm numerics). 8-bit tracks bf16 token-for-token on the synthetic
312
- checkpoint. Drafter numerics only move ACCEPTANCE, never correctness, so the
313
- entire quality gate for any drafter quantization is one acceptance A/B.
314
-
315
- ### 4.4 The wall-clock problem (why spec is opt-in)
316
-
317
- Spec decode amortizes fixed draft overhead only when the TARGET forward is
318
- expensive. The recurring finding across every drafter and target:
319
-
320
- - **e4b is near the worst case** (fast target): assistant γ=2 ran 0.78× of
321
- a 54 tok/s baseline at ~23% acceptance (Phase 6, 2026-06-10); even a τ≈3
322
- DSpark drafter nets ~0.4–0.5×.
323
- - **12B assistant γ=1 is a modest win** — see History 2026-06-14.
324
- - **12B + DeepSpec block7 (first live run, 2026-07-07, loaded M4 Pro
325
- 24 GB, directional only):** τ ≈ 2.8 committed tokens per target forward
326
- at 26–33% per-token acceptance (the predicted OptiQ-4bit-target
327
- degradation vs their bf16 training) — the algorithm WORKS — but
328
- wall-clock LOST ~3.4× (spec 14.6 vs serial 49.8 aggregate tok/s, conc-4).
329
- The tax is the drafter: 6.9 GB bf16 → ~3 GB of weight reads per round
330
- (2 GB untied lm_head + 7 × 134 MB markov_w2 + layers), ~14 host syncs
331
- per round in the transcription-faithful `draftBlock`, tapped verifies
332
- falling back to the unoptimized monolith, and ~14 GB of paired weights
333
- on a 24 GB box.
334
- - **27B + Qwen native MTP (2026-08-18, quiet M1 Max 32 GB, interleaved
335
- off/on ×3, spreads 2.2%/4.4%, arms token-identical):** median OFF 15.75
336
- vs ON 12.93 tok/s — 0.821× at γ=2 despite 61% acceptance and 2.25
337
- tokens/forward. The bf16 head's serial per-draft cost (full-vocab lm-head
338
- matmul + sample per draft) exceeds the saved 27B forwards. Levers if
339
- reopened: quantize the head; batch/defer its per-draft lm-head sampling.
340
- - **GLM-5.2 native MTP (M1 Max 32 GB, Colibri G4):** 1.235× wall
341
- throughput, 32/92 accepted, 2.065 tokens per verify forward — the one
342
- measured wall-clock win, on a target whose forward is expensive enough
343
- (docs/reference/benchmarks.md).
344
-
345
- Doctrine: a lone request must never get slower because a flag exists; spec
346
- stays opt-in per (target, drafter) pair until it wins a clean-machine
347
- paired A/B; losing configs stay documented default-off. Denominator
348
- discipline: speedups only ever vs the SAME model's non-spec baseline.
349
-
350
- ### 4.5 The DSpark serving program (12B) — phases and gates
351
-
352
- Goal: spec ≥ 1.3× serial decode on 12B at the best config, clean-machine
353
- paired A/B — then decide when it is on by default. Objective stated right
354
- (Josh, 2026-07-07): 26–33% acceptance with the bf16 drafter is the
355
- WORKABLE baseline (τ≈2.8 already pays in forwards); the question
356
- quantization must answer is PRESERVATION — do we keep that number at
357
- 4-bit? Sensitivity is MEASURED, never guessed: optiq's method (simulate-quant
358
- one group → KL vs reference on a calibration mix → greedy knapsack to target
359
- bpw), cheaper on the drafter because cached tapped hiddens remove the 12B
360
- from the sweep loop. TurboQuant's role is better-quant-at-EQUAL-bits (finer
361
- levels around the Gaussian peak) — a preservation instrument, measured at
362
- equal bpw before any lower rung.
363
-
364
- | phase | what | state |
365
- |---|---|---|
366
- | 0a | oracle-gate the bf16 DeepSpec port ONCE (round-for-round vs their temp-0 trace on the bf16 HF 12B; OptiQ arm acceptance-only) | open (Josh: torch venv + GPU) |
367
- | 0b | γ sweep {2,3,5,7} at conc 1 → per-γ tok/s / acceptance / TTFT table | open |
368
- | 0c | time attribution (draft GPU / draft host syncs / verify / tap+commit; paging check) — orders phases 1–3 by measured value | open |
369
- | 1a | `scripts/dspark.ts quantize` | done 2026-07-07 |
370
- | 1b | quantized forward in `deepspec-module.ts` | done 2026-07-07 |
371
- | 1c | `scripts/dspark.ts ab` — same target, same ≥32 prompts, temp 0, drafter A vs B; per-position acceptance, τ, tok/s, paired verdict (drop ≤ 3 pts AND wall-clock strictly improves); stats in `ab-stats.ts`, unit-tested | done 2026-07-07 |
372
- | 1d | RUN 4-bit vs bf16 at best-γ | open (GPU) |
373
- | 1e | measured sensitivity map (optiq's method on cached hiddens) → knapsack allocation → one confirming A/B | open |
374
- | 1.5 | OPTIONAL acceptance upside: adaptation fine-tune on OUR target's tapped hiddens (their checkpoint as init; gradient in their torch trainer vs a local path — undecided); threshold sweep at 26% (objective = tok/s, not τ); target-fidelity axis measurement | open |
375
- | 2 | tighten `Gemma4DSparkModel.draftBlock`: on-device token chaining (one host read per round), compute no confidence at threshold ≤ 0, skip unread `baseLogits`; gate = bit-identity on the synthetic checkpoint + real-checkpoint determinism | open |
376
- | 3 | `captureLayer` in the generated Gemma forwards (`scripts/gen-model.ts`) so tapped prefill/verify keep the fast path; drop the `hiddenTap !== null` fallback guard; regen 12b/26b/e4b; gate = generated-parity + tap-parity + spec bench | open (guard still present 2026-08-23) |
377
- | 4 | serving UX on 0–3 data: per-kind γ default (trained γ is the cap, not necessarily the default), quantized-drafter UX (`mlx-bun get` → quantize), spec × prompt-cache composition, surface audit (`/stats` drafter+kind+γ; server-config "speculative serving" section), the default decision per pair written down | open |
378
- | 5 | TurboQuant weight scheme for the drafter: TQ-4 vs affine-4 AT EQUAL BPW through the 1c harness (headline); lower rungs only after that is won; method (TQ vs affine) and allocation (knapsack) are orthogonal axes, never mixed within an artifact; per-tensor mixed rungs from the measured table (lm_head ~2 GB read fully per round; embed ~2 GB gather-only; markov_w2 134 MB ×7) | open |
379
- | 6 | clean-machine protocol → promote to benchmarks + default decision; close or re-defer loose ends (gated RNN head, paper Alg-1/STS, per-slot spec) | open |
380
-
381
- Exit criteria: (1) ≥ 1.3× on 12B clean-machine paired, or a written verdict
382
- that it cannot be reached and why; (2) acceptance PRESERVED and understood
383
- (the quantized drafter holds the bf16 baseline, or the mixed rung that does
384
- is adopted, or the loss is measured and accepted); (3) the
385
- acceptance-per-byte curve exists (TQ vs 4-bit vs mixed vs bf16); (4) the
386
- surface is documented, defaults decided per pair, and `mlx-bun get` →
387
- serve → speedup works without reading a design doc.
388
-
389
- ### 4.6 The 27B program (Qwen3.8, 2026-08-19)
390
-
391
- The TurboQuant-weights campaign's 17 GB Qwen3.8-27B (~11.8 tok/s decode,
392
- 2026-08-19) is the slow-target regime where τ≈3 nets 2–3×, and it retires
393
- the "27B infeasible to train on 24 GB" premise: with the quantized trunk
394
- FROZEN (inference-only taps + teacher logits) and only the ~5-layer drafter
395
- holding grads/optimizer state, drafter training fits a 32 GB machine.
396
- Verify/rollback across the 48 DeltaNet layers is proven engine behavior
397
- (§2). Two tracks, cheap-first, queued behind the certification suite's GPU
398
- ownership (PLAN.md TurboQuant-weights phase, "Queued follow-ups"):
399
-
400
- - **Track A — confidence-scheduled verification on the stock MTP head (no
401
- training).** Dynamic per-step block length: v0 confidence = the draft's
402
- own token probability, mapped through the `scripts/dspark.ts calibrate`
403
- pattern to expected acceptance; stop drafting when marginal
404
- expected-accept drops below the verify amortization point. Calibration
405
- data = thousands of (confidence, accepted?) pairs from served traffic.
406
- Gate: paired A/B tok/s vs fixed-γ MTP; lossless by construction.
407
- - **Track B — true DSpark drafter for the 27B.** The faithful v2 retargeted:
408
- tap-layer selection for the 64-layer trunk (bias toward the 16
409
- full-attention layers + post-finalNorm), data by TEACHER-FORCED passes
410
- over corpus text with the frozen trunk (every position yields a sample).
411
- Open idea: initialize/distill against the shipped MTP head. Gate:
412
- generalizing per-position acceptance must beat the MTP head's 0.68 or
413
- Track B is dropped for Track A + MTP.
414
-
415
- Drafts trained on the TQ artifact are married to its rotation basis (card
416
- note if published). `RadixArk/Qwen3.8-27B-DSpark` exists as a cross-check
417
- candidate (PLAN 14h, optional).
418
-
419
- ### 4.7 Packed 27B MTP recheck after kernel changes (2026-09-05)
420
-
421
- The M4 Pro screen uses the actual packed k300 artifact and the existing
422
- folded MTP companion from the affine flagship. Their recorded R1 seed,
423
- hidden size and R2 setting agree; the head's documented final-gamma
424
- approximation remains a draft-quality limitation. No head was downloaded,
425
- trained or changed. The target verifies every MTP proposal; this is separate
426
- from strict fill's no-verification assertions.
427
-
428
- `tests/parity/qwen38-mtp.test.ts` now supports a warm native wall-time
429
- diagnostic via `MLX_BUN_TEST_MTP_REPORT`. Target/draft path overrides select
430
- the exact pair; `MLX_BUN_TEST_MTP_GAMMA` selects 1..4. The report records
431
- both token sequences, actual completion counts, first-token and full-request
432
- wall time, speculation counters, source and artifact identity, and machine
433
- state. It does not compare differently defined decode-rate counters.
434
-
435
- Six AB/BA blocks on the short enumeration fixture compare gamma 2 on variants
436
- 6 and 8, then gamma 1/2/3/4 on variant 8. The optimized verification kernels
437
- improve MTP's economics on that fixture; gamma 4 loses when the target append
438
- crosses the packed/expanded boundary. Exact emitted-token identity holds in
439
- the observed cells. These are loaded-machine diagnostics under
440
- `reports/qwen38-rd/mtp-v*-gamma*.json`, not default-promotion evidence.
441
-
442
- A predeclared code, explanation, arithmetic, extraction, JSON and prose
443
- screen has one warm pair per prompt with alternating arm order. Four valid
444
- cases improve, while prose loses with lower draft acceptance. The extraction
445
- case ran past chat EOS and is excluded from serving comparisons. All six
446
- preserve the observed token IDs, including that invalid overrun. Reports and the frozen prompts are under
447
- `reports/qwen38-rd/mtp-corpus-screen/` and `mtp-screen-prompts.json`. The
448
- fixed output budget truncates some answers; this is not a task-quality eval.
449
- MTP remains opt-in. Repeat diverse longer sessions, include rejection/rollback
450
- cost and memory, and test a bounded adaptive policy on held-out prompts before
451
- changing that decision.
452
-
453
- The next adaptive screen stays within the existing `DraftSource` contract:
454
- start at two drafts, decrease after a partial acceptance and increase toward
455
- three only after two fully accepted rounds. Per-request state stays with the
456
- source. Keep one draft as the floor for this experiment. Qwen MTP's current
457
- `draft(n<=0)` returns without appending its pending cache row, and the
458
- zero-draft commit does not add that row. A skip/recovery policy therefore
459
- needs an explicit cache-advancement design before it can preserve the source's
460
- alignment. This is a source-audit constraint on a future policy, not a failure
461
- in the current fixed-positive-length path. Compare complete native/HTTP
462
- requests and memory against both fixed gamma and non-spec generation on new
463
- frozen prompts before retaining an adaptive policy.
464
-
465
- The subsequent three-arm serving gate freezes three new prompts and the
466
- adaptive rule before measurement. It uses the lossless interleaved packed
467
- artifact, variant 13 and shared affine dispatch, with one serial request at a
468
- time, temperature zero, seed 42, thinking disabled, no prompt/SSD cache and a
469
- 128-token budget. Six balanced-order blocks start 18 fresh servers. All exit
470
- cleanly; measured sources remain fixed. Across 126 warm/measured responses,
471
- all 84 native-versus-MTP text, prompt-count and finish-reason comparisons
472
- match. The 12 JSON completion-count differences remain in the report. Native
473
- `generateInner` counts the stopping EOS before excluding it from content;
474
- `specRunInner` excludes it from both emitted content and `generatedTokens`.
475
- Thus JSON reports 112 versus 111 despite identical response text. Compare
476
- complete wall time for that cell, not the differently counted decode rates.
477
-
478
- On the M4 Pro 24 GB, these are diagnostic medians in seconds:
479
-
480
- | Frozen prompt | Native | Fixed two drafts | Adaptive one to three drafts |
481
- |---|---:|---:|---:|
482
- | TypeScript run-length encoder | 11.912 | 6.690 | 6.483 |
483
- | Composite database index explanation | 11.914 | 7.217 | 7.062 |
484
- | Normalize three records to JSON | 10.969 | 5.968 | 5.751 |
485
-
486
- Every fixed/native, adaptive/native and adaptive/fixed complete-time pair
487
- improves. Median paired adaptive/fixed time ratios are 0.9698, 0.9782 and
488
- 0.9624 respectively. Fixed MTP also reduces observed TTFT by about 160 ms;
489
- adaptive/fixed TTFT is essentially unchanged. Median per-server peak RSS is
490
- 12,370,534,400 bytes native, 13,045,129,216 fixed and 13,029,392,384 adaptive.
491
- The adaptive source exercises widths one, two and three; its policy state is
492
- per request through the existing provider interface. The earlier broad corpus
493
- and these held-out cells still do not establish a universal MTP gain. Code
494
- and explanation stop at the token budget, so this is not a task-quality eval.
495
- Machine preflight fails on existing swap use. Fixed MTP remains opt-in and
496
- the adaptive wrapper is research-only. Raw evidence and source identities are
497
- `reports/qwen38-rd/mtp-heldout-{plan,http,http-review}.json`. The native follow-up runs both arm orders and preserves every emitted token
498
- ID in all 18 runs. Its 500 draft rounds include 78 partial-acceptance commits
499
- and all three adaptive widths. Assertions check source offsets before and
500
- after each draft and all 488 commits; all 12 sources dispose cleanly. Both
501
- JSON runs emit exactly 111 IDs on every arm, confirming the separate native
502
- EOS accounting. This is an alignment and trajectory gate, not a claim of
503
- identical cache bytes between speculative and serial computation. The first
504
- native run includes loading, so its times do not form a warm performance
505
- comparison. Evidence: `reports/qwen38-rd/mtp-heldout-native{,-review}.json`.
506
- Longer contexts, other quants and both quiet-machine gates remain open.
507
-
508
- The EOS usage discrepancy is now fixed in the speculative serving loop.
509
- It counts a stopping EOS from the initial sample, accepted draft, correction,
510
- bonus or plain-decode fallback without emitting it. A callback or token-budget
511
- stop before that position excludes the unused suffix. This follows the pinned
512
- mlx-lm `stream_generate` final `generation_tokens=n+1` accounting. Nine
513
- model-free cases reproduce seven old failures and cover both early-stop
514
- controls. All typechecks, hygiene and 1,812 model-free tests pass. The fresh
515
- three-arm HTTP gate completes 21 requests with fixed sources, clean exits and
516
- no text, prompt-count, completion-count or finish-reason differences. JSON
517
- now reports 112 tokens on every arm. This single block checks the fix; the
518
- historical timing reports retain their original counts. Evidence:
519
- `spec-token-counts-{before,after,typecheck,hygiene,model-free}.txt` and
520
- `mtp-post-eos-http{,-review}.json` under the campaign reports.
521
-
522
- An observed EOS now survives the method and serving adapters as
523
- `finishReason: "stop"`. Native and speculative loops record it explicitly;
524
- the adapters use the observed cause before their legacy count-based fallback.
525
- Tool-call and explicit text-stop handling retain precedence. The first HTTP
526
- replay exposed two additional serving overrides: at the known 112-token JSON
527
- EOS budget, it still returned `"length"`. Three regression tests reproduced
528
- those failures before the adapter fix. The final fresh replay completes all
529
- 21 requests with fixed sources, clean exits and unchanged text/counts.
530
- Native, fixed and adaptive MTP now report `"stop"` for that JSON response
531
- and `"length"` for the two genuinely budget-limited responses. All typechecks,
532
- hygiene and 1,820 model-free tests pass. The speculative tests also cover
533
- bonus EOS at the limit and early callback stops. Evidence:
534
- `spec-eos-serving-{before,after,typecheck,hygiene,model-free}.txt`, the failed
535
- `mtp-eos-budget-http{,-review}.json` gate and the passing
536
- `mtp-eos-budget-integrated-http{,-review}.json` replay.
537
-
538
- The continuous scheduler already records the terminal cause in `BatchStats`;
539
- the gateway now preserves it when returning `GenerateStats`. Two regression
540
- tests reproduce the dropped stop/length field before this correction. The
541
- expanded model-free tier passes 1,822 tests, with all typechecks and hygiene
542
- passing. Two fresh default-scheduler servers then preserve all six HTTP
543
- responses under synchronous/bounded projection evaluation. Both measured JSON
544
- responses count 112 tokens and finish with `"stop"`; every request reports
545
- the wire lane `"batched"` and increments the scheduler submission count.
546
- Sources stay fixed and both servers exit cleanly. The test seeds MLX to 42
547
- at startup and uses greedy sampling; an explicit request seed would force
548
- serial placement. The initial harness used the wrong wire enum and stopped
549
- at its routing assertion. Evidence:
550
- `spec-eos-continuous-{before,after,typecheck,hygiene,model-free}.txt`,
551
- `eos-budget-continuous-http.json` and
552
- `eos-budget-continuous-v2-http{,-review}.json`.
553
-
554
- The first draft-only quantization screen replaces the MTP head's eight dense
555
- projections with affine g64 8-bit or 4-bit matmuls. Target computation and
556
- fixed two-token verification are unchanged. Three fresh servers complete 21
557
- requests with identical response text, counts and finish reasons, fixed
558
- sources and clean exits. Relative to the dense draft, 8-bit complete time is
559
- flat on code and falls about 2% on explanation/JSON; 4-bit falls 0.92%, 3.15%
560
- and 3.06%. These are single-block diagnostics, requiring repeated pairs.
561
- Acceptance changes slightly, including an extra code verification round.
562
- The prototype retains dense buffers in every arm and adds 451/239 MB of
563
- quantized arrays, so it establishes no memory saving. Its preload explicitly
564
- disposes providers after request shutdown because normal CLI exit retains
565
- the resident provider; the initial experiment stopped on that cleanup check.
566
- Evidence: `mtp-quantized-draft-screen-http.json` and the completed
567
- `mtp-quantized-draft-screen-v2-http{,-review}.json` under campaign reports.
568
-
569
- The six-block repeat completes 126 requests across 18 fresh servers with
570
- all 84 paired response comparisons identical, fixed sources, clean exits
571
- and explicit provider disposal. Median paired request wall time with the
572
- 4-bit draft falls 1.45% on code, 3.09% on explanation and 2.03% on JSON,
573
- with six/five/four improving pairs. The 8-bit reductions are 0.34%, 2.16%
574
- and 2.07%, with six/six/five improving pairs. TTFT is approximately flat.
575
- The retained dense buffers mean sampled RSS remains unsuitable evidence
576
- for the benefit of removing those weights. Quantized on-disk artifacts and
577
- their actual loader/serving/memory gates are next; broader prompts, quants
578
- and quiet acceptance remain. Evidence:
579
- `mtp-quantized-draft-repeat-http{,-review}.json`.
580
-
581
- The existing atomic CPU converter produces separate folded 4-bit and 8-bit
582
- MTP artifacts with 238,930,944 and 451,267,584 tensor bytes, respectively.
583
- Both retain all seven dense norm tensors and quantize exactly eight matrices.
584
- The 4-bit artifact fails the initial comparison against GPU quantization,
585
- but all 31 saved tensors match fresh CPU quantization or source passthrough.
586
- CPU/GPU results differ in 16 quantized tensors, totaling 810,927 differing
587
- bytes. The pinned Python oracle reproduces Bun's CPU and GPU hashes for all
588
- three fc tensors, and the original companion files are unchanged. MLX 0.31.2
589
- uses `std::rint` in the CPU affine quantizer and `round` in Metal; these
590
- artifacts therefore require fresh serving/acceptance gates rather than
591
- inheriting the GPU prototype's results. The 8-bit artifact passes all 31 CPU
592
- comparisons, with fixed sources and zero active allocation after cleanup.
593
- The provider now loads companion projections through the existing
594
- `QuantizedLinear` when scales are present and retains dense projection
595
- behavior otherwise. Its resource stack continues to own dense transpose
596
- views; quantized companions retain no dense matrices. No draft policy or
597
- target verification changes. The loader and initial serving/memory gates
598
- pass below. Evidence: `mtp-draft-artifact-q4-{build,audit}.json`,
599
- `mtp-draft-quant-oracle.json` and `mtp-draft-artifact-q8-build.json`.
600
-
601
- The integrated loader passes all three typechecks, hygiene and the existing
602
- 1,822-test model-free suite. Its ownership file also passes a new malformed
603
- quantization-metadata case after earlier dense projections have loaded.
604
- Nine materialized provider cycles cover dense/8-bit/4-bit heads three times
605
- each. Loaded allocation is exactly 849,398,784/451,267,584/238,930,944 bytes,
606
- and every cycle returns to its initial zero active allocation after repeated
607
- disposal, without GC. The initial ownership helper read the counter before
608
- lazy weight materialization and failed its coverage assertion at zero/zero;
609
- it did not reproduce a leak. These are companion allocation measurements,
610
- not complete-server RSS. The additional typecheck after the new ownership
611
- case also passes. Evidence: `mtp-quantized-loader-{typecheck,hygiene,model-free}.txt`,
612
- `mtp-quantized-loader-typecheck-v2.txt`,
613
- `mtp-quantized-loader-ownership-tests-v2.txt` and
614
- `mtp-artifact-ownership{,-v2}.json`.
615
-
616
- The saved-artifact HTTP screen completes 21 responses across three fresh
617
- servers. All 14 dense-versus-quantized comparisons preserve text, prompt and
618
- completion counts, and finish reasons. Sources remain fixed; all servers exit
619
- cleanly. Observation confirms dense projections only in the dense arm and
620
- quantized projections only in the smaller arms. Explicit provider disposal
621
- releases exactly each companion's tensor allocation and reaches the same
622
- 11,936,569,478-byte resident-target baseline. This proves the smaller heads
623
- load without retained dense matrices. Both quantized heads need one extra
624
- target call on the code prompt, so cheaper projections do not guarantee a
625
- faster request. Six balanced repeat blocks and instrumented native replay
626
- are the next gates. Evidence: `mtp-artifact-screen-http{,-review}.json`.
627
-
628
- Six balanced saved-artifact blocks then complete 126 responses from 18 fresh
629
- servers, with all 84 paired text/count/finish comparisons exact, fixed sources
630
- and clean exits. On the M4 Pro 24 GB, median paired complete-time changes
631
- for code/explanation/JSON are -1.48/-3.67/-2.95% for the 4-bit companion and
632
- -0.65/-2.29/-1.86% for 8-bit. The 4-bit arm wins 5/6, 6/6 and 5/6 pairs;
633
- 8-bit wins all six pairs per fixture. TTFT is approximately flat. Median
634
- per-server peak RSS is 13,036,756,992 bytes dense, 12,740,034,560 bytes 8-bit
635
- and 12,531,777,536 bytes 4-bit. Unlike the earlier GPU projection prototype,
636
- these arms actually replace the dense companion tensors. Every explicit
637
- provider disposal releases exactly its companion's allocation to the same
638
- resident-target baseline. Existing system swap prevents canonical benchmark
639
- claims. Instrumented native token/logit/state checks, broader contexts/quants,
640
- pressure and quiet-machine acceptance remain. Evidence:
641
- `mtp-artifact-repeat-http{,-review}.json`.
642
-
643
- The subsequent native audit runs all three saved companions against the same
644
- full-prefill native generation on each short fixture. All nine emitted-ID,
645
- usage and terminal-cause comparisons pass. For every verification window,
646
- it clones the incoming target state and replays the same tokens individually
647
- with the same hidden taps. All 1,184 complete vocabulary-logit rows across
648
- 395 windows match byte-for-byte. At the first three and every sixteenth
649
- window per request, every live attention and recurrent cache array also
650
- matches, covering 48 complete state samples. All 386 commits, including 58
651
- partial accepts, preserve the source position; all nine sources dispose.
652
- Three fresh children exit cleanly with fixed sources and the same 16,390-byte
653
- post-model-disposal active counter. This is instrumented correctness evidence,
654
- not a timing result. Longer fixtures and other quants remain separate gates.
655
- Evidence: `mtp-artifact-native{,-review}.json`.
656
-
657
- Two new warehouse-ledger fixtures extend that audit to 688 and 1,968 prompt
658
- tokens, using matching 2,048-token full-prefill chunks. Dense, 8-bit and 4-bit
659
- companions preserve all six native/MTP ID, count and finish comparisons.
660
- All 180 vocabulary-logit rows and 18 sampled complete cache states match;
661
- 54 fully accepted commits preserve position. All twelve native/MTP outputs
662
- are valid JSON and contain the independently computed requested records.
663
- Each response counts 30 generation tokens. Sources stay fixed, all three
664
- children exit cleanly and each leaves the same 16,390-byte active counter.
665
- This extends correctness coverage; long serving pressure and other quants
666
- remain open. Evidence: `mtp-artifact-native-context{,-review}.json`.
667
-
668
- The matching saved-companion HTTP screen completes 15 warm/measured responses
669
- from three fresh servers at those two context lengths. All ten paired
670
- text/count/finish comparisons match, with fixed sources and clean exits.
671
- Both measured fixtures stop at 30 generation tokens. Dense and quantized
672
- projection counts and explicit provider release pass on every arm. This is
673
- one serving screen, not a repeated performance or long-agent pressure claim.
674
- Evidence: `mtp-artifact-context-screen-http.json`.
675
-
676
- Matched-prefill timing screens now separate native tail-split, native full
677
- prefill and dense fixed-two MTP in fresh HTTP servers. All 21 packed responses
678
- preserve text/count/finish across the three arms. The RTN4 screen also completes
679
- 21 responses; MTP matches native full-prefill throughout, while the two measured
680
- code/explanation tail-split responses retain their known text differences.
681
- Both screens have fixed sources and clean exits. On the M4 Pro 24 GB, complete
682
- MTP request times improve 38.8–44.0% on packed and 27.6–38.1% on RTN4 relative
683
- to native full-prefill. Each is one block, pending balanced repeats. MTP's
684
- remaining TTFT advantage motivates checking native work before the first yield;
685
- its decode loop constructs the next step before emitting the current token.
686
- No default prefill policy changes. Evidence:
687
- `mtp-matched-prefill-{r6,rtn4}-screen-http{,-review}.json`.
688
-
689
- The packed matched-prefill repeat completes six balanced three-arm blocks,
690
- 18 servers and 126 warm/measured responses. All text, token counts and finish
691
- reasons match across native tail-split, native full-prefill and fixed-two
692
- MTP. Sources stay fixed and all servers exit cleanly. Against native full
693
- prefill on the M4 Pro 24 GB, MTP changes median paired complete request time
694
- by -43.22% for code, -38.81% for explanation and -44.92% for JSON. Every pair
695
- improves. Corresponding TTFT changes are -7.36/-8.42/-5.47%. Median paired
696
- peak server RSS rises by 684,335,104 bytes, about 653 MiB; this samples startup
697
- and the whole request sequence, not each fixture's physical-memory demand.
698
- Native tail-split adds roughly 0.77–0.86% complete time and 7.37–10.46% TTFT
699
- relative to native full-prefill. Existing swap and background CPU activity
700
- in the final block keep these results diagnostic. All samples are retained.
701
- Broader workloads and quiet acceptance remain.
702
- Evidence: `mtp-matched-prefill-r6-repeat{,-review}.json`.
703
-
704
- The RTN4 repeat also completes six balanced blocks, 18 servers and 126
705
- responses with fixed sources and clean exits. MTP and native full-prefill
706
- match all text/count/finish comparisons. Native tail-split retains the code
707
- and explanation differences in every block. Relative to native full-prefill
708
- on the M4 Pro 24 GB, median paired MTP complete-time changes are -34.95% for
709
- code, -26.95% for explanation and -38.48% for JSON. Code and explanation
710
- improve in five of six pairs; JSON improves in all six. Median paired sampled
711
- peak server RSS rises by 850,173,952 bytes, about 811 MiB. The losing block
712
- overlaps another application's TypeScript and Playwright work, followed by
713
- system indexing and increased swap. Retain that block and the other samples;
714
- these diagnostic medians do not substitute for quiet acceptance. Evidence:
715
- `mtp-matched-prefill-rtn4-repeat{,-review}.json`.
716
-
717
- The RTN4 cross-quant screen uses its unrotated target and the original
718
- unrotated mlx-community MTP head, with the same three held-out prompts and
719
- fixed/adaptive policies. All three servers exit cleanly with fixed sources.
720
- Usage counts and finish reasons match, but both MTP arms differ from native
721
- on the measured code and explanation responses. Those timings do not establish
722
- identical-work speedups. The JSON response matches at 117 counted tokens;
723
- its single-block complete times are 8.292/5.042/4.776 seconds for native,
724
- fixed and adaptive MTP. Repeated timing and a first-divergence audit remain.
725
- Keep the packed result specific to its tested artifact and prompts. Evidence:
726
- `mtp-rtn4-screen-{plan,http,http-review}.json` under the campaign reports.
727
-
728
- The RTN4 native follow-up reproduces code/explanation divergence at emitted
729
- token indices 48/26 with seed 42 and greedy sampling. For each of 140 target
730
- verification windows, it clones the pre-forward cache and replays the same
731
- IDs through serial M1 forwards with the same hidden taps. All 420 projected
732
- logit rows have identical values, and all cache offsets agree. Instrumented
733
- MTP preserves the uninstrumented emitted IDs on all three fixtures. Thus a
734
- local verification-window arithmetic difference does not explain these
735
- responses. Native tail-split versus MTP full-prompt prefill and state
736
- accumulation require separate controls.
737
- Sources remain fixed. Evidence: `mtp-rtn4-divergence-v2-audit{,-review}.json`.
738
-
739
- Two fresh processes isolate the prefill choice using the existing
740
- `MLX_BUN_PREFILL_TAIL_SPLIT` setting. Native traces show 57+1, 53+1 and 91+1
741
- prompt forwards with splitting, versus 58, 54 and 92 without it. MTP uses
742
- full-prompt prefill in both processes. With splitting enabled, the earlier
743
- code/explanation token differences recur. With splitting disabled, every
744
- native emitted ID matches MTP on all three fixtures; MTP itself is unchanged
745
- across processes. Counts are 128/128/117 including JSON EOS. Both children
746
- exit cleanly and all source hashes stay fixed. This isolates the observed
747
- trajectory differences to the native prefill policy for these fixtures.
748
- Retain the original failed comparisons and add a native full-prefill arm
749
- when measuring MTP, so prefill savings and drafting savings are distinct.
750
- Changing that policy still needs its own oracle/quality gates. Evidence:
751
- `mtp-rtn4-prefill-control{,-review}.json` under the campaign reports.
752
-
753
- Variant 10 further shares small-M scatter work. Six warm AB/BA blocks at
754
- gamma 2 and 3 on both enumeration and the earlier losing prose prompt now
755
- retain an observed wall-time gain with identical token IDs. Enumeration
756
- prefers gamma 3; prose prefers gamma 2. This changes the measured cost of
757
- verification, not the recorded acceptance sequences. The reports are
758
- `reports/qwen38-rd/mtp-v10-{planets,prose}-gamma{2,3}.json`. These repeated
759
- fixtures are still diagnostic, with no general workload or quiet-machine
760
- verdict. Native request rows are recorded in the eval DB under
761
- `mlx-bun-native-diagnostic`, which does not replace the status page's
762
- ordinary inference measurements.
763
-
764
- The native MTP test now also uses the server's config-plus-tokenizer EOS set
765
- and records it. The repeated variant-10 enumeration/prose cells contain no
766
- additional EOS ID within their fixed budget, so that correction does not
767
- truncate those sequences. The broader corpus audit found chat-EOS overrun
768
- in extraction only. Its two DB rows and the old strict-fill rows now carry
769
- explicit exclusion notes; corrected strict-fill reports are recorded separately.
770
-
771
- The provider-lifetime audit found a separate cleanup bug. The Qwen provider's
772
- old no-op `dispose()` assumed process-lifetime mapped weights, but `Weights`
773
- now owns native MLX maps and each dense projection also retains a transpose
774
- view. Three bounded load/use/source-dispose/provider-dispose cycles retained
775
- 849,398,784 additional MLX bytes each, including after garbage collection.
776
- The provider now owns a `DisposableStack`: weight-map cleanup is registered
777
- first, then each derived transpose view, so disposal releases views before
778
- maps. The same scope unwinds a partially constructed module. Disposal is
779
- idempotent and opening a source after provider disposal is rejected. The
780
- caller closes active request sources before unloading their provider, as for
781
- the other providers. Inference operations and their ordering are unchanged.
782
-
783
- The real 27B MTP-head probe returns active MLX memory to zero after each of
784
- three cycles, before GC. Model-free tests also cover map/view release, repeat
785
- disposal and failure at the final norm after earlier views have been created.
786
- Evidence: `reports/qwen38-rd/mtp-provider-dispose-{before,after}.json` and
787
- `mtp-provider-ownership-tests.txt`. All three typechecks, hygiene and the full
788
- model-free tier pass: 1,803 tests, 10 skips and no failures. The post-change serving
789
- gate completes all three native/fixed/adaptive servers with clean exits and
790
- matching response text on the frozen requests. Only the previously documented
791
- JSON EOS-count difference remains; sources stay fixed. Evidence:
792
- `reports/qwen38-rd/mtp-post-dispose-http.json`. This addresses drafter unload/replacement;
793
- it is separate from the already-fixed target-cache disposal and admission work.
794
-
795
- ### 4.8 Paired Qwen MTP conversation state
796
-
797
- `DraftSource.checkpoint` supplies the method's companion state. Qwen captures
798
- draft KV and the preceding target hidden row; the executor pairs these with
799
- target KV/recurrent state and exact processed token IDs in the shared cache.
800
- Restore requires an exact prefix under the same compatibility namespace.
801
- RAM hits lend immutable views and preserve the donor. Reuse never trims
802
- recurrent state to an arbitrary common prefix.
803
-
804
- The MTP cache is one position behind the target at a prefill boundary. Extending
805
- it first pairs the next incoming token with the saved preceding hidden row,
806
- then processes the remaining suffix. Future generation cannot alter the retained
807
- prefix. The working-tree migration removes the provider's private store and
808
- separate budget. All tensors use the existing RAM budget, eviction and SSD
809
- queue. Native bf16/KV4 tests pass on M1 Max and M4 Pro through repeated RAM
810
- reuse and SSD cache/provider restart. Completed generated-state publication
811
- and matched performance remain open. Storage and identity details live in
812
- [kv-cache method companion state](kv-cache.md#method-companion-state).
813
-
814
- `DraftProvider.grouped` now opens method-owned row state using the same
815
- companion checkpoint format. Its sampler is injected; the provider retains
816
- weights while the group owns draft KV and hidden rows. Prepared requests can
817
- join, retire, reorder, drain and rejoin without exposing tensor layouts to
818
- scheduling or introducing another cache store. Capture and membership changes
819
- occur between committed rounds. The request source and group share Qwen's
820
- attachment encoding and restoration.
821
-
822
- The provider interface is adopted in the working tree after bf16/KV4/KV8
823
- full-round controls at B=1/2/4 on both Macs, including rebuilt checkpoints,
824
- SSD restart continuations and both complete model-free suites. Extraction
825
- removes padding, so the continuation control also rebuilds its raw state to
826
- match attention geometry. The initial M4 comparison against retained padding
827
- changed subsequent draft KV despite matching tokens and target state; that
828
- result is retained. These are internal refactor controls. Coordinated
829
- target/draft admission now uses staged state publication, described in
830
- batching §7. Source
831
- identity and raw checks are in the composition-baseline reports under
832
- `mtp-provider-group-manifest.json`.
833
-
834
-
835
- The isolated serving candidate adds method preparation and advancement to the
836
- existing execution group. The scheduler retains its queue, execution lease and
837
- admission policy; the method owns target/draft state, acceptance and sampling.
838
- The gateway asks the binding for its supported grouped methods and forwards
839
- output metadata and speculative usage. No second MTP scheduler is introduced.
840
- Its real KV4 HTTP test passes on M1 Max and M4 Pro: four submitted requests
841
- reach a target verification graph with shape B=4, each returns seeded samples
842
- and logprobs, and the group drains. B=1 token and acceptance checks compare
843
- against the existing request producer. Callback-stop, failure, cancellation,
844
- EOS and shared RAM-prefix checks are included in the extended native gate.
845
- Mixed HTTP checks on both Macs compose structured JSON, seeded sampling and
846
- logprobs with ordinary requests in the same MTP group. Shared-method SSD
847
- restart preserves target/draft state, tokens and acceptance traces on both.
848
- An extended M1 native check also admits a shorter-lived request with a different
849
- prompt length while another request is decoding, then repeats the same schedule
850
- with matching outputs. Admission now publishes target state, draft state and
851
- executor membership together before releasing superseded state. A reproduced
852
- cleanup failure formerly left a rejected row active; the admission failure
853
- handler now retires that row before another method round. The candidate remains
854
- isolated pending complete composition and paired performance acceptance.
855
- Timing results and their limits live in benchmarks.md. Evidence:
856
- `reports/qwen38-closeout/composition-baseline/mtp-serving-method-manifest.json`.
857
-
858
- For the original private-cache candidate, the M4 Pro unit gate passed 18 tests. Native prompts at 128, 513 and
859
- 2,051 tokens pass six restore checks with unchanged state hashes, token IDs
860
- and acceptance traces; repeated allocation is stable at each length. The HTTP
861
- gate completes eight responses with identical text, token counts and finish
862
- reasons across uncached and cached MTP policies, including repeated cache hits.
863
- The native restore check also passes with Luke's coding sampling recipe,
864
- temperature 0.6, top-p 0.95, top-k 20 and seed 42: six unchanged state restores
865
- and exact repeated continuations. Evidence: `mtp-prefix-luke-sampling.json`.
866
- These gates establish the initial composition, not complete long-task speed or
867
- pressure acceptance. The fresh thinking-off Pi task reused saved prefixes and
868
- finished generation without compaction, but its generated app failed acceptance.
869
- The task disposition and next frozen configuration are in decode-speed-program
870
- section 7.7. Raw evidence: `reports/qwen38-rd/mtp-cache-candidate-native.json` and
871
- `reports/qwen38-rd/mtp-cache-http-gate.json`.
872
-
873
- ## 5. Files
874
-
875
- - `src/spec/source.ts` — the seam. `src/spec/serve-loop.ts` — the executor.
876
- - Sources: `two-model.ts`, `assistant-source.ts` (+ `drafter.ts`),
877
- `dflash-source.ts`, `deepspec-source.ts`, `ngram-source.ts`,
878
- `qwen-mtp-source.ts`, `glm52-mtp-source.ts`.
879
- - `src/spec/generate.ts` — the standalone optiq-oracled assistant loop
880
- (`specGenerate`, full-prompt prefill, throws on ring wrap).
881
- - `src/spec/dspark/`: `module-dflash.ts` (our module), `deepspec-module.ts`
882
- (DeepSeek's), `loader.ts`, `data-dflash.ts`, `generate-dflash.ts`,
883
- `loss.ts`, `sample.ts`, `calibration.ts`, `quantize-drafter.ts`,
884
- `ab-stats.ts`; `module.ts`/`data.ts`/`generate.ts` = v1 baseline (refused
885
- for serving).
886
- - `src/model/gemma4.ts` `hiddenTap`/`captureLayer`; `src/model/qwen3_5.ts`
887
- `hiddenTap`; `src/model/gemma4-base.ts` `Cache` (`trim(n, bypass)`, the
888
- spec-round trio); `src/model/qwen3-delta.ts` `SSMCache.specRound*`.
889
- - `src/server.ts` `detectDraftKind` / `loadContext` (provider selection, γ
890
- pinning, tokenizer probe, fail-fast pairing probe-open, admission).
891
- - `scripts/dspark.ts <train|regen|calibrate|quantize|ab>` — the DSpark
892
- pipeline (one dispatcher, jobs in `scripts/dspark/`):
893
- ```
894
- bun scripts/dspark.ts regen --topics <topics.txt> --out <data> --max-resp 320
895
- bun scripts/dspark.ts train --data <data> --out <ckpt> --iters 8000 --batch 8 [--resume] [--ddraft 2560] [--seq-head rnn]
896
- bun scripts/dspark.ts calibrate --drafter <ckpt> --data <prompts.jsonl>
897
- bun scripts/dspark.ts quantize <drafter-dir> [--out <dir>] [--bits 4|8] [--group-size 32|64]
898
- bun scripts/dspark.ts ab --target <id|dir> --drafter-a <dir> --drafter-b <dir> [--num-draft-tokens N] [--n-prompts 32] [--max-drop 3]
899
- ```
900
- (The standalone measure/compare/bench one-offs — `dspark-measure-dflash`,
901
- `dspark-deepspec-compare`, `spec-bench`, `spec-dump`, `qwen38-mtp-ab` —
902
- were deleted 2026-08-23; git history.)
903
- - Oracles: `scripts/oracle/oracle-spec.py` (optiq spec_generate),
904
- `scripts/oracle/oracle-dspark-deepspec.py` (DeepSpec temp-0 trace),
905
- `scripts/oracle/oracle-qwen38-mtp-logits.py` (mlx-vlm drafter logits).
906
- - Tests: `spec-decode.test.ts` (e4b assistant, exact + knife-edge),
907
- `spec-decode-12b.test.ts` (long-prefix rollback gate), `spec-serve.test.ts`
908
- (L1 knife-edge, grammar × spec), `spec-serve-assistant.test.ts`,
909
- `spec-ngram.test.ts`, `qwen35-spec-ngram.test.ts`,
910
- `qwen-ssm-specround.test.ts`, `qwen38-mtp.test.ts` (opt-in),
911
- `dspark-{smoke,infer-loop,rnn,calibration,deepspec,ab-stats}.test.ts`.
912
- - User-facing mirror: docs/reference/server-config.md (`--draft-model`,
913
- `--draft-kind`, `--num-draft-tokens`, `--ngram-*`, `--mtp`,
914
- `MLX_BUN_PREFILL_TAIL_SPLIT`, `MLX_BUN_DSPARK_MINCONF`), cli.md,
915
- docs/reference/server-config.md.
916
-
917
- ## 6. Composition rules (as served)
918
-
919
- - Method selection precedes placement. Qualified Qwen MTP uses grouped
920
- target/draft state, sampling and checkpoint interfaces in the shared
921
- executor. Other draft providers still use the serial verifier.
922
- - KV policy stays with the request. Shared Qwen MTP supports bf16 and
923
- start-zero uniform KV4/KV8/TurboQuant. Unsupported compositions select
924
- ordinary decoding. Current defaults and qualifications live in server-config.md.
925
- - Structured output composes (the constrained verify walk).
926
- - Qwen MTP prefill state uses the shared RAM/SSD cache. The source supplies
927
- draft KV and the pending hidden row through its checkpoint interface;
928
- the cache owns retention and persistence. Other speculative sources still
929
- begin with fresh state until they supply a checkpoint codec. Generated-output
930
- capture remains R17 work.
931
- - Native GLM MTP and an explicit `--draft-model`/`--draft-kind` are mutually
932
- exclusive; one provider per request so two draft histories never advance
933
- target or grammar state twice.
934
-
935
- ## 7. Token fast-forwarding — lookup, not speculation
936
-
937
- **No draft/verify contract is involved.** Nothing in this section routes
938
- through `src/spec/`; there is no drafter, no verify forward, no rollback, and
939
- no comparison against what the model "would have" produced. It is filed here
940
- only because the spec lane is the neighbouring mechanism and the two are
941
- routinely confused. The one fact borrowed from it is that a multi-token
942
- mid-decode append works — which the grammar jump-forward burst already proved.
943
-
944
- **The doctrine.** A transformer is a next-token function over a token
945
- sequence. It has no memory of who wrote which token, so an APPENDED token is
946
- indistinguishable from a sampled one: injection is pure context extension. When
947
- the engine already knows the next *m* tokens, consulting the model for them
948
- buys nothing — it writes them into the KV itself with ONE chunked forward and
949
- resumes sampling after them. The model is consulted only for tokens it does
950
- not already know.
951
-
952
- **What "already knows" means in the strict tier.** A request's
953
- `tools` array plus the model's own chat template determine large parts of the
954
- assistant turn: the tool-call opening scaffold, the remainder of a tool name
955
- after its first disambiguating token, the punctuation from the name to the
956
- arguments object, and — when the schema has exactly one required property —
957
- the argument key itself. Those spans are compiled per request by **scaffold
958
- probing**: render the same conversation several ways, tokenize each, and diff
959
- the token id sequences. What does not change is the template's fixed scaffold,
960
- by construction. The compiler knows nothing about tool-call syntax: Qwen3.5's
961
- `<function=NAME>`/`<parameter=KEY>` XML, a JSON `<tool_call>` template, and
962
- GLM's `<arg_key>`/`<arg_value>` all compile through the same diff, and a
963
- template that does not render `tool_calls` (or renders them without the name)
964
- produces identical probes, an empty diff, and no rows — degrade to no-fill,
965
- never wrong output. Same technique as `request-prep.ts::stableLenFor` uses on
966
- the generation primer, applied to the assistant turn.
967
-
968
- **Serialization and state invariants.**
969
- - **Every span is sliced from a rendering the model could actually produce** —
970
- real tool names, real schema keys. The diff only decides WHERE to cut; the
971
- ids always come from the real-name rendering, so every cut is a token
972
- boundary of a producible stream. This is not a stylistic preference. Qwen3.5
973
- renders `<function=get_weather>`, which the tokenizer encodes as
974
- `< function =get _weather >` — `=get` is ONE token. A probe named
975
- `zzalphatoolqq` splits the same position as `… = zzalpha…`, inventing a
976
- boundary after `=`. A scaffold row cut there injects a bare `=`, the model
977
- then emits `get` where it would have emitted `=get`, and the result is
978
- byte-identical TEXT over divergent token IDS — with KV that no longer
979
- matches what a plain decode would have written. Caught on Qwen3.5-0.8B
980
- (2026-08-31); the regression gate is id containment, not text containment
981
- (`tests/unit/fill-schema-rows.test.ts`, `tests/parity/fill-strict.test.ts`).
982
- With one tool the whole header through the sole key is one span; with
983
- several, the scaffold ends exactly where the real merged name tokens diverge
984
- (`=get` vs `=search`) and per-tool name rows resume from there.
985
- - Never `encode(fragment)` in isolation — same reason.
986
- - Every row is anchored at a token carrying LETTERS (e.g. `<tool_call>`,
987
- `</parameter`), with leading template-join whitespace dropped from the
988
- trigger. Markup alone is not enough: `</` opens every closing tag a model
989
- might write in prose or a code block, so arming on it would inject tool-call
990
- markup into an HTML snippet.
991
- - A close row exists only when EVERY tool in the request takes exactly one
992
- required argument — otherwise the model may still be about to write a second
993
- `<parameter=…>`, and injecting the close would silently drop it. The
994
- request's own schema decides whether the row exists.
995
- - Ending the turn stays the model's decision: every span is cut at the first
996
- EOS id (the grammar jump burst's missing EOS check is deliberately not
997
- inherited).
998
- - A span shorter than 2 ids is rejected — it would save no forward.
999
- - The cache-alignment invariant: the fill forward carries ONLY the injected
1000
- ids. The normal step already consumed the trigger token and wrote its KV;
1001
- forwarding `[trigger, ...ids]` would duplicate a position and silently
1002
- corrupt both the KV and `PromptCache.put`'s key. `MLX_BUN_FILL_TRACE=1`
1003
- asserts `cache[0].offset === promptTokens.length + forwarded.length` on both
1004
- sides of every append.
1005
- - Injected tokens flow through `CompletionSink.push` one at a time, in the
1006
- same shape as the jump burst, so `StopMatcher` fires mid-burst exactly where
1007
- it would have. The append happens BEFORE any of the burst's yields, which is
1008
- what makes a consumer break mid-burst safe — `forwarded` already describes
1009
- the cache exactly.
1010
-
1011
- **The deviation, stated plainly.** Injection bypasses the sampler. At
1012
- `temperature > 0` a filled reply is not the same draw an unfilled one would
1013
- have been (the sampler is never asked about those positions). That is a
1014
- behavior-policy deviation, not a numerics one, and it is why the feature is
1015
- opt-in (`MLX_BUN_FILL=strict`, default off). At `temperature 0` the strict
1016
- tier must demonstrate token identity against ordinary generation. A fixed
1017
- template serialization does not prove that the unconstrained model chooses
1018
- it. The weights gate is `tests/parity/fill-strict.test.ts`; section 7.4 states
1019
- the stronger contract needed for a claim of guaranteed deterministic replay.
1020
-
1021
- **Mechanism.** `src/generate.ts::generateInner` — the same burst shape as the
1022
- grammar `jumpEmit` branch, with the DEFERRED trigger (fill reads the token the
1023
- pipelined loop already read back; it does not add grammar's eager readback).
1024
- On an ASSERT fill the in-flight sample for the next position is dropped
1025
- unexamined — a discarded pipeline dispatch, counted as `wastedSamples`, not a
1026
- rejected draft. Grammar and fill are forbidden in the same iteration
1027
- (asserted).
1028
-
1029
- ### 7.1 One interface, two policies (`src/fill/proposal.ts`)
1030
-
1031
- Every source that can propose the next tokens implements one interface, and
1032
- one apply primitive in the decode loop hosts both policies — because the
1033
- EXPENSIVE half is identical: one chunked forward advancing the KV (and the
1034
- recurrent SSM state) over the whole span.
1035
-
1036
- ```ts
1037
- interface ProposalSource { propose(tail: TokenView): Proposal | null }
1038
- interface Proposal { ids: number[]; policy: "assert" | "verify"; origin: … }
1039
- ```
1040
-
1041
- - **assert** — the tokens are DETERMINED (a template scaffold). Append and move
1042
- on: no readback, no checkpoint, no rewind. Strict rows are always assert.
1043
- - **verify** — the tokens are LIKELY (a session self-copy). Position 0 is
1044
- checked BEFORE the forward against the in-flight sample — free, and a
1045
- mismatch costs nothing because nothing has been written yet. Positions
1046
- 1..m−1 are checked against the argmax already sitting in THAT SAME forward's
1047
- logits, so verification adds no pass over the weights. The rejected tail is
1048
- rewound and decode resumes at the first disagreement, which reproduces
1049
- exactly the stream an unfilled run would have produced. A wrong guess costs
1050
- a rewound forward, never a wrong token.
1051
-
1052
- **Rewind reuses the spec lane's cache contract, not its executor.** Trimmable
1053
- caches drop the tail with `trim(n)`; NON-trimmable recurrent caches (SSMCache —
1054
- gated-DeltaNet conv + recurrent state) go through `specRoundBegin()` before the
1055
- forward and `specRoundRollback(keep)` after the accept walk, which restores the
1056
- pre-round snapshot and bit-exactly REPLAYS the accepted prefix (§2). Those are
1057
- plain `Cache`-interface methods driven by the owning layer, so nothing in
1058
- `src/spec/serve-loop.ts` is touched. A model whose caches can do neither still
1059
- gets assert fills; verify proposals are dropped and counted
1060
- (`usage.fill.verifyUnsupported`). Checkpointing is real work (~48 DeltaNet
1061
- states on Qwen3.8) and is measured: `usage.fill.checkpointMs`.
1062
-
1063
- **Remaining fill integration.** Prompt lookup, Qwen MTP and standalone
1064
- drafting now share the grouped method lifecycle at B1/B>1 for qualified
1065
- target layouts (see batching.md). DSpark and other provider migrations remain.
1066
- Unifying fill proposals with draft proposals through one apply primitive is
1067
- still open; the existing fill implementation retains its cache transaction
1068
- contract while that method composition is qualified.
1069
-
1070
- ### 7.2 The echo tier (K3c, Lab, `MLX_BUN_FILL=echo`)
1071
-
1072
- A growing per-request k-gram index over promptIds plus everything emitted
1073
- (injected tokens included — the model cannot tell them apart, so neither does
1074
- the index). The structure is the TS port of `GrowingMatcher` from the corpus
1075
- study: sequence + k-gram → positions, appended incrementally, bucket scan
1076
- capped at the NEAREST occurrences (`src/fill/echo-index.ts`).
1077
-
1078
- The lookup is the boring half. The **stopping rule** is the measurement that
1079
- made the tier worth building:
1080
-
1081
- - **Branch-point stopping.** A match says where this context occurred before;
1082
- it says nothing about how far the future agrees with the past. So a span
1083
- extends only while EVERY nearby occurrence continues the same way, and stops
1084
- the moment they disagree. That fork is exactly where old-query-vs-new-query
1085
- divergence lives, and delimiters fall out for free — a closing quote is where
1086
- histories fork.
1087
- - **Corroboration decides the policy.** `assert` requires (a) no branch stop,
1088
- (b) the span ends at a delimiter-class token, AND (c) at least TWO
1089
- occurrences agreed across the whole span. A single occurrence is a copy, not
1090
- a pattern: it will happily replay whatever followed it in the transcript —
1091
- including another role's turn — so it is the model's call. Verified on the
1092
- 0.8B tokenizer against a synthetic agent transcript: an uncorroborated copy
1093
- ran 30 tokens past `</tool_call>` into a mocked tool RESULT. Under verify
1094
- that costs one rewound forward; under assert it would have been wrong
1095
- output. Everything else is `verify`.
1096
- - **Delimiters** are read off the template by the strict-row compiler (the
1097
- first non-whitespace token that follows an argument value — `"` for JSON,
1098
- `</` for Qwen3.5's XML). They clamp ECHO spans only: a strict scaffold
1099
- legitimately contains the same tokens as structure (`{"name": "` is three
1100
- quotes deep) and is determined by construction.
1101
-
1102
- Deterministic value transforms (url-encode, JSON-escape) as
1103
- `(source-span, transform)` table entries are NOT implemented — the seam is the
1104
- same `ProposalSource` interface, and the corpus rates that motivate them are in
1105
- PLAN K3.
1106
-
1107
- The bar for this tier is NOT token identity (sampling never guaranteed that):
1108
- it is a paired A/B on task success and wall clock over mocked-replay agent
1109
- sessions. Default off until that lands.
1110
-
1111
- **Composition.** Serial lane only, and it never FORCES a request serial — a
1112
- batch-placed request simply does not fill, because `generate()` is the only
1113
- site that reads `options.fill`. Refused for: a compiled grammar (forced tokens
1114
- are its job), `logprobs`/`top_logprobs` (injected tokens have no distribution
1115
- row — the same rule as `shouldUseGrammarJump`), a user-fixed `seed`
1116
- (reproducibility: the step index would skip injected positions), media
1117
- prompts, a mounted draft model, quantized/TurboQuant KV (post-conversion
1118
- multi-token append is L-generic but unvalidated), and sliding-window models
1119
- (RotatingKVCache multi-token append is O(window) via `#updateConcat` — one
1120
- warning, then no fill).
1121
-
1122
- **Mismatch policy.** If `parseGeneratedToolCalls` rejects the emitted markup
1123
- on a request whose rows fired, `usage.fill.parseFallback` increments and
1124
- strict rows disarm. Today the served parse runs at sink flush, i.e. after the
1125
- generation ends, so within one request this is telemetry; the seam is wired so
1126
- an incremental parse (or a plan cached across requests) disarms for real.
1127
-
1128
- ### 7.3 Measuring it (K3d, `scripts/fill.ts`)
1129
-
1130
- Neither tier ships on a vibe. The harness replays RECORDED agent sessions with
1131
- their tool results mocked verbatim from the JSONL — the transcript IS the
1132
- environment, a model cannot tell an executed result from a recorded one — so a
1133
- whole corpus replays deterministically and side-effect-free. The session reader
1134
- is a straight port of the corpus study's (`reports/k3-replication/analyze.py`
1135
- `load_session` / `serialize_tool_call`), including its `excludeFromContext` rule
1136
- and corrupt-line tolerance, so the measured rates in PLAN K3 keep describing
1137
- what the harness actually sends. The recordings carry no `tools` array, so the
1138
- harness SYNTHESIZES one per session (union of observed argument keys; keys
1139
- present in every call marked required) — a reconstruction that is itself part
1140
- of what the A/B measures.
1141
-
1142
- ```
1143
- bun scripts/fill.ts replay --sessions <file|dir> --server-url <url>
1144
- bun scripts/fill.ts ab --sessions <file|dir> --url-a <off> --url-b <echo>
1145
- bun scripts/fill.ts ab --showcase fixtures/showcase-silicon-exchange.txt --url-a … --url-b …
1146
- bun scripts/fill.ts report <runs.jsonl> [--by-tool]
1147
- ```
1148
-
1149
- Fill is a process-wide lever, so the two arms are two SERVERS (A without
1150
- `MLX_BUN_FILL`, B with it). Turns interleave A, B, A, B … so load drift and
1151
- thermal state land on both arms, and the verdict pairs on (session, turn, rep).
1152
-
1153
- **The echo-tier gate**: task-output agreement must not drop within CI AND
1154
- median wall clock must strictly improve. Agreement is the CALL the model made
1155
- (tool name + arguments, key-order independent) or a prose turn's normalized
1156
- text — never token identity, which sampling never promised and which an agent
1157
- loop does not care about. "Does not drop" is McNemar's paired counts with a
1158
- one-sided 95% bound, so a tie or a swing inside the noise passes and a real
1159
- regression does not. The strict tier keeps its own, stricter bar (token
1160
- identity at temperature 0, `tests/parity/fill-strict.test.ts`).
1161
-
1162
- **The showcase** runs ONE large tool-dense prompt ×3 interleaved and reports
1163
- emitted vs decoded tok/s, the fill fraction, time-to-first-tool-call, and the
1164
- comparison with a one-token-per-forward bandwidth estimate. An emitted rate
1165
- above that estimate can show amortized weight reads; the injected positions
1166
- still pass through the state-update graph. `apparent = decoded / (1 − fillFrac)`
1167
- omits append cost and is not a speedup measurement.
1168
-
1169
- The verdict math is model-free and unit-tested against a stub server
1170
- (`tests/research/fill-echo-replay.test.ts`), including the ways it FAILS — a
1171
- harness that can only produce PASS is not a gate.
1172
-
1173
- **Live result, 2026-09-02 (M1 Max 32 GB, mjriii/Qwen3.8-27B 4.80 bpw, six
1174
- recorded pi sessions, 32 paired turns, `--batch 1`).** Two servers do not fit
1175
- the wired ceiling at 27B, so the arms ran SERIALLY (one server restarted
1176
- between them; `fill report` now prints the paired verdict for two `replay`
1177
- files). Two false starts are findings in their own right: (1) the default
1178
- server lane is the batch scheduler, which never fills — an A/B on it is two
1179
- identical arms (fill 0.0%, same 4925 tokens); the serial lane needs
1180
- `--batch 1`. (2) The strict tier compiled ZERO rows on the 27B: Qwen3.x
1181
- thinking templates end the primer in `<think>\n` and the reply opens with
1182
- `\n</think>`, the tokenizer merges the two newlines, the primer stops being a
1183
- token prefix and the compiler bailed silently. Fixed (`schema-rows.ts
1184
- after()`: text-level boundary, suffix tokenized on its own; 4 rows compile on
1185
- the 27B). So the run measured the ECHO tier alone:
1186
-
1187
- | arm | tok/s emitted · decoded | fill | median wall | agreement |
1188
- |---|---|---|---|---|
1189
- | fill-off | 8.0 · 8.0 | 0 | 7663 ms | 3.1% |
1190
- | fill-echo | 8.1 · 7.5 | 7.4% (bash 10%, read 32%) | 7630 ms | 6.3% |
1191
-
1192
- 369 tokens injected in 66 echo events; verify accepted 369 / rejected 1195
1193
- (76% of proposed span positions), 31 branch stops, 147 ms of checkpoints.
1194
- Agreement held (B-only agreements 1, A-only 0; the McNemar bound cannot
1195
- resolve +3 pts at n=32). **Wall clock ×1.00 — the echo gate FAILS**: each
1196
- event pays a multi-token forward for its whole span and a rewind, and with
1197
- three of four proposed positions rejected the forward compute per accepted
1198
- token exceeds a decode step. Echo stays Lab, default off. Levers before a
1199
- rerun: a longer anchor (`MLX_BUN_FILL_K`), fewer candidates, a span cap near
1200
- the observed accepted length (~5–6), and asserting corroborated spans instead
1201
- of verifying. Absolute rates are not quotable: the box ran at 8 tok/s (2.3 GB
1202
- swapped, the 16 GB server plus the day's leftovers) where 19 is predicted;
1203
- both arms saw the same conditions.
1204
-
1205
- **Strict tier, same day, corrected rows** (serial arms back to back, 32 paired
1206
- turns): fill 5.3% of emitted tokens, all proposed strict spans injected
1207
- (assert policy, no agreement readback), identical tool calls on every paired turn,
1208
- median wall ×0.99 (the win is bounded by the fill fraction). The proposal
1209
- trace (`MLX_BUN_FILL_TRACE=<file.jsonl>`, `bun scripts/fill.ts trace`) is
1210
- what made the rows right: the first strict run's list showed the scaffold row
1211
- firing on `</think>` and asserting `<tool_call>…` where the model would have
1212
- written prose ("Hey", "You", "I") in 10 of 47 template proposals, while the
1213
- schema-derived name/close rows agreed 9/9. The scaffold trigger now runs
1214
- through the first token where the call rendering diverges from a plain-
1215
- content rendering — the model's own `<tool_call>` — and the failure class is
1216
- gone by construction. Echo-tier list, same tool: 66 events, verify accepted
1217
- 369 / rejected 1195, survival by position falls to ~50% by position 6 — the
1218
- policy levers named above are what the list points at. **Showcase measured**
1219
- (same day, `fixtures/showcase-silicon-exchange.txt`, 1024 tokens × 2 reps,
1220
- serial arms): echo 8.9 tok/s emitted = 8.9 decoded, off 9.0 — fill 0.0%.
1221
- A fresh build prompt has no earlier session text to copy, so the echo index
1222
- has nothing to propose; the bandwidth-ceiling demonstration (25.2 tok/s for
1223
- 14.76 GiB at 400 GB/s) needs a turn that REPEATS earlier context. The fixture
1224
- is the wrong shape for the tier it was written for — a multi-turn edit loop
1225
- is the honest showcase. Repro to file: turn 8 of session
1226
- `2026-08-18T04-34-36-341Z_01a01326…` kills the server on either lane (bare
1227
- MLX C++ exception, three of three runs, `runs/k3/serve-*.log`).
1228
-
1229
- **Files.** `src/fill/proposal.ts` (the interface + the two policies),
1230
- `src/fill/fill-session.ts` (sources, clamping, flags, telemetry),
1231
- `src/fill/schema-rows.ts` (scaffold probing + value delimiters),
1232
- `src/fill/echo-index.ts` (k-gram index + branch-point rule), the apply
1233
- primitive in `src/generate.ts`, `fillPlanFor` in `src/serve/request-prep.ts`,
1234
- the attach + serve-level refusals in `src/serve/chat-stage.ts`, `usage.fill` in
1235
- `src/serve/{completion-executor,openai-wire}.ts`. Tests:
1236
- `tests/unit/fill-{session,schema-rows,echo-index,generate-loop}.test.ts`,
1237
- `tests/serve/fill-{composition,stream}.test.ts`,
1238
- `tests/parity/fill-strict.test.ts` (weights-gated). Harness: `scripts/fill.ts`
1239
- + `scripts/fill/{session-replay,runner,client,metrics,args,replay,ab,report}.ts`,
1240
- `tests/unit/fill-session-reader.test.ts`,
1241
- `tests/research/fill-echo-replay.test.ts`,
1242
- `fixtures/showcase-silicon-exchange.txt`.
1243
- User-facing mirror: `docs/reference/server-config.md` (`MLX_BUN_FILL`,
1244
- `MLX_BUN_FILL_MAX_SPAN`, `MLX_BUN_FILL_TRACE`, `MLX_BUN_FILL_K`,
1245
- `MLX_BUN_FILL_CANDIDATES`, `MLX_BUN_FILL_INDEX_MAX`), `server-api.md`
1246
- (`usage.fill`).
1247
-
1248
- ### 7.4 No-verification replay contract and next experiments
1249
-
1250
- The 2026-09-04 Qwen performance program makes this a dedicated track.
1251
- Production `assert` must remain free of verification. Separate correctness
1252
- runs may inspect every skipped prediction; those instrumented runs cannot
1253
- supply the performance number. `applyProposal` in `generate.ts` already
1254
- implements this separation: `verify` or a trace filename computes logits at
1255
- all appended positions, while normal `assert` projects only the final hidden
1256
- state to resume sampling. It still forwards the span through model layers
1257
- and updates attention KV and GDN state.
1258
-
1259
- This track appends serialization fixed by the current tool-call state. It
1260
- does not predict unknown tokens. Once the protocol fixes a field following
1261
- a selected tool name, that field can become context immediately; sampling
1262
- resumes where the tool or argument value has a choice. Map fixed protocol
1263
- fields through the model's own chat template, including the preceding token
1264
- boundary. Pi's client-side tool representation and a model's wire rendering
1265
- can differ. Parser state must distinguish structural fields from the same
1266
- text inside argument strings, quoted examples or code. Offline parity audits
1267
- validate the implementation; they do not add verification to served asserts.
1268
-
1269
- There are different possible guarantees:
1270
-
1271
- | source | what would justify an assertion |
1272
- |---|---|
1273
- | Enforced output grammar/protocol | All legal continuations share the emitted token span at the current parser state. Account for tokenizer boundaries and stop conditions. Grammar uniqueness over bytes does not automatically imply unique token IDs. |
1274
- | Deterministic application transform | An explicit contract fixes the source span and transformation, such as escaping a selected literal. Choosing which source/value to emit remains a model/application decision. |
1275
- | Cached continuation | Exact model/quant, prefix, recurrent state, positional metadata and sampler/constraint state establish an identical continuation. A matching short suffix is insufficient. |
1276
- | Template/schema row | Establishes a valid serialization. Without enforced generation constraints, token identity with the model is an empirical gate and can fail even at temperature zero. |
1277
- | Repeated text | Agreement among observed occurrences is evidence, not certainty; retain the existing Lab distinction for echo assertions. |
1278
-
1279
- Audit suffix triggers against real parser/role state, tool-name prefixes,
1280
- optional/extra parameters, `additionalProperties`, multiple calls, quoted
1281
- markup, code blocks, escaping, EOS and partial-burst cancellation. The original
1282
- strict row source matched a token suffix without an independent parser-state
1283
- guard. Do not interpret an all-assert `accepted` count as verified agreement.
1284
-
1285
- A CPU-only audit with the actual R6 tokenizer/template finds a concrete close
1286
- boundary error in both thinking modes. The compiled close trigger ends at
1287
- `</parameter`, before `>`. A valid bash argument containing
1288
- `printf "%s" "</parameterization>"` therefore triggers the structural close
1289
- inside argument data. Injecting the current row changes the parsed command to
1290
- `printf "%s" "`. Both complete calls parse; the suffix alone cannot establish
1291
- that the value ended. The audit constructs valid token streams from the exact
1292
- compiled trigger IDs; it does not claim these are observed model trajectories
1293
- or quantify their frequency. Scaffold/close rows also match reasoning, quoted
1294
- prose and fenced examples without a role/parser guard. Evidence:
1295
- fill-parser-boundary-r6.json. Keep fill off by default. Complete the delimiter
1296
- and establish protocol state before asserting its continuation; runtime model
1297
- verification is not the remedy for a missing structural proof.
1298
-
1299
- An isolated parser prototype now waits for the complete `</parameter>` or
1300
- `</arg_value>` delimiter and requires a request-local tool-only assistant
1301
- context, optionally following closed reasoning. It reuses the tool parser
1302
- with full input consumption and no repair; quoted or fenced examples,
1303
- nested reasoning, unfinished literals, duplicate XML/GLM keys and requests
1304
- above the probe budget decline strict fills. Cached row plans create context
1305
- from the current prompt and do not retain the first request's messages.
1306
- The CPU-only real-R6-tokenizer audit rejects all sixteen negative contexts
1307
- and preserves four positive rendered token streams across both thinking
1308
- modes, including the original `</parameterization>` command. Each positive
1309
- case injects nineteen tokens in two spans with zero verification events.
1310
- The broader parser/stream suite passes 138 tests. Evidence:
1311
- `fill-boundary-tokenizer-audit-v4.json` and `fill-boundary-cpu-tests-v8.log`.
1312
- The parser and model-owned append method are integrated in the main tree.
1313
- The gates below distinguish the original short-context integration from
1314
- the subsequent attention-boundary correction.
1315
- On the consolidated MLX 0.32.2 core with the interleaved R6 artifact and
1316
- variant 13, the first weather fixture emits identical tokens but four-token
1317
- appends change recurrent state and continuation logits. One-token appends
1318
- restore exact state, all four continuation probes and disposal accounting,
1319
- with the same twelve injected tokens and zero verification. The earlier
1320
- four-token acceptance used a different runtime/artifact configuration;
1321
- it does not establish this combination. Isolate the changed arithmetic
1322
- before selecting an append shape. Evidence: `fill-boundary-native-r6.json`
1323
- and `fill-boundary-native-m1-r6.json`. A first-layer operation audit localizes
1324
- the drift to native affine projections: embedding and normalization match,
1325
- but MLX's new multirow matvec differs from repeated M1. Broadcast weight
1326
- views with input shape `[M,1,K]` select native batched M1 instead. All six
1327
- boundary/chunk cases then match every captured intermediate and both state
1328
- arrays exactly, without copying stored weights. The arithmetic prototype
1329
- then passes seven full native fixtures per R6/RTN4 quant, with exact emitted
1330
- IDs, complete state, four continuation probes, usage and disposal accounting.
1331
- Runtime verification remains zero. RTN4's negative-intent code-block prompt
1332
- produces a valid weather call in both arms; its original no-fill expectation
1333
- is retained as a failed fixture assumption, and the rerun classifies that
1334
- cell as a generated-call identity control. Forced-token negative contexts
1335
- remain covered by the tokenizer/parser gates. Evidence:
1336
- `fill-first-layer-shape-probe.json` and `fill-first-layer-batched-m1.json`.
1337
- The full-model reports are `fill-boundary-native-batch1-r6.json` and
1338
- `fill-boundary-native-batch1-v2-rtn4.json`.
1339
- The original guarded-parser/arithmetic prototype also passes six balanced
1340
- three-arm HTTP blocks per quant on the loaded M4 Pro 24 GB. All 144 responses
1341
- match across 36 clean server exits, with fixed effective seed 42, empty prompt
1342
- caches, identical tool calls and zero verification. Four-token append reduces
1343
- paired-median complete time for weather/bash fixtures by 9.95%/15.24% on R6
1344
- and 1.92%/3.34% on RTN4; every corresponding pair improves. M1 append is
1345
- mostly flat, and RSS supplies no memory-reduction claim. Tool output is
1346
- buffered until completion here, so these are complete-request and first
1347
- meaningful-tool-output results, not decode-rate measurements. Source hashes
1348
- remain fixed across the initial screen and five remaining order blocks.
1349
- Evidence: `fill-boundary-http-{screen,repeat,review}.json`. The model-owned
1350
- integration passes the same fourteen native cases and six balanced four-arm
1351
- HTTP blocks per quant. All 192 responses match across 48 clean server exits.
1352
- Paired-median weather/bash complete time improves by 9.87%/15.37% on R6
1353
- and 1.97%/2.77% on RTN4, with every pair improving. Ordinary generation's
1354
- before/after medians differ by at most 0.23%. These remain loaded M4 Pro
1355
- diagnostics. The independent-row operator matches sequential M1 across
1356
- 720 shape/bit/group/dtype/layout cases, with unchanged stored weight bytes.
1357
- The main-source state regression passes on both quants, and eight actual
1358
- HTTP cancellations recover exact responses and steady active allocations.
1359
- Evidence: `fill-append-integration.json`,
1360
- `fill-append-http-integration-review.json`, `fill-append-main-native.json`
1361
- and `fill-append-lifecycle.json`.
1362
-
1363
- A longer-context audit finds another native dispatch dependency. MLX 0.32.2
1364
- changes its attention kernel and two-pass reduction block count according
1365
- to sequence length and GPU architecture. The rules are in
1366
- [MLX's attention dispatch](https://github.com/ml-explore/mlx/blob/v0.32.2/mlx/backend/metal/scaled_dot_product_attention.cpp).
1367
- The qualified GQA-6/D256 path on
1368
- `applegpu_g16s` must end chunks at inclusive KV lengths 1023, 1024, 8192,
1369
- 32768 and 65536. An unsplit append that crosses a transition can change
1370
- arithmetic for its earlier positions. The first operation screen reproduces
1371
- 18 failures among 108 cases. The corrected model-owned limit is rechecked
1372
- after every chunk; MLX still selects the kernels. Other architectures retain
1373
- single-token appends pending their own qualification.
1374
-
1375
- All 225 corrected attention cases through 65,537 cached tokens are exact
1376
- across bf16, fp16 and fp32, and active allocation returns to zero. Full R6
1377
- and RTN4 models also match eight committed positions from identical prefixes
1378
- of 1021 and 8189 tokens. Hidden rows, per-position M1 vocabulary projections,
1379
- complete live state and four ordinary continuation probes match exactly;
1380
- the borrowed prefix state and post-arm active allocation are unchanged.
1381
- These teacher-forced checks establish arithmetic identity, not that the
1382
- model would independently choose those committed IDs. Production regression
1383
- coverage checks the attention boundaries and dynamic model limit. The final
1384
- model-free suite passes 1,947 tests with ten skips and no failures; all three
1385
- typechecks pass. The corrected path also passes eight actual HTTP
1386
- cancellations, fourteen completed responses and exact steady allocation
1387
- recovery across both quants. Final serving timing acceptance passes the
1388
- internal-SSD diagnostic below; held-out, combined, pressure and quiet gates
1389
- remain open.
1390
- Evidence: `fill-sdpa-context-probe.json`,
1391
- `fill-sdpa-context-boundaries.json`, `fill-append-context.json`,
1392
- `fill-append-final-model-free.log` and `fill-append-lifecycle-boundaries.json`.
1393
- The final-source timing attempts complete 116 responses with exact content,
1394
- tool calls and usage comparisons. Two RTN4 first-request warmups hit Metal
1395
- GPU timeouts, one with fill disabled and one with fill enabled. Neither
1396
- executes an append forward. Keep both failed requests and the incomplete
1397
- block in the evidence. Complete three-arm blocks cover six R6 pairs and
1398
- three RTN4 pairs; every append-versus-native pair improves complete time on
1399
- both fixtures. The dynamic boundary checks retain the earlier append benefit.
1400
- Separate recovery, storage and loading controls do not fill missing timing
1401
- cells. The failure investigation and its limits live in
1402
- [environment.md](../reference/environment.md#external-storage-loading).
1403
- Evidence: `fill-append-http-final.json`, `fill-append-http-resume.json` and
1404
- `fill-append-http-final-partial-review.json`.
1405
-
1406
- After Josh specifies internal storage for active models, a separate final
1407
- comparison uses byte-verified R6 and RTN4 copies with ordinary lazy loading.
1408
- All 96 responses across 24 fresh servers match, with twelve balanced pairs,
1409
- zero failures, zero verification calls and identical final active allocation.
1410
- On the M4 Pro 24 GB, the median paired complete-time reductions are 9.83% and
1411
- 15.40% for R6's weather/bash fixtures, and 1.63% and 2.94% for RTN4. Every pair
1412
- improves on each fixture. Tool output is buffered, so first visible output
1413
- arrives near completion; these figures do not establish faster ordinary
1414
- decode. The machine still fails the quiet preflight, so these are diagnostic
1415
- results. Both external-drive failures remain recorded separately. Evidence:
1416
- `fill-append-http-internal-repeat.json` and `fill-append-http-internal-review.json`.
1417
-
1418
- Quantized-KV composition remains disabled. A direct R6 append screen with
1419
- TurboQuant k8v3 preserves hidden rows, logits, live cache state and four
1420
- continuations at prefixes 128 and 1021, but exposes retained temporary state
1421
- views between chunks. `appendFillHidden` now uses the existing
1422
- `leaseCacheStates` ownership interface, as prefill does. Explicit release
1423
- removes the extra 21,528,576 and 131,153,920 active bytes in those two checks;
1424
- both ordinary and fused TurboQuant decoding then have identical post-arm
1425
- allocation and numerical results. This fixes the research composition's
1426
- view lifetime; the ordinary served path already excludes this combination.
1427
- Seven targeted ownership tests and all three typechecks pass. The affine
1428
- KV4 comparison fails both contexts, first changing recurrent state at layer
1429
- 4 after the first attention layer. Keep its failure and the composition
1430
- guard. Other quant/cache modes, longer contexts and actual serving
1431
- performance remain unqualified. Evidence:
1432
- `fill-append-quant-cache-review.json` and
1433
- `fill-append-state-ownership-native-test.log`.
1434
-
1435
- The literal/reasoning checks deliberately decline ambiguous text, including some
1436
- otherwise valid prose with unmatched quote characters. This is bounded
1437
- coverage, not proof for every unconstrained model trajectory.
1438
-
1439
- The schema-choice audit reproduced assertions that selected a first key or
1440
- closed the call while other valid arguments remained. The compiler now uses
1441
- a sole required key only when `additionalProperties: false` and no nonempty
1442
- `patternProperties` map establish that no other key is legal. Optional keys
1443
- may come before a required key. When no property is required, the common
1444
- span also includes the empty-argument rendering. This follows the
1445
- [JSON Schema object applicators](https://json-schema.org/draft/2020-12/json-schema-core#section-10.3.2),
1446
- where omitting `additionalProperties` leaves those properties unconstrained.
1447
- Open objects also compare an undeclared-key rendering, even when declared
1448
- keys share a tokenizer prefix. Twenty-seven regressions across XML, JSON and
1449
- GLM templates preserve these
1450
- choices, alongside the existing positive fill cases. This is a compiler
1451
- correctness fix, with no runtime verification or new inference operation.
1452
- The real packed-Qwen and RTN4 tokenizers each pass 20 cases across thinking
1453
- on/off with nonzero row matches. The packed weighted regression also retains
1454
- token-identical output and nonzero injection with four-token appends. All
1455
- 1,867 model-free tests and three typechecks pass. Parser/role-state proof
1456
- and the broader session corpus remain. Evidence is
1457
- `fill-schema-open-objects-before.json`,
1458
- `fill-schema-choices-original-control-corrected.txt`,
1459
- `fill-schema-shared-prefix-merged-before.txt`,
1460
- `fill-schema-choices-model-free-final.txt`,
1461
- `fill-schema-tokenizer-{r6,rtn4}-final.json` and
1462
- `fill-schema-choices-weighted-r6.json` in the campaign report directory.
1463
-
1464
- Run the actual artifact regression with
1465
- `MLX_BUN_TEST_FILL_MODEL=<local-model-dir> MLX_BUN_FILL=strict bun test tests/parity/fill-strict.test.ts`.
1466
- It selects one artifact per process and defaults to the small Qwen fixture
1467
- when no override is set. This is an initial positive-fill identity check;
1468
- extend it to the held-out 27B session/adversarial corpus before a default.
1469
- Track token IDs and subsequent logits/state as well as parsed tool arguments.
1470
-
1471
- On 2026-09-05 the actual packed Qwen3.8-27B passes this regression on both
1472
- trellis variants 6 and 7. Both runs perform nonzero assert injection with
1473
- zero verification events and emit the same token IDs as their unfilled
1474
- control. Logs: `reports/qwen38-rd/strict-fill-variant{6,7}.txt`. This is one
1475
- weather/tool-call fixture; it does not establish universal determinism or a
1476
- wall-time win. Test duration includes loading and warmup and is not the
1477
- performance comparison. The older logs' `baselineForwardSteps` field contains
1478
- the baseline generated-token count; the test now labels it accordingly.
1479
-
1480
- The native tests now union config EOS with tokenizer EOS, matching
1481
- `loadRuntimeModel`. The packed config alone omits the chat terminator. Earlier
1482
- `strict-fill-v10-{wall,max4-wall}.json` runs continued beyond the first chat
1483
- turn and are excluded from serving-performance conclusions. This was a test
1484
- policy error; the server already applies the union. The initial variant-6/7
1485
- logs also used the config-only stop policy.
1486
-
1487
- Corrected variant-10 diagnostics warm complete answers and retain six
1488
- alternating on/off pairs per cap. Both the default cap and the existing
1489
- `MLX_BUN_FILL_MAX_SPAN=4` cap preserve the first-turn token IDs with nonzero
1490
- assertions and no verification. Both improve observed request time; the
1491
- shorter cap injects fewer tokens and avoids an expanded append. These are
1492
- separate diagnostic sessions with prefill noise, not a default-policy verdict
1493
- or a direct comparison of truncation against chunking. Corrected reports:
1494
- `reports/qwen38-rd/strict-fill-v10-cap{32,4}-served-eos.json`.
1495
- `MLX_BUN_TEST_FILL_REPORT` enables the retained test's paired diagnostic;
1496
- `MLX_BUN_TEST_FILL_BLOCKS`, `MLX_BUN_TEST_FILL_PROMPT` and
1497
- `MLX_BUN_TEST_FILL_REVERSE` select its workload and schedule. Reports record
1498
- the cap, rows, prompt IDs, exact emitted IDs, counters and whole-request time.
1499
- `MLX_BUN_TEST_FILL_THINKING=0|1` and `MLX_BUN_TEST_FILL_TOKENS` select the
1500
- thinking policy and completion budget for broader fixtures. The test rejects
1501
- emitted EOS IDs; its report records the effective stop set.
1502
- The corrected variant-11 default-cap repeat also preserves first-turn IDs
1503
- with nonzero injection, zero verification and lower complete-request time.
1504
- Evidence: `reports/qwen38-rd/strict-fill-v11-cap32-served-eos.json`.
1505
- The EOS-correct five-case audit (weather control plus four frozen adversarial
1506
- prompts) preserves all emitted IDs, cache-token records and offsets. Quoted
1507
- malformed markup and code blocks produce no injections and retain exact
1508
- state/probe logits. The three injected cases preserve the response but differ
1509
- in live state after longer appends; subsequent teacher-forced logits differ
1510
- too. All verification counts remain zero. Explicit seed 42 reproduces those
1511
- results; temperature-zero sampling uses argmax. Reducing the span cap to four
1512
- restores exact state and four subsequent probes in the weather/multiple-call
1513
- fixtures, while injecting fewer tokens. Evidence:
1514
- `reports/qwen38-rd/fill-adversarial-state-v12-eos-review.json`,
1515
- `fill-state-v12-cap4-eos.json` and
1516
- `fill-state-v12-cap{32,4}-seed42.json` in the same directory.
1517
-
1518
- An isolated follow-up removes generation and sampling entirely. It copies a
1519
- nine-token span from the unfilled model's recorded output and appends it to
1520
- byte-identical cloned starting caches. Serial and repeated serial appends,
1521
- full M3/4 appends, and chunks of at most four tokens agree exactly in state,
1522
- final logits and the next teacher-forced probe. Full M5/9 appends reproduce
1523
- the numerical difference. Restoring the saved serial cache preserves its
1524
- bytes and next logits exactly. Thus copying a cached state works as expected;
1525
- recomputing known tokens through a different graph shape changes numerics.
1526
- The packed projection dispatch crosses from small-M matvec to prefill at
1527
- M=5; that prefill path also rounds reconstructed weights to the activation
1528
- dtype. This is a compute-path distinction, not a changed token span or seed.
1529
- Evidence: `reports/qwen38-rd/fill-identical-input-append-review.json`.
1530
-
1531
- Four-token execution chunks now preserve every asserted token and pass all
1532
- five complete-generation fixtures at seed 42. Emitted ids, cache-covered ids,
1533
- live cache bytes and four subsequent teacher-forced logits are exact against
1534
- ordinary generation. Positive cases still inject 12, 8 and 20 tokens with no
1535
- verification; both negative cases inject zero. The production implementation
1536
- passes the same audit, without the prototype model adapter. It is a shared
1537
- MLX append operation, selected by a captured FillSession setting rather than
1538
- model inspection in the generation loop. Verify proposals retain a single
1539
- forward because recurrent rollback records one forward per round. Tests cover
1540
- budget clamping, one final vocabulary projection, a consumer break inside the
1541
- burst, cancellation, failed-chunk cleanup and recurrent verification rollback.
1542
- The setting and default live in server-config.md.
1543
-
1544
- Six balanced three-arm blocks per positive timing fixture compare ordinary
1545
- generation, existing full-span fill and four-token execution chunks. The full
1546
- span is retained in both fill arms; every pair preserves emitted ids and
1547
- cache-covered ids. The chunked prototype improves complete-request diagnostic
1548
- timing in every pair, with higher peak allocation than ordinary generation.
1549
- These are local M4 Pro observations with residual swap, not quiet benchmark
1550
- rows or a universal determinism guarantee. Six production HTTP server pairs
1551
- then preserve all 24 paired warm/measured responses, tool calls and injection
1552
- counts. Every measured fixture pair improves complete-request time. Dispatch
1553
- counters confirm chunking is used only by the candidate, all twelve servers
1554
- exit cleanly, and source hashes remain fixed. HTTP uses greedy temperature zero
1555
- without an explicit seed because explicit HTTP seeds currently exclude fill;
1556
- the separate native acceptance uses seed 42. Broader held-out/parser, model
1557
- and quiet/second-Mac gates remain. Evidence:
1558
- `reports/qwen38-rd/fill-chunked-complete-requests-review.json`,
1559
- `fill-chunked-state-v12-seed42-review.json`,
1560
- `fill-integrated-state-v12-seed42-review.json` and
1561
- `fill-integrated-http-packed-review.json`, plus
1562
- `fill-chunked-integrated-{unit,model-free,typecheck,hygiene}.txt`.
1563
-
1564
- Performance experiments: eliminate redundant in-flight sampling/head work,
1565
- merge adjacent determined spans, reuse compiled schema plans with exact
1566
- identity keys, and choose append batch size from the packed-trellis M curve.
1567
- For the discarded head, test scheduling the next model body first, reading
1568
- the current token while that body runs, then projecting only when the fill
1569
- decision requires a sampled continuation. The existing graph interface already
1570
- separates body and head. Cache advancement must be tracked independently of
1571
- a pending sample. Verify and trace policies still require their sampled
1572
- position; assert must not acquire runtime verification. Check EOS, cancellation,
1573
- budget limits and consumer breaks, and measure unfilled tokens for any loss
1574
- of pipeline overlap before retaining this change.
1575
- The first staged-body prototype passes 26 lifecycle/verification checks and
1576
- all five frozen 27B fixtures. Both arms retain strict fill and four-token
1577
- appends, with compiled decode disabled. Emitted IDs, cache-covered tokens,
1578
- live cache bytes and all continuation probes match. The positive fixtures
1579
- retain their injected spans with zero wasted samples and zero verification;
1580
- the two negative fixtures still inject nothing. This establishes the staged
1581
- path's tested correctness, not its speed or a universal forced-token proof.
1582
- Six complete-request pairs per fixture include an enabled-fill negative
1583
- fixture and an explicit fill-disabled control. All emitted IDs, cache-covered
1584
- tokens and injection counts match. Timing is approximately flat in every
1585
- fixture; the disabled control moves by as much as the best positive fixture.
1586
- Peak allocation is unchanged or slightly higher. Close this staging prototype
1587
- without integration: removing discarded samples does not establish a useful
1588
- complete-request gain. Evidence: `reports/qwen38-rd/fill-staged-unit.txt`,
1589
- `fill-staged-state-v13-seed42-review.json` and
1590
- `fill-staged-complete-requests-review.json`. Closed staging helpers are removed.
1591
- Superseded chunking/state/HTTP research helpers are removed; their frozen
1592
- fixtures, raw reports and the retained production append helper remain.
1593
- On default variant 6, M>4 triggers dense expansion. Experimental variants
1594
- 11/12 add direct tiles and change that cost curve; span policy needs the
1595
- selected variant's measured append costs. A longer known span is not
1596
- necessarily cheaper per token. Inspect cache writes and resume logits in traces, then
1597
- time with `MLX_BUN_FILL_TRACE` unset. Measure emitted/predicted counts, total
1598
- wall time, first-tool-call latency and task success. Extend the current
1599
- grammar/KV/batch/logprobs/seed exclusions individually after their own gates.
1600
- The full experimental matrix and completion rule are in
1601
- [decode-speed-program.md](decode-speed-program.md#7-qwen38-27b-research-program).
1602
-
1603
- ### 7.5 Logical jump-ahead prefill
1604
-
1605
- Proposal by Josh Rossi, recorded 2026-09-08. Research proposal, not an
1606
- implemented semantic intervention or a measured task-time improvement.
1607
- This is the canonical write-up and proposed preprint question. The existing
1608
- fixed-serialization implementation is described in §7.4. The broader
1609
- proposal deliberately permits a different continuation.
1610
-
1611
- The governing [product objective](../planning/PRODUCT_ROADMAP.md#product-north-star),
1612
- in Josh's words, is "The absolute BEST local AI experience on a mac". It
1613
- covers the entire project, including simple installation, direct application
1614
- integration and JavaScript/TypeScript development. A preprint is optional.
1615
- This research serves that objective by finishing tasks sooner, producing
1616
- better results, or making a more capable model practical on the same hardware.
1617
- A hypothetical 30% reduction in task completion time would be a meaningful
1618
- product improvement, not a measured result of this proposal. Evaluate the
1619
- complete experience, including quality, reliability, latency and memory,
1620
- rather than treating a token-rate increase or a publication as the goal.
1621
-
1622
- The model often spends time deciding what to do, writes relevant content
1623
- in its reasoning, and then generates that content again inside a tool call.
1624
- Josh proposes letting the runtime recognize the intended action, construct
1625
- some or all of its tool-call representation, and append it through prefill.
1626
- The model continues from the supplied history and actual tool result.
1627
- The runtime does not ask the target model to verify that it would have
1628
- sampled each supplied token.
1629
-
1630
- The research question has two parts:
1631
-
1632
- 1. With perfect knowledge of an eligible continuation, how much decode time
1633
- can be replaced by cheaper prefill and construction work?
1634
- 2. With an imperfect predictor, how often can the model recover from an
1635
- unsuitable intervention, and what does that recovery cost?
1636
-
1637
- The deciding quantity is time saved by logical jump-ahead prefill minus time
1638
- spent on recovery, with intervention overhead included and task quality held
1639
- to an independently evaluated standard. Prediction errors do not by
1640
- themselves rule out a useful speedup. Their consequences determine the cost.
1641
-
1642
- #### Context management is the broader premise
1643
-
1644
- Josh's framing is that working with LLMs is largely context management.
1645
- For the local inference system under our control, the runtime owns every
1646
- token supplied to the model. At each generation step, the abstraction is
1647
- `token sequence in -> next-token distribution -> sampled token`.
1648
- The KV and recurrent caches are computed representations of that history,
1649
- not an independent record of the path the model might have taken.
1650
-
1651
- The runtime can construct, insert, replace, summarize or remove context.
1652
- It can choose what the model needs to decide and what software can supply.
1653
- The full original transcript need not remain the active inference context.
1654
- It can remain in the audit log while a different sequence drives the next
1655
- step. The resulting sequence still must be processed consistently by the
1656
- model and preserve enough information for the task.
1657
-
1658
- Logical jump-ahead prefill and micro-compaction are two applications of this
1659
- control. One supplies a continuation before it is redundantly generated.
1660
- The other retires or rewrites material after its immediate purpose is served.
1661
- Both should be judged by successful task completion, elapsed time and the
1662
- cost of recovering from an inadequate context decision.
1663
-
1664
- #### Mechanism and example
1665
-
1666
- Suppose the reasoning contains a settled instruction to write `hello.txt`
1667
- with the contents `hello, world!`. The normal path generates a tool name,
1668
- argument names, path and escaped contents. The proposed path constructs that
1669
- representation from the available trace, appends it as assistant tool-call
1670
- content, and executes the call through the normal tool dispatcher.
1671
-
1672
- An intervention consists of:
1673
-
1674
- 1. Detect an eligible boundary using only context available at that moment.
1675
- 2. Select an action or source span and construct the continuation, including
1676
- required escaping and protocol delimiters.
1677
- 3. Append the supplied tokens through the model's state-update path, then
1678
- resume generation at the next unresolved choice or dispatch a complete call.
1679
- 4. Return the actual tool outcome and allow the model to continue, including
1680
- correcting an unsuitable action when needed.
1681
-
1682
- The state update still runs model forward computation. Attention KV,
1683
- recurrent state, positions and parser state must describe the new sequence.
1684
- It saves sequential token decisions and can avoid intermediate vocabulary
1685
- projections and verification work. Copying text from an earlier position
1686
- does not generally permit copying that position's KV, because its context
1687
- and position differ. Editing an existing prefix also requires invalidating
1688
- and recomputing affected state. Appending at the current boundary is the
1689
- smallest implementation to study.
1690
-
1691
- There is no hidden record of the alternate tokens the model would have
1692
- sampled. Given consistent state for the supplied prefix, subsequent decoding
1693
- conditions on that prefix. This does not imply that every supplied action is
1694
- appropriate or that the model always recovers. Those are empirical questions.
1695
-
1696
- #### Perfect prediction and imperfect recovery
1697
-
1698
- Perfect prediction supplies an upper-bound experiment. Replacing a known
1699
- span's serial generation with a cheaper append saves time. Span size,
1700
- context length, kernel behavior and construction overhead determine the
1701
- amount. Very short spans can cost more to detect and append than to decode.
1702
-
1703
- For the oracle experiment, use the saved future continuation to choose the
1704
- span, while requiring the copied source content to exist before the jump.
1705
- This deliberately uses hindsight to measure opportunity. It is not evidence
1706
- that an online detector can identify those opportunities. An online policy
1707
- must receive only the prefix available at the intervention boundary.
1708
-
1709
- With an imperfect policy, an action may still satisfy the task even when it
1710
- differs from the saved continuation. Another action may require correction.
1711
- For example, the runtime supplies `ls` in an unsuitable working directory.
1712
- The returned listing fails to answer the model's question, so the model
1713
- changes directory or asks for a different listing. The extra reasoning,
1714
- request prefill, tool calls and tool execution count as recovery cost.
1715
-
1716
- Record errors the model fails to notice, persistent loops and unsuccessful
1717
- tasks too. Recovery is possible, not guaranteed. A retry alone is not proof
1718
- of recovery; the task must regain useful progress and ultimately meet its
1719
- acceptance criteria. Identical sampled tokens are not required for success.
1720
-
1721
- #### Cost model
1722
-
1723
- For an intervention, define:
1724
-
1725
- | Symbol | Cost |
1726
- |---|---|
1727
- | `D` | Serial decode time avoided for the replaced span |
1728
- | `P` | Time to prefill the supplied continuation and resume |
1729
- | `H` | Detection, selection, serialization and other intervention overhead |
1730
- | `R` | Additional downstream time caused by correction or other consequences |
1731
-
1732
- The local accounting model is:
1733
-
1734
- ```text
1735
- net saving = D - P - H - R
1736
- ```
1737
-
1738
- If a fraction `q` of interventions require recovery, and their mean added
1739
- cost is `C`, a simplified expectation is:
1740
-
1741
- ```text
1742
- expected net saving = E[D - P - H] - q * C
1743
- break-even recovery frequency = E[D - P - H] / C
1744
- ```
1745
-
1746
- The break-even expression assumes positive `C`, successful eventual recovery
1747
- and no other downstream differences. Here `q` measures harmful interventions
1748
- requiring recovery, not disagreement with a hypothetical sampled string.
1749
- Span length, uncertainty and recovery cost may be correlated; measure their
1750
- joint behavior rather than multiplying unrelated averages.
1751
-
1752
- This decomposition explains the mechanism. The authoritative measurement is
1753
- complete task wall time against a paired baseline. A changed trajectory can
1754
- alter later reasoning, tool latency, cache reuse and compaction. Those effects
1755
- belong in the result even when they cannot be assigned to one jump. Do not
1756
- double-count overlapping spans or add savings from dependent interventions
1757
- as if each occurred in the original unmodified run.
1758
-
1759
- #### Evidence and first experiment
1760
-
1761
- The completed Kanban trace supplies a workload and an offline opportunity
1762
- analysis. Generation-to-generation duplicate coverage and the narrower
1763
- reasoning-to-tool coverage are recorded in the
1764
- [completed Kanban benchmark](../reference/benchmarks.md#completed-fresh-pi-kanban-with-bounded-range-cache-m4-pro-2026-09-08).
1765
- Those counts retain first occurrences, merge overlapping later positions and
1766
- exclude replayed request history. They use retokenized payloads rather than
1767
- the original sampled token stream, so they are opportunity estimates.
1768
- The longest whole-file repeat was a write followed by a read. It does not
1769
- establish that the model first wrote that whole file in reasoning.
1770
-
1771
- Start with these experiment arms:
1772
-
1773
- | Arm | Purpose |
1774
- |---|---|
1775
- | Ordinary decoding | Establish task success, timing and natural retry behavior |
1776
- | Existing strict fill | Isolate savings from fixed serialization already supported |
1777
- | Oracle content jumps | Estimate savings with hindsight and no selection mistakes |
1778
- | Online logical jumps | Measure an implementable policy using only the current prefix |
1779
- | Controlled mistaken jumps | Measure detection, recovery and failure under known perturbations |
1780
-
1781
- For the online arm, begin with complete literal content already in the trace
1782
- and a partially generated tool call that identifies the destination. This
1783
- reduces the decisions the runtime must infer. Then evaluate earlier action
1784
- selection as a separate policy. Track changes in opportunity coverage and
1785
- recovery cost as the policy becomes more permissive.
1786
-
1787
- Use fresh workspaces and matched prompts, models, quantization, tool access,
1788
- context limits and hardware conditions. Preserve the same cache and decoding
1789
- configuration across arms where supported. The Kanban configuration and
1790
- strict fill currently have composition restrictions; establish a supported
1791
- common control before attributing a difference to logical jumps. Keep the
1792
- separate MLX-LM comparison as a reference experiment, not a substitute for
1793
- this within-engine ablation.
1794
-
1795
- Record supplied and sampled tokens separately, source-span provenance,
1796
- intervention boundaries, append cost, selection cost, tool outcomes, extra
1797
- turns, recovery time, compactions and complete task time. Keep raw generated
1798
- text and supplied text distinguishable in the audit log, while making the
1799
- model-visible history consistent with the action actually executed.
1800
-
1801
- Repeat paired runs over multiple tasks and sampling trials. A matching seed
1802
- does not preserve the trajectory after an intervention. Evaluate artifacts
1803
- with independent task checks. Report success rate and the distribution of
1804
- task times, including slow recoveries, timeouts and failures. Fast failed
1805
- runs must not improve the reported time-to-success result. Where retries
1806
- are allowed, include their cost under a predefined retry budget.
1807
-
1808
- #### Micro-compaction after tool execution
1809
-
1810
- Josh proposes replacing a long reasoning segment after its associated tool
1811
- calls have completed. For example, the model may spend roughly 50,000 tokens
1812
- planning and drafting, then write files and inspect the results. At that
1813
- boundary, the runtime could replace the reasoning with a concise account of
1814
- decisions, completed actions, observed results and unresolved work.
1815
-
1816
- This is selective compaction at a task boundary. It need not wait until the
1817
- whole context window approaches its limit. A candidate procedure is:
1818
-
1819
- 1. Identify the reasoning segment whose associated actions have completed.
1820
- 2. Preserve current requirements, decisions that still constrain the work,
1821
- observed failures and pending questions in a compact replacement.
1822
- 3. Retain the relevant tool-call/result pairs and references to written
1823
- artifacts. Keep the original trace available outside the active context.
1824
- 4. Build the revised token sequence, recompute affected model state, and
1825
- continue the task from that sequence.
1826
-
1827
- The replacement may come from a model-generated summary or deterministic
1828
- extraction of completed actions and results. Compare these methods rather
1829
- than assuming another long generation is necessary. A successful file write
1830
- provides an artifact the model can read again; whether that makes the full
1831
- earlier draft disposable depends on the remaining task. Missing rationale
1832
- or a discarded constraint can require recovery even when the file survives.
1833
-
1834
- Removing earlier tokens changes the context for retained later tokens.
1835
- Ordinary prefix caching can preserve the unchanged prefix, but it cannot
1836
- generally reuse the old suffix state as if the removed reasoning were still
1837
- present. Recompute that suffix under the new history. Any method that keeps
1838
- stale state intentionally is a separate approximation to evaluate.
1839
-
1840
- Micro-compaction cannot recover the time already spent generating the long
1841
- reasoning segment. It can reduce later context-processing work and memory
1842
- use, and may change subsequent generation or avoid a larger compaction.
1843
- Measure those effects over the remaining task:
1844
-
1845
- ```text
1846
- net saving = future context-processing and trajectory savings
1847
- - summary/extraction cost
1848
- - state-rebuild and management cost
1849
- - extra recovery cost
1850
- ```
1851
-
1852
- Because a warm prefix cache already avoids much repeated prefill, removed
1853
- token counts alone do not establish savings. Context length can still affect
1854
- subsequent attention work and capacity. Record actual cache reuse, rebuild
1855
- time, memory, later decode time and rereads caused by missing information.
1856
-
1857
- Compare the existing compaction policy, post-tool micro-compaction,
1858
- jump-ahead alone, and their combination. Use the same task checks and failure
1859
- accounting as the jump experiment. Their gains need not add: removing a
1860
- reasoning segment also removes a potential source for later content copying.
1861
- An external source store could retain it, with retrieval cost included.
1862
-
1863
- #### Related work and the proposed preprint
1864
-
1865
- Josh identified the connection to structured output and grammars at the
1866
- outset. [XGrammar](https://github.com/mlc-ai/xgrammar) implements efficient
1867
- grammar-constrained generation. SGLang's
1868
- [jump-forward decoding](https://www.lmsys.org/blog/2024-02-05-compressed-fsm/)
1869
- directly prefills strings fixed by a grammar until the next branch. These
1870
- are established relatives of fixed-serialization fill.
1871
-
1872
- [Copy-as-Decode](https://arxiv.org/abs/2604.18170), an April 2026 preprint,
1873
- is direct prior art for unverified content prefill. The model emits an
1874
- explicit instruction selecting input line ranges, then the runtime prefills
1875
- their contents without probabilistic verification. It studies an oracle
1876
- savings bound and span-selection errors. Editing belongs within this
1877
- proposal's scope, including when edits are tool calls; the application label
1878
- does not establish a separate invention. The relevant difference to study
1879
- is runtime inference from reasoning or a partial call versus an explicit
1880
- model-selected copy instruction, and the cost of subsequent agent recovery.
1881
- The paper does not establish agentic multi-file coverage.
1882
-
1883
- Harness intervention is also established. AI SDK supports
1884
- [custom tool-call repair](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#tool-call-repair),
1885
- Pydantic AI exposes
1886
- [JSON and argument modification hooks](https://github.com/pydantic/pydantic-ai/blob/main/docs/capabilities/custom.md),
1887
- and OpenClaw can
1888
- [insert loop warnings and block repeated calls](https://docs.openclaw.ai/tools/loop-detection).
1889
- These show that an agent system can alter actions or subsequent context
1890
- outside the sampler. They do not measure this proposal's speedup.
1891
-
1892
- Exact [speculative sampling](https://proceedings.mlr.press/v202/leviathan23a.html)
1893
- preserves the target distribution conditional on a given context and sampling
1894
- policy. Logical jumps choose supplied context without that requirement.
1895
- An exact speculative decoder could still accelerate free generation between
1896
- jumps.
1897
-
1898
- The search so far has not identified a direct demonstration of runtime
1899
- inference of a tool continuation from current reasoning, unverified prefill
1900
- of that continuation, and whole-agent evaluation of savings against recovery
1901
- cost. This is a bounded search finding, not a priority claim or proof of
1902
- absence. The broader unverified-copy mechanism already has overlapping work.
1903
-
1904
- A working preprint title is **Logical jump-ahead prefill: trading redundant
1905
- agent decoding for recovery cost**. The contribution to pursue is a defined
1906
- intervention mechanism, an oracle opportunity bound, an online policy, and a
1907
- measured relationship between saved decode time and recovery cost at retained
1908
- task quality. A negative or workload-dependent result would still answer the
1909
- question. The current trace analysis motivates that study; it does not yet
1910
- establish the paper's performance result. A broader literature review and
1911
- the controlled experiments above remain necessary before claiming novelty
1912
- or a practical speedup.
1913
-
1914
- ## 8. Open items
1915
-
1916
- - K3 (fill): the packed 27B positive-fill token-identity fixture passes;
1917
- held-out/adversarial token and state identity plus a paired wall-time win
1918
- remain before default-on (§7.4). Echo-tier policy levers before any
1919
- rerun (§7.3); a multi-turn showcase fixture (the single-prompt one fills
1920
- 0%); the turn-8 server-crash repro (`lab/repro/serve-crash-turn8`).
1921
-
1922
- - DSpark serving program phases 0, 1d, 1e, 1.5, 2, 3, 4, 5, 6 (§4.5) —
1923
- every GPU measurement is Josh's shell; Phase 3 (generated-forward tap)
1924
- and Phase 2 (draftBlock tightening) are agent-runnable.
1925
- - 27B Track A / Track B (§4.6; PLAN.md boxes) + 14h DSpark cross-check.
1926
- - Spec × prompt-cache composition (serve-loop v1 bypass).
1927
- - Per-slot spec under batching (unified-engine frontier row, not this
1928
- program).
1929
- - Qwen MTP wall-clock: head quantization / deferred per-draft lm-head
1930
- sampling (the 0.821× verdict's levers); the TQ-artifact divergence-flag
1931
- margin analysis (§2.1).
1932
- - Assistant drafter γ≥2 on 12B: the 262k tied-embedding argmax per draft
1933
- step is the dominant cost; capping/approximating it could extend the win
1934
- past γ=1. 26B-MoE unmeasured (expected loss — decode reads top-8/128
1935
- experts, so per-token target cost is lower than its size).
1936
- - Optional `strictVerify` (per-position verify, "spec output identical to
1937
- spec-off") — a product nicety no oracle provides; not implemented; ask.
1938
- - Verify the Eq 6 / §3.2.1 shapes against the paper PDF (both flagged in
1939
- code); paper-faithful gated RNN head and survival-product scheduler stay
1940
- Lab items.
1941
-
1942
- ## History
1943
-
1944
- - **2026-06-10** — e4b assistant drafter measured a net loss (γ=2 0.78× of
1945
- 54 tok/s, ~23% acceptance); spec ships default-off.
1946
- - **2026-06-14** — `docs/design/speculative-decoding.md` result: the verify path
1947
- had targeted the WRONG oracle (per-position lm-head to match stock decode;
1948
- no real implementation does this) — fixed with `picksBatched`, now
1949
- bit-exact to optiq `spec_generate` on e4b γ=2 (identical output and
1950
- accept/reject trace). 12B + `gemma-4-12B-it-assistant-bf16`, batched
1951
- verify, loaded machine (paired ratios only): γ=1 **1.09×** (42%
1952
- acceptance), γ=2 0.91× (29%), γ=3 0.72× (23%), γ=4 0.56× (17%) — the
1953
- earlier "net loss at every γ" (0.96/0.77/0.67/0.52×) was partly the
1954
- wrong-oracle tax. γ≥2 loses because the 12B drafter is heavy (hidden
1955
- 1024, 16 heads, 262k tied head per draft step). Clean-machine rerun
1956
- never promoted; 26B not run.
1957
- - **2026-06-29/30** — DSpark v1 (single-vector, superseded) then faithful
1958
- v2 (KV injection + Markov + confidence) built; TV-loss off-by-one found;
1959
- overfit τ 3.24 proves the architecture (M1 Max 32 GB, e4b).
1960
- - **2026-07-06** — Serve integration: the seam extended to carry target
1961
- state (TargetView, tapLayers, ctx flow); provider selection by artifact
1962
- kind; γ pinned to the trained block. Paper components code-complete
1963
- (pruning, calibration, RNN variant, loop tightening, `dspark` rename +
1964
- loader). DeepSpec audit: our loss/heads match the paper; the released
1965
- scheduler is threshold-truncation, not the paper's STS/Alg-1; DeepSpec's
1966
- temp-0 trace becomes the oracle. Tests 21/21 smoke, 17/17 dspark files,
1967
- 3/3 real-weights serve gate.
1968
- - **2026-07-07** — DeepSpec port first live run on 12B (τ≈2.8, 26–33%
1969
- acceptance, wall-clock −3.4× on a loaded M4 Pro 24 GB); the serving
1970
- program written; 1a/1b/1c landed the same day; oracle prefill convention
1971
- re-anchored to mlx-lm's spec shape (4/4 token-for-token incl. knife-edge);
1972
- accepted-draft EOS leak fixed.
1973
- - **2026-08-17/18** — Qwen native MTP: DeltaNet rollback solved via the
1974
- spec-round snapshot/replay contract (bit-exact by construction, gated);
1975
- 27B pairing gate token-identical (88% = 30/34, 2.82 tok/forward); quiet
1976
- M1 Max A/B 0.821× at γ=2 (61% acceptance) — durable negative perf
1977
- verdict, opt-in only; drafter-logit parity vs mlx-vlm 0.6.14 exact
1978
- through the chained block. TQ artifact gauntlet: MTP 76% accept / 2.53
1979
- tok/forward with a divergence flag, not claimed lossless.
1980
- - **2026-08-19** — 27B program (Track A / Track B) recorded, queued behind
1981
- the certification suite.
1982
- - **2026-08-20** — MTP `[1,V]` sampler-shape fix (serve-lane 500 under
1983
- top-k); GLM native MTP source carries the same note.
1984
- - **2026-08-23** — DSpark scripts consolidated under `scripts/dspark.ts
1985
- <job>`; one-off measure/compare/bench scripts deleted (git history); this
1986
- doc consolidates the three design docs.
1987
- - **2026-09-02** — K3 live A/B on the 27B (§7.3): echo tier injects 7.4%
1988
- but wall clock ×1.00 (76% verify rejection) — gate FAILS, stays Lab; batch
1989
- lane never fills; strict rows were silently empty on thinking templates
1990
- (primer newline merge) — fixed.