@voltro/runtime 0.70.0 → 0.71.1

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.
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ import { CaughtUpVerdict } from '@voltro/database';
7
7
  import { ChangeEvent } from '@voltro/database';
8
8
  import { classifyConstraintViolation } from '@voltro/database';
9
9
  import { clearRetentions } from '@voltro/database';
10
+ import { ColumnBuilder } from '@voltro/database';
10
11
  import { ConnectionCredential } from '@voltro/protocol';
11
12
  import { ConnectionInfo } from '@voltro/protocol';
12
13
  import { ConnectionInfoMiddleware } from '@voltro/protocol';
@@ -35,6 +36,7 @@ import { extractDbCause } from '@voltro/database';
35
36
  import { Fiber } from 'effect';
36
37
  import { FieldBinding } from '@voltro/database';
37
38
  import { FieldCipher } from '@voltro/database';
39
+ import { FieldDefinitions } from '@voltro/database';
38
40
  import * as http from 'node:http';
39
41
  import { HttpClient } from '@effect/platform';
40
42
  import { HttpRequestInterceptor } from '@voltro/protocol';
@@ -62,6 +64,7 @@ import { MutationIdempotencyScope } from '@voltro/protocol';
62
64
  import { NoRowFound } from '@voltro/protocol';
63
65
  import { PendingApproval } from '@voltro/protocol';
64
66
  import { PluginHttpRoute } from '@voltro/protocol';
67
+ import { PluginHttpRouteByteStream } from '@voltro/protocol';
65
68
  import { PluginRefOrphanPolicy } from '@voltro/database';
66
69
  import { Predicate } from '@voltro/database';
67
70
  import { PublicApiSpec } from '@voltro/protocol';
@@ -99,6 +102,7 @@ import { SubjectService } from '@voltro/protocol';
99
102
  import { SubscriptionEvent } from '@voltro/protocol';
100
103
  import { sweepRetention } from '@voltro/database';
101
104
  import { SyncLogger } from '@voltro/logger';
105
+ import { Table } from '@voltro/database';
102
106
  import { TableLike } from '@voltro/database';
103
107
  import { TableName } from '@voltro/protocol';
104
108
  import { TableValidationFailed } from '@voltro/protocol';
@@ -113,8 +117,13 @@ import { ValidationError } from '@voltro/protocol';
113
117
  import { ValidationErrors } from '@voltro/protocol';
114
118
  import { VersionConflict } from '@voltro/database';
115
119
  import { WebSocketGatewayRoute } from '@voltro/protocol';
120
+ import { WorkflowControlRequest } from '@voltro/workflow';
121
+ import { WorkflowControlResult as WorkflowControlResult_2 } from '@voltro/protocol';
116
122
  import { WorkflowParentClosePolicy } from '@voltro/protocol';
123
+ import { WorkflowQueuedCancellationResult } from '@voltro/protocol';
117
124
  import { WorkflowRunHandle } from '@voltro/protocol';
125
+ import { WorkflowRunSnapshot as WorkflowRunSnapshot_2 } from '@voltro/protocol';
126
+ import { WorkflowStartResolution } from '@voltro/workflow';
118
127
  import { WorkflowUpdateResult } from '@voltro/protocol';
119
128
 
120
129
  /** Typed denial — declare on an rpc `error:` union so a refusal surfaces to the
@@ -1067,6 +1076,14 @@ export declare interface AppAccess {
1067
1076
  }
1068
1077
 
1069
1078
  export declare interface AppContext {
1079
+ /** Run database work with a freshly transaction-bound context. Reuses an
1080
+ * existing mutation transaction (no savepoint). The callback may be retried;
1081
+ * keep external I/O out of it and use its store/workflows, not captured ones.
1082
+ * A system context may select a tenant before opening the transaction;
1083
+ * nested transactions cannot change tenants. */
1084
+ readonly transaction: <A>(work: (context: AppContext) => Promise<A>, options?: {
1085
+ readonly tenantId?: string;
1086
+ }) => Promise<A>;
1070
1087
  readonly store: FluentStore;
1071
1088
  /**
1072
1089
  * The same store, scoped to ONE tenant.
@@ -2423,6 +2440,33 @@ export declare interface CoordinatedScheduleDeps {
2423
2440
  * opt a task out of backing off entirely — which is the right call only for a
2424
2441
  * task whose work cannot announce itself.
2425
2442
  */
2443
+ /**
2444
+ * Drive this task from OUTSIDE instead of from a timer — see
2445
+ * `externalizedTasks.ts`.
2446
+ *
2447
+ * When set, the task arms nothing and instead registers itself so the
2448
+ * external trigger endpoint can run one tick on demand. A cadence no platform
2449
+ * scheduler can express is REFUSED, loudly, and the timer is armed as usual:
2450
+ * a task silently left ticking while the deployment believes it was
2451
+ * externalised is worse than one that was never externalised.
2452
+ */
2453
+ readonly externalize?: boolean;
2454
+ /**
2455
+ * The gate an EXTERNALLY driven tick claims through. Defaults to
2456
+ * `coordinator`, which is right only when that is store-backed.
2457
+ *
2458
+ * It is a separate field because the two gates answer different questions.
2459
+ * `coordinator` gates a TIMER tick, and `singleCoordinator` answers `true`
2460
+ * unconditionally for a very good reason: one process's timer cannot race
2461
+ * itself, so there is nothing to gate and a claim row per tick would be pure
2462
+ * cost — at 250 ms, four rows a second, forever. An externally driven tick is
2463
+ * the opposite case: the duplicate does not come from a second replica, it
2464
+ * comes from the same platform delivering once more, and a one-pod deployment
2465
+ * is fully exposed to it. Sharing one field would have meant either a dedupe
2466
+ * that silently does nothing on the simplest deployment, or a write storm on
2467
+ * every fast task.
2468
+ */
2469
+ readonly externalClaim?: Coordinator;
2426
2470
  readonly maxIdleIntervalMs?: number;
2427
2471
  /**
2428
2472
  * **Stop ticking entirely** on an idle tick with no known deadline, and come
@@ -2771,6 +2815,18 @@ export declare const countRunningWorkflows: (store: DataStore) => Promise<number
2771
2815
 
2772
2816
  export declare const crdtCompactMaxBytes: () => number;
2773
2817
 
2818
+ /**
2819
+ * The cron expression that fires `intervalMs` apart, or `undefined` when no
2820
+ * five-field expression does.
2821
+ *
2822
+ * Cron is not an interval language: it fires at MATCHING instants, so only
2823
+ * cadences that tile the hour (or the day) evenly can be expressed. Seven
2824
+ * minutes cannot — `*\/7 * * * *` fires at :00 :07 … :56 and then again at :00,
2825
+ * four minutes later. Refusing is the honest answer; emitting it would produce a
2826
+ * schedule that is subtly wrong once an hour, forever.
2827
+ */
2828
+ export declare const cronForInterval: (intervalMs: number) => string | undefined;
2829
+
2774
2830
  /**
2775
2831
  * Every firing instant of `def` in `(from, to]`, oldest first, bounded by
2776
2832
  * `cap + 1` entries.
@@ -3077,6 +3133,28 @@ export declare const DEFAULT_CRDT_COMPACT_MAX_BYTES: number;
3077
3133
  */
3078
3134
  export declare const DEFAULT_DELIVERY_CONCURRENCY = 8;
3079
3135
 
3136
+ /** How often unfired occurrences are reconciled. Coarse on purpose: it exists
3137
+ * to make silence visible, not to be a second clock. */
3138
+ export declare const DEFAULT_EXTERNAL_RECONCILE_MS: number;
3139
+
3140
+ /**
3141
+ * How much later than its own occurrence an external trigger may arrive and
3142
+ * still be attributed to it.
3143
+ *
3144
+ * The number exists because a platform scheduler is not punctual, and that is
3145
+ * not a defect in it: a k8s CronJob waits on the controller, on scheduling, on
3146
+ * an image pull, and a Job with `backoffLimit` retries minutes later. EVERY one
3147
+ * of those arrivals means the same occurrence, and the exactly-once property
3148
+ * depends on all of them snapping to one `scheduledAt`.
3149
+ *
3150
+ * It is bounded on the other side by the schedule's own period (see
3151
+ * `triggerExternal`), so an arrival can never be attributed to an occurrence
3152
+ * older than the previous one. That bound is the half that matters for safety:
3153
+ * a CronJob carrying a cron expression the app no longer declares must be
3154
+ * REJECTED, not silently accepted as a very late firing of something else.
3155
+ */
3156
+ export declare const DEFAULT_EXTERNAL_TRIGGER_SKEW_MS: number;
3157
+
3080
3158
  /** 180 days + subdomains. Deliberately NOT `preload`: preload is effectively
3081
3159
  * irreversible for a domain, so it must be a deployment decision, never a
3082
3160
  * framework default. */
@@ -3328,7 +3406,7 @@ export declare const defineExperiment: (input: ExperimentDefinitionInput) => Exp
3328
3406
  * delivery is at-least-once, so a process that dies between "the remote
3329
3407
  * accepted it" and "we marked it delivered" will retry.
3330
3408
  */
3331
- export declare const defineOutboxHandler: (definition: OutboxHandlerDefinition) => OutboxHandlerDefinition;
3409
+ export declare const defineOutboxHandler: <R = never>(definition: OutboxHandlerDefinition<R>) => OutboxHandlerDefinition<R>;
3332
3410
 
3333
3411
  /** Declare a reaction. Validates that the MANDATORY guard is present — an
3334
3412
  * ungated reaction is a spend-storm footgun, so this fails LOUD at boot. */
@@ -3624,9 +3702,13 @@ export declare interface DispatcherDependencies {
3624
3702
  * invisible to a waker looking under the other's key. */
3625
3703
  export declare const DORMANCY_WAKEUP_TENANT = "default";
3626
3704
 
3627
- export declare interface DrainDeps {
3705
+ export declare interface DrainDeps<R = never> {
3628
3706
  readonly store: Pick<DataStore, 'query' | 'update' | 'updateMany' | 'insert' | 'delete'>;
3629
- readonly handlers: ReadonlyMap<string, OutboxHandlerDefinition>;
3707
+ readonly handlers: ReadonlyMap<string, OutboxHandlerDefinition<R>>;
3708
+ /** Execute with the application's services. Required by drainOutbox when R
3709
+ * is not never. A service-free standalone drain uses Effect.runPromise.
3710
+ * Row identity is metadata, not a restored authorization decision. */
3711
+ readonly runEffect?: (effect: Effect.Effect<unknown, unknown, R>, ctx: OutboxHandlerContext) => Promise<unknown>;
3630
3712
  readonly now?: () => Date;
3631
3713
  /** Max rows per drain pass. */
3632
3714
  readonly batchSize?: number;
@@ -3689,7 +3771,9 @@ export declare const drainForShutdown: (options: {
3689
3771
  }) => void;
3690
3772
  }) => void;
3691
3773
 
3692
- export declare const drainOutbox: (deps: DrainDeps) => Promise<DrainResult>;
3774
+ export declare const drainOutbox: <R = never>(deps: DrainDeps<R> & ([R] extends [never] ? unknown : {
3775
+ readonly runEffect: NonNullable<DrainDeps<R>["runEffect"]>;
3776
+ })) => Promise<DrainResult>;
3693
3777
 
3694
3778
  export declare interface DrainResult {
3695
3779
  readonly delivered: number;
@@ -4156,7 +4240,7 @@ export declare interface EventPublishContext {
4156
4240
  * A display reacting to a game start the database rolled back is not a race
4157
4241
  * that is unlikely, it is one that happens on every constraint violation.
4158
4242
  */
4159
- readonly deferToCommit?: ((deliver: () => void) => void) | undefined;
4243
+ readonly deferToCommit?: ((deliver: () => Promise<void>) => void) | undefined;
4160
4244
  /**
4161
4245
  * The DURABLE audience — the workflow-trigger path.
4162
4246
  *
@@ -4165,11 +4249,11 @@ export declare interface EventPublishContext {
4165
4249
  * starts whatever workflows are triggered by it. Absent when the app declares
4166
4250
  * no triggers, so an app that only fans out to clients pays nothing.
4167
4251
  *
4168
- * Deliberately fire-and-forget from `publish`'s point of view: a trigger that
4169
- * fails must not fail the mutation that published, for the same reason a
4170
- * broker outage does not. The trigger's own delivery row records the failure.
4252
+ * Completion acknowledges this audience's write, not completion of triggered
4253
+ * workflows or HTTP delivery. A persistence failure rejects publication. In a
4254
+ * mutation the receipt is deferred; the post-commit drain must await delivery.
4171
4255
  */
4172
- readonly onPublished?: ((eventName: string, payload: unknown, descriptor: unknown) => void) | undefined;
4256
+ readonly onPublished?: ((eventName: string, payload: unknown, descriptor: unknown) => void | Promise<void>) | undefined;
4173
4257
  }
4174
4258
 
4175
4259
  export declare interface EventPublisher {
@@ -4224,6 +4308,7 @@ export declare interface EventsFacadeOptions {
4224
4308
  readonly subject?: Subject;
4225
4309
  readonly traceId?: string;
4226
4310
  readonly parentExecutionId?: string | null;
4311
+ readonly parentGeneration?: string | null;
4227
4312
  readonly startWorkflow: (workflowName: string, payload: unknown, callerContext: WorkflowCallerContext) => Promise<WorkflowRunHandle>;
4228
4313
  readonly emit?: (channel: 'workflowDomainEvents' | 'workflowEventDeliveries', event: {
4229
4314
  readonly op: 'insert' | 'update';
@@ -4774,11 +4859,91 @@ export declare interface ExperimentVariantSpec {
4774
4859
  readonly weight?: number;
4775
4860
  }
4776
4861
 
4862
+ /**
4863
+ * How far AHEAD of now an external trigger's stated instant may be.
4864
+ *
4865
+ * The skew budget is one-directional — it forgives a LATE arrival, which is the
4866
+ * only kind a platform scheduler produces. Nothing bounded an EARLY one, and
4867
+ * the gap was reachable: a caller stating `at` a month out had that future
4868
+ * occurrence snapped, claimed and run, and the real firing was then answered as
4869
+ * a duplicate and its work silently never happened. One wrong value in a
4870
+ * manifest and a schedule loses a firing with nothing in the ledger saying so.
4871
+ *
4872
+ * A minute covers the clock drift between a job pod and the app; beyond that it
4873
+ * is not drift, it is a caller asserting a time it cannot know.
4874
+ */
4875
+ export declare const EXTERNAL_TRIGGER_AHEAD_TOLERANCE_MS = 60000;
4876
+
4877
+ /** Why a task cannot be externalised, in words an operator can act on. */
4878
+ export declare const externalizationRefusal: (name: string, intervalMs: number) => string | undefined;
4879
+
4880
+ export declare interface ExternalizedTask {
4881
+ readonly name: string;
4882
+ readonly intervalMs: number;
4883
+ /** The five-field expression a manifest for this task carries. */
4884
+ readonly cron: string;
4885
+ /** Run one tick now. Claims the same bucket a timer tick would, so a
4886
+ * redelivery inside one interval is a no-op rather than a second run. */
4887
+ readonly run: () => Promise<'ran' | 'deduplicated'>;
4888
+ }
4889
+
4890
+ export declare const externalizedTask: (name: string) => ExternalizedTask | undefined;
4891
+
4892
+ export declare const externalizedTaskNames: () => ReadonlyArray<string>;
4893
+
4777
4894
  /** Build an Effect `ExternalSpan` parent from a `traceparent` header so
4778
4895
  * inbound HTTP work continues the caller's trace. Returns undefined
4779
4896
  * when the header is missing/invalid (no parent → a fresh root span). */
4780
4897
  export declare const externalSpanFromTraceparent: (header: string | undefined | null) => Tracer.ExternalSpan | undefined;
4781
4898
 
4899
+ /** What an external trigger did. Every arm is a deliberate answer to the
4900
+ * caller — a job pod whose exit code an operator's alerting reads. */
4901
+ export declare type ExternalTriggerOutcome =
4902
+ /** The occurrence was claimed by THIS request; the handler is now running.
4903
+ * `runId` is `undefined` only when the ledger write itself failed — the run
4904
+ * still happens (see `runQueued`'s note on the same trade). */
4905
+ {
4906
+ readonly kind: 'accepted';
4907
+ readonly runId: string | undefined;
4908
+ readonly scheduledAt: Date;
4909
+ }
4910
+ /** Someone already claimed this occurrence — a retry, a second delivery, or
4911
+ * another replica's CronJob. NOT an error: it is the guarantee working. */
4912
+ | {
4913
+ readonly kind: 'duplicate';
4914
+ readonly runId: string | undefined;
4915
+ readonly scheduledAt: Date;
4916
+ }
4917
+ /** The arrival snapped to no occurrence within the skew budget. Almost always
4918
+ * an out-of-date manifest: the cron expression in the cluster is no longer
4919
+ * the one in the code. */
4920
+ | {
4921
+ readonly kind: 'noOccurrence';
4922
+ readonly arrivalAt: Date;
4923
+ readonly skewMs: number;
4924
+ readonly cron: string;
4925
+ readonly timezone: string;
4926
+ }
4927
+ /** The stated instant is further ahead than clock drift explains. Refused
4928
+ * rather than honoured: claiming an occurrence before it is due turns the
4929
+ * real firing into a duplicate and loses its work. */
4930
+ | {
4931
+ readonly kind: 'ahead';
4932
+ readonly arrivalAt: Date;
4933
+ readonly now: Date;
4934
+ readonly toleranceMs: number;
4935
+ } | {
4936
+ readonly kind: 'unknownSchedule';
4937
+ readonly name: string;
4938
+ }
4939
+ /** The schedule is driven by the in-app timer. Firing it from outside as well
4940
+ * would run it twice, so this is refused rather than honoured. */
4941
+ | {
4942
+ readonly kind: 'notExternal';
4943
+ readonly name: string;
4944
+ readonly trigger: ScheduleTrigger;
4945
+ };
4946
+
4782
4947
  export { extractDbCause }
4783
4948
 
4784
4949
  export declare interface FieldChange {
@@ -5113,7 +5278,7 @@ export declare interface GroupState {
5113
5278
  }
5114
5279
 
5115
5280
  /** What a user handler may return. */
5116
- export declare type HandlerBody = void | Promise<unknown> | Effect.Effect<unknown, unknown, never>;
5281
+ export declare type HandlerBody<R = never> = void | Promise<unknown> | Effect.Effect<unknown, unknown, R>;
5117
5282
 
5118
5283
  /**
5119
5284
  * Deterministic hash of a string into the unit interval `[0, 1)` — FNV-1a
@@ -5274,6 +5439,9 @@ export declare class InMemoryDataStore implements DataStore {
5274
5439
  insertIgnore(table: string, row: Row, options: {
5275
5440
  conflictColumns: ReadonlyArray<string>;
5276
5441
  }): Promise<Row>;
5442
+ insertIgnoreWithOutcome(table: string, row: Row, options: {
5443
+ conflictColumns: ReadonlyArray<string>;
5444
+ }): Promise<InsertIgnoreResult>;
5277
5445
  transactional<T>(work: (tx: DataStore) => Promise<T>): Promise<T>;
5278
5446
  onChange(listener: (event: ChangeEvent) => void): () => void;
5279
5447
  /** Cross-instance reactivity seam — emit an externally-sourced event to
@@ -5304,14 +5472,18 @@ export declare interface InspectRoute {
5304
5472
  * rpc server reads it (UTF-8) and passes it as the 4th arg; GET
5305
5473
  * callers pass undefined. Body parsing — JSON, validation —
5306
5474
  * happens in the handler. */
5307
- readonly handle: (url: string, headers: Readonly<Record<string, string>>, method: string, body?: string) => {
5475
+ readonly handle: (url: string, headers: Readonly<Record<string, string>>, method: string, body?: string,
5476
+ /** Aborted when the HTTP request disconnects or the server shuts down. */
5477
+ signal?: AbortSignal) => {
5308
5478
  readonly status: number;
5309
5479
  readonly contentType: string;
5480
+ readonly byteStream?: PluginHttpRouteByteStream;
5310
5481
  readonly body: string;
5311
5482
  readonly headers?: Readonly<Record<string, string>>;
5312
5483
  } | Promise<{
5313
5484
  readonly status: number;
5314
5485
  readonly contentType: string;
5486
+ readonly byteStream?: PluginHttpRouteByteStream;
5315
5487
  readonly body: string;
5316
5488
  readonly headers?: Readonly<Record<string, string>>;
5317
5489
  } | null> | null;
@@ -5325,6 +5497,8 @@ export { InspectStore }
5325
5497
  * stream. The cli's dev.ts wires this with its inspect bus.
5326
5498
  */
5327
5499
  export declare interface InspectStream {
5500
+ readonly replicaId?: string;
5501
+ readonly select?: (url: string, headers: Readonly<Record<string, string>>) => Stream.Stream<Uint8Array, unknown> | undefined;
5328
5502
  /** Same shape as the inspect auth resolver — returns ok or reason. */
5329
5503
  readonly authorize: (headers: Readonly<Record<string, string>>) => {
5330
5504
  readonly ok: true;
@@ -5949,7 +6123,13 @@ export declare const makeConnectionState: (connection: RpcConnection, afterCommi
5949
6123
  * handle is tracked by the caller (the CLI) so `onDeactivate` can stop
5950
6124
  * every task a plugin armed.
5951
6125
  */
5952
- export declare const makeCoordinatedScheduler: (deps: CoordinatedScheduleDeps) => PluginScheduleCoordinated;
6126
+ export declare const makeCoordinatedScheduler: (deps: CoordinatedScheduleDeps & {
6127
+ /** Decides, per task, whether the platform drives it instead of a timer.
6128
+ * Bound here rather than passed by each caller: every framework and plugin
6129
+ * periodic task goes through this factory, so this is the one place the
6130
+ * policy can be applied without a caller being forgotten. */
6131
+ readonly externalizeTask?: (name: string) => boolean;
6132
+ }) => PluginScheduleCoordinated;
5953
6133
 
5954
6134
  /**
5955
6135
  * Build a request-scoped loader. One instance per AppContext — see the module
@@ -6090,8 +6270,6 @@ export declare const makeOriginGuardExemption: (input: {
6090
6270
 
6091
6271
  export declare const makeOutboxFacade: (deps: OutboxFacadeDeps) => OutboxFacade;
6092
6272
 
6093
- export declare const makePostCommitWorkflowFacade: (base: WorkflowsAppContext, afterCommit: (work: () => Promise<unknown>) => void) => WorkflowsAppContext;
6094
-
6095
6273
  /**
6096
6274
  * A `WakeAdapter` that brings a single-node app process up via an
6097
6275
  * `AppSupervisor`. `wake` is idempotent — `start()` no-ops when the app
@@ -6187,6 +6365,8 @@ export declare const makeSubmitTokenExecutor: (deps: ConnectionBuiltinDeps) => (
6187
6365
 
6188
6366
  export declare const makeSubscriptionOutbox: <T = ReadonlyArray<Row>>(options: SubscriptionBackpressureOptions) => SubscriptionOutbox<T>;
6189
6367
 
6368
+ export declare const makeTransactionalWorkflowFacade: (base: WorkflowsAppContext, enqueueStart: WorkflowStartEnqueue) => WorkflowsAppContext;
6369
+
6190
6370
  /**
6191
6371
  * The factory the serve entrypoints inject into `makeMutationRunner`'s
6192
6372
  * `undoCapture` dep. Collects changes from the wrapped tx; `persist` writes ONE
@@ -6222,9 +6402,9 @@ export declare const makeWorkflowFacadeWithCallerContext: (base: WorkflowsAppCon
6222
6402
 
6223
6403
  export declare const makeWorkflowHandlers: (workflows: ReadonlyArray<DiscoveredWorkflowLike>, facade?: WorkflowsAppContext) => Record<string, (input: unknown) => Effect.Effect<WorkflowRunHandle, unknown, unknown>>;
6224
6404
 
6225
- export declare const makeWorkflowLayers: <Context>(workflows: ReadonlyArray<DiscoveredWorkflowLike<Context>>, options: WorkflowLayerOptions<Context>, wrapExecute?: (workflow: DiscoveredWorkflowLike<Context>, execute: (payload: never, executionId: string) => Effect.Effect<unknown, unknown, unknown>, callerContext: ResolvedWorkflowCallerContext) => (payload: never, executionId: string) => Effect.Effect<unknown, unknown, unknown>) => ReadonlyArray<Layer.Layer<unknown, unknown, unknown>>;
6405
+ export declare const makeWorkflowLayers: <Context>(workflows: ReadonlyArray<DiscoveredWorkflowLike<Context>>, options: WorkflowLayerOptions<Context>, wrapExecute?: (workflow: DiscoveredWorkflowLike<Context>, execute: (payload: never, executionId: string, generation?: string) => Effect.Effect<unknown, unknown, unknown>, callerContext: ResolvedWorkflowCallerContext) => (payload: never, executionId: string) => Effect.Effect<unknown, unknown, unknown>) => ReadonlyArray<Layer.Layer<unknown, unknown, unknown>>;
6226
6406
 
6227
- export declare const makeWorkflowRunHandle: (workflowName: string, executionId: string) => WorkflowRunHandle;
6407
+ export declare const makeWorkflowRunHandle: (workflowName: string, executionId: string, status: "running" | "succeeded") => WorkflowRunHandle;
6228
6408
 
6229
6409
  /**
6230
6410
  * Build the shared WORKFLOW START runner — the third member of the family,
@@ -6500,6 +6680,9 @@ export declare type MetricSampleType = 'counter' | 'gauge' | 'histogram' | 'summ
6500
6680
  * into the OTel MeterProvider when a `metricReader` is configured. */
6501
6681
  export declare type MetricsMode = 'off' | 'console' | 'otlp' | 'memory';
6502
6682
 
6683
+ /** The lowest cadence any platform scheduler can express. */
6684
+ export declare const MIN_EXTERNALIZABLE_INTERVAL_MS = 60000;
6685
+
6503
6686
  /**
6504
6687
  * Ordering token for one mirrored write.
6505
6688
  *
@@ -6531,6 +6714,10 @@ export declare const missingRowError: (info: NoRowFoundInfo, onMissing: OnMissin
6531
6714
 
6532
6715
  export declare interface MutationLifecycle {
6533
6716
  readonly afterCommit: (work: () => Promise<unknown>) => void;
6717
+ /** Already selected physical, namespace-bound nontransactional store.
6718
+ * Required by contexts delivering durable events after commit; never resolve
6719
+ * placement again or reuse the transaction for that delivery. */
6720
+ readonly committedStore?: unknown;
6534
6721
  }
6535
6722
 
6536
6723
  export declare interface MutationLike {
@@ -6568,6 +6755,9 @@ export declare interface MutationOutputCodec<Output> {
6568
6755
 
6569
6756
  export declare interface MutationRunnerDeps {
6570
6757
  readonly store: TransactionalStore;
6758
+ /** Select and provision the request's physical store before opening its
6759
+ * transaction. Resolved once, including across retries and post-commit folds. */
6760
+ readonly resolveStore?: (request: ServeRequestContext) => Promise<TransactionalStore> | TransactionalStore;
6571
6761
  /**
6572
6762
  * How many times a mutation's transaction runs before a replayable failure
6573
6763
  * (contention, a transaction-read conflict) surfaces. Resolved by the CLI
@@ -7109,6 +7299,11 @@ export declare interface OutboxFacadeDeps {
7109
7299
  }
7110
7300
 
7111
7301
  export declare interface OutboxHandlerContext {
7302
+ /** Stable identity of this delivery intent, preserved across retries. */
7303
+ readonly outboxId: string;
7304
+ /** Caller-supplied enqueue key, or null. Unlike outboxId this can be reused
7305
+ * for a distinct intent after an earlier intent has been delivered. */
7306
+ readonly idempotencyKey: string | null;
7112
7307
  readonly payload: Record<string, unknown>;
7113
7308
  readonly attempt: number;
7114
7309
  readonly subjectId: string | null;
@@ -7116,9 +7311,9 @@ export declare interface OutboxHandlerContext {
7116
7311
  readonly traceId: string | null;
7117
7312
  }
7118
7313
 
7119
- export declare interface OutboxHandlerDefinition {
7314
+ export declare interface OutboxHandlerDefinition<R = never> {
7120
7315
  readonly effect: string;
7121
- readonly handler: (ctx: OutboxHandlerContext) => Promise<unknown>;
7316
+ readonly handler: (ctx: OutboxHandlerContext) => Promise<unknown> | Effect.Effect<unknown, unknown, R>;
7122
7317
  /** Give up after this many attempts, then dead-letter. Default 8. */
7123
7318
  readonly maxAttempts?: number;
7124
7319
  }
@@ -7784,6 +7979,13 @@ export declare interface ReadClassification {
7784
7979
  readonly restricting: ReadonlySet<string>;
7785
7980
  }
7786
7981
 
7982
+ /** First-or-null for a boot DataStore or a query-only tuple source. It invokes
7983
+ * the supplied query path, preserving that path's filters and codecs. A raw
7984
+ * driver remains raw; this helper does not add request scoping. */
7985
+ export declare const readMaybeOne: <R = Row>(store: Pick<DataStore, "query">, query: QueryDescriptor<R> | {
7986
+ readonly descriptor: QueryDescriptor<R>;
7987
+ }) => Promise<R | null>;
7988
+
7787
7989
  /**
7788
7990
  * The ceiling, tunable per deployment via `VOLTRO_POLL_CEILING_MS`.
7789
7991
  *
@@ -7912,6 +8114,10 @@ export declare const recordEventPublished: (event: string) => void;
7912
8114
  /** Subscriber attached (+1) or detached (-1). */
7913
8115
  export declare const recordEventSubscribers: (event: string, delta: number) => void;
7914
8116
 
8117
+ export declare const recordExternalMissed: (name: string, count?: number) => void;
8118
+
8119
+ export declare const recordExternalTrigger: (name: string, outcome: "accepted" | "duplicate" | "noOccurrence" | "ahead" | "notExternal" | "unknownSchedule") => void;
8120
+
7915
8121
  /**
7916
8122
  * Record one emitted event into the identity's ring. Snapshots RESET the ring
7917
8123
  * (the snapshot's revision becomes the new replay base — a client holding an
@@ -7970,6 +8176,26 @@ export declare const recordReads: <A>(procedure: string, fn: () => Promise<A>, d
7970
8176
  */
7971
8177
  export declare const recordSample: (s: RecordedSample) => void;
7972
8178
 
8179
+ export declare const recordScheduleQueued: (name: string, delta: number) => void;
8180
+
8181
+ /**
8182
+ * A firing whose replica stopped reporting, and what became of it.
8183
+ *
8184
+ * `adopted` — the firing behind it took it over and ran it. Expected on a
8185
+ * rolling deploy; a deployment seeing it constantly is losing replicas mid-wait,
8186
+ * which is a different problem from a queue that is merely behind.
8187
+ *
8188
+ * `abandoned` — it was silent past the adoption bound, so it was recorded
8189
+ * `missed` and its work will NOT run. That is the one an alert should watch: a
8190
+ * `queue` schedule is chosen precisely because every firing matters.
8191
+ */
8192
+ export declare const recordScheduleQueueRecovery: (name: string, outcome: "adopted" | "abandoned") => void;
8193
+
8194
+ /** How long a firing sat in the line before it started. The lateness a `queue`
8195
+ * schedule accumulates is invisible in the duration histogram — that one starts
8196
+ * at `firedAt`, which is precisely after this. */
8197
+ export declare const recordScheduleQueueWait: (name: string, waitedMs: number) => void;
8198
+
7973
8199
  /**
7974
8200
  * Record one schedule firing into the registry: the run counter (by name +
7975
8201
  * status), the duration histogram, and — on success — the last-success gauge.
@@ -8145,6 +8371,8 @@ export declare const registerConnectionStream: (clientId: number, interrupt: ()
8145
8371
  */
8146
8372
  export declare const registerDefaultTupleSource: (source: TupleSource) => boolean;
8147
8373
 
8374
+ export declare const registerExternalizedTask: (task: ExternalizedTask) => void;
8375
+
8148
8376
  export { registerRetention }
8149
8377
 
8150
8378
  /**
@@ -8380,6 +8608,9 @@ export declare class ReplicatedDataStore implements DataStore {
8380
8608
  insertIgnore(table: string, row: Row, options: {
8381
8609
  conflictColumns: ReadonlyArray<string>;
8382
8610
  }): Promise<Row>;
8611
+ insertIgnoreWithOutcome(table: string, row: Row, options: {
8612
+ conflictColumns: ReadonlyArray<string>;
8613
+ }): Promise< InsertIgnoreResult>;
8383
8614
  transactional<T>(work: (tx: DataStore) => Promise<T>): Promise<T>;
8384
8615
  onChange(listener: (event: ChangeEvent) => void): () => void;
8385
8616
  /** Cross-instance reactivity seam — delegates to the primary, where the
@@ -8443,6 +8674,25 @@ export declare interface ReplicatedDataStoreOptions {
8443
8674
  readonly now?: () => number;
8444
8675
  }
8445
8676
 
8677
+ declare const Request_2: Schema.Struct<{
8678
+ id: Schema.filter<Schema.filter<typeof Schema.String>>;
8679
+ name: Schema.filter<Schema.filter<typeof Schema.String>>;
8680
+ from: typeof Schema.DateFromSelf;
8681
+ to: typeof Schema.DateFromSelf;
8682
+ occurrences: Schema.Array$<Schema.filter<typeof Schema.Number>>;
8683
+ status: Schema.Literal<["queued", "running", "completed", "interrupted"]>;
8684
+ owner: Schema.NullOr<Schema.filter<Schema.filter<typeof Schema.String>>>;
8685
+ createdAt: typeof Schema.DateFromSelf;
8686
+ updatedAt: typeof Schema.DateFromSelf;
8687
+ cursor: Schema.filter<Schema.filter<typeof Schema.Number>>;
8688
+ activeIndex: Schema.NullOr<Schema.filter<Schema.filter<typeof Schema.Number>>>;
8689
+ results: Schema.Array$<Schema.Struct<{
8690
+ runId: Schema.filter<Schema.filter<typeof Schema.String>>;
8691
+ status: Schema.Literal<["succeeded", "failed", "skipped", "missed"]>;
8692
+ }>>;
8693
+ error: Schema.NullOr<typeof Schema.String>;
8694
+ }>;
8695
+
8446
8696
  export declare interface ResendOptions {
8447
8697
  /** Operator's note, recorded on the resulting attempt row. */
8448
8698
  readonly reason?: string;
@@ -8607,8 +8857,9 @@ export declare interface ResolvedReactiveConfig {
8607
8857
  /**
8608
8858
  * A `WorkflowCallerContext` after the engine has filled in what a workflow
8609
8859
  * always needs. A run started by a request carries the caller's subject; a
8610
- * BOOTSTRAP run — one resumed after a restart with no recorded start context —
8611
- * carries `SYSTEM_SUBJECT`.
8860
+ * deliberate system start carries a persisted system identity. Hosts without
8861
+ * a context resolver run explicitly in system mode; a configured resolver
8862
+ * returning no identity is an error, not a system grant.
8612
8863
  *
8613
8864
  * The engine resolves this so `buildContext` never has to. When each boot path
8614
8865
  * supplied its own fallback, `voltro dev` used the dev REQUEST fallback
@@ -8738,6 +8989,10 @@ export declare type ResolveSubjectFn = (input: {
8738
8989
  */
8739
8990
  export declare const resolveTimelineMode: (env?: Record<string, string | undefined>) => TimelineMode;
8740
8991
 
8992
+ /** Select identity before opening a transaction, never while one owns writes.
8993
+ * Only the framework's system subject may select a different tenant. */
8994
+ export declare const resolveTransactionSubject: (subject: Subject, tenantId: string | undefined, transactionActive: boolean) => Subject;
8995
+
8741
8996
  /**
8742
8997
  * Resolve the trusted-proxy configuration.
8743
8998
  *
@@ -9630,6 +9885,24 @@ export declare const SCHEDULE_SUBJECT: Subject;
9630
9885
  * from the last-fired-at row). */
9631
9886
  export declare type ScheduleBackfillPolicy = 'skip' | 'latest' | 'all';
9632
9887
 
9888
+ export declare type ScheduleBackfillRequest = typeof Request_2.Type;
9889
+
9890
+ export declare const scheduleBackfillRequestsTable: Table<"_voltro_schedule_backfill_requests", FieldDefinitions<{
9891
+ readonly id: ColumnBuilder<string, "id", boolean>;
9892
+ readonly name: ColumnBuilder<string, "text", boolean>;
9893
+ readonly from: ColumnBuilder<Date, "timestamp", boolean>;
9894
+ readonly to: ColumnBuilder<Date, "timestamp", boolean>;
9895
+ readonly occurrences: ColumnBuilder<unknown, "json", boolean>;
9896
+ readonly status: ColumnBuilder<"completed" | "running" | "queued" | "interrupted", "text", boolean>;
9897
+ readonly owner: ColumnBuilder<string | null, "text", boolean>;
9898
+ readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
9899
+ readonly updatedAt: ColumnBuilder<Date, "timestamp", boolean>;
9900
+ readonly cursor: ColumnBuilder<number, "integer", boolean>;
9901
+ readonly activeIndex: ColumnBuilder<number | null, "integer", boolean>;
9902
+ readonly results: ColumnBuilder<unknown, "json", boolean>;
9903
+ readonly error: ColumnBuilder<string | null, "text", boolean>;
9904
+ }>, true, never>;
9905
+
9633
9906
  /** Context handed to a schedule handler. Mirrors a mutation
9634
9907
  * executor's `ctx` — same store, same plugin interceptors — plus the
9635
9908
  * firing metadata. A schedule is "a mutation the clock invokes." */
@@ -9740,16 +10013,18 @@ export declare type ScheduleFireInterceptor = (next: () => Promise<void>, ctx: S
9740
10013
  * thing that silently did nothing. Same bridge a file-based migration's `up`
9741
10014
  * has carried since it shipped.
9742
10015
  *
9743
- * `R = never`: the effect must carry its own requirements. Everything a
9744
- * schedule needs is on `ctx.app`.
10016
+ * The app supplies its outbound HttpClient, including its tracing and SSRF
10017
+ * policy. Other requirements must be provided by the handler.
9745
10018
  */
9746
- export declare type ScheduleHandler = (ctx: ScheduleContext) => void | Promise<void> | Effect.Effect<unknown, unknown, never>;
10019
+ export declare type ScheduleHandler = (ctx: ScheduleContext) => void | Promise<void> | Effect.Effect<unknown, unknown, HttpClient.HttpClient>;
9747
10020
 
9748
10021
  /** What happens when a firing arrives while the previous run of the
9749
10022
  * same schedule is still in flight. */
9750
10023
  export declare type ScheduleOverlapPolicy = 'skip' | 'queue' | 'parallel';
9751
10024
 
9752
10025
  export declare interface SchedulerDeps {
10026
+ /** App-owned service environment; the signal interrupts Effect work on timeout. */
10027
+ readonly runEffect?: (effect: Extract<ReturnType<ScheduleHandler>, Effect.Effect<unknown, unknown, unknown>>, signal: AbortSignal) => Promise<unknown>;
9753
10028
  readonly store: DataStore;
9754
10029
  readonly coordinator: Coordinator;
9755
10030
  /** Default trigger when a schedule doesn't override it. Schedules
@@ -9781,6 +10056,32 @@ export declare interface SchedulerDeps {
9781
10056
  * turning the beat into a spin.
9782
10057
  */
9783
10058
  readonly runHeartbeatMs?: number;
10059
+ /**
10060
+ * How long an abandoned `queue` waiter stays adoptable, measured from its last
10061
+ * beat. Omitted → {@link DEFAULT_QUEUE_ADOPTION_WINDOW_MS}. Both boot paths
10062
+ * pass the resolved `scheduling.queueAdoptionWindowMs`.
10063
+ */
10064
+ readonly queueAdoptionWindowMs?: number;
10065
+ /** How late an external trigger may arrive and still be attributed to its
10066
+ * occurrence. Default `DEFAULT_EXTERNAL_TRIGGER_SKEW_MS`; always additionally
10067
+ * bounded by the schedule's own period. */
10068
+ readonly externalTriggerSkewMs?: number;
10069
+ /**
10070
+ * What to do about an occurrence of an `external` schedule that nobody
10071
+ * triggered. Default `'record'`.
10072
+ *
10073
+ * - `'record'` — write a `missed` row and count it. The ledger then tells
10074
+ * "the platform stopped firing" apart from "this schedule
10075
+ * had nothing to do", which in `external` mode look
10076
+ * identical otherwise: there is no timer to notice.
10077
+ * - `'record-and-run'` — also run it. Opt-in, because the whole premise of
10078
+ * this mode is that the PLATFORM owns the clock, and a
10079
+ * framework that quietly fires anyway has taken it back.
10080
+ * - `'off'` — neither.
10081
+ */
10082
+ readonly externalMissedPolicy?: 'record' | 'record-and-run' | 'off';
10083
+ /** How often the external-schedule reconciliation runs. Default 5 minutes. */
10084
+ readonly externalReconcileMs?: number;
9784
10085
  readonly log: SchedulerLogger;
9785
10086
  /** Notify on every run-row insert/update so the SSE bridge can
9786
10087
  * mirror schedule runs to the cloud dashboard in real time. */
@@ -9802,6 +10103,23 @@ export declare interface SchedulerDeps {
9802
10103
  * a single-tenant / self-host app; the orchestrator (P3) resolves it
9803
10104
  * to a deployment. Only consulted in `dormancy: 'sleep'`. */
9804
10105
  readonly dormancyTenantId?: string;
10106
+ /**
10107
+ * How the run ledger announces a change — `store.onChange`, narrowed to what
10108
+ * this engine needs. Both boot paths pass it.
10109
+ *
10110
+ * It shortens a `queue` firing's wait: the blocker clearing is an UPDATE on
10111
+ * `_voltro_schedule_runs`, so a waiter can re-check on the event instead of on
10112
+ * its next poll. It does NOT replace the poll, and the reason is the same
10113
+ * asymmetry the coordinated-task runner documents: the blocking run usually
10114
+ * finishes on ANOTHER replica, and that replica's change event reaches this
10115
+ * process only where CDC or a broadcast transport carries it. Where it does
10116
+ * not, the poll is the whole answer — so the floor stays what it was and this
10117
+ * buys latency, not a longer interval.
10118
+ */
10119
+ readonly onChange?: (listener: (event: {
10120
+ readonly table: string;
10121
+ readonly op: string;
10122
+ }) => void) => () => void;
9805
10123
  /**
9806
10124
  * Optional interceptor wrapping every firing of every schedule.
9807
10125
  * Composed across plugins by the CLI (`composeScheduleFireInterceptors`
@@ -9817,9 +10135,35 @@ export declare interface SchedulerDeps {
9817
10135
 
9818
10136
  export declare interface SchedulerHandle {
9819
10137
  /** Fire a schedule immediately, bypassing the cron clock + the
9820
- * coordinator. Used by the manual "Run now" dashboard button + the
9821
- * `external` HTTP trigger endpoint. Returns the recorded run id. */
10138
+ * coordinator. Waits for handler completion and returns the recorded run id. */
9822
10139
  fireNow(name: string, trigger: ScheduleTrigger | 'manual'): Promise<string | undefined>;
10140
+ /** Start immediately and return once the run is recorded, before its handler completes. */
10141
+ startNow(name: string, trigger: ScheduleTrigger | 'manual'): Promise<string | undefined>;
10142
+ /**
10143
+ * The `external` trigger driver's front door: an outside scheduler says "the
10144
+ * cadence says now" and this decides what that means.
10145
+ *
10146
+ * Three things happen here that `fireNow` deliberately does not do, and each
10147
+ * is the reason this is a separate method rather than a flag on that one:
10148
+ *
10149
+ * 1. **The arrival is snapped to an occurrence** of the declared cron
10150
+ * expression, so `scheduledAt` is derived from the CODE and not from the
10151
+ * caller's clock. Two deliveries of the same firing therefore carry the
10152
+ * same instant.
10153
+ * 2. **That instant is claimed** through the store-backed gate, whatever
10154
+ * coordination the deployment declared. So a redelivery is a no-op
10155
+ * instead of a second run — which is what makes a platform scheduler's
10156
+ * at-least-once delivery safe to build on. k8s states plainly that a
10157
+ * CronJob may create a Job twice for one scheduled time.
10158
+ * 3. **It returns as soon as the RUN ROW exists**, not when the handler is
10159
+ * done. A schedule may legitimately run for `maxRuntimeMs` (30 minutes
10160
+ * by default) and no HTTP hop between a job pod and this process will
10161
+ * hold a connection that long. The caller polls `runStatus`.
10162
+ */
10163
+ triggerExternal(name: string, arrivalAt: Date): Promise<ExternalTriggerOutcome>;
10164
+ /** One run's terminal-or-not state, for a caller polling an accepted trigger
10165
+ * to completion. `undefined` when no such run exists. */
10166
+ runStatus(runId: string): Promise<ScheduleRunStatus | undefined>;
9823
10167
  /** Fire from a cluster-cron tick. `@effect/cluster` has already
9824
10168
  * guaranteed exactly-once across the fleet (this runner owns the
9825
10169
  * shard), so we bypass the in-app coordinator + record the run
@@ -9852,6 +10196,12 @@ export declare interface SchedulerHandle {
9852
10196
  readonly fired: number;
9853
10197
  readonly runIds: ReadonlyArray<string | null>;
9854
10198
  }>;
10199
+ /** Persist an operator range and return its receipt before sequential execution completes. */
10200
+ startBackfillRange(name: string, from: Date, to: Date, options: {
10201
+ readonly cap: number;
10202
+ readonly requestId: string;
10203
+ }): Promise<ScheduleBackfillRequest>;
10204
+ backfillRequest(requestId: string): Promise<ScheduleBackfillRequest | undefined>;
9855
10205
  /** Stop all timers + abort in-flight watchdogs. */
9856
10206
  shutdown(): Promise<void>;
9857
10207
  }
@@ -9882,6 +10232,20 @@ export declare interface ScheduleRunRecord {
9882
10232
  readonly completedAtSec?: number;
9883
10233
  }
9884
10234
 
10235
+ /** One run, as a poller sees it. */
10236
+ export declare interface ScheduleRunStatus {
10237
+ readonly runId: string;
10238
+ readonly name: string;
10239
+ readonly status: string;
10240
+ readonly trigger: string;
10241
+ readonly scheduledAt: Date | null;
10242
+ readonly firedAt: Date | null;
10243
+ readonly completedAt: Date | null;
10244
+ readonly durationMs: number | null;
10245
+ readonly errorTag: string | null;
10246
+ readonly errorMessage: string | null;
10247
+ }
10248
+
9885
10249
  /** Trigger driver — `self` runs an in-app supervised timer; `external`
9886
10250
  * exposes an HTTP endpoint that an outside scheduler (k8s CronJob,
9887
10251
  * AWS EventBridge, GCP Cloud Scheduler, …) invokes. */
@@ -10336,7 +10700,9 @@ export declare const setTimelineRecorderForTest: (recorder: TimelineRecorder | u
10336
10700
  * `undefined` means the body was synchronous and has already run. Anything else
10337
10701
  * is a promise the caller disposes of as its context requires.
10338
10702
  */
10339
- export declare const settleHandlerBody: (body: HandlerBody) => Promise<unknown> | undefined;
10703
+ export declare function settleHandlerBody(body: HandlerBody): Promise<unknown> | undefined;
10704
+
10705
+ export declare function settleHandlerBody<R>(body: HandlerBody<R>, run: (effect: Effect.Effect<unknown, unknown, R>) => Promise<unknown>): Promise<unknown> | undefined;
10340
10706
 
10341
10707
  /**
10342
10708
  * Register (or clear) the process-global tuple source. Last write wins.
@@ -10945,8 +11311,9 @@ export { sweepRetention }
10945
11311
  */
10946
11312
  export declare const synthesizeInverse: (cs: ChangeSet) => ReadonlyArray<InverseOp>;
10947
11313
 
10948
- /** The Subject non-request work runs as: a schedule firing, a workflow resumed
10949
- * after a restart, a `*.subscribe.ts` handler. `tenantId: null` means
11314
+ /** The Subject system work runs as: a schedule firing, an explicitly system-started
11315
+ * workflow, a `*.subscribe.ts` handler. User-started workflows restore their
11316
+ * recorded identity; a missing context refuses execution. `tenantId: null` means
10950
11317
  * `applyTenantScope` adds no tenant filter, so system work sees every tenant
10951
11318
  * rather than one arbitrary one. See this file's header for why it is a single
10952
11319
  * shared value. */
@@ -11013,6 +11380,14 @@ export { TenantRowNotFound }
11013
11380
 
11014
11381
  export { TenantScopeViolation }
11015
11382
 
11383
+ /** The statuses a run never leaves — where a poller stops.
11384
+ *
11385
+ * `skipped` is terminal and is NOT a failure: it means the overlap policy
11386
+ * found the previous occurrence still running, which is the policy doing its
11387
+ * job. A caller that maps it to a non-zero exit turns correct behaviour into
11388
+ * a page. */
11389
+ export declare const TERMINAL_RUN_STATUSES: ReadonlySet<string>;
11390
+
11016
11391
  export declare type TimeBucket = 'minute' | 'hour' | 'day' | 'week' | 'month';
11017
11392
 
11018
11393
  export declare interface TimelineEvent {
@@ -11478,6 +11853,8 @@ export declare class UndoStack<E> {
11478
11853
  */
11479
11854
  export declare const undoSurfaceEnabled: (env?: Record<string, string | undefined>) => boolean;
11480
11855
 
11856
+ export declare const unregisterExternalizedTask: (name: string) => void;
11857
+
11481
11858
  /** One `source:` entry that resolves to no declared table. */
11482
11859
  export declare interface UnresolvedSource {
11483
11860
  /** The procedure that declares it. */
@@ -11850,8 +12227,7 @@ export declare const withServerTracer: <A, E, R>(effect: Effect.Effect<A, E, R>)
11850
12227
 
11851
12228
  /**
11852
12229
  * What the gate decided. `passthrough` is the common case — no controls
11853
- * declared — and is distinct from `start` so the facade can skip the commit
11854
- * bookkeeping entirely rather than calling a no-op.
12230
+ * declared — and is distinct from a durably reserved admission.
11855
12231
  */
11856
12232
  export declare type WorkflowAdmissionOutcome = {
11857
12233
  readonly kind: 'passthrough';
@@ -11860,11 +12236,12 @@ export declare type WorkflowAdmissionOutcome = {
11860
12236
  /** What to start WITH. Differs from the caller's payload only for a batch
11861
12237
  * that filled up on this arrival. */
11862
12238
  readonly payload: unknown;
11863
- /** Called once the engine returns an execution id: links the ledger row,
11864
- * consumes any batched intents, evicts a singleton incumbent. */
11865
- readonly commit: (executionId: string) => Promise<void>;
12239
+ /** Dispatch the resident submission and return its durably acknowledged
12240
+ * execution ID. Recovery owns retries; callers must not execute separately. */
12241
+ readonly accept: () => Promise<string>;
11866
12242
  } | {
11867
12243
  readonly kind: 'queued';
12244
+ readonly receiptId: string;
11868
12245
  readonly intentId: string;
11869
12246
  readonly mode: string;
11870
12247
  readonly dueAt: number;
@@ -11880,9 +12257,8 @@ export declare interface WorkflowAdmissionRequest {
11880
12257
  readonly workflowName: string;
11881
12258
  readonly payload: unknown;
11882
12259
  readonly callerContext: WorkflowCallerContext | undefined;
11883
- /** `true` for `run(...)` and `start(..., { wait: true })` — a caller blocking
11884
- * for the RESULT. A deferring control has no coherent answer for one, so the
11885
- * gate refuses it by name rather than doing something surprising. */
12260
+ /** `true` for `run(...)` and `start(..., { wait: true })`. Admission still
12261
+ * applies normally; queued results are followed through the caller receipt. */
11886
12262
  readonly waiting: boolean;
11887
12263
  }
11888
12264
 
@@ -11891,26 +12267,33 @@ export declare interface WorkflowCallerContext {
11891
12267
  readonly traceId?: string | null;
11892
12268
  readonly source?: string | null;
11893
12269
  readonly parentExecutionId?: string | null;
12270
+ readonly parentGeneration?: string | null;
11894
12271
  readonly parentClosePolicy?: WorkflowParentClosePolicy | null;
11895
12272
  }
11896
12273
 
11897
12274
  export declare interface WorkflowChildOptions {
11898
12275
  readonly wait?: boolean;
12276
+ /* Excluded from this release type: enqueueStart */
11899
12277
  readonly parentClosePolicy?: WorkflowParentClosePolicy;
11900
12278
  /* Excluded from this release type: callerContext */
11901
12279
  }
11902
12280
 
11903
- /**
11904
- * Reject a blocking caller on a workflow whose flow control can DEFER.
11905
- *
11906
- * `run(...)` and `start({ wait: true })` block for the run's RESULT, and there
11907
- * is no result to return for a start that was collapsed into a future run.
11908
- * Silently starting it anyway would break the declared limit; silently
11909
- * returning nothing would break the caller's type. So it is an error that names
11910
- * both halves.
11911
- */
11912
- export declare class WorkflowDeferredResultError extends Error {
11913
- constructor(workflowName: string, method: string, mode: string);
12281
+ export declare interface WorkflowControlOptions {
12282
+ /** Reuse for retries of the same control intent. Requires a resident control
12283
+ * adapter; the identity remains bound to its original execution generation. */
12284
+ readonly requestId: string;
12285
+ /** Stored with first acceptance; retries retain the original cause. */
12286
+ readonly cause?: NonNullable<WorkflowControlRequest['cause']>;
12287
+ }
12288
+
12289
+ /** Control acceptance and execution outcome are independent facts. A resolved
12290
+ * engine command alone supplies neither durable acceptance nor completion. */
12291
+ export declare type WorkflowControlResult = WorkflowControlResult_2;
12292
+
12293
+ export declare interface WorkflowControlTarget extends WorkflowControlOptions {
12294
+ readonly operation: 'cancel' | 'resume';
12295
+ readonly workflowName: string;
12296
+ readonly executionId: string;
11914
12297
  }
11915
12298
 
11916
12299
  export declare interface WorkflowDefinitionLike {
@@ -11973,6 +12356,30 @@ export declare interface WorkflowEventTriggerResult {
11973
12356
  }
11974
12357
 
11975
12358
  export declare interface WorkflowFacadeOptions {
12359
+ readonly cancelQueued?: (input: WorkflowQueuedControlTarget & {
12360
+ readonly callerContext: WorkflowCallerContext | undefined;
12361
+ readonly cause?: NonNullable<WorkflowControlRequest['cause']>;
12362
+ }) => Promise<WorkflowQueuedCancellationResult>;
12363
+ readonly readQueuedCancellation?: (input: WorkflowQueuedControlTarget & {
12364
+ readonly callerContext: WorkflowCallerContext | undefined;
12365
+ }) => Promise<WorkflowQueuedCancellationResult>;
12366
+ /** Read-only resident receipt observation; must not submit an engine command. */
12367
+ readonly readControl?: (target: WorkflowControlTarget) => Promise<WorkflowControlResult>;
12368
+ /** Resident control owner. When configured it owns acceptance, dispatch and
12369
+ * observation completely; the facade must not issue a second engine command
12370
+ * or substitute an engine-only observation when this adapter fails. */
12371
+ readonly control?: (input: {
12372
+ readonly operation: 'cancel' | 'resume';
12373
+ readonly workflowName: string;
12374
+ readonly executionId: string;
12375
+ readonly requestId?: string;
12376
+ readonly cause?: NonNullable<WorkflowControlRequest['cause']>;
12377
+ }) => Promise<WorkflowControlResult>;
12378
+ readonly resolveQueuedStart?: (input: {
12379
+ readonly receiptId: string;
12380
+ readonly workflowName: string;
12381
+ readonly callerContext: WorkflowCallerContext | undefined;
12382
+ }) => Promise<WorkflowStartResolution>;
11976
12383
  readonly workflows: ReadonlyArray<DiscoveredWorkflowLike>;
11977
12384
  readonly runEffect: <A>(effect: Effect.Effect<A, unknown, unknown>) => Promise<A>;
11978
12385
  readonly rememberStartContext?: (workflowName: string, executionId: string, context: WorkflowCallerContext) => void | Promise<void>;
@@ -12043,6 +12450,7 @@ export declare interface WorkflowFacadeOptions {
12043
12450
  /** Epoch ms. Always strictly in the future — the facade handles the past. */
12044
12451
  readonly at: number;
12045
12452
  }) => Promise<{
12453
+ readonly receiptId: string;
12046
12454
  readonly intentId: string;
12047
12455
  readonly dueAt: number;
12048
12456
  }>;
@@ -12051,7 +12459,10 @@ export declare interface WorkflowFacadeOptions {
12051
12459
  export declare interface WorkflowLayerExecutionContext {
12052
12460
  readonly workflowName: string;
12053
12461
  readonly executionId: string;
12462
+ /** This body's committed entry generation, not its parent's generation. */
12463
+ readonly generation?: string;
12054
12464
  readonly parentExecutionId?: string | null;
12465
+ readonly parentGeneration?: string | null;
12055
12466
  readonly parentClosePolicy?: WorkflowParentClosePolicy | null;
12056
12467
  }
12057
12468
 
@@ -12068,8 +12479,8 @@ export declare interface WorkflowLayerOptions<Context> {
12068
12479
  * cookie one layer down. This hook is what re-establishes the authority, from
12069
12480
  * the app's live source, on every execution attempt.
12070
12481
  *
12071
- * Called ONLY for a run that has a recorded caller. A bootstrap run — no
12072
- * recorded context — is `SYSTEM_SUBJECT` and is not put through it:
12482
+ * Called ONLY for a non-system caller. An explicitly recorded system start
12483
+ * (or a host without a context resolver) is not put through it:
12073
12484
  * `SYSTEM_SUBJECT` already states its own authority, and running it through
12074
12485
  * an app resolver keyed on a null id would be asking a question with no
12075
12486
  * answer.
@@ -12111,6 +12522,17 @@ export declare class WorkflowPayloadError extends Error {
12111
12522
  missingFields: ReadonlyArray<string>, detail: string);
12112
12523
  }
12113
12524
 
12525
+ export declare interface WorkflowQueuedControlOptions {
12526
+ readonly callerContext?: WorkflowCallerContext;
12527
+ readonly cause?: NonNullable<WorkflowControlRequest['cause']>;
12528
+ }
12529
+
12530
+ export declare interface WorkflowQueuedControlTarget {
12531
+ readonly workflowName: string;
12532
+ readonly receiptId: string;
12533
+ readonly requestId: string;
12534
+ }
12535
+
12114
12536
  /**
12115
12537
  * A blocking caller hit a `rateLimit` cap.
12116
12538
  *
@@ -12190,7 +12612,7 @@ export declare interface WorkflowRunOptions {
12190
12612
  export declare interface WorkflowRunRecord {
12191
12613
  readonly name: string;
12192
12614
  /** Terminal outcome only. `failed` is the dead-letter state (no framework retry). */
12193
- readonly status: 'succeeded' | 'failed';
12615
+ readonly status: 'succeeded' | 'failed' | 'cancelled';
12194
12616
  readonly durationMs: number;
12195
12617
  /** UNIX SECONDS of completion — set only on success (drives the last-success
12196
12618
  * gauge). Omit on failure so the gauge keeps pointing at the last real success. */
@@ -12203,17 +12625,9 @@ export declare interface WorkflowRunRecord {
12203
12625
  * stored column exactly. */
12204
12626
  export declare type WorkflowRunRecordStatus = 'running' | 'succeeded' | 'failed' | 'cancelled' | 'suspended';
12205
12627
 
12206
- export declare interface WorkflowRunSnapshot<Success = unknown> {
12207
- readonly id: string;
12208
- readonly workflowName: string;
12209
- readonly executionId: string;
12210
- readonly status: WorkflowRunStatus;
12628
+ export declare type WorkflowRunSnapshot<Success = unknown> = Omit<WorkflowRunSnapshot_2, 'output'> & {
12211
12629
  readonly output?: Success;
12212
- readonly error?: {
12213
- readonly tag: string | null;
12214
- readonly message: string;
12215
- };
12216
- }
12630
+ };
12217
12631
 
12218
12632
  export declare type WorkflowRunStatus = 'running' | 'succeeded' | 'failed' | 'cancelled' | 'suspended' | 'unknown';
12219
12633
 
@@ -12250,8 +12664,12 @@ export declare interface WorkflowsAppContext {
12250
12664
  query<Success = unknown>(workflowName: string, executionId: string): Promise<WorkflowRunSnapshot<Success>>;
12251
12665
  wait<Success = unknown>(handle: WorkflowRunHandle, options?: WorkflowWaitOptions): Promise<WorkflowRunSnapshot<Success>>;
12252
12666
  wait<Success = unknown>(workflowName: string, executionId: string, options?: WorkflowWaitOptions): Promise<WorkflowRunSnapshot<Success>>;
12253
- cancel(workflowName: string, executionId: string): Promise<WorkflowRunSnapshot>;
12254
- resume(workflowName: string, executionId: string): Promise<WorkflowRunSnapshot>;
12667
+ cancel(workflowName: string, executionId: string, options?: WorkflowControlOptions): Promise<WorkflowControlResult>;
12668
+ resume(workflowName: string, executionId: string, options?: WorkflowControlOptions): Promise<WorkflowControlResult>;
12669
+ /** Observe the original resident request without submitting or retrying it. */
12670
+ readControl(target: WorkflowControlTarget): Promise<WorkflowControlResult>;
12671
+ cancelQueued(target: WorkflowQueuedControlTarget, options?: WorkflowQueuedControlOptions): Promise<WorkflowQueuedCancellationResult>;
12672
+ readQueuedCancellation(target: WorkflowQueuedControlTarget, options?: Pick<WorkflowQueuedControlOptions, 'callerContext'>): Promise<WorkflowQueuedCancellationResult>;
12255
12673
  signal(target: WorkflowSignalTarget, signalName: string, payload?: unknown): Promise<{
12256
12674
  readonly eventId: string;
12257
12675
  }>;
@@ -12288,6 +12706,20 @@ export declare class WorkflowSingletonHeldError extends Error {
12288
12706
  constructor(workflowName: string, executionId: string);
12289
12707
  }
12290
12708
 
12709
+ /** Persist a validated start through the business transaction's store. The
12710
+ * returned identity is provisional until that transaction commits. */
12711
+ export declare type WorkflowStartEnqueue = (input: {
12712
+ readonly workflowName: string;
12713
+ /** Encoded using the arrival schema, not the decoded in-process payload. */
12714
+ readonly payload: unknown;
12715
+ readonly callerContext: WorkflowCallerContext | undefined;
12716
+ readonly at: number;
12717
+ }) => Promise<{
12718
+ readonly receiptId: string;
12719
+ readonly intentId: string;
12720
+ readonly dueAt: number;
12721
+ }>;
12722
+
12291
12723
  /** The slice of a `workflow()` definition the start runner reads — its name
12292
12724
  * and its resolved access decision (`workflowAccessDescriptor` produces it). */
12293
12725
  export declare interface WorkflowStartLike {
@@ -12295,8 +12727,23 @@ export declare interface WorkflowStartLike {
12295
12727
  readonly guards?: ReadonlyArray<AnyCheckSpec> | undefined;
12296
12728
  }
12297
12729
 
12730
+ export declare class WorkflowStartNotExecuted extends WorkflowStartNotExecuted_base {
12731
+ }
12732
+
12733
+ declare const WorkflowStartNotExecuted_base: Schema.TaggedErrorClass<WorkflowStartNotExecuted, "WorkflowStartNotExecuted", {
12734
+ readonly _tag: Schema.tag<"WorkflowStartNotExecuted">;
12735
+ } & {
12736
+ workflowName: typeof Schema.String;
12737
+ handleId: typeof Schema.String;
12738
+ reason: Schema.Literal<["dropped", "skipped", "superseded", "cancelled", "abandoned", "rejected"]>;
12739
+ failureId: Schema.NullOr<typeof Schema.String>;
12740
+ incumbentExecutionId: Schema.NullOr<typeof Schema.String>;
12741
+ }>;
12742
+
12298
12743
  export declare interface WorkflowStartOptions {
12744
+ /* Excluded from this release type: preparedExecutionId */
12299
12745
  readonly wait?: boolean;
12746
+ /* Excluded from this release type: enqueueStart */
12300
12747
  /**
12301
12748
  * Delayed one-off start: park this start DURABLY and let it arrive at `at`.
12302
12749
  *
@@ -12316,9 +12763,9 @@ export declare interface WorkflowStartOptions {
12316
12763
  * handle has `status: 'queued'` with `deferral: { mode: 'delayed', dueAt }`.
12317
12764
  *
12318
12765
  * An `at` in the past (or now) starts immediately — the caller said "no
12319
- * earlier than", and that is already true. Combining `at` with
12320
- * `wait: true` is refused: there is no result to block on for a start that
12321
- * exists only as a future row.
12766
+ * earlier than", and that is already true. With `wait: true`, follow the
12767
+ * durable caller receipt through the delayed arrival and any further
12768
+ * admission deferral, then wait for the accepted execution's result.
12322
12769
  */
12323
12770
  readonly at?: Date;
12324
12771
  /* Excluded from this release type: callerContext */
@@ -12341,6 +12788,7 @@ export declare interface WorkflowUpdateOptions {
12341
12788
  export declare interface WorkflowWaitOptions {
12342
12789
  readonly pollIntervalMs?: number;
12343
12790
  readonly timeoutMs?: number;
12791
+ /* Excluded from this release type: callerContext */
12344
12792
  }
12345
12793
 
12346
12794
  /**