@orkestrel/workflow 0.0.8 → 0.0.10

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.
@@ -92,6 +92,27 @@ export declare function buildWorkflowContext(node: WorkflowContext): WorkflowCon
92
92
  */
93
93
  export declare function canTransitionTask(from: TaskStatus, to: TaskStatus): boolean;
94
94
 
95
+ /**
96
+ * Capture every top-level {@link WorkflowOptions} value exactly once into an owned plain bag.
97
+ *
98
+ * @remarks
99
+ * Direct property reads preserve inherited and non-enumerable option values while preventing
100
+ * accessor-backed caller bags from shifting policy, handlers, hooks, or nested options between
101
+ * construction stages. Nested bags and the functions registry retain their original identities so
102
+ * entity constructors can snapshot keyed child options and live additions can resolve against the
103
+ * same registry.
104
+ *
105
+ * @param options - The caller-owned workflow construction options
106
+ * @returns An owned top-level options bag containing the captured values
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const captured = captureWorkflowOptions(options)
111
+ * const workflow = createWorkflow(definition, captured)
112
+ * ```
113
+ */
114
+ export declare function captureWorkflowOptions(options?: WorkflowOptions): WorkflowOptions;
115
+
95
116
  /**
96
117
  * Validate and clone one complete task activity frame.
97
118
  *
@@ -113,9 +134,11 @@ export declare function cloneTaskActivity(input: unknown, updated?: number): Tas
113
134
  * Validate and own a workflow snapshot before live construction.
114
135
  *
115
136
  * @param input - The hostile snapshot boundary
137
+ * @param id - The optional storage key the owned snapshot must match
116
138
  * @returns A deeply owned frozen snapshot
139
+ * @throws {WorkflowError} With `RESTORE` when the snapshot is invalid or does not match `id`
117
140
  */
118
- export declare function cloneWorkflowSnapshot(input: unknown): WorkflowSnapshot;
141
+ export declare function cloneWorkflowSnapshot(input: unknown, id?: string): WorkflowSnapshot;
119
142
 
120
143
  /**
121
144
  * Flatten a nested list of per-phase {@link TaskResult} lists into one positional list
@@ -242,7 +265,7 @@ export declare interface ControllerInterface<TInput, TResult> {
242
265
  * @remarks
243
266
  * Builds a one-table database (`snapshots`, keyed by `id`) over the supplied driver, the snapshot
244
267
  * held as ONE OPAQUE JSON COLUMN — the column map is `{ id; snapshot }` where `snapshot` is a
245
- * `rawShape` (a JSON blob), exactly as {@link createDatabaseQueueStore} stores its `input`. The
268
+ * `rawShape` (a JSON blob), exactly as `createDatabaseQueueStore` stores its `input`. The
246
269
  * snapshot is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless
247
270
  * AND keeps the row type FLAT — a structured multi-column snapshot table would force the contract to
248
271
  * `Infer` the deeply-nested snapshot shape (workflow → phases → tasks → results) and trip TS2589;
@@ -258,7 +281,8 @@ export declare interface ControllerInterface<TInput, TResult> {
258
281
  *
259
282
  * @example
260
283
  * ```ts
261
- * import { createDatabaseWorkflowStore, createMemoryDriver, createWorkflow, restoreWorkflow } from '@src/core'
284
+ * import { createMemoryDriver } from '@orkestrel/database'
285
+ * import { createDatabaseWorkflowStore, createWorkflow, restoreWorkflow } from '@orkestrel/workflow'
262
286
  *
263
287
  * const store = createDatabaseWorkflowStore(createMemoryDriver()) // a durable driver swaps in here
264
288
  * const workflow = createWorkflow(definition)
@@ -285,7 +309,7 @@ export declare function createDeferred<T>(): DeferredInterface<T>;
285
309
  *
286
310
  * @remarks
287
311
  * The snapshot analogue of the server package's `createMemorySessionStore`
288
- * (and the {@link createMemoryQueueStore} family), but LEANER — there is no idle-TTL, so no
312
+ * (and the `createMemoryQueueStore` family), but LEANER — there is no idle-TTL, so no
289
313
  * options bag (AGENTS §21 minimal): a persisted run-state lives until an explicit `delete`. This is
290
314
  * the zero-plumbing DEFAULT (a plain `Map`); its driver-pluggable twin is
291
315
  * {@link createDatabaseWorkflowStore} (the snapshot as one opaque JSON column over a `databases`
@@ -297,7 +321,7 @@ export declare function createDeferred<T>(): DeferredInterface<T>;
297
321
  *
298
322
  * @example
299
323
  * ```ts
300
- * import { createMemoryWorkflowStore, createWorkflow, restoreWorkflow } from '@src/core'
324
+ * import { createMemoryWorkflowStore, createWorkflow, restoreWorkflow } from '@orkestrel/workflow'
301
325
  *
302
326
  * const store = createMemoryWorkflowStore()
303
327
  * const workflow = createWorkflow(definition)
@@ -337,7 +361,7 @@ export declare function createMemoryWorkflowStore(): WorkflowStoreInterface;
337
361
  *
338
362
  * @example
339
363
  * ```ts
340
- * import { createRunner } from '@src/core'
364
+ * import { createRunner } from '@orkestrel/workflow'
341
365
  *
342
366
  * // A handler that fans out one sibling per declared unit, then returns its own value.
343
367
  * const runner = createRunner<number, number>({
@@ -363,7 +387,8 @@ export declare function createRunner<TInput, TResult>(options: RunnerOptions<TIn
363
387
  * `yield()` gives the host a turn via a zero-delay macrotask (so pending I/O,
364
388
  * timers, and rendering actually run — a microtask would not); `delay(ms)` resumes
365
389
  * after at least `ms`. Pass `options.signal` to make a pending yield/delay reject
366
- * with the signal's `reason` on abort (with full timer/listener cleanup).
390
+ * with the signal's exact `reason`; the shared owned-signal lifecycle clears the timer
391
+ * without invoking caller-owned listener methods.
367
392
  * `options.priority` is accepted for contract compliance but treated uniformly by
368
393
  * this default — environment backends honour it.
369
394
  *
@@ -371,7 +396,8 @@ export declare function createRunner<TInput, TResult>(options: RunnerOptions<TIn
371
396
  *
372
397
  * @example
373
398
  * ```ts
374
- * import { createAbort, createScheduler } from '@src/core'
399
+ * import { createAbort } from '@orkestrel/abort'
400
+ * import { createScheduler } from '@orkestrel/workflow'
375
401
  *
376
402
  * const abort = createAbort()
377
403
  * const scheduler = createScheduler()
@@ -385,7 +411,7 @@ export declare function createRunner<TInput, TResult>(options: RunnerOptions<TIn
385
411
  *
386
412
  * @example
387
413
  * ```ts
388
- * import { createScheduler } from '@src/core'
414
+ * import { createScheduler } from '@orkestrel/workflow'
389
415
  *
390
416
  * // A backoff: wait a growing interval between retries.
391
417
  * const scheduler = createScheduler()
@@ -424,7 +450,7 @@ export declare function createScheduler(): SchedulerInterface;
424
450
  *
425
451
  * @example
426
452
  * ```ts
427
- * import { createWorkflow } from '@src/core'
453
+ * import { createWorkflow } from '@orkestrel/workflow'
428
454
  *
429
455
  * const workflow = createWorkflow(definition, { on: { complete: () => done() } })
430
456
  * const phase = workflow.phase('phase-build')
@@ -452,7 +478,7 @@ export declare function createWorkflow(definition: WorkflowDefinition, options?:
452
478
  *
453
479
  * @example
454
480
  * ```ts
455
- * import { createWorkflowContract } from '@src/core'
481
+ * import { createWorkflowContract } from '@orkestrel/workflow'
456
482
  *
457
483
  * const contract = createWorkflowContract()
458
484
  * const definition = contract.generate() // a valid WorkflowDefinition
@@ -482,7 +508,7 @@ export declare function createWorkflowContract(): ContractInterface<WorkflowDefi
482
508
  *
483
509
  * @example
484
510
  * ```ts
485
- * import { createMemoryWorkflowStore, createWorkflowManager } from '@src/core'
511
+ * import { createMemoryWorkflowStore, createWorkflowManager } from '@orkestrel/workflow'
486
512
  *
487
513
  * const manager = createWorkflowManager({
488
514
  * store: createMemoryWorkflowStore(),
@@ -527,7 +553,7 @@ export declare function createWorkflowManager(options?: WorkflowManagerOptions):
527
553
  *
528
554
  * @example
529
555
  * ```ts
530
- * import { createWorkflowRunner } from '@src/core'
556
+ * import { createWorkflowRunner } from '@orkestrel/workflow'
531
557
  *
532
558
  * const runner = createWorkflowRunner()
533
559
  * const definition = { id: 'w', name: 'W', phases: [{ id: 'p', name: 'P', tasks: [
@@ -569,7 +595,8 @@ export declare function createWorkflowRunner(options?: WorkflowRunnerOptions): W
569
595
  * the row `{ id: snapshot.id, snapshot }`.
570
596
  * - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to
571
597
  * a {@link WorkflowSnapshot} ({@link import('../helpers.js').isWorkflowSnapshot} — the AGENTS §14
572
- * boundary narrow for an untrusted storage read), or `undefined` if none is stored.
598
+ * boundary narrow for an untrusted storage read), or `undefined` if none is stored. A present
599
+ * snapshot whose own id differs from the requested key rejects with normalized `RESTORE` evidence.
573
600
  * - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).
574
601
  *
575
602
  * UNLIKE the server package's `SessionStoreInterface` there is NO
@@ -580,7 +607,8 @@ export declare function createWorkflowRunner(options?: WorkflowRunnerOptions): W
580
607
  *
581
608
  * @example
582
609
  * ```ts
583
- * import { createDatabaseWorkflowStore, createMemoryDriver, createWorkflow, restoreWorkflow } from '@src/core'
610
+ * import { createMemoryDriver } from '@orkestrel/database'
611
+ * import { createDatabaseWorkflowStore, createWorkflow, restoreWorkflow } from '@orkestrel/workflow'
584
612
  *
585
613
  * const store = createDatabaseWorkflowStore(createMemoryDriver()) // a durable driver swaps in here
586
614
  * const workflow = createWorkflow(definition)
@@ -599,7 +627,7 @@ export declare class DatabaseWorkflowStore implements WorkflowStoreInterface {
599
627
  * {@link WorkflowSnapshotRow} `{ id; snapshot }` shape (the snapshot one opaque JSON column)
600
628
  */
601
629
  constructor(table: TableInterface<WorkflowSnapshotRow>);
602
- /** Resolve the persisted snapshot for `id`, narrowing the opaque JSON column back to a `WorkflowSnapshot`. */
630
+ /** Resolve and key-check the snapshot for `id`, narrowing the opaque column to `WorkflowSnapshot`. */
603
631
  get(id: string): Promise<WorkflowSnapshot | undefined>;
604
632
  /** Insert or replace under the snapshot's OWN `id` (no separate id param) — the row is `{ id, snapshot }`. */
605
633
  set(snapshot: WorkflowSnapshot): Promise<void>;
@@ -799,8 +827,19 @@ export declare function failure<E>(error: E): Failure<E>;
799
827
  */
800
828
  export declare function findFailure(results: readonly TaskResult[]): TaskResult | undefined;
801
829
 
802
- /** Test that every present behavior reference resolves before dispatch. */
803
- export declare function hasWorkflowHandlers(snapshot: WorkflowSnapshot, functions: WorkflowFunctions | undefined): boolean;
830
+ /**
831
+ * Test that every named task has a callable runtime handler before dispatch.
832
+ *
833
+ * @remarks
834
+ * A snapshot lookup reads each unique `run` binding at most once from `functions`. A live workflow
835
+ * validates its tasks' already-resolved handlers without consulting the retained registry again.
836
+ *
837
+ * @param workflow - The persisted snapshot or constructed live workflow to validate
838
+ * @returns Whether every named task resolves to a callable handler
839
+ */
840
+ export declare function hasWorkflowHandlers(workflow: WorkflowInterface): boolean;
841
+
842
+ export declare function hasWorkflowHandlers(workflow: WorkflowSnapshot, functions: WorkflowFunctions | undefined): boolean;
804
843
 
805
844
  /**
806
845
  * Insert one `[key, value]` entry at a positional index into a readonly entries array —
@@ -945,7 +984,7 @@ export declare const MAX_TIMER_MS = 2147483647;
945
984
  *
946
985
  * @example
947
986
  * ```ts
948
- * import { createMemoryWorkflowStore, createWorkflow, restoreWorkflow } from '@src/core'
987
+ * import { createMemoryWorkflowStore, createWorkflow, restoreWorkflow } from '@orkestrel/workflow'
949
988
  *
950
989
  * const store = createMemoryWorkflowStore()
951
990
  * const workflow = createWorkflow(definition)
@@ -1017,9 +1056,9 @@ export declare function parkSignal(signal: AbortSignal): Promise<void>;
1017
1056
  *
1018
1057
  * @remarks
1019
1058
  * - **Derived status.** `status` is `#override` when one is in force, else
1020
- * {@link derivePhaseStatus} over the live tasks' statuses. {@link #recompute} (passed to
1059
+ * {@link derivePhaseStatus} over the live tasks' statuses. `#recompute` (passed to
1021
1060
  * each child {@link Task}) re-derives on every child transition; a CHANGE emits the matching
1022
- * event AND escalates to the workflow ({@link #escalate}, the upward step of the cascade).
1061
+ * event AND escalates to the workflow (`#escalate`, the upward step of the cascade).
1023
1062
  * - **Override (AGENTS §10).** `skip` / `stop` FORCE the phase's status (e.g. skipping a whole
1024
1063
  * phase), overriding the derived value; the override is PERSISTED in the snapshot's own
1025
1064
  * `override` field and restored DIRECTLY (no divergence guess), so a forced phase round-trips.
@@ -1050,10 +1089,12 @@ export declare function parkSignal(signal: AbortSignal): Promise<void>;
1050
1089
  * construction path {@link #append} uses at build time, so a live mint and a restored/built
1051
1090
  * task are wired IDENTICALLY. At construction, the workflow-level
1052
1091
  * {@link import('../types.js').WorkflowFunctions} registry (threaded from
1053
- * {@link import('../types.js').WorkflowOptions.functions}) resolves each task's `run` name into
1054
- * its runtime {@link import('../types.js').TaskInterface.handler} ONCE; a `run` that is omitted
1055
- * or unregistered resolves to no handler; only an omitted `run` is a no-op, while an
1056
- * unresolved present name makes the containing tree non-drivable.
1092
+ * {@link import('../types.js').WorkflowOptions.functions}) resolves every unique initial `run`
1093
+ * name ONCE before any task is built; siblings sharing a name receive the exact same captured
1094
+ * runtime {@link import('../types.js').TaskInterface.handler}. A later live {@link add} reads
1095
+ * that name once from the retained registry at its own mint moment. An omitted or unregistered
1096
+ * `run` resolves to no handler; only omission is a no-op, while an unresolved present name makes
1097
+ * the containing tree non-drivable.
1057
1098
  * - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` mirror the workflow's own
1058
1099
  * quartet, scoped to this phase — a driving
1059
1100
  * {@link import('../types.js').WorkflowRunnerInterface.execute} gates a task's own
@@ -1649,6 +1690,11 @@ export declare const phaseUpdateShape: ObjectShape<{
1649
1690
  /**
1650
1691
  * Rebuild an interrupted workflow at its remaining retry budget.
1651
1692
  *
1693
+ * @remarks
1694
+ * Each phase captures every unique initial `run` binding once before constructing tasks. Recovery
1695
+ * validates those live tasks' captured callable handlers without rereading the registry, while the
1696
+ * retained registry identity remains available to resolve future live additions at their mint time.
1697
+ *
1652
1698
  * @param snapshot - The hostile persisted snapshot
1653
1699
  * @param options - Runtime handlers and entity options
1654
1700
  * @returns A recoverable live workflow
@@ -1699,7 +1745,7 @@ export declare function resolveTaskSilence(value: number | undefined, fallback:
1699
1745
  *
1700
1746
  * @example
1701
1747
  * ```ts
1702
- * import { restoreWorkflow } from '@src/core'
1748
+ * import { restoreWorkflow } from '@orkestrel/workflow'
1703
1749
  *
1704
1750
  * const restored = restoreWorkflow(workflow.snapshot()) // bail comes from the snapshot
1705
1751
  * restored.status === workflow.status // true
@@ -1794,7 +1840,7 @@ export declare class Runner<TInput, TResult> implements RunnerInterface<TInput,
1794
1840
  */
1795
1841
  spawn(input: TInput): Promise<TResult> | undefined;
1796
1842
  execute(inputs: readonly TInput[]): Promise<readonly TResult[]>;
1797
- abort(reason?: unknown): void;
1843
+ abort(reason?: unknown): Promise<void>;
1798
1844
  /**
1799
1845
  * Suspend dispatch (AGENTS §10 — resumable): delegates to the backing queue's own
1800
1846
  * `pause`, which holds the NEXT dispatch while any in-flight unit finishes.
@@ -1824,8 +1870,8 @@ export declare class Runner<TInput, TResult> implements RunnerInterface<TInput,
1824
1870
  * gate, no recorded failure, no fail-fast trip) rather than a genuine failure — a
1825
1871
  * dispatched unit's rejection while stopping is still a real failure. Idempotent.
1826
1872
  */
1827
- stop(): void;
1828
- destroy(): void;
1873
+ stop(): Promise<void>;
1874
+ destroy(): Promise<void>;
1829
1875
  }
1830
1876
 
1831
1877
  /**
@@ -1957,8 +2003,9 @@ export declare interface RunnerInterface<TInput, TResult> {
1957
2003
  * `execute` reject.
1958
2004
  *
1959
2005
  * @param reason - An optional cancellation reason propagated to every unit's signal
2006
+ * @returns The stable cleanup barrier
1960
2007
  */
1961
- abort(reason?: unknown): void;
2008
+ abort(reason?: unknown): Promise<void>;
1962
2009
  /**
1963
2010
  * Suspend dispatch (AGENTS §10 — resumable): the backing queue holds the NEXT dispatch
1964
2011
  * while any in-flight unit finishes; idempotent.
@@ -1995,10 +2042,15 @@ export declare interface RunnerInterface<TInput, TResult> {
1995
2042
  * runner.stop() // the in-flight unit finishes; the rest are gracefully dropped
1996
2043
  * await results // resolves with whatever settled — never rejects
1997
2044
  * ```
2045
+ * @returns The stable graceful-cleanup barrier
1998
2046
  */
1999
- stop(): void;
2000
- /** Tear the runner down — `abort` plus stop the backing queue; idempotent. */
2001
- destroy(): void;
2047
+ stop(): Promise<void>;
2048
+ /**
2049
+ * Tear the runner down, awaiting backing-queue cleanup before destroying the emitter last.
2050
+ *
2051
+ * @returns The stable teardown barrier
2052
+ */
2053
+ destroy(): Promise<void>;
2002
2054
  }
2003
2055
 
2004
2056
  /**
@@ -2008,11 +2060,11 @@ export declare interface RunnerInterface<TInput, TResult> {
2008
2060
  * - `handler` — runs each unit's work against its {@link ControllerInterface};
2009
2061
  * rejecting fails the unit (and, after retries are exhausted, fails the run).
2010
2062
  * - `concurrency` — the maximum units in flight at once; defaults to `1` (ordered,
2011
- * one-at-a-time). Floored at `1`.
2063
+ * one-at-a-time) and must be a positive safe integer.
2012
2064
  * - `retries` — the default extra attempts per unit on failure (or a per-attempt
2013
- * timeout); defaults to `0`.
2014
- * - `timeout` — the per-attempt deadline in milliseconds; defaults to none (a
2015
- * non-positive value means no deadline).
2065
+ * timeout); defaults to `0` and must be a nonnegative safe integer.
2066
+ * - `timeout` — the per-attempt deadline in milliseconds; defaults to `0`, must be
2067
+ * an integer in `0..2_147_483_647`, and `0` disables the deadline.
2016
2068
  * - `entries` — per-entry `retries` / `timeout` overrides, resolved from each
2017
2069
  * input; falls back to the runner-level `retries` / `timeout` defaults.
2018
2070
  * - `on` — the reserved {@link EmitterHooks} key (§8): initial listeners for the runner's
@@ -2048,6 +2100,25 @@ export declare interface RunnerUnit<TInput> {
2048
2100
  readonly input: TInput;
2049
2101
  }
2050
2102
 
2103
+ /**
2104
+ * Schedule one cancellable host operation behind an owned settlement signal.
2105
+ *
2106
+ * @remarks
2107
+ * The completion and failure paths each own an {@link AbortController}; their native composite is
2108
+ * linked to the optional caller signal before `start` can arm host work. Scheduler backends attach
2109
+ * only to that safe composite, so caller mutation of `addEventListener` or `removeEventListener`
2110
+ * cannot strand the operation. The first completion resolves, the first host failure rejects with
2111
+ * its exact value, and caller abort rejects with its exact linked reason. Caller abort and host
2112
+ * failure cancel an armed handle; synchronous settlement also cancels the handle immediately after
2113
+ * `start` returns it. Cancellation is secondary cleanup: if its closure throws, the already-winning
2114
+ * completion, exact host failure, or exact caller reason still settles without escape or replacement.
2115
+ *
2116
+ * @param start - Arm host work and return its cancellation closure
2117
+ * @param signal - Optional caller cancellation signal
2118
+ * @returns A promise settled exactly once by completion, host failure, or caller abort
2119
+ */
2120
+ export declare function scheduleHost(start: (complete: () => void, failure: (error: unknown) => void) => () => void, signal?: AbortSignal): Promise<void>;
2121
+
2051
2122
  /**
2052
2123
  * The safe cross-environment cooperative-yield default — a {@link SchedulerInterface}
2053
2124
  * built on `setTimeout` / `clearTimeout` alone, so it runs unchanged in both the
@@ -2064,11 +2135,10 @@ export declare interface RunnerUnit<TInput> {
2064
2135
  * regains control, so it would not actually let pending I/O, timers, or
2065
2136
  * rendering run — it only defers within the current task. A zero-delay timer is
2066
2137
  * the correct cross-environment "give the host a turn".
2067
- * - **Abort-aware.** A pending `yield` / `delay` rejects with `signal.reason` when
2068
- * the signal aborts (the standard `AbortSignal` convention). An already-aborted
2069
- * signal rejects immediately without arming a timer. Either settle path clears
2070
- * the timer and removes the abort listener no leaked timer, no leaked
2071
- * listener, and no double-settle.
2138
+ * - **Abort-aware.** A pending `yield` / `delay` rejects with `signal.reason` exactly.
2139
+ * {@link scheduleHost} links an owned settlement composite to the caller before arming
2140
+ * the timer, so pre-abort schedules nothing, caller signal method mutation is harmless,
2141
+ * cancellation clears the handle, and native first-settlement wins exactly once.
2072
2142
  * - **Priority is accepted but uniform.** `options.priority` is part of the
2073
2143
  * contract, but a `setTimeout`-based default cannot act on urgency, so it treats
2074
2144
  * every priority the same. Environment backends honour it.
@@ -2956,11 +3026,11 @@ export declare type UnitOutcome<TResult> = {
2956
3026
  * - **Construction.** Built from a {@link WorkflowSnapshot} (the unified input —
2957
3027
  * {@link import('./factories.js').createWorkflow} seeds an initial snapshot from a
2958
3028
  * {@link import('./types.js').WorkflowDefinition}, {@link import('./factories.js').restoreWorkflow}
2959
- * passes a persisted one). Each child {@link Phase} is wired to escalate to {@link #recompute}.
3029
+ * passes a persisted one). Each child {@link Phase} is wired to escalate to `#recompute`.
2960
3030
  * - **Derived status.** `status` is `#override` when forced, else
2961
3031
  * {@link deriveWorkflowStatus} over the live phases' statuses feeding `bail`. `failed` is
2962
3032
  * reachable ONLY under `bail: true` (a single failed task halts the workflow); under
2963
- * `bail: false` a failed phase folds into `completed`. {@link #recompute} diffs on each phase
3033
+ * `bail: false` a failed phase folds into `completed`. `#recompute` diffs on each phase
2964
3034
  * change; a CHANGE emits.
2965
3035
  * - **Override (AGENTS §10).** `skip` / `stop` FORCE the status; an executed task-free pending tree
2966
3036
  * may also be force-completed vacuously. The override is PERSISTED in the snapshot's own
@@ -3414,12 +3484,11 @@ export declare interface WorkflowInterface {
3414
3484
  * `functions` registry in) and stores it under `definition.id` — an already-present id
3415
3485
  * OVERWRITES (last write wins). `count` is the map size, `workflow(id)` looks one up,
3416
3486
  * `workflows()` lists them in insertion order.
3417
- * - **Durable open / save.** `open(id)` returns an already-registered workflow directly; on a
3418
- * registry MISS with a `store` set it rehydrates through {@link restoreWorkflow} (flowing the
3419
- * manager's `functions` registry in so the rehydrated tree is RUNNABLE), registers it, and
3420
- * returns it lenient (`undefined`) with no store or a store miss. `save(id)` persists a
3421
- * registered workflow's `snapshot()` to the `store` — lenient (`false`) with no store or an
3422
- * unknown id.
3487
+ * - **Durable open / save.** `open(id)` returns an already-registered workflow directly; same-id
3488
+ * misses share one hydration. A concurrent `add` wins, while `remove` / `clear` invalidate
3489
+ * earlier reads; wrong-key payloads reject with `RESTORE`. `save(id)` captures a registered
3490
+ * workflow's snapshot at invocation and serializes same-id writes without coupling other ids.
3491
+ * Both remain lenient without a store or registered id.
3423
3492
  * - **Removal.** `remove` drops one by id, or a batch (§9.2, array overload FIRST) — `true` when
3424
3493
  * any was removed. `clear` empties the registry.
3425
3494
  * - **No active pointer.** Unlike its `ConversationManager` / `WorkspaceManager` twins, there is
@@ -3469,10 +3538,17 @@ export declare class WorkflowManager implements WorkflowManagerInterface {
3469
3538
  * `workflows()` lists them in insertion order.
3470
3539
  * - **Durable open / save (the optional `store` seam).** When a {@link WorkflowStoreInterface}
3471
3540
  * is supplied (the `store` option), `open(id)` resolves an already-registered workflow
3472
- * directly (no store hit); on a registry MISS it HYDRATES one from `store.get(id)` through
3541
+ * directly (no store hit); same-id registry misses share one in-flight hydration. On a MISS
3542
+ * it HYDRATES one from `store.get(id)` through
3473
3543
  * {@link import('./factories.js').restoreWorkflow} — flowing this manager's `functions`
3474
- * registry in so the rehydrated tree is RUNNABLE — registers it, and returns it. `save(id)`
3475
- * PERSISTS a registered workflow's {@link WorkflowInterface.snapshot} to the store. Both are
3544
+ * registry in so the rehydrated tree is RUNNABLE — registers it, and returns it. Registry
3545
+ * mutation wins over an earlier pending hydration: `add` supplies the live result, while
3546
+ * `remove` (even for an absent id) and `clear` invalidate the earlier read. Missed and failed
3547
+ * reads leave no stale in-flight entry, and a payload whose own id differs from the requested
3548
+ * key rejects with `RESTORE` instead of registering under either id. `save(id)` captures a
3549
+ * registered workflow's {@link WorkflowInterface.snapshot} at invocation, then PERSISTS it.
3550
+ * Same-id writes run serially in invocation order; different ids remain independent, and an
3551
+ * earlier rejection reaches its caller without preventing a later queued write. Both are
3476
3552
  * LENIENT without a store — `open` resolves only registered ids, `save` is a no-op
3477
3553
  * (`false`) — never a throw. The EXACT analogue of
3478
3554
  * `ConversationManagerInterface.open` / `.save` and `WorkspaceManagerInterface.open` /
@@ -3486,7 +3562,7 @@ export declare class WorkflowManager implements WorkflowManagerInterface {
3486
3562
  *
3487
3563
  * @example
3488
3564
  * ```ts
3489
- * import { createWorkflowManager } from '@src/core'
3565
+ * import { createWorkflowManager } from '@orkestrel/workflow'
3490
3566
  *
3491
3567
  * const manager = createWorkflowManager({
3492
3568
  * functions: { compile: async (controller) => `built ${controller.task.id}` },
@@ -3520,11 +3596,17 @@ export declare interface WorkflowManagerInterface {
3520
3596
  *
3521
3597
  * @remarks
3522
3598
  * - If `id` is ALREADY registered, it is returned directly — no store hit.
3523
- * - Else if a `store` is set, `store.get(id)` is awaited; on a HIT the snapshot is
3599
+ * - Same-id registry misses share one in-flight `store.get(id)` and resolve to the same live
3600
+ * object. On a HIT the snapshot is
3524
3601
  * rehydrated into a fresh {@link WorkflowInterface} via
3525
3602
  * {@link import('./factories.js').restoreWorkflow}, flowing this manager's `functions`
3526
3603
  * registry in (so the rehydrated tree carries real resolved `handler`s and can RESUME
3527
- * real work), registers it, and returns it.
3604
+ * real work), registers it, and returns it. A payload whose own id differs from `id` rejects
3605
+ * with a normalized `RESTORE` error carrying the requested and payload ids.
3606
+ * - Registry mutation after the store read starts has precedence: `add(definition)` for the
3607
+ * same id wins and becomes every pending caller's result; `remove(id)` invalidates that read
3608
+ * even when the id was absent; `clear()` invalidates every earlier read. A miss or rejection
3609
+ * clears the in-flight entry so a later call retries.
3528
3610
  * - Else (no store, or a store MISS) ⇒ `undefined` (lenient — no throw).
3529
3611
  *
3530
3612
  * @param id - The workflow id to open
@@ -3536,9 +3618,10 @@ export declare interface WorkflowManagerInterface {
3536
3618
  * {@link WorkflowStoreInterface} (`store`).
3537
3619
  *
3538
3620
  * @remarks
3539
- * Lenient: when a `store` is set AND `id` is registered, `store.set(workflow.snapshot())`
3540
- * is awaited and `true` is returned; otherwise (no store, OR an unknown id) it is a NO-OP
3541
- * returning `false` never a throw.
3621
+ * When a `store` is set AND `id` is registered, the snapshot is captured synchronously at
3622
+ * invocation. Same-id `store.set` calls are serialized in invocation order; different ids are
3623
+ * independent. A rejected write reaches that caller unchanged but does not poison a later
3624
+ * queued write. Otherwise (no store, OR an unknown id) it is a NO-OP returning `false`.
3542
3625
  *
3543
3626
  * @param id - The id of the registered workflow to persist
3544
3627
  * @returns `true` when the snapshot was persisted; `false` when no store / unknown id
@@ -3716,8 +3799,9 @@ export declare interface WorkflowResult {
3716
3799
  * - **Composes, never re-implements.** Per-phase bounded concurrency is one
3717
3800
  * {@link createRunner} per phase (the substrate {@link RunnerInterface} over the workers
3718
3801
  * `Queue`); `bail` maps onto that Runner's fail-fast vs settle-all; the run-level abort /
3719
- * timeout / budget / entity `signal` fold through {@link createAbort} / {@link createTimeout} +
3720
- * `AbortSignal.any` (exactly as the agent runtime folds its bounds); pacing is the shipped
3802
+ * timeout / budget / entity `signal` fold through the `@orkestrel/abort` signal contract,
3803
+ * {@link createTimeout}, and `AbortSignal.any` (exactly as the agent runtime folds its bounds);
3804
+ * pacing is the shipped
3721
3805
  * {@link SchedulerInterface}. The runner writes ZERO concurrency / retry / abort logic of
3722
3806
  * its own — it only sequences phases, dispatches a task's own handler, and drives the live
3723
3807
  * entity. The workflow layer owns per-task deadlines because timeout settlement must
@@ -4115,8 +4199,8 @@ export declare type WorkflowStatus = LifecycleStatus;
4115
4199
  /**
4116
4200
  * The durable persistence seam for a {@link WorkflowSnapshot} — three async primitives
4117
4201
  * (`get` / `set` / `delete`) keyed by a workflow id, the snapshot analogue of
4118
- * the server package's `SessionStoreInterface` (and the
4119
- * {@link QueueStoreInterface} driver-swap pattern).
4202
+ * the server package's `SessionStoreInterface` (and the `@orkestrel/queue`
4203
+ * `QueueStoreInterface` driver-swap pattern).
4120
4204
  *
4121
4205
  * @remarks
4122
4206
  * The store persists the W-a {@link WorkflowSnapshot} — the COMPLETE, self-contained,
@@ -4129,7 +4213,7 @@ export declare type WorkflowStatus = LifecycleStatus;
4129
4213
  *
4130
4214
  * Every primitive is async (a `Promise`), so a durable backend (a database round-trip) fits the
4131
4215
  * same shape as the memory one. The snapshot carries its OWN id, so `set` takes no separate id
4132
- * param (mirroring {@link QueueStoreInterface.save} / the server package's
4216
+ * param (mirroring `QueueStoreInterface.save` from `@orkestrel/queue` / the server package's
4133
4217
  * `SessionStoreInterface.set`, which key off the value's own
4134
4218
  * `id`). UNLIKE a session store there is NO idle-TTL / eviction — a persisted workflow run-state
4135
4219
  * lives until an explicit `delete`, never silently expiring (it is durable orchestration state,
@@ -4139,6 +4223,8 @@ export declare type WorkflowStatus = LifecycleStatus;
4139
4223
  export declare interface WorkflowStoreInterface {
4140
4224
  /**
4141
4225
  * Resolve the persisted snapshot for `id`, or `undefined` if none is stored.
4226
+ * A present payload whose own `id` differs from the requested storage key is corrupt and
4227
+ * rejects with a normalized `RESTORE` error carrying both ids.
4142
4228
  *
4143
4229
  * @param id - The workflow id to resolve (a {@link WorkflowSnapshot.id})
4144
4230
  * @returns The persisted snapshot, or `undefined` if absent
@@ -4146,7 +4232,7 @@ export declare interface WorkflowStoreInterface {
4146
4232
  get(id: string): Promise<WorkflowSnapshot | undefined>;
4147
4233
  /**
4148
4234
  * Insert or replace a snapshot under its own `snapshot.id` (no separate id param —
4149
- * mirroring {@link QueueStoreInterface.save}).
4235
+ * mirroring `QueueStoreInterface.save` from `@orkestrel/queue`).
4150
4236
  *
4151
4237
  * @param snapshot - The snapshot to store (keyed by its `id`)
4152
4238
  */