1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
//! Which drafter an install carries, whether this run may use it, and the
//! [`DraftPolicies`] to open with.
//!
//! **THE POLICY LIVES HERE RATHER THAN IN A BINARY, because two front ends
//! need it and a second copy names the wrong cause the first time they
//! disagree.** It was `crates/cli/src/generate/speculation.rs`, a private
//! module of a crate with two binaries and no lib target
//! (`crates/cli/CLAUDE.md` Gotcha 9), so `turbospark-server` could not reach
//! a line of it -- and the server needs exactly these three decisions, in
//! exactly this order, for exactly the reason the CLI does: the drafter is
//! allocated at OPEN and there is one runner per process.
//!
//! The enums here are runtime-native and the parser crate keeps its own,
//! meeting in one mapping function per front end. That is the shape
//! `map_power_profile` and the `ExpertCacheSlots` mapping already use
//! (`crates/cli/CLAUDE.md` Gotchas 2 and 6): `crates/invocation` is pure and
//! may not read an install or a machine, and every decision below needs one
//! or the other.
//!
//! macOS-only, like `families` and for the same reason: `resolve_drafter`
//! reads a resident index and [`DraftPolicies`] names GPU state. The CLI's
//! whole `generate` module is already gated this way, so nothing portable
//! loses a capability.
use Path;
use crate;
use crateDEFAULT_SPECULATION_BLOCK;
/// Whether and how far this run drafts ahead. The runtime-native mirror of
/// `invocation::Speculation`.
/// Which drafter [`Speculation`] drives. The two are ALTERNATIVES
/// (`docs/MTP_SPECULATIVE.md`, `docs/DFLASH2.md`): the checkpoint's own MTP
/// head drafts a token at a time, the DFlash2 block-diffusion drafter
/// proposes a whole block in one pass. A third value is not a spectrum.
/// What [`resolve_drafter`] decided: the drafter to open, and a NOTE for the
/// case where `auto` FOUND a drafter and deliberately did not enable it.
/// What a front end decided about speculative decoding, resolved once.
/// Which drafter an install actually carries, for
/// [`SpeculativeDrafter::Auto`].
///
/// Reads the resident INDEX -- the leading index region of
/// `model_weights.bin`, kilobytes, not the weights -- and asks the same two
/// presence predicates `RealForwardRunner::open` asks. It is deliberately a
/// presence check and NOT a can-it-run check: whether the verify pass can
/// serve this install is `speculation_blocker`'s question, asked after the
/// open with the full architecture in hand, and answering it twice in two
/// places is how the two answers drift apart.
///
/// **DETECTION IS NOT ENABLEMENT, AND THAT SPLIT IS THE WHOLE POINT OF THIS
/// FUNCTION.** `auto` resolves to `Mtp` even on an install whose only
/// drafter is DFlash2, and returns a note naming the flag that would run it.
/// The measurement is why: through the shipped loop on the real install over
/// 600-token generations on an idle machine, DFlash2 at block 2 reads
/// **1.33x on code and 1.47x on math against 0.90x on PROSE**, and its own
/// power A/B reads 0.88x throughput at **+17.4% J/token** on the protocol's
/// prose case (`docs/DFLASH2.md`). So a default that switched it on would
/// make one common workload slower AND hungrier without being asked. (Two
/// numbers, two captures: the 0.90x is the block sweep and the 0.88x the
/// `COOLING=max` energy arm. Do not merge them -- an earlier version of this
/// comment quoted 0.96x, which is neither; that is the PREFILL ratio from
/// the priming measurement.) The MTP head is the opposite case (1.44-1.66x)
/// and keeps its `auto`. Resolving to `Mtp` also means `open` allocates no DFlash2 state,
/// which is 213 MiB of peak footprint on the real 27B install -- measured,
/// as the gap between the two arms of the same protocol case.
///
/// An install with BOTH resolves to `Mtp`, which is what the default was
/// before `Auto` existed, so no run that worked changes. An install with
/// NEITHER also resolves to `Mtp` with no note, so the "no drafter" message
/// such a caller sees is the one they have always seen.
///
/// An unreadable index resolves to `Mtp` rather than failing: this function
/// picks a drafter, and `open` is entitled to be the one that refuses a
/// broken install.
///
/// **THE INDEX IS READ WHATEVER THE REQUEST, and that is not the same
/// question as which drafter to pick.** An explicit ask is still passed
/// through untouched -- naming a drafter is a promise the caller made to
/// themselves -- but `install_has_mtp_head` is recorded either way, because
/// [`draft_policies`] needs it to avoid asking the open for a head that is
/// not there, and a caller who named BOTH a drafter and a block is exactly
/// the one that used to get the wrong reason. It costs one read of the
/// resident index, which is kilobytes and which `open` is about to do again
/// regardless.
/// The [`DraftPolicies`] to open a runner with, given the resolved drafter
/// and what the caller asked for.
///
/// **THE DRAFTER THE FLAG NAMED OWNS THE REQUEST, and the other is pinned
/// OFF**, so a dflash-carrying install never silently opens BOTH drafters'
/// state.
///
/// The `note` arm is the subtle one and it is load-bearing. A NOTE MEANS THE
/// DECISION IS ALREADY MADE, so do not ask the open for a head we know is not
/// there. Without it, `--speculative 2` on a DFlash2-only install fails at
/// OPEN with `MtpDraftPolicy::Fixed`'s message -- "this install carries no
/// multi-token-prediction head ... stream an install that adds the official
/// checkpoint's last shard" -- which sends someone who is holding a working
/// drafter off to download a different one. `Off` lets the open succeed so
/// [`resolve_speculation`] can refuse with the note, which names the flag.
///
/// **THE HEADLESS ARM EXISTS ON BOTH DRAFTERS, and each asks about its own.**
/// The MTP branch declines to ask the open for a head when
/// `install_has_mtp_head == Some(false)`; the DFlash2 branch does the same on
/// `install_has_dflash`. The second was missing until 2026-08-22, so
/// `--speculative-drafter dflash --speculative 2` on the real `ornith35b`
/// failed at OPEN with `DflashState::build`'s "this install carries none ...
/// stream it beside the trunk", which on a MoE checkpoint is a wild goose
/// chase: the published DFlash2 drafter targets the DENSE half of this
/// architecture, so no artifact satisfies that instruction. `auto` reported
/// the right thing on the same install in the same run, exactly as it had for
/// the MTP path, and for exactly the same reason: `auto` reaches
/// [`resolve_speculation`] and a named block did not.
///
/// **THE HEADLESS ARM IS THE `note` ARM'S ARGUMENT ON A WIDER INPUT, and the
/// two are deliberately not collapsed into one condition.** The `note` arm
/// asks "has the decision already been made"; the headless arm asks "is there
/// a drafter to ask the open for at all". They agree on a DFlash2-only install
/// and part company on every other headless one -- which is exactly where this
/// was wrong. Measured 2026-08-21 on the real `ornith35b` install: `--speculative
/// 2` reached `MtpDraftPolicy::Fixed`, failed at OPEN with "carries no
/// multi-token-prediction head ... stream an install that adds the official
/// checkpoint's last shard", and so sent a caller after a 4.4 GB shard that
/// CANNOT help -- that install routes to 256 experts, and no published MoE
/// conversion of this architecture carries an ingestible drafter, so no shard
/// of any checkpoint changes the answer. `auto` got the same install
/// right, because `auto` reaches [`resolve_speculation`] and a named block
/// did not. `speculation_blocker` has always reported the ARCHITECTURAL
/// obstacle ahead of the missing head (`crates/runtime/CLAUDE.md` Gotcha 16);
/// it was simply never reached, because the open failed first. `Off` lets the
/// open succeed so it is.
///
/// **The hard fail is UNCHANGED.** `resolve_speculation` still returns `Err`
/// for a named block it cannot serve -- a caller who named a block is
/// measuring, and a run that quietly did not speculate is the number that
/// ends up in a table. Only the reason improves, and on a DENSE headless
/// install it is the SAME sentence as before, because that is what
/// `speculation_blocker` returns once its architectural checks pass.
/// Decides speculation from the request and what the install turned out to
/// be. Pure, so the hard-fail/warn split is testable without a 14 GB model.
///
/// **THE TWO OUTCOMES DIFFER BY WHO ASKED.** A named block is a promise the
/// caller made to themselves -- they are measuring, or they have read
/// `docs/MTP.md` and chosen 2 -- so failing to keep it is an ERROR carrying
/// the reason. `auto` asked for "speculate if you can", so the same condition
/// is a warning and the run continues. Silently doing neither is what the
/// engine did before the head was detected at all.
///
/// The sampled case is refused only for the BLOCK drafter: the step drafter
/// serves any temperature through exact rejection sampling
/// (`crates/runtime/src/speculative.rs`, ROADMAP P1 item 4), while DFlash2's
/// selection is a greedy structured search with no q(x) to ratio against.
/// A named block on a sampled run under the block drafter is still a
/// hard fail rather than a quiet downgrade, for the reason it always was.