fyflow-orchestration 0.7.0 → 0.8.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.
@@ -12,11 +12,11 @@
12
12
  * driver turns out to need a second copy of run truth, that is a finding about the
13
13
  * spec rather than a thing to build.
14
14
  */
15
- import type { FlowDefinition, GateTypes, Languages, NodeTypes, Pools, TerminalState, Workers } from '../definition/types.js';
15
+ import type { Finding, FlowDefinition, GateTypes, Languages, NodeTypes, Pools, TerminalState, Workers } from '../definition/types.js';
16
16
  import { type SchemaFinding } from '../definition/schema.js';
17
17
  import { type LogRecord, type OutcomeRecord } from './log.js';
18
18
  import { type PredicateRegistry } from './routing.js';
19
- import type { RegionEvent } from './events.js';
19
+ import { type RegionEvent } from './events.js';
20
20
  import type { JoinPolicy, Region, UnsatisfiableJoin } from './policy.js';
21
21
  import type { FoldInput } from '../fold/run-state.js';
22
22
  import type { RetryPolicy } from '../definition/types.js';
@@ -33,6 +33,19 @@ export declare class InvalidDefinition extends Error {
33
33
  */
34
34
  readonly rules: string[];
35
35
  constructor(rules: string[], message: string);
36
+ /**
37
+ * **A definition's findings as the error, built in ONE place** — P34 fact 12, W169. `new Driver` throws it for what
38
+ * `validateDefinition` found, and `createRuntime`'s `start` and `resume` throw it for what `runtime.check` found (ADR 0096
39
+ * rule 5), so the two roads cannot drift apart: `rules` is `violatedRules(findings)` — sorted, each once — and the
40
+ * message is `definition "<flowId>" is invalid: ` then every finding as `<rule> at <at>: <detail>`, `at` reading
41
+ * `<document>` where a finding has none, joined by `; `. *(Until W169 the two roads each built it inline, the same text
42
+ * twice.)* `at` is printed as given — a node id, an edge site (`id#edgeN`), or `<document>` for `null`.
43
+ *
44
+ * **Its precondition is at least one finding**, and an EMPTY list is refused with a `RangeError` (the conformity reader's
45
+ * C3): an `InvalidDefinition` naming no defect would read *"is invalid: "* and name nothing. Both callers throw only
46
+ * when they found something.
47
+ */
48
+ static fromFindings(flowId: string, findings: Finding[]): InvalidDefinition;
36
49
  }
37
50
  export declare class DuplicateTaskId extends Error {
38
51
  constructor(message?: string, options?: ErrorOptions);
@@ -111,7 +124,7 @@ export declare class UnresolvedReference extends Error {
111
124
  * rather than to the document. It stops the run through `OR-R10` (reported from a listener, reported AND
112
125
  * rethrown from `start()` and `resolve()`), and **nothing is appended for the gate**: no `gated` element,
113
126
  * because no route was taken, and the memory the type may have written before returning is the driver's own
114
- * and dies with the stopped run. *A type whose `fn` THROWS takes the same road with its OWN error* — the road
127
+ * and dies with the halted run. *A type whose `fn` THROWS takes the same road with its OWN error* — the road
115
128
  * a throwing decision predicate takes, measured at W93's declaration step.
116
129
  */
117
130
  export declare class UndeclaredRoute extends Error {
@@ -125,6 +138,12 @@ export declare class UndeclaredRoute extends Error {
125
138
  * [W73](../../doc/work-items/w73-the-wiring-check-a-verdict-per-edge-and-two-boundaries-validated-once.md).
126
139
  * Once per run and once per human action, never per settlement (that is OUT of P19 as a default).
127
140
  *
141
+ * **A task that declares a `transform` is not held at `start()`** — ADR 0101,
142
+ * [W170](../../doc/work-items/w170-a-transforming-task-is-not-held-at-start.md), Q633's road 2: its
143
+ * worker's `inputSchema` holds the transform's RESULT (`FD-C5`), which only the worker wrapper makes;
144
+ * `givenVerdict` (`tryTransform(…, { input })` on the node as declared) on that task, with the value it
145
+ * RECEIVES — the payload composed through its `needs`, a map branch's element — is the check before a run.
146
+ *
128
147
  * **A REJECTED COMMAND and not a run failure**, on `RS-R1`'s own reading of `resolve` — *it came from
129
148
  * outside and a caller can retry* — and it is the maintainer's choice at W73's stop-and-ask 2.
130
149
  * Thrown at the caller with NOTHING appended, nothing reported through `onRunFailure` and the run
@@ -145,9 +164,9 @@ export declare class UndeclaredRoute extends Error {
145
164
  export declare class BoundaryRefused extends Error {
146
165
  /** Which boundary refused: the run's payload at `start`, or a person's value at `resolve`. */
147
166
  readonly boundary: 'start' | 'resolve';
148
- /** The task node whose worker's schema refused the value. */
167
+ /** The task node whose worker's schema refused the value — or, at `resolve`, the ASK whose `answerSchema` did (W180). */
149
168
  readonly nodeId: string;
150
- /** The suspended task, at `resolve`; `start` has dispatched nothing yet. */
169
+ /** The suspended task — or parked ask — at `resolve`; `start` has dispatched nothing yet. */
151
170
  readonly taskId: string | null;
152
171
  /** Every way the value failed the schema — `findings`' own list, one line each in `message`. */
153
172
  readonly findings: readonly {
@@ -186,6 +205,75 @@ export declare class MultipleRuns extends Error {
186
205
  export declare class NotStarted extends Error {
187
206
  constructor(message?: string, options?: ErrorOptions);
188
207
  }
208
+ /**
209
+ * **Why a run-control call was refused** — `RN-R31`, v0.18 ([ADR 0104](../../doc/decisions/0104-a-run-its-owner-cancels-is-cancelling-until-its-last-task-has-stopped.md)),
210
+ * and `RN-R33`, v0.19 ([ADR 0105](../../doc/decisions/0105-a-run-its-owner-stops-goes-deaf-before-it-cancels-and-resumes-elsewhere.md)).
211
+ *
212
+ * | reason | refused when |
213
+ * |---|---|
214
+ * | `unsupported` | the driver's scheduler has no `cancel` — `SchedulerLike`'s optional member (P34 (b)); a cancel and a stop alike |
215
+ * | `not-started` | before `start()` or `resume()`, or on a driver whose resume was refused before it regenerated a run (`RN-R23`) — one refused for a halt regenerated in its lost tail keeps that run, and is `halted` |
216
+ * | `halted` | the run was halted by a refusal (`OR-R10`) |
217
+ * | `cancelled` | a task of the run settled `cancelled` with no request before it, so `RN-R28` ended it — and, for a STOP, a cancel its owner requested too (P34 (c): *after a cancel, a stop is refused*) |
218
+ * | `interrupted` | a CANCEL of a run its owner has STOPPED (`RN-R33`, P34 (c): *a cancel after a stop is refused by name*) — v0.19; and since v0.20 a PAUSE or an UNPAUSE of one (`RN-R35`) |
219
+ * | `paused` | a PAUSE of a run already paused — or whose pause, called mid-step, is still to be taken (`RN-R35`, P34 (e): *a second pause is refused by name*) — v0.20 |
220
+ * | `not-paused` | an UNPAUSE of a run that is not paused (`RN-R35`, P34 (e)) — v0.20 |
221
+ * | `ended` | nothing is in flight and nothing is parked AT REST — the run has finished. Mid-step (a callback on the driver's own step) it is not refused: see `stepping`. A cancel, a stop and a pause — never an unpause, which rewrites no outcome |
222
+ *
223
+ * **A SECOND cancel, and a SECOND stop, is not refused**: each forwards its `graceMs` (`RN-R31`, `RN-R33`). **Widened
224
+ * by W174 with `interrupted`, and by W175 with `paused` and `not-paused`** (`control` `'pause'` and `'unpause'`); a
225
+ * consumer branching on either should not switch on it exhaustively.
226
+ */
227
+ export type RunControlRefusalReason = 'unsupported' | 'not-started' | 'halted' | 'cancelled' | 'interrupted' | 'paused' | 'not-paused' | 'ended';
228
+ /**
229
+ * **A run-control call the driver will not make, thrown at the caller BEFORE anything is recorded or sent** —
230
+ * `RN-R31`, ADR 0104. Thrown, as `MultipleRuns` and `ResumeRefused` are, because the caller is on the stack and nothing
231
+ * has been appended; **one class with a `reason`**, on `ResumeRefused`'s precedent, so a consumer catches one class and
232
+ * branches on `reason`. `control` names the call — `'cancel'`, since v0.19 `'stop'` (`RN-R33`, ADR 0105), and since
233
+ * v0.20 `'pause'` and `'unpause'` (`RN-R35`, ADR 0106).
234
+ */
235
+ export declare class RunControlRefused extends Error {
236
+ readonly control: 'cancel' | 'stop' | 'pause' | 'unpause';
237
+ readonly reason: RunControlRefusalReason;
238
+ constructor(control: 'cancel' | 'stop' | 'pause' | 'unpause', reason: RunControlRefusalReason, message: string);
239
+ }
240
+ /** What `Driver.cancel` takes — `RN-R31`, P34 stop-and-ask (c). */
241
+ export interface RunCancelOptions {
242
+ /** Who cancelled the run — recorded on the `cancelRequested` element and in `RunState.cancelRequest`. */
243
+ actor: string;
244
+ /** Why — recorded beside `actor` (`null` when absent), and handed to the scheduler's `cancel` as given. */
245
+ reason?: string;
246
+ /**
247
+ * Threaded pools: the ms each task's worker gets to stop by itself before the scheduler forces it — the pool's
248
+ * `cancelGraceMs` when absent. **A request, not a guarantee**: at the pin `0` arms a 0 ms timer a prompt worker's
249
+ * reply can beat (P34 fact 9, S12). A SECOND call with a shorter value escalates the tasks still stopping.
250
+ */
251
+ graceMs?: number;
252
+ }
253
+ /**
254
+ * **What `Driver.stop` takes** — `RN-R33`, P34 stop-and-ask (d): the cancel's three options, read the same way.
255
+ */
256
+ export interface RunStopOptions {
257
+ /** Who stopped the run — recorded on the `interrupted` element and in `RunState.interruption`. */
258
+ actor: string;
259
+ /** Why — recorded beside `actor` (`null` when absent), and handed to the scheduler's `cancel` as given. */
260
+ reason?: string;
261
+ /**
262
+ * Threaded pools: the ms each task's worker gets to stop by itself before the scheduler forces it — the pool's
263
+ * `cancelGraceMs` when absent. **A request, not a guarantee** (P34 fact 9, S12). A SECOND call with a shorter value
264
+ * escalates the tasks still stopping.
265
+ */
266
+ graceMs?: number;
267
+ }
268
+ /**
269
+ * **What `Driver.pause` and `Driver.unpause` take** — `RN-R35`, P34 stop-and-ask (e): who paused or unpaused the run,
270
+ * recorded on the `paused` or `unpaused` element. Nothing else: a pause cancels nothing, so it has no `reason` to hand
271
+ * the scheduler and no `graceMs`.
272
+ */
273
+ export interface RunPauseOptions {
274
+ /** Who paused (or unpaused) the run — recorded on the element. */
275
+ actor: string;
276
+ }
189
277
  /**
190
278
  * **What a checkpoint's header says, and the number a resume refuses on** — `RN-R16`,
191
279
  * ADR 0064 rule 3. `1` at this version; it moves only when what a checkpoint CARRIES moves, and
@@ -247,33 +335,40 @@ export interface Checkpoint {
247
335
  * | `format` | the value is not `{ header, events[] }`, or `header.format` is not {@link CHECKPOINT_FORMAT} (`RN-R16`) | `null` |
248
336
  * | `run` | `header.runId` is not the run this driver was constructed for (`ID-R3`) — every stored task id embeds it | `null` |
249
337
  * | `flow` | `header.flowId` or `header.version` is not this driver's definition's — **no migration** (P23 decision 7) | `null` |
250
- * | `stopped` | the run was STOPPED — a stored `stopped` element, or one the replay regenerates past the stored events (P23 decision 10) | the `stopped` element's position |
338
+ * | `halted` | the run was HALTED by a refusal — a stored `halted` element, or a stored `stopped` element, 0.7.0's name for it and read as one ([ADR 0102](../../doc/decisions/0102-a-run-the-driver-halts-is-halted-and-a-stored-stopped-reads-as-one.md) rule 3), or one the replay regenerates past the stored events (P23 decision 10) | that element's position |
339
+ * | `cancelled` | the run was CANCELLED — a stored `outcome` element whose record is `terminalState: 'cancelled'`, checked after `halted` ([ADR 0094](../../doc/decisions/0094-a-cancelled-task-is-a-terminal-state-that-routes-no-edge-and-ends-its-run-cancelled.md) rule 5, W158) — **or, since v0.18, a stored `cancelRequested` element** (`RN-R31`, ADR 0104, W173), whichever comes first. Only a STORED one: a settlement is fed to the replay, never regenerated, so a replay cannot produce a cancel the store does not hold | the first such element's position |
251
340
  * | `diverged` | a stored element the replay does not produce at the same position — a predicate, a definition or an entry payload that is not what the stored run had (P23 decision 9) | the FIRST such position |
252
341
  */
253
- export type ResumeRefusalReason = 'format' | 'run' | 'flow' | 'stopped' | 'diverged';
342
+ /**
343
+ * **Widened by W158 with `cancelled`, and that is BREAKING for a consumer switching on `reason` exhaustively** —
344
+ * `resumed`'s and `gated`'s precedent (ADR 0094 consequence 1). **And W171 RENAMED `stopped` to `halted`** (ADR 0102, P34
345
+ * stop-and-ask (f)) — BREAKING, and loudly: a consumer's `reason === 'stopped'` no longer compiles, so it cannot silently
346
+ * come to mean P34's resumable stop. A 0.7.0 checkpoint holding a `stopped` element is refused `halted`.
347
+ */
348
+ export type ResumeRefusalReason = 'format' | 'run' | 'flow' | 'halted' | 'cancelled' | 'diverged';
254
349
  /**
255
350
  * **A resume the driver will not make, thrown at the caller** — `RN-R23`, ADR 0065.
256
351
  *
257
352
  * Thrown rather than reported, for `start()`'s reason: the caller is on the stack, and a checkpoint is a
258
353
  * value the CALLER brought (`OR-R12`'s asymmetry). **One class with a `reason`, on `BoundaryRefused`'s
259
354
  * precedent** of one class for two boundaries: a consumer catching a refused resume wants one `catch`,
260
- * and branches on `reason` — `diverged` and `stopped` carry the POSITION a store can be inspected at.
355
+ * and branches on `reason` — `diverged` and `halted` carry the POSITION a store can be inspected at.
261
356
  *
262
357
  * **What it leaves behind is the driver SPENT, and nothing handed to a scheduler.** A refusal before the
263
- * replay (`format`, `run`, `flow`, a stored `stopped`) and a `diverged` one leave no run to checkpoint;
264
- * a `stopped` the replay itself regenerated leaves the STOPPED run, whose checkpoint a store may take —
265
- * its lost tail, `stopped` included, was delivered to `onFoldInput` before the refusal was thrown.
358
+ * replay (`format`, `run`, `flow`, a stored `halted`, a stored `cancelled`) and a `diverged` one leave no run to checkpoint;
359
+ * a `halted` the replay itself regenerated leaves the HALTED run, whose checkpoint a store may take —
360
+ * its lost tail, `halted` included, was delivered to `onFoldInput` before the refusal was thrown.
266
361
  */
267
362
  export declare class ResumeRefused extends Error {
268
363
  readonly reason: ResumeRefusalReason;
269
- /** The position `stopped` and `diverged` name; `null` for the three a header decides. */
364
+ /** The position `halted`, `cancelled` and `diverged` name; `null` for the three a header decides. */
270
365
  readonly index: number | null;
271
366
  constructor(reason: ResumeRefusalReason, index: number | null, message: string);
272
367
  }
273
368
  /**
274
369
  * **What a resume refuses a checkpoint for BEFORE replaying anything** — `RN-R23`, ADR 0065 rule 2.
275
370
  * `format`, `run` (only when `runId` is given — a runtime resuming names the run BY the header), `flow`,
276
- * and a stored `stopped` element, in that order; `null` when the header and the events' kinds say
371
+ * a stored `halted` element (or a `stopped` one, 0.7.0's name for it — ADR 0102 rule 3) and a stored `cancelled` settlement, in that order; `null` when the header and the events' kinds say
277
372
  * nothing against it — which is not a promise the replay will agree, and that is `diverged`'s road.
278
373
  *
279
374
  * **Pure, exported, and called from two places**: `Driver.resume`, and `runtime.resume` BEFORE it builds
@@ -321,10 +416,12 @@ export declare class MissingJoinPolicy extends Error {
321
416
  * [ADR 0027](../../doc/decisions/0027-an-inbound-task-id-that-does-not-parse-was-minted-by-no-driver.md)
322
417
  * moved it to `onUnmintedTask` and `ID-R5`, so it no longer does.) Every one of them is reached from inside a
323
418
  * `scheduler.addEventListener` callback, and `EventTarget.dispatchEvent`
324
- * **discards a listener throw**: measured on Node 24, the remaining listeners
325
- * still run, `dispatchEvent` returns normally, and the error resurfaces
326
- * out-of-band on `process.nextTick`. In a browser it is a `window.onerror` report
327
- * and the page carries on.
419
+ * **never hands a listener throw back to the dispatcher**: measured on Node 24, the
420
+ * remaining listeners still run, `dispatchEvent` returns normally, and the error
421
+ * resurfaces out-of-band on `process.nextTick` — where node 24 and deno each raise
422
+ * it as an UNCAUGHT exception that ends the process, unless a global handler is
423
+ * installed (P35 fact 10). In a browser it is a `window.onerror` report and the page
424
+ * carries on.
328
425
  *
329
426
  * So the run would have kept draining with nothing told — the silent stop
330
427
  * `spec/flow-definition.md` section 3 refuses on P1's own validity grounds, which
@@ -431,6 +528,68 @@ export interface NonconformingResult {
431
528
  /** One line naming the task, the node, the worker and every finding — `BoundaryRefused`'s shape, without the throw. */
432
529
  message: string;
433
530
  }
531
+ /**
532
+ * **Whose task this is, as FIELDS** — `RN-R42`, v0.27,
533
+ * [ADR 0114](../../doc/decisions/0114-a-tasks-attribution-is-fields-and-its-messages-and-progress-are-passed-through.md),
534
+ * P36 stop-and-ask (k): what {@link TaskMessage} and {@link TaskProgress} carry beside the scheduler's own fields, so a
535
+ * consumer never parses a task id. `runId`, `nodeId`, `branchPath` and `flowAttempt` are the id's four segments
536
+ * (`spec/task-id.md` section 1) — `NodeRun`'s names for them (`RN-R41`); `attemptNumber` is the SCHEDULER's 1-based
537
+ * number of the attempt the event came from, as the event carried it, and `null` where it carried no positive integer (a
538
+ * scheduler older than the pin, or a double).
539
+ */
540
+ export interface TaskAttribution {
541
+ runId: string;
542
+ nodeId: string;
543
+ /** `'-'` when the node is in no fan-out region. */
544
+ branchPath: string;
545
+ taskId: string;
546
+ /** The task id's flow-level attempt segment — `0` on every id minted today (P36 (j)). Not the scheduler's number. */
547
+ flowAttempt: number;
548
+ /** The scheduler's attempt number, from the event itself. */
549
+ attemptNumber: number | null;
550
+ }
551
+ /**
552
+ * **One `context.emit()` of a task THIS run dispatched — the scheduler's `task.message`, passed through** — `RN-R42`,
553
+ * v0.27, ADR 0114, P36 stop-and-ask (k), what {@link DriverOptions.onTaskMessage} is handed. The scheduler's own fields
554
+ * (`name`, `data`, `seq`, `timestamp`, and `dropped` and `error` only when the event carried them — its `AGENTS.md` §6)
555
+ * beside the attribution. A COPY taken while the listener runs (ADR 0007 rule 1) and FROZEN (`OR-R7`): `data` is the
556
+ * driver's own clone, shallow-frozen as `onFoldInput`'s values are. `workerId` is not carried: it names a pool's worker,
557
+ * not the run's task.
558
+ */
559
+ export interface TaskMessage extends TaskAttribution {
560
+ /** The name the worker emitted under — the scheduler defines none. */
561
+ name: string;
562
+ /**
563
+ * What it emitted. `undefined` when the SCHEDULER could not clone it — `error` is then set — and, with NO marker, when the
564
+ * driver's own copy could not clone it (`RN-R42`; unreachable for a message the scheduler cloned at the `emit`).
565
+ */
566
+ data: unknown;
567
+ /** The emit's index within its attempt, from 0 — a retry starts again at 0 under the next `attemptNumber`. */
568
+ seq: number;
569
+ /** `Date.now()` at the emit, in the worker. */
570
+ timestamp: number;
571
+ /** Emits the scheduler discarded at its cap immediately before this one. Present only when the event carried it. */
572
+ dropped?: number;
573
+ /** Why `data` is missing. Present only when the event carried it. */
574
+ error?: string;
575
+ }
576
+ /**
577
+ * **One progress report of a task THIS run dispatched — the scheduler's `task.progress`, passed through** — `RN-R42`, v0.27,
578
+ * ADR 0114, P36 stop-and-ask (k), what {@link DriverOptions.onTaskProgress} is handed: the report's own fields beside the
579
+ * attribution, and NONE of the task's — the scheduler spreads the whole task flat into this event's detail (its §6), and
580
+ * its `workerType` there is `'inline'`/`'thread'`, not the pool key, so neither is carried. A frozen COPY, as
581
+ * {@link TaskMessage} is.
582
+ */
583
+ export interface TaskProgress extends TaskAttribution {
584
+ /** A fraction, 0-1, as the worker reported it. */
585
+ progress: number;
586
+ /** The worker's message, if it gave one. */
587
+ message: string | undefined;
588
+ /** The worker's details, if it gave any — the driver's own clone; `undefined`, with no marker, if they cannot be cloned. */
589
+ details: unknown;
590
+ /** `Date.now()` when the report was made. */
591
+ timestamp: number;
592
+ }
434
593
  /**
435
594
  * What the driver needs from a scheduler. Structural, so W3 is testable without one — and
436
595
  * satisfied by a real `FyflowScheduler` under EVERY lib configuration, which is what closed
@@ -448,14 +607,34 @@ export interface NonconformingResult {
448
607
  * reads `e` as an event** — `detailOf` narrows to `detail` at the subscription site and ADR 0007
449
608
  * normalises from there, exactly as before. What changed is the declared shape, not what is read.
450
609
  *
451
- * The two-method shape is what the seam IS — an `EventTarget` without `addTask` still does not
610
+ * The two REQUIRED methods are what the seam IS — an `EventTarget` without `addTask` still does not
452
611
  * get through — so Q71's own objection to widening does not apply to this widening.
612
+ *
613
+ * **A THIRD member since W173, and it is OPTIONAL** — `cancel`, P34 stop-and-ask (b),
614
+ * [ADR 0104](../../doc/decisions/0104-a-run-its-owner-cancels-is-cancelling-until-its-last-task-has-stopped.md). It is
615
+ * the scheduler's own signature, so a real `FyflowScheduler` satisfies the seam as it is (the `_Q71` assertion below is
616
+ * unmoved) and `Driver.cancel` reaches the scheduler from the driver — the only thing with side effects (ADR 0001) — for a
617
+ * driver built by hand exactly as for a runtime's, whose per-run view forwards it. **Optional, so no double breaks**:
618
+ * a scheduler without it still drives every run, and `Driver.cancel` over it is REFUSED by name before anything is
619
+ * recorded (`RunControlRefused`, `reason: 'unsupported'`) — and, since W174, so is `Driver.stop` (`RN-R33`, ADR 0105). **BREAKING in 0.8.0 only for code that IMPLEMENTS the seam
620
+ * with a `cancel` of another shape** (P34 decision 10).
453
621
  */
454
622
  export interface SchedulerLike {
455
623
  addTask(task: unknown, options?: {
456
624
  createPromise?: boolean;
457
625
  }): unknown;
458
626
  addEventListener(type: string, listener: (e: object) => void): void;
627
+ /**
628
+ * Stop a task, waiting or running — the scheduler's `cancel(taskId, { reason, graceMs })`, resolving `true` once it
629
+ * has stopped and `false` for an id it does not hold or a task that finished first. Read only by `Driver.cancel` and,
630
+ * since W174, `Driver.stop` (`RN-R33`) — whose run's `done`, on a runtime, settles once each stop's promise has settled.
631
+ * **It must not REJECT for a task's state** (the scheduler's never does): the run ends only when the scheduler SETTLES
632
+ * each task, so a double whose `cancel` rejects and fires nothing leaves the run `cancelling` for ever (W173, F8).
633
+ */
634
+ cancel?(taskId: string, options?: {
635
+ reason?: unknown;
636
+ graceMs?: number;
637
+ }): Promise<boolean>;
459
638
  }
460
639
  /**
461
640
  * What `createTask` builds from what the driver hands it. **Optional since W57**: omitted, the
@@ -476,6 +655,18 @@ export type TaskFactory = (config: {
476
655
  retryPolicy?: RetryPolicy;
477
656
  /** Present only when the node declared it — W58. The scheduler unions it with the pool's. */
478
657
  workerGroups?: string[];
658
+ /**
659
+ * **The attempts this task ALREADY made, in an earlier process** — `RN-R21` v0.26, `RN-R40`,
660
+ * [ADR 0113](../../doc/decisions/0113-a-tasks-attempts-are-recorded-and-carried-across-a-resume.md), W186, P36 (g). Present
661
+ * ONLY on a task `resume()` hands on again, and only when the stored run recorded an attempt of it (an `attempted`
662
+ * element): the HIGHEST `attemptNumber` recorded — so the attempt in flight at the crash or the stop counts as spent (P36
663
+ * (h)). With or without a `retryPolicy`: the scheduler's attempt numbering continues either way. **A spent budget is
664
+ * handed on all the same**, and the scheduler runs the task ONCE more and settles it — at most one attempt over budget
665
+ * per resume; the driver writes no settlement (P36 decision 7). The scheduler's own `FyflowTask` field of the same name.
666
+ * **A `createTask` that builds the task FIELD BY FIELD must copy it**, or the resumed task starts at a fresh budget again.
667
+ * Additive in 0.8.0: a dispatch that is not a resume's never carries it.
668
+ */
669
+ attemptsSpent?: number;
479
670
  }) => unknown;
480
671
  export interface DriverOptions {
481
672
  /**
@@ -562,10 +753,11 @@ export interface DriverOptions {
562
753
  /**
563
754
  * **A consumer's GATE types, beside the built-ins — the sixth set, and the TWENTIETH member**
564
755
  * ([W93](../../doc/work-items/w93-what-a-gate-does-in-a-run.md), ADR 0067). Absent, the built-ins alone
565
- * (`distinct`); the set only ever ADDS, a name that is a built-in's is refused (`NameCollision`) and a
756
+ * (`distinct`, `if`, `switch`); the set only ever ADDS, a name that is a built-in's is refused (`NameCollision`) and a
566
757
  * value that is not a `GateType` is `GateTypeRefused` — both by `checkRegistry`, at construction. The
567
- * validator reads each type's `routes`, `defaultRoute` and `settingsSchema` (`FD-V39`-`FD-V41`); the driver
568
- * reads `fn`, where a branch enters a gate. **The runtime's road is `runtime.registry.gateTypes`**, from
758
+ * validator reads each gate's route set through `routesOf` (a static type's members, or a derived type's
759
+ * `routesFor` over the gate's settings — ADR 0092), `settingsSchema` and `settingsProblem` (`FD-V39`-`FD-V41`);
760
+ * the driver resolves each gate's set once at construction and reads `fn` where a branch enters a gate. **The runtime's road is `runtime.registry.gateTypes`**, from
569
761
  * `RegistryCode.gateTypes` — code registered once per runtime, as `languages` is (W76's road (b)).
570
762
  */
571
763
  gateTypes?: GateTypes;
@@ -584,8 +776,13 @@ export interface DriverOptions {
584
776
  onUnsatisfiableJoin?: (info: UnsatisfiableJoin) => void;
585
777
  /**
586
778
  * Called when a refusal terminates a run. See `RunFailure` — a `throw` from a
587
- * listener is discarded by `dispatchEvent`, so this is the only way the failure
588
- * `FD-R1`, `ID-R1` and `OR-R6` describe can actually reach a caller.
779
+ * listener never reaches the scheduler's caller (`dispatchEvent` rethrows it out of
780
+ * band), so this is the only way the failure `FD-R1`, `ID-R1` and `OR-R6` describe
781
+ * can actually reach a caller — and a throw from your own `onFoldInput` on an element
782
+ * a scheduler event's step appends, a pause, unpause or stop taken at its end
783
+ * included (`OR-R10`). **And on a driver call's step too, since W184** (`OR-R10` v0.19, P35 (k)): a throw from your
784
+ * `onFoldInput` on any element `start`, `resume`, `resolve`, `cancel`, `stop`, `pause` or `unpause` appends is
785
+ * reported here ONCE, the run halted, and then thrown to that call's caller as well.
589
786
  */
590
787
  onRunFailure?: (failure: RunFailure) => void;
591
788
  /**
@@ -623,15 +820,66 @@ export interface DriverOptions {
623
820
  * stream is named by its length. **It is the driver's index and never the listener's own count**
624
821
  * — the two agree while one listener sees a run from its first event, and a runtime composing
625
822
  * this callback (ADR 0058) hands the driver's `seq` on unchanged.
823
+ *
824
+ * **A THROW from it HALTS the run, on every road** (`OR-R10` v0.19, W184): reported once through `onRunFailure` under
825
+ * its own class, the `halted` element appended after the element thrown on — and, on a driver call's step, thrown to
826
+ * that call's caller as well — the SAME error that was reported. **The FIRST throw halts**: a pause, an unpause or an
827
+ * answer still pending behind it is dropped, and a stop pending behind it is taken SILENTLY, its own throw discarded
828
+ * (P36's brief point 5). The one exception is a throw on the `halted` element itself: reported nowhere again, and on a
829
+ * driver call it reaches the caller in place of the first (`OR-R10`, W178's S-1).
626
830
  */
627
831
  onFoldInput?: (input: FoldInput, seq: number) => void;
832
+ /**
833
+ * **The run's checkpoint header, handed over BEFORE its first event** — `RN-R39`,
834
+ * [ADR 0112](../../doc/decisions/0112-a-run-delivers-its-header-before-its-first-event.md), P36 decision 1, (d) and (e).
835
+ * Called ONCE, synchronously, inside `start()` — after `FD-R4`'s check and the entry payload's clone, and BEFORE anything is
836
+ * appended (so before the first `onFoldInput`) or handed to the scheduler — with a FROZEN copy of its own, equal to
837
+ * `checkpoint().header` and to a runtime's `handle.header`. So a store writes the header first and every element after it.
838
+ * *The copy is SHALLOW-frozen with a fresh clone of the entry payload — `checkpoint()`'s own shape — so it is a whole copy
839
+ * only while every other `CheckpointHeader` field is a primitive; a field added holding an object must be cloned here too.*
840
+ *
841
+ * **A THROW from it leaves the driver UNSTARTED**: nothing appended, nothing handed on, `onRunFailure` NOT called — no run
842
+ * exists yet, as after an `FD-R4` refusal — and the throw reaches `start()`'s caller; a corrected retry may start the same
843
+ * driver. **Never called** when `start()` is refused (`MultipleRuns`, `BoundaryRefused`, a `DataCloneError`), and never by
844
+ * `resume()`: its caller already holds the header. Additive in 0.8.0.
845
+ */
846
+ onHeader?: (header: CheckpointHeader) => void;
847
+ /**
848
+ * **Every `context.emit()` of a task this run dispatched, with its attribution as fields** — `RN-R42`, v0.27,
849
+ * [ADR 0114](../../doc/decisions/0114-a-tasks-attribution-is-fields-and-its-messages-and-progress-are-passed-through.md),
850
+ * P36 stop-and-ask (k): the scheduler's `task.message`, copied while the listener runs and handed over FROZEN
851
+ * ({@link TaskMessage}). **A PASS-THROUGH, never a fold input**: nothing is appended, nothing a checkpoint holds moves,
852
+ * and the run routes exactly as it would without it.
853
+ *
854
+ * **The driver subscribes to `task.message` ONLY when this is given** — the scheduler sends nothing while nobody listens
855
+ * (its §6), so a run that passes none costs its workers nothing to emit. **Delivered only for a task THIS run dispatched**:
856
+ * another run's, an id no driver minted and a task this run never dispatched are DROPPED in silence — never
857
+ * `onUnmintedTask`, never `UndispatchedTask`: a message never stops a run by existing. **Nothing once the driver is
858
+ * deaf** — stopped (`RN-R33`) or spent by a refused resume (`RN-R23`) — and, on a runtime, nothing once the run is
859
+ * released (ADR 0091). A HALTED run's tasks still run, and their messages still arrive.
860
+ *
861
+ * **A THROW from it takes `onUnmintedTask`'s road** (`OR-R10`): caught at the listener boundary, reported ONCE through
862
+ * `onRunFailure` under its own class, and the run HALTED — a scheduler event's step, so nothing is thrown on; on a run
863
+ * already halted it is discarded. The scheduler delivers `task.message` asynchronously (its §6; W187's declaration step
864
+ * measured an inline and a threaded pool at the pin), so it never arrives on a driver call's own step. Additive in 0.8.0.
865
+ */
866
+ onTaskMessage?: (message: TaskMessage) => void;
867
+ /**
868
+ * **Every progress report of a task this run dispatched, with its attribution as fields** — `RN-R42`, v0.27, ADR 0114,
869
+ * P36 stop-and-ask (k): the scheduler's `task.progress`, copied and handed over FROZEN ({@link TaskProgress}) — the
870
+ * report's fields and none of the task's. Everything {@link DriverOptions.onTaskMessage} says holds here too: subscribed
871
+ * only when given, this run's dispatched tasks only, nothing once deaf or released, a throw reported once and the run
872
+ * halted. Additive in 0.8.0.
873
+ */
874
+ onTaskProgress?: (progress: TaskProgress) => void;
628
875
  /**
629
876
  * **`FD-R4`'s switch — W73.** `true` (the default): the run's payload is held to the input schema of
630
877
  * every task it reaches with no worker in between at `start()`, and a person's `done` resolution to
631
878
  * the parked node's worker output schema at `resolve()`, each refused as a `BoundaryRefused`
632
879
  * thrown at the caller with the run unaffected. `false` turns both off — the one road P19 leaves
633
880
  * to a consumer who has priced the check and wants none of it. A worker declaring no schema on
634
- * that side is unchecked either way (P18 decision 3).
881
+ * that side is unchecked either way (P18 decision 3), and so, at `start()`, is a task that declares
882
+ * a `transform` (ADR 0101, W170): what its worker's input schema holds is the transform's RESULT.
635
883
  */
636
884
  validateBoundaries?: boolean;
637
885
  /**
@@ -701,6 +949,13 @@ export declare class Driver {
701
949
  private readonly graph;
702
950
  private readonly regions;
703
951
  private readonly dispatched;
952
+ /**
953
+ * **Every ASK this run has reached, by task id** — `RN-R37`, v0.22, W180, ADR 0109: the ids of the `asked` elements
954
+ * appended, so `RS-R1`'s widened *suspended* (an `asked` element and no record after it) is one lookup rather than a
955
+ * scan of the input sequence at every `resolve()`, and an ask's id is minted once per path (`ID-R1`'s guard). Ids only —
956
+ * the kept INPUT lives on the element alone. `dispatched`'s twin for the one node that is never dispatched.
957
+ */
958
+ private readonly asked;
704
959
  private readonly now;
705
960
  private readonly opts;
706
961
  private readonly def;
@@ -778,6 +1033,29 @@ export declare class Driver {
778
1033
  * quiescent point (`RN-R10`), not here — ADR 0046 rule 5.
779
1034
  */
780
1035
  private readonly arrivals;
1036
+ /**
1037
+ * **What a map INSTANCE keeps for `"$item"`** — `RN-R13`'s sixth term since v0.28 (`spec/run-state.md`), P38 (e),
1038
+ * [ADR 0118](../../doc/decisions/0118-a-map-instance-keeps-its-item-while-a-node-of-it-may-still-be-entered.md),
1039
+ * [W191](../../doc/work-items/w191-the-map-instances-item-as-a-reference.md). Two halves, one field:
1040
+ *
1041
+ * - **`kept`** — decided from the DOCUMENT at construction: the maps whose region holds a `"$item"` reader (a node, or
1042
+ * an edge of a node, whose innermost containing map is that map — `innermostMapOf`, P38 (c)), and for each whether
1043
+ * its received value may be the CALLER's own object (`true`: reached from the entry through gates alone, with no
1044
+ * `needs` — `start()` hands `enter` the value it was called with, as an entry task and an entry ask receive it). A
1045
+ * map no reader names keeps NOTHING, and a flow declaring no `"$item"` has `kept` empty (P38 decision 2).
1046
+ * - **`held`** — map id → the instance's branch path → its item. Written by the MAP ARM, where the instance opens —
1047
+ * by a run and by a resume's replay alike, so a replay REBUILDS it (`RN-R19`; P38's brief: *taken at rest*, never by
1048
+ * a caller's call) — **BY REFERENCE to the element the map received** (the driver's own: an edge-hop clone, or a
1049
+ * `needs` composition's per-key copy), and as its OWN copy only where `kept` says that element is the caller's
1050
+ * (P35 (f)'s reason for `asked.input`). Every READ clones (`JP-R11`), so no node receives the held object.
1051
+ * **Released when NOTHING of that instance may still be entered** — its own branch terminal (`branch.arrived`,
1052
+ * `branch.lost` or `branch.exited`, `JP-R5`: one per opened branch) AND no branch of a region NESTED in it still live
1053
+ * (`within`, `nested`, `ended` — `trackItems`): a nested join a policy fires before its last branch is terminal lets
1054
+ * the instance's branch arrive while that branch can still read (W191's conformity reader, C1). A suspended node (an
1055
+ * ask, a parked task) keeps its branch live, and so its item. And at the run-level moments
1056
+ * `arrivals` is cleared at — a halt, a cancel, a stop, a `cancelled` settlement — because no node is entered after.
1057
+ */
1058
+ private readonly items;
781
1059
  /**
782
1060
  * **The one run this driver serves** — ADR 0022 rule 1, fixed at construction
783
1061
  * and never `null`.
@@ -820,16 +1098,184 @@ export declare class Driver {
820
1098
  * elements a consumer does not already hold ({@link Driver.delivers}).
821
1099
  */
822
1100
  private replay;
823
- /** The run has been stopped by a refusal. Nothing dispatches for it again. */
1101
+ /** The run has been HALTED by a refusal (`OR-R10`). Nothing dispatches for it again. */
824
1102
  private failed;
825
1103
  /**
826
- * **A resume refused because its replay regenerated a STOP in the lost tail** — `RN-R23`: that driver keeps the
827
- * stopped run (its `header`, so a store may take its checkpoint) and still hears NOTHING. `onEvent`'s other test,
1104
+ * **A task of this run settled `cancelled`, so the run is HALTED** — `RN-R28`,
1105
+ * [ADR 0094](../../doc/decisions/0094-a-cancelled-task-is-a-terminal-state-that-routes-no-edge-and-ends-its-run-cancelled.md)
1106
+ * rule 3, W158. Every door `failed` shuts, this shuts — nothing is dispatched or routed for the run again,
1107
+ * a later settlement is appended and routed nowhere, and `resolve()` is refused — and it differs from `failed`
1108
+ * in three things only: no `halted` element is appended (the fold reads the run `cancelled` from the
1109
+ * settlement itself), `onRunFailure` is not called (a cancel is its owner's act, not a defect), and
1110
+ * `hasFailed()` stays false. **Separate rather than `failed` with a reason**, because those three are each
1111
+ * a reader of `failed` that must not fire.
1112
+ */
1113
+ private cancelled;
1114
+ /**
1115
+ * **A resume refused because its replay regenerated a HALT in the lost tail** — `RN-R23`: that driver keeps the
1116
+ * halted run (its `header`, so a store may take its checkpoint) and still hears NOTHING. `onEvent`'s other test,
828
1117
  * *started with no header*, cannot see it, because this refusal is the one that keeps its header: until W151 such a
829
1118
  * driver built by hand appended and delivered a later settlement and reported an unminted id (W151's conformity
830
- * reader, finding 6). A driver stopped by `failRun` on a run it started is NOT deaf: `OR-R10` stops routing only.
1119
+ * reader, finding 6). A driver halted by `failRun` on a run it started is NOT deaf: `OR-R10` halts routing only.
1120
+ *
1121
+ * **And a STOPPED driver, since W174** (`RN-R33`, P34 decision 3): a stop sets it with its hold — before the
1122
+ * `interrupted` element, and so BEFORE it sends the first cancel — so no `task.cancelled` of the stop — nor any later settlement — is appended,
1123
+ * routed, delivered or reported, and the log ends with the in-flight set dispatched and unsettled for a resume to
1124
+ * re-dispatch. A replay feeding a stored `interrupted` element does NOT set it: the replay hears nothing anyway.
831
1125
  */
832
1126
  private deaf;
1127
+ /**
1128
+ * **The run's OWNER cancelled it** — `RN-R31`, v0.18, W173,
1129
+ * [ADR 0104](../../doc/decisions/0104-a-run-its-owner-cancels-is-cancelling-until-its-last-task-has-stopped.md). `null`
1130
+ * until `cancel()` is accepted; then what every LATER cancel of a task sends the scheduler — the caller's `reason` and
1131
+ * `graceMs`, each only if given — and, per task id, the promise the scheduler's `cancel` returned for it, which is what
1132
+ * `cancel()`'s own promise waits on. **A halt** (`shut()`), set BEFORE the `cancelRequested` element is appended, so a
1133
+ * consumer's callback on that element already sees the run held. It holds no result: one promise per task STILL
1134
+ * stopping — each dropped as it settles, so none once the run is `cancelled` — and the two option values.
1135
+ */
1136
+ private cancelRequest;
1137
+ /**
1138
+ * **The run's OWNER stopped it, to resume it elsewhere** — `RN-R33`, v0.19, W174,
1139
+ * [ADR 0105](../../doc/decisions/0105-a-run-its-owner-stops-goes-deaf-before-it-cancels-and-resumes-elsewhere.md). `null`
1140
+ * until `stop()` is accepted; then, as `cancelRequest`'s shape, what every cancel the stop sends carries and one promise
1141
+ * per task STILL stopping, each dropped as it settles — and the `actor`, which the element is appended with.
1142
+ *
1143
+ * **`pending` while the stop has been ACCEPTED and not yet TAKEN**: a stop called MID-STEP — from a callback on the
1144
+ * driver's own step — takes effect when that step ends (`stepping` back to `0`), so the step's routing is all in the log
1145
+ * and none of it is lost to a resume (`RN-R33`, W174). While pending it is the resolver of the promise every call made
1146
+ * before then waits on, `resolve()` and `cancel()` are already refused, and the hold is NOT yet shut; `null` once the
1147
+ * stop is taken, from which moment it IS a halt (`shut()`). `took` is the promise a second call made while pending
1148
+ * waits on. A function and a promise held only until the step ends — `null` both once taken, never at rest.
1149
+ *
1150
+ * **And a REPLAY sets it too**, taken, with no options and nothing stopping, where it feeds a stored `interrupted`
1151
+ * element (`RN-R19`): the stored run was held from that position, so the replay holds there as well, until a fed
1152
+ * `resumed` — or the resume itself, once the replay is over — sets it back to `null`. The stop's deafness is `deaf`'s.
1153
+ */
1154
+ private stopRequest;
1155
+ /**
1156
+ * **Tasks whose `dispatched` element is appended and which the scheduler has NOT been handed yet** — `RN-R29` (i)'s
1157
+ * window, ADR 0103's inherited hazard, W173. A scheduler's `cancel()` of an id it does not hold resolves `false` and
1158
+ * does nothing, so `cancel()` sends nothing for these: the hand-off site cancels each right AFTER its `addTask` —
1159
+ * `dispatch` for the task in progress, `resume()` for its in-flight set. Empty at rest; at most the one task being
1160
+ * dispatched, or a resume's in-flight set while it is handed back.
1161
+ */
1162
+ private readonly unhanded;
1163
+ /**
1164
+ * **How deep the driver is in its OWN step** — `start`, `resume`, `resolve` or a scheduler event, each counting itself
1165
+ * in (`RN-R31`, W173's conformity reader, D-1). `0` at rest. A consumer callback the driver calls mid-step sees a
1166
+ * PREFIX that can read finished while the step is about to dispatch more (`RN-R12`'s quiescent point, P34 fact 3), so
1167
+ * `cancel()` refuses `ended` only at rest: mid-step, *nothing in flight and nothing parked* is not *finished*.
1168
+ */
1169
+ private stepping;
1170
+ /**
1171
+ * **The run's OWNER has paused it, and the pause is TAKEN** — `RN-R35`, v0.20, W175,
1172
+ * [ADR 0106](../../doc/decisions/0106-a-run-its-owner-pauses-routes-on-and-holds-every-dispatch-until-unpaused.md):
1173
+ * set when a `paused` element is appended (by `pause()`, or fed to a replay) and cleared when an `unpaused` one is —
1174
+ * so it is always what `RunState.paused` folds to on the driver's own sequence. **Not a halt**: it does not join
1175
+ * `shut()`, because a paused run still routes and still accepts a resolution (ADR 0103's consequence). It is the
1176
+ * dispatch hold's DEFERRED form, at the dispatch door (ADR 0103 rule 6): each dispatch is appended, and held.
1177
+ */
1178
+ private paused;
1179
+ /**
1180
+ * **Every dispatch the pause HELD, in dispatch order** (`RN-R35`): appended — so the fold reads each `running`, in flight
1181
+ * under `RN-R21` (P34 fact 3) — and NOT handed to the scheduler. An `unpause` hands each on, first held first; a
1182
+ * `cancel` hands each on and cancels it (ADR 0104 rule 7); a `stop` sends nothing for them, so they stay in flight in
1183
+ * the log for a resume to hold again. Empty unless the run is paused — or an unpause's hand-off is under way, while
1184
+ * which a dispatch joins the END of it, so the order holds; or a halt broke that hand-off off, after which nothing is
1185
+ * handed on for the run and what is left here never is. One `createTask` configuration per held task, no result.
1186
+ */
1187
+ private readonly pauseHeld;
1188
+ /**
1189
+ * **Pause and unpause calls ACCEPTED mid-step and not yet TAKEN**, in call order (`RN-R35`): a call from a callback on
1190
+ * the driver's own step is taken when that step ends (`stepping` back to `0`, `leaveStep()`), as a stop is (`RN-R33`) —
1191
+ * so its element lands BETWEEN two steps, where a replay can feed it (`RN-R19`). Empty at rest.
1192
+ * **And, since W180, a RESOLUTION given mid-step** — an ask's answer or a parked task's (`RN-R37`, ADR 0109 rule 5, P35's
1193
+ * brief: *an owner element a resume's replay feeds is TAKEN AT REST*): accepted at the call — every refusal already asked — and its resolution
1194
+ * appended when the step ends, in the same call order as the pauses beside it.
1195
+ */
1196
+ private readonly pausePending;
1197
+ /**
1198
+ * **The highest attempt number RECORDED for each task, by task id** — `RN-R40`, v0.26, W186, P36 (f) and (g): the
1199
+ * number the last `attempted` element for it carried — appended, or fed by a replay. What decides that a `task.running`
1200
+ * repeating a number (a requeue) appends nothing, and what a resume re-hands an in-flight task with as `attemptsSpent`.
1201
+ * One integer per task that ever started an attempt, no result; kept for the life of the run, as `dispatched`'s ids are.
1202
+ */
1203
+ private readonly lastAttempt;
1204
+ /**
1205
+ * **`attempted` elements HEARD and not yet appended**, in the order heard (`RN-R40`): a `task.running` arrives on the
1206
+ * driver's OWN step when an inline pool starts the task inside `addTask`, and an element appended there would sit between
1207
+ * two elements of one step — where the replay, which regenerates a step whole, would refuse the checkpoint `diverged`. So
1208
+ * every one is appended when the step ends, BEFORE a pause, an unpause, an answer or a stop pending on that step is taken
1209
+ * (`leaveStep()`): the attempt started before any of them. Empty at rest.
1210
+ */
1211
+ private readonly attemptsPending;
1212
+ /**
1213
+ * **The dispatch hold is SHUT: a halt is on the run, and the driver dispatches and routes nothing further for it** —
1214
+ * `RN-R29`,
1215
+ * [ADR 0103](../../doc/decisions/0103-every-halt-shares-one-dispatch-hold.md), P34
1216
+ * decisions 5 and 8, W172. **The ONE predicate every halt shares**, read at these doors and no others — `RN-R29`
1217
+ * states the two things it does not hold: before each of `resume()`'s re-dispatches (the maintainer's answer to
1218
+ * W172's S-1), at the head of `enter` (door (a) of [Q605](../../doc/questions/q605.md) — a fan-out's or a map's remaining
1219
+ * branches), at the head of `dispatch`, after a resolution's append and again before its routing (door (b)),
1220
+ * after a settlement's append (`settle`), after a join's `joinSettled` append (`settleJoin`), and at `resolve()`'s
1221
+ * entry. A halt P34 adds — a cancel or a stop — joins this disjunction, and every door follows.
1222
+ *
1223
+ * **A METHOD over the halt flags, never a field of its own**: a second copy of *the run is halted* is a field that
1224
+ * can disagree with `failed` or `cancelled`, and each of those has readers this predicate must not stand in for
1225
+ * (`hasFailed()`, `onRunFailure`). **The replay's hold is the OTHER form and is not this** — it appends a dispatch
1226
+ * and defers handing it on (`RN-R19`), where a shut hold appends nothing; `dispatch` keeps both.
1227
+ *
1228
+ * **A REQUESTED cancel joins it since W173** (`RN-R31`, ADR 0104) — ADR 0103's *a halt P34 adds joins this
1229
+ * disjunction*: from the moment `cancel()` is accepted, every door above holds for the run. **And a STOP since W174**
1230
+ * (`RN-R33`, ADR 0105): from the moment the stop is TAKEN — at once when `stop()` is called at rest, and when the step
1231
+ * ends when it is called mid-step (`stopRequest.pending`) — and, in a replay, from a fed `interrupted` element on.
1232
+ */
1233
+ private shut;
1234
+ /**
1235
+ * **What holds a resume's re-dispatch: the hold, EXCEPT a requested cancel** (ADR 0103 rule 7, ADR 0104 rule 7) — read
1236
+ * through `shut()` and never as a second copy of its disjunction (ADR 0103 rule 1; W173's conformity reader, F7), so a
1237
+ * halt W174 adds to `shut()` holds here too. **W174's stop never reaches this loop taken** (`RN-R33`): a stop is taken
1238
+ * at rest, and a stop called on this stack — on the `resumed` element, or in the replay's lost tail — is PENDING until
1239
+ * `resume()` returns, so each task is handed on here and the stop then cancels it; and the replay's own fed stop is
1240
+ * cleared before the loop. A request is the one halt that hands a task on: its tasks are `dispatched` and unsettled,
1241
+ * and only the scheduler can settle them, so each is handed on and cancelled (`handOn`).
1242
+ */
1243
+ private holdsResume;
1244
+ /**
1245
+ * **The driver's own step ends** — each of `start`, `resume`, `resolve` and a scheduler event counts itself out here.
1246
+ * At rest again, a stop called on that step is TAKEN (`RN-R33`, W174): the step's routing is whole in the log, and
1247
+ * every task it handed on is in flight for the stop to cancel. **And a pause or an unpause called on it** (`RN-R35`,
1248
+ * W175), each in call order and BEFORE the stop — a pause or an unpause after a stop is refused, so any pending one was
1249
+ * called first. Each take is a step of its own and ends here too, so the next pending call is taken after it.
1250
+ * **And a resolution given on the step** (`RN-R37`, W180), in the same queue and the same call order.
1251
+ * **And FIRST, every `attempted` element heard on the step** (`RN-R40`, W186): the attempts started before anything
1252
+ * taken here, and a stop taken before them would leave them unrecorded — the driver deaf — under P36 (h)'s *a stopped
1253
+ * attempt counts*.
1254
+ */
1255
+ private leaveStep;
1256
+ /**
1257
+ * **Every `attempted` element heard and not yet appended, appended — AT REST, as ONE step of their own** (`RN-R40`,
1258
+ * W186): in the order heard, each BETWEEN two of the driver's steps, where a replay feeds it (`RN-R19`). A step, so a
1259
+ * call a consumer makes from a callback on one — a pause, a stop — is taken after the last of them. **Appended on a
1260
+ * halted run too**, as a settlement is: the scheduler started the attempt whatever the run's routing does. A consumer's
1261
+ * `onFoldInput` THROWING on one HALTS the run, reported once (`OR-R10` v0.19, W184), and the throw reaches whoever is on
1262
+ * the stack — a driver call's caller, or the listener boundary of a scheduler event's step; the elements still pending
1263
+ * behind it are appended by this step's own end, SILENTLY (the run is halted, its failure reported), and so is a throw
1264
+ * on any of them once the run is halted.
1265
+ */
1266
+ private takeAttempts;
1267
+ /**
1268
+ * **The ONE rule for a throw that reaches a driver call's boundary once the call is ACCEPTED** — `OR-R10` v0.19, P35 (k),
1269
+ * P36 (b) and (c), W184: reported ONCE through `onRunFailure` under its own class, and the run HALTED — its `halted`
1270
+ * element appended after the element thrown on — unless the run is already halted, whose halt was reported when it was
1271
+ * raised. `start()`'s treatment since W3, brought to every call: `resolve`, `resume` and `cancel` at their own element,
1272
+ * and EVERY take — a stop's, a pause's, an unpause's, an answer's — at its own element, at rest or at a step's end, so
1273
+ * the FIRST throw halts and a call still pending behind it is dropped by its own halted guard (the readers' C1). The
1274
+ * caller rethrows: the consumer's error reaches it.
1275
+ * A refusal the call raises BEFORE it is accepted — `RunControlRefused`, `BoundaryRefused`, `ResumeRefused`,
1276
+ * `MultipleRuns`, a `DataCloneError` — never reaches this: nothing was appended, and a rejected command halts nothing.
1277
+ */
1278
+ private haltOnce;
833
1279
  /** The registry's node types, normalised once (W59; entries the document or the class form since W65). Read at dispatch for the type's retry default and its settings defaults. */
834
1280
  private readonly nodeTypes;
835
1281
  /** The registry's languages, normalised once (W61). Read at construction only; the driver evaluates nothing. */
@@ -838,6 +1284,11 @@ export declare class Driver {
838
1284
  private readonly workers;
839
1285
  /** The gate types — the built-ins and the registry's — normalised once (W93). Read where a branch enters a gate, and at `start()` for an ENTRY gate's `FD-R4` target. */
840
1286
  private readonly gateTypes;
1287
+ /**
1288
+ * **Each gate's RESOLVED route set, by gate id** — ADR 0092 rule 3: `routesOf` asked once per gate at
1289
+ * construction, after validation passed, and read by every decision. Never the type's members.
1290
+ */
1291
+ private readonly gateRoutes;
841
1292
  /**
842
1293
  * **Each gate's MEMORY: one per gate per RUN, the driver's, and never written down** — `RN-R26`,
843
1294
  * [ADR 0067](../../doc/decisions/0067-a-gate-runs-in-the-driver-its-memory-is-the-drivers-and-its-decision-is-recorded.md),
@@ -887,6 +1338,8 @@ export declare class Driver {
887
1338
  * substitution rather than dropping it.
888
1339
  */
889
1340
  start(payload: unknown): void;
1341
+ /** `start`'s body — wrapped so the driver knows it is on the stack of its own step (`stepping`, `RN-R31`, W173 D-1). */
1342
+ private startStep;
890
1343
  /**
891
1344
  * **Resume the run a checkpoint stored, in place of `start()`** — `spec/run-state.md` section 9,
892
1345
  * `RN-R19` to `RN-R24`,
@@ -898,35 +1351,43 @@ export declare class Driver {
898
1351
  * **In order, and each step is a rule:**
899
1352
  * 1. **Refuse what the header says** (`RN-R23`) — {@link resumeRefusal}.
900
1353
  * 2. **REPLAY** (`RN-R19`): enter the entry node with a copy of `header.entryPayload` exactly as
901
- * `start()` enters it, then FEED every stored settlement, resolution and `resumed` element at its
902
- * own position through the routing the listener and `resolve()` use — every dispatch HELD. Every
1354
+ * `start()` enters it, then FEED every stored settlement, resolution and `resumed` element — and, since
1355
+ * v0.19 and v0.20, every `interrupted`, `paused` and `unpaused` one, and since v0.26 every `attempted` one — at its own position through the routing the listener and `resolve()` use — every dispatch HELD. Every
903
1356
  * other element the replay REGENERATES.
904
1357
  * 3. **The PREFIX check** (`RN-R20`): each regenerated element at a position the stored events cover
905
1358
  * must be the stored one; the first that is not is refused as `diverged`, naming its position, and
906
1359
  * so is a replay that ends short. A replay that runs PAST the stored events is the stored run's
907
1360
  * lost tail — a checkpoint taken inside `onFoldInput` ends mid-step (ADR 0064) — and it is
908
1361
  * DELIVERED, because the consumer does not hold it (`RN-R24`).
909
- * 4. **A run the replay STOPS is refused** as `stopped` (P23 decision 10), its tail delivered first.
1362
+ * 4. **A run the replay HALTS is refused** as `halted` (P23 decision 10), its tail delivered first.
910
1363
  * 5. **Append `resumed`** naming the IN-FLIGHT set — dispatched and never settled, in dispatch order
911
- * (`RN-R21`, `RN-R22`) — and then hand each to the scheduler at the SAME task id.
1364
+ * (`RN-R21`, `RN-R22`) — and then hand each to the scheduler at the SAME task id, with `attemptsSpent` the highest
1365
+ * attempt recorded for it where one was (v0.26, W186); or, where the replay
1366
+ * ends PAUSED (a fed `paused`, no `unpaused` after it), HOLD each again for an unpause (`RN-R35`).
912
1367
  *
913
1368
  * **At-least-once, and that is the contract rather than a defect** (P23 decision 6): a task named in
914
- * `resumed` may have finished in the process that is gone, and runs again here with a FRESH retry
915
- * budget ([Q365](../../doc/questions/q365.md) — measured: a budget of two attempts ran three times
916
- * across one kill). A task parked at `user_action` has SETTLED and is not in flight: it stays parked,
1369
+ * `resumed` may have finished in the process that is gone, and runs again here. **Not at a fresh retry budget since
1370
+ * W186** (`RN-R21` v0.26, ADR 0113): it is handed on with `attemptsSpent`, the highest attempt the stored run recorded
1371
+ * for it — the attempt in flight at the kill or the stop counted as spent — so a spent budget runs ONCE more and no more.
1372
+ * *Until W186 it ran at a FRESH budget ([Q365](../../doc/questions/q365.md) — measured: a budget of two attempts ran
1373
+ * three times across one kill).* A task parked at `user_action` has SETTLED and is not in flight: it stays parked,
917
1374
  * and `resolve()` works on it.
918
1375
  *
919
1376
  * @throws MultipleRuns — the driver has already started or resumed a run.
920
1377
  * @throws ResumeRefused — see {@link ResumeRefusalReason}; the driver is then spent.
1378
+ * @throws the consumer's own error — a callback that threw on the `resumed` element, or on an element the resume's step
1379
+ * appended after it: the run is HALTED and reported once, and nothing further is handed on (P36 (c), W184).
921
1380
  */
922
1381
  resume(checkpoint: Checkpoint): void;
1382
+ /** `resume`'s body — wrapped so the driver knows it is on the stack of its own step (`stepping`, `RN-R31`, W173 D-1). */
1383
+ private resumeStep;
923
1384
  /**
924
1385
  * **The replay** — `RN-R19` and `RN-R20`. Every fed element goes through {@link Driver.feed}, which
925
- * turns a refusal routing raises into a `stopped` element exactly as the listener's boundary does
926
- * (`OR-R10`), so the replay regenerates a stop where the stored run had one.
1386
+ * turns a refusal routing raises into a `halted` element exactly as the listener's boundary does
1387
+ * (`OR-R10`), so the replay regenerates a halt where the stored run had one.
927
1388
  */
928
1389
  private replayStored;
929
- /** One fed element — a settlement, a resolution, or an earlier resume — at stored position `i`. */
1390
+ /** One fed element — a settlement, a resolution, an earlier resume, a stop, a pause, an unpause or an attempt — at stored position `i`. */
930
1391
  private feedOne;
931
1392
  /** The listener's boundary, for the replay: a refusal routing raises stops the run (`OR-R10`); a refused resume passes through. */
932
1393
  private feed;
@@ -950,6 +1411,11 @@ export declare class Driver {
950
1411
  * second resolution is refused like any other. A rejected command, not a run
951
1412
  * failure: it came from outside and a caller can retry.
952
1413
  *
1414
+ * **And an ASK's answer** — `RS-R1` v0.18, `RN-R37`, ADR 0109: an ask the run reached and nobody has answered is
1415
+ * suspended too, so the person's answer is this call — `'done'` with the answer as `result`, held to the ask's
1416
+ * `answerSchema` (`FD-R4`), or `'failed'` with an `error`, the person DECLINING, held to nothing. The answer IS the
1417
+ * node's result and routes as a task's settlement does.
1418
+ *
953
1419
  * @returns `false` when refused. Nothing is appended and the run is unaffected.
954
1420
  */
955
1421
  resolve(taskId: string, resolvedAs: 'done' | 'failed', value: {
@@ -957,6 +1423,8 @@ export declare class Driver {
957
1423
  error?: string;
958
1424
  actor: string;
959
1425
  }): boolean;
1426
+ /** `resolve`'s body — wrapped so the driver knows it is on the stack of its own step (`stepping`, `RN-R31`, W173 D-1). */
1427
+ private resolveStep;
960
1428
  /**
961
1429
  * **A resolution appended, and the region told** — the half of `resolve()` the REPLAY shares
962
1430
  * (`RN-R19`): a stored resolution is appended as it was stored and routes exactly as the live one
@@ -964,8 +1432,178 @@ export declare class Driver {
964
1432
  * written by a second road.
965
1433
  */
966
1434
  private appendResolution;
1435
+ /**
1436
+ * **Cancel the run: hold it, record the request, and ask the scheduler to cancel each task in flight** — `RN-R31`,
1437
+ * `spec/run-state.md` section 13, [ADR 0104](../../doc/decisions/0104-a-run-its-owner-cancels-is-cancelling-until-its-last-task-has-stopped.md),
1438
+ * P34 decision 4 and stop-and-asks (b), (c), (i), (j). **Resolves once every task it cancelled has stopped**; the fold
1439
+ * reads the run `cancelling` until then and `cancelled` after (`RN-R30`), so a runtime's `done` settles after the last
1440
+ * `task.cancelled` from the fold alone.
1441
+ *
1442
+ * **In order, and each step is a rule:**
1443
+ * 1. **Refuse** — {@link RunControlRefused}, before anything is recorded or sent; and a `graceMs` the scheduler
1444
+ * would throw on is thrown here, as it would be (`TypeError`, `RangeError`).
1445
+ * 2. **Hold** (`RN-R29`): `cancelRequest` is set, so `shut()` is true for every door; every region's collection is
1446
+ * released, `failRun`'s run-level moment (ADR 0046 rule 3, `JP-R12`) — no join of this run settles again.
1447
+ * 3. **Record** ONE `cancelRequested` element, BEFORE the scheduler is asked for anything.
1448
+ * 4. **Cancel** each task in flight the scheduler holds — `RN-R21`'s set, so a PARKED task, settled already, is sent
1449
+ * nothing (P34 (i); the scheduler cannot end it, fact 8). A task not yet handed on is cancelled by `handOn` after
1450
+ * its hand-off (`RN-R29` (i)), and so is each task a PAUSE held (`RN-R35`, P34 (c)). Tasks a worker SPAWNED are
1451
+ * not this run's (P34 (j)). **In a `finally` around step 3** (`RN-R31` v0.21, P35 decision 4): a consumer's
1452
+ * `onFoldInput` that throws on the element still has every cancel sent and every held task handed on and
1453
+ * cancelled — and THEN the run is HALTED and reported once, and the throw reaches the caller (`RN-R31` v0.24,
1454
+ * `OR-R10` v0.19, P36 (b), W184).
1455
+ *
1456
+ * **A SECOND call** appends nothing and forwards its `graceMs` to each task still stopping — the scheduler's own
1457
+ * escalation — and resolves once those have stopped. It is never refused for the halt its own first call made.
1458
+ *
1459
+ * @throws RunControlRefused — `unsupported`, `not-started`, `halted`, `cancelled` or `ended`; nothing recorded or sent.
1460
+ * @throws TypeError / RangeError — `graceMs` not a number, or negative or `NaN`; nothing recorded or sent.
1461
+ */
1462
+ cancel(options: RunCancelOptions): Promise<void>;
1463
+ /**
1464
+ * **Stop the run so that a resume ELSEWHERE can continue it: hold it, record the stop, go DEAF, and only then ask the
1465
+ * scheduler to cancel each task in flight** — `RN-R33`, `spec/run-state.md` section 14,
1466
+ * [ADR 0105](../../doc/decisions/0105-a-run-its-owner-stops-goes-deaf-before-it-cancels-and-resumes-elsewhere.md), P34
1467
+ * decisions 3 and 9 and stop-and-ask (d). **Resolves once every task it cancelled has stopped**; the fold reads the run
1468
+ * `interrupted` from the element on (`RN-R32`), and the log ends with the cancelled tasks dispatched and UNSETTLED —
1469
+ * the in-flight set a resume on another runtime re-dispatches at the same ids (`RN-R21`).
1470
+ *
1471
+ * **In order, and the ORDER is the rule** (P34 decision 3):
1472
+ * 1. **Refuse** — {@link RunControlRefused} with `control: 'stop'`, before anything is recorded or sent: `unsupported`,
1473
+ * `not-started`, `halted`, `cancelled` (`RN-R28`'s, or a cancel its owner requested — P34 (c)) and `ended` (at
1474
+ * rest only, as for a cancel); and a `graceMs` the scheduler would throw on is thrown here. From the accepted call
1475
+ * on, `resolve()` and `cancel()` are refused (P34 decision 9, (c)).
1476
+ * 2. **Hold** (`RN-R29`): the stop is TAKEN, so `shut()` is true for every door, and every region's collection is
1477
+ * released.
1478
+ * 3. **Go DEAF**: nothing the scheduler fires for this run is heard again — from BEFORE the element, so not even a
1479
+ * settlement a callback on it provokes — and no `task.cancelled` of the stop is appended.
1480
+ * 4. **Record** ONE `interrupted` element.
1481
+ * 5. **Cancel** each task in flight the scheduler holds — none for a parked task (P34 fact 4), and none for a task a
1482
+ * PAUSE held, which the scheduler was never handed and which stays in flight for the resume (`RN-R35`).
1483
+ *
1484
+ * **Steps 2-5 are taken at REST** — at once when the call is made at rest, and when the driver's own step ends when it
1485
+ * is made from a callback on that step (`stepping`). A hold taken mid-step would drop the rest of the step's routing
1486
+ * — a fan-out's later branches, a settlement's successor — and a resume re-dispatches only what is IN FLIGHT, so that
1487
+ * work would be lost to the resumed run for good. Taken at rest, the step is whole in the log; the task whose own
1488
+ * `dispatched` element carried the call is handed on in the step and cancelled here (`RN-R29` (i)), and so is a resumed
1489
+ * run's in-flight set, handed back before `resume()` returns.
1490
+ *
1491
+ * **A SECOND call** appends nothing, forwards its `graceMs` to each task still stopping, and resolves as the first does.
1492
+ *
1493
+ * @throws RunControlRefused — `unsupported`, `not-started`, `halted`, `cancelled` or `ended`; nothing recorded or sent.
1494
+ * @throws TypeError / RangeError — `graceMs` not a number, or negative or `NaN`; nothing recorded or sent.
1495
+ */
1496
+ stop(options: RunStopOptions): Promise<void>;
1497
+ /**
1498
+ * **The stop, TAKEN** — `RN-R33` steps 2-5, at rest: from `stop()` itself, or from the end of the driver's own step
1499
+ * when `stop()` was called on it (each step's wrapper calls this when `stepping` returns to `0`).
1500
+ */
1501
+ private takeStop;
1502
+ /** What a stop's promise resolves on once it is taken: a microtask on, every task it sent a cancel to has stopped. */
1503
+ private stopSettled;
1504
+ /**
1505
+ * **Pause the run: it routes on, and every dispatch it makes from here is appended and HELD until `unpause()`** —
1506
+ * `RN-R35`, `spec/run-state.md` section 15,
1507
+ * [ADR 0106](../../doc/decisions/0106-a-run-its-owner-pauses-routes-on-and-holds-every-dispatch-until-unpaused.md),
1508
+ * P34 stop-and-ask (e). Tasks already handed to the scheduler run on and settle, and their settlements route as ever; a
1509
+ * person's `resolve()` is accepted. **Not a halt** (`shut()` is untouched): only the scheduler's `addTask` waits. The
1510
+ * fold reads `RunState.paused` and no new outcome — a held task is `dispatched`, so it reads `running` (P34 fact 3).
1511
+ *
1512
+ * **In order**: (1) **refused by name** before anything is recorded — {@link RunControlRefused} with `control: 'pause'`:
1513
+ * `not-started`, `halted`, `cancelled` (`RN-R28`'s or a requested cancel), `interrupted` (a stop, taken or pending),
1514
+ * `paused` (already paused, or a pause called earlier in this step still to be taken — P34 (e): *a second pause is
1515
+ * refused by name*) and `ended` (nothing in flight and nothing parked, at rest only); (2) **TAKEN AT REST** — at once
1516
+ * when called at rest, and when the driver's own step ends when called from a callback on it: the run is paused and ONE
1517
+ * `paused` element is appended, BETWEEN two steps, where a resume's replay can feed it (`RN-R19`). A pause taken
1518
+ * mid-step would land between two elements of one step, and the replay — which regenerates a step whole — would refuse
1519
+ * its checkpoint `diverged` there; so the step a pause is called on finishes unpaused, its dispatches handed on.
1520
+ */
1521
+ pause(options: RunPauseOptions): void;
1522
+ /**
1523
+ * **Unpause the run: record it, then hand every held task to the scheduler in DISPATCH ORDER** — `RN-R35`, ADR 0106,
1524
+ * P34 (e). Refused by name — `control: 'unpause'`: `not-started`, `halted`, `cancelled`, `interrupted` (a stopped
1525
+ * run's held tasks are a resume's: unpause it where it resumes) and `not-paused`. **Never `ended`**: an unpause rewrites
1526
+ * no outcome, so a paused run that finished may still be unpaused. Taken at rest, as a pause is: ONE `unpaused` element,
1527
+ * then the hand-off — each held task's `createTask` and `addTask`, first held first.
1528
+ */
1529
+ unpause(options: RunPauseOptions): void;
1530
+ /** The pause the owner has ASKED for: the last call still to be taken, else the pause taken (`RN-R35`). */
1531
+ private pauseWanted;
1532
+ /**
1533
+ * The refusals a pause and an unpause share, in `RN-R35`'s order — each a run that is not this driver's to hold or
1534
+ * release: none started, or the run halted, cancelled or stopped. Thrown before anything is recorded.
1535
+ */
1536
+ private refuseRunHeld;
1537
+ /**
1538
+ * **A pause or an unpause, TAKEN** — `RN-R35`, at rest: from the call itself, or from the end of the driver's own step
1539
+ * (`leaveStep()`). **A step of its own** (`stepping`), so a call a consumer makes from a callback on the element — a
1540
+ * second pause, a stop, a `resolve()` that dispatches — is taken, or routes, as on any step. **A pause** sets the flag
1541
+ * BEFORE its element, so a dispatch a callback on the element causes is held. **An unpause** clears it before its
1542
+ * element and hands the held tasks on AFTER it, in a `finally`: a consumer's `onFoldInput` that throws on the element
1543
+ * still has the element in the log, and the run it says is unpaused is (W174's conformity reader's finding 5, the stop's
1544
+ * lesson) — and then the take HALTS the run itself, reported once (`takePause`, `RN-R35` v0.24, W184), so a call pending
1545
+ * behind it is dropped, and the throw reaches whoever is on the stack: the caller of the call at rest or of the driver
1546
+ * call mid-step, or, on a SCHEDULER EVENT's step, the listener boundary (`onEvent`, P35 (j)). A dispatch a callback on the element causes joins the end
1547
+ * of the held queue (`dispatch`), so the order is the dispatch order still.
1548
+ */
1549
+ private takePending;
1550
+ /**
1551
+ * **A resolution given mid-step, TAKEN at rest** — `RN-R37`, W180 (the conformity reader's R4; S-4 for every
1552
+ * resolution): a step of its own, so the resolution lands BETWEEN two steps — where a resume's replay FEEDS it
1553
+ * (`RN-R19`) — after the step that parked its node has completed (an ask's `asked` or a task's `user_action`
1554
+ * settlement, and in a region `branch.suspended`). Answered on its own `asked` element and appended
1555
+ * there instead, it sat between two elements of one step, and the replay — which regenerates a step whole — refused the
1556
+ * run's checkpoint `diverged` at it. **Dropped, recording nothing, when the step it was given on HALTED or CANCELLED the
1557
+ * run** (or the node is no longer parked) — `takePause`'s rule: it would be refused were it given now. It routes as
1558
+ * `resolve()` at rest routes: a throw — from routing, or a consumer's on the resolution's own element — HALTS the run
1559
+ * here, reported once, so a call pending behind it is dropped; it then reaches whoever is on the stack: a driver call's
1560
+ * caller, or the listener boundary of a scheduler event's step (`onEvent`, `OR-R10` v0.19, W184).
1561
+ */
1562
+ private takeAnswer;
1563
+ private takePause;
1564
+ /**
1565
+ * **The unpause's hand-off**: each held task to the scheduler, first held first (`RN-R35`). Taken out of the hold and
1566
+ * marked `unhanded` before its `addTask`, as a resume's re-dispatch is (`RN-R31`), so a cancel raised inside one
1567
+ * hand-off sends nothing for a task not yet handed — and `handOn` then cancels it. **Held for a HALT** — checked before
1568
+ * each, as a resume's re-dispatch is (`RN-R29` (4), `holdsResume()`): a halt raised inside one hand-off hands nothing
1569
+ * further on, and what is left is never handed. A dispatch made on this stack joins the end of the queue and is handed
1570
+ * on in its turn (the `Map` visits what is added while it is walked). Caller-initiated, so a throw from the scheduler
1571
+ * is reported AND rethrown, `start()`'s and `resume()`'s treatment.
1572
+ */
1573
+ private drainHeld;
1574
+ /**
1575
+ * Whether the scheduler holds this task — dispatched, not held by a replay, past its `addTask` (`RN-R31`), and not held
1576
+ * by a pause (`RN-R35`, W175): a stop of a paused run cancels only what this says the scheduler holds (ADR 0105's last
1577
+ * consequence), and a cancel hands each held task on before it cancels it.
1578
+ */
1579
+ private handedOn;
967
1580
  private onEvent;
968
- /** Whether this run has been stopped by a refusal. */
1581
+ /** `onEvent`'s body — wrapped so the driver knows it is on the stack of its own step (`stepping`, `RN-R31`, W173 D-1). */
1582
+ private onEventStep;
1583
+ /**
1584
+ * **A `task.running`: an attempt started** — `RN-R40`, W186, P36 (f). Its id and number are COPIED here, while the
1585
+ * listener runs (`OR-R1`). Recorded only for a task THIS run dispatched, and only when the number EXCEEDS the last one
1586
+ * recorded for it — a requeue repeats its number and appends nothing (the scheduler's own recipe's `max`). **Everything
1587
+ * else is dropped in SILENCE, and none of it is a refusal**: an id that does not parse (a plain task on a shared
1588
+ * scheduler — its SETTLEMENT is what `onUnmintedTask` reports, `ID-R5`), another run's (`ID-R4` tier 1), one this run
1589
+ * never dispatched (its settlement is `ID-R4` tier 2's `UndispatchedTask`), and a detail carrying no positive integer
1590
+ * `attemptNumber` (a scheduler older than the pin, or a double). An attempt start never stops a run by existing. **Queued, not
1591
+ * appended**: {@link Driver.takeAttempts} appends it when the step it was heard on ends — at once, on a scheduler
1592
+ * event's own step; at the step's end when an inline pool fires it inside the driver's `addTask`.
1593
+ */
1594
+ private heardRunning;
1595
+ /**
1596
+ * **A `task.message` or a `task.progress`: PASSED THROUGH to its callback** — `RN-R42`, W187, P36 (k), ADR 0114. The
1597
+ * task's id is the detail's `taskId` for a message — `task.message` carries no `id` (P36 fact 2) — and its `id` for a
1598
+ * progress report, whose detail is the task spread flat. Handed on only for a task THIS run dispatched (`ID-R4`'s two
1599
+ * tiers, asked SILENTLY, as {@link Driver.heardRunning} asks them): anything else is dropped and is not a refusal. Every
1600
+ * field is COPIED here, while the listener runs (ADR 0007 rule 1, `OR-R1`), and the copy FROZEN (`OR-R7`); a value that
1601
+ * cannot be cloned is handed on `undefined` — the scheduler's own treatment of an uncloneable `emit` (its §6) — so a
1602
+ * pass-through never stops a run by existing. **The consumer's throw is NOT caught here**: it leaves through
1603
+ * `onEventOrThrow` to the listener boundary, which reports it and halts the run (`onUnmintedTask`'s road, `OR-R10`).
1604
+ */
1605
+ private heardPassThrough;
1606
+ /** Whether this run has been HALTED by a refusal (`OR-R10`; its outcome `halted`). */
969
1607
  hasFailed(): boolean;
970
1608
  private failRun;
971
1609
  private onEventOrThrow;
@@ -1101,6 +1739,9 @@ export declare class Driver {
1101
1739
  * the edge carried, so the composed object is the whole payload and what the
1102
1740
  * worker gets is what the document says, character for character.
1103
1741
  *
1742
+ * **`item` is a THUNK, read only for a `"$item"` key** (v0.45, P38 (e)): in a run it reads the instance's held item
1743
+ * (`itemOf`), at `FD-R4` it is the walk's own value — and a node declaring no `"$item"` never asks.
1744
+ *
1104
1745
  * @throws UnresolvedReference — `FD-R3`.
1105
1746
  */
1106
1747
  private compose;
@@ -1121,6 +1762,25 @@ export declare class Driver {
1121
1762
  * @throws UnresolvedReference — `FD-R3`.
1122
1763
  */
1123
1764
  private resolveReference;
1765
+ /**
1766
+ * **The value the run ENTERED with — the header's copy** (`RN-R15`, P38 fact 4 and (f)): taken at `start()` from the
1767
+ * caller's payload, or on a resume from the stored header, and held for the life of the run. What a `"$payload"`
1768
+ * reference composes from, in both doors; `FD-R4` hands the caller's own value instead, because it runs before the
1769
+ * header exists. Read only where a node or an edge composes the token — a run declaring none never clones it
1770
+ * (P38 decision 2). The header is set before the first `enter` in both doors, so `null` here is not a run.
1771
+ */
1772
+ private runPayload;
1773
+ /**
1774
+ * **The item of the map instance `nodeId` is entered in** — `"$item"`'s source in a run (v0.45, P38 (c), (e)): the
1775
+ * innermost map containing the node (`innermostMapOf`, the walk `FD-V49` checked), and the instance its `itemIndices`
1776
+ * name — the branch path that map's branch entry has in it. **By reference to the held item, uncloned**: the caller
1777
+ * clones, per key. Read only where a node or an edge declares `"$item"`.
1778
+ *
1779
+ * **A miss THROWS a plain `Error` naming the invariant**: a node is entered only while its instance is live, and an
1780
+ * instance's item is released only once nothing of it may still be entered (`trackItems`), so a reader with nothing held is a driver
1781
+ * defect — reported as a halt (`OR-R10`) rather than handed `undefined`, which an item may legitimately be.
1782
+ */
1783
+ private itemOf;
1124
1784
  /**
1125
1785
  * **What a decision sees, beyond its node's outcome** — `FD-C3`, and
1126
1786
  * [ADR 0041](../../doc/decisions/0041-a-decision-sees-its-nodes-outcome-and-whatever-its-own-edge-declares.md).
@@ -1131,7 +1791,10 @@ export declare class Driver {
1131
1791
  *
1132
1792
  * **No `"."`, and `FD-V26` refuses it on an edge**: *what this predicate would have
1133
1793
  * received anyway* is `ctx.outcome`, which it already has. So there is no
1134
- * `edgeValue` parameter here and none is derivable — see the call site.
1794
+ * `edgeValue` parameter here and none is derivable — see the call site. **A
1795
+ * `"$payload"` key needs none either** (v0.44, ADR 0117): it is not derived from what
1796
+ * the NODE received but read from the run's header, `runPayload()` above, its own
1797
+ * copy per key per read.
1135
1798
  *
1136
1799
  * @throws UnresolvedReference — `FD-R3`, from the same expression a node's does.
1137
1800
  */
@@ -1145,6 +1808,57 @@ export declare class Driver {
1145
1808
  * graph, and there is no longer a carried value that could disagree with it.
1146
1809
  */
1147
1810
  private dispatch;
1811
+ /**
1812
+ * **A branch reached an ASK: it parks, keeping what reached it, and NOTHING is handed to the scheduler** — `RN-R37`,
1813
+ * v0.22, [ADR 0109](../../doc/decisions/0109-an-ask-parks-with-nothing-dispatched-and-its-answer-is-its-result.md),
1814
+ * P35 stop-and-asks (b), (c) and (f), decision 5.
1815
+ *
1816
+ * 1. **The hold** (`RN-R29`), as `dispatch`'s: `enter` checked it at its head and `compose` ran consumer code since.
1817
+ * 2. **Its task id** by `spec/task-id.md`'s scheme, attempt `0` — the address a person's answer names. Minted once
1818
+ * per path: a second `asked` at one id is `ID-R1`'s defect and refused as one (`DuplicateTaskId`), never a
1819
+ * second park.
1820
+ * 3. **ONE `asked` element**, carrying `received` — what reached the ask, composed through its `needs` — as the
1821
+ * driver's OWN deep copy: at the entry `received` is the caller's own `start()` payload, and a kept input must not
1822
+ * move when the caller's object does (P35 (f)). No `dispatched` element, no `createTask`, no `addTask`: the ask is
1823
+ * never in flight (`RN-R21`), so a stop cancels nothing for it and a resume re-dispatches nothing (P35 fact 5).
1824
+ * 4. **In a region, `branch.suspended`** — what a parked task emits (`settle`'s `user_action` arm): the branch is
1825
+ * live and its join waits for the person (Q7), which is how an ask in a fan-out holds its join. AFTER the element,
1826
+ * as the parked task's follows its settlement, and the hold checked between them (`RN-R29`): a consumer's
1827
+ * callback on `asked` may halt the run, and a halted run emits nothing further. It may also ANSWER the ask: the
1828
+ * answer is accepted and PENDING until the step ends (reading R4), so the park completes first.
1829
+ *
1830
+ * **During a replay this is how the park is REGENERATED** (`RN-R19`): the element is compared with the stored one,
1831
+ * input and all (`sameData`, `RN-R20`), so a replay that composes a different input is refused `diverged` here.
1832
+ */
1833
+ private ask;
1834
+ /**
1835
+ * **`RS-R1`'s *suspended*, for one task id** — a dispatched task whose last record is a `user_action` settlement, or,
1836
+ * since v0.18 (`RN-R37`, ADR 0109), an ask this run reached that has no record at all. The FIVE readers of the
1837
+ * predicate read it here: `resolve()`, the replay's fed resolution, and the three `ended` refusals (`hasParked`).
1838
+ */
1839
+ private parked;
1840
+ /**
1841
+ * **Whether anything in the run waits on a person** — a parked task or an unanswered ask. `cancel`, `stop` and `pause`
1842
+ * refuse a run `ended` only when nothing is in flight and THIS is false: a run whose only live node is a parked ask is
1843
+ * waiting on a person, not finished (P35 fact 5 (iv)). The rest of an ask under run control needs no line of its own:
1844
+ * never dispatched, it is never in flight, so every road reading `inFlight()` passes it by (`RN-R38`, W181).
1845
+ */
1846
+ private hasParked;
1847
+ /**
1848
+ * **Hand one task to the scheduler — and, if the run's owner cancelled it while the task was on its way, cancel it
1849
+ * at once** (`RN-R29` (i), `RN-R31`, ADR 0103's inherited hazard, W173). The ONE place `addTask` is called: by
1850
+ * `dispatch` for the task in progress, and by `resume()` for each task in flight. A cancel raised on this task's own
1851
+ * `dispatched` element — or on a resume's `resumed` element — ran before the scheduler held the id, and sent nothing
1852
+ * for it; this is where that task is cancelled, so it ends with a genuine `task.cancelled` rather than running.
1853
+ */
1854
+ private handOn;
1855
+ /**
1856
+ * **One task cancelled through the scheduler, with the request's options**, its promise kept for `cancel()` — or,
1857
+ * since W174, `stop()` — to wait on (`RN-R31`, `RN-R33`). Each option is passed only when the caller gave it, so an
1858
+ * absent `graceMs` is the pool's `cancelGraceMs` and not `undefined` read as a value. A cancel and a stop never both
1859
+ * hold — each refuses the other — so the one present is the one whose options these are.
1860
+ */
1861
+ private cancelTask;
1148
1862
  /**
1149
1863
  * **A node's `branchPath`, derived from the graph** — `spec/task-id.md` section
1150
1864
  * 1's *"a property of the node"*, and W21 is where the driver stopped carrying
@@ -1193,6 +1907,18 @@ export declare class Driver {
1193
1907
  * neither the log record's `result` nor the worker's own object.
1194
1908
  */
1195
1909
  private releaseArrivals;
1910
+ /**
1911
+ * **The release horizon of what instances keep** — ADR 0118 rule 5. A `branch.opened` of a region nested in a kept map's
1912
+ * instance counts one live branch there; its terminal event uncounts it. The instance's OWN terminal event releases its
1913
+ * item when that count is zero, and otherwise marks it ENDED, released by the nested branch that ends last. O(nesting
1914
+ * depth) per event, and nothing at all where no map keeps (`kept` empty). Run in a replay as in a run, so a resume
1915
+ * rebuilds the counts with the items.
1916
+ */
1917
+ private trackItems;
1918
+ /** **One instance's item, dropped** — and the map's own key with its last instance, as `releaseArrivals` drops a region's. */
1919
+ private releaseItem;
1920
+ /** **Everything instances keep, dropped** — the run-level moments, where no node of the run is entered again. */
1921
+ private releaseAllItems;
1196
1922
  /** The arrival map for one region, created on first arrival. */
1197
1923
  private arrivalsOf;
1198
1924
  private pathOf;
@@ -1259,6 +1985,12 @@ export declare class Driver {
1259
1985
  * driver's own array could forge a settlement and bypass `RS-R1`.
1260
1986
  */
1261
1987
  private append;
1988
+ /**
1989
+ * **A stored `interrupted` element, fed** (`RN-R19`, `RN-R32`): the replay holds as the stopped run was held — the hold
1990
+ * BEFORE the element, as a stop is taken — and appends the element as stored. Neither `deaf` nor a cancel nor the
1991
+ * collections' release: those were the stopped driver's, and a replay hands a scheduler nothing.
1992
+ */
1993
+ private feedInterrupted;
1262
1994
  private emit;
1263
1995
  /**
1264
1996
  * Ask the policy, and act on the answer.