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
//! Small output/status types: [`StoryStatus`], [`Step`], [`OutputLine`],
//! [`BlockId`], [`Element`], [`StepOutcome`], [`Choice`], [`Stats`].
use BTreeMap;
use ;
use Vec;
/// The current execution status of a story.
/// Opaque identifier grouping a run of adjacent content lines.
///
/// `docs/prose-dialect-spec.md` §3.7/§8d.2 (RULED): "block id is universal"
/// — every run of same-element adjacent content lines carries one; hosts
/// aggregate consecutive [`OutputLine`]s sharing a `BlockId` or ignore it
/// entirely. Two `OutputLine`s carry the same id iff they belong to the same
/// uninterrupted run — a terminal ([`Step::Choices`]/[`Step::Done`]/
/// [`Step::End`]) or a host-directed jump always starts a new one.
///
/// Compile-time-baked block ids (per §3.6's attachment mechanism) are a
/// superset of this. Issue #2108 (`docs/decision-log.md` 2026-08-03 "The
/// element output model") delivers the first real instance: an
/// `attach = StructName` convention handler's data is merged into the VM's
/// output buffer (`brink_runtime::vm`'s `Opcode::AttachElement`/
/// `Opcode::EndElementRun`) and every line materialized while it's live gets
/// a copy in [`Element::data`] — but `BlockId` itself is **not** re-derived
/// from that mechanism; it stays the plain terminator-counting value it
/// always was (this field's own `next_block_id` doc, `brink_runtime::story::
/// call_stack::Flow`). A run of adjacent lines sharing one attach group can
/// therefore span more than one `BlockId` if it also crosses a real
/// terminator — the two concepts have not been unified.
;
/// A line's classification — kind + an open, preset-defined data map
/// (`docs/prose-dialect-spec.md` §7/§3.5b, §3.6, sitting-5 ruling item 8:
/// "the output format bakes no scene-specific fields — element data is an
/// open map produced by conventions and handlers"). Deliberately a `String`
/// kind, not a closed enum: the vocabulary belongs to whichever preset or
/// `@[element]` handler classified the line, never to the runtime.
///
/// **`data` is real** (issue #2108, `docs/decision-log.md` 2026-08-03 "The
/// element output model: attachment is block-level metadata, delivery is
/// per-line"): an `attach = StructName` convention handler's claimed line
/// consumes itself (no event — item 6, "AN EVENT EXISTS IFF A LINE EXISTS")
/// and its returned struct's fields merge into `data` on every line in the
/// run that follows (item 3: multiple attach handlers, e.g. `cue` then
/// `parenthetical`, accumulate onto the same run). **`kind` stays the
/// degenerate [`Self::NARRATIVE`] regardless** — classifying `kind` itself
/// for a non-attach single-line handler (`heading`/`transition` reporting
/// their own handler name as `kind`) is a distinct, still-open gap; see
/// issue #2108's own follow-up notes. A line with no preceding attach
/// convention still reports the always-correct [`Element::narrative`].
/// One line of story content, carried inside [`Step::Line`].
///
/// `.text` stays a plain field rather than a `Vec<Part>` decomposition —
/// that structured-markup surface (`docs/prose-dialect-spec.md` §7/§9.1's
/// `Part::Span`) is still out of scope (issue #2108 populated
/// [`Element::data`], the other half of the spec's element/markup layer, but
/// deliberately not this one — see this issue's own tracked follow-up).
/// A single step of story output from [`Story::continue_single`].
///
/// The enum tells the caller what to do next:
/// - `Line` — more output may follow, keep calling `continue_single`.
/// - `Done` — this turn's output is complete. Call `continue_single`
/// again for the next turn (the story isn't over).
/// - `Choices` — pick a choice via [`Story::choose`], then resume.
/// - `End` — the story has permanently ended.
///
/// **Terminals carry no payload** (`docs/prose-dialect-spec.md` §7, RULED —
/// this replaces the earlier `Line` enum, whose terminal variants fused
/// trailing text onto the outcome). Any trailing content that precedes a
/// terminal is always delivered first as its own `Step::Line` — a caller
/// draining `continue_single` in a loop sees the same total text either
/// way, just spread across one more step when a turn ends mid-line.
/// Outcome of a single [`FlowInstance::advance`] step.
///
/// Like [`Step`], but with an extra variant for when a binding handler
/// deferred an external call ([`ExternalResult::Pending`]) — e.g. a
/// world-access query hit during normal playback. The flow is paused with
/// its state intact: inspect the pending call via
/// [`pending_external_name`](FlowInstance::pending_external_name) /
/// [`pending_external_args`](FlowInstance::pending_external_args), supply
/// the result with [`resolve_external`](FlowInstance::resolve_external),
/// then call [`advance`](FlowInstance::advance) again.
///
/// [`step_single_line`](FlowInstance::step_single_line) is the simpler API
/// for consumers whose handler never pauses — it maps `AwaitingExternal`
/// to an error.
/// A single choice presented to the player.
// ── Stats ───────────────────────────────────────────────────────────────────
/// Lightweight counters tracking VM activity over a story's lifetime.
///
/// Always-on — incrementing a `u64` is effectively free compared to opcode
/// dispatch. Use [`Story::stats`] to read after a run.