@sanity/workflow-engine 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { analyzeCondition } from "@sanity/groq-condition-describe";
2
2
  import { AtomInsight } from "@sanity/groq-condition-describe";
3
+ import { atomReadsDataset } from "@sanity/groq-condition-describe";
3
4
  import { AtomRequirement } from "@sanity/groq-condition-describe";
4
5
  import { checklistLines as checklistLines_2 } from "@sanity/groq-condition-describe";
5
6
  import { ComparisonOp } from "@sanity/groq-condition-describe";
@@ -40,15 +41,16 @@ export declare function abortReason(
40
41
 
41
42
  /**
42
43
  * The structured half of applicability: does a required subject entry
43
- * (`doc.ref` / `doc.refs`, workflow scope) accept `documentType`? Name-blind
44
- * and EXISTENTIAL: ANY required ref entry counts — a multi-input definition
45
- * (say, contract + counterparty) surfaces from either document's picker, and
46
- * the start dialog collects the remaining required entries (their fail-hard
47
- * validation backstops). An entry without `types` accepts any type; a
48
- * definition with NO required ref entry takes no subject and never matches.
49
- * Cheap and indexable — no GROQ evaluation — so a consumer can pre-filter
50
- * before loading document content. (`required` is pinned to caller-filled
51
- * `input` entries by a deploy invariant, so it alone identifies the handoff.)
44
+ * ({@link isSubjectEntry} — `doc.ref` / `doc.refs`, workflow scope) accept
45
+ * `documentType`? Name-blind and EXISTENTIAL: ANY required ref entry counts —
46
+ * a multi-input definition (say, contract + counterparty) surfaces from
47
+ * either document's picker, and the start dialog collects the remaining
48
+ * required entries (their fail-hard validation backstops). An entry without
49
+ * `types` accepts any type; a definition with NO required ref entry takes no
50
+ * subject and never matches. Cheap and indexable — no GROQ evaluation — so a
51
+ * consumer can pre-filter before loading document content. (`required` is
52
+ * pinned to caller-filled `input` entries by a deploy invariant, so it alone
53
+ * identifies the handoff.)
52
54
  */
53
55
  export declare function acceptsDocumentType(
54
56
  definition: Pick<ApplicabilitySource, "fields">,
@@ -77,7 +79,9 @@ export declare function aclPathForResource(
77
79
  res: WorkflowResource,
78
80
  ): string | undefined;
79
81
 
80
- export declare type Action = ActionFields<Op, string[]>;
82
+ export declare type Action = ActionFields<Op, string[]> & {
83
+ roles?: string[] | undefined;
84
+ };
81
85
 
82
86
  /**
83
87
  * The engine's per-reason detail fragment — the same wording
@@ -121,11 +125,22 @@ export declare type ActionDisabledReason = Exclude<
121
125
  export declare interface ActionEvaluation {
122
126
  action: Action;
123
127
  allowed: boolean;
128
+ /**
129
+ * The action is cascade-fired (`when`): the engine fires it on truth, it
130
+ * is never fireAction-able, and a consumer must not render it as a
131
+ * button — narrate it ("will escalate when overdue") via `whenInsight`.
132
+ * Always set together with the `cascade-fired` `disabledReason` — either
133
+ * check suffices; the flag is the ergonomic spelling.
134
+ */
135
+ triggered?: true;
124
136
  /** Present iff `allowed === false`. The first failing gate wins. */
125
137
  disabledReason?: DisabledReason;
126
138
  /** Derived state of the action's `filter` gate — why it holds or fails,
127
139
  * atom by atom. Present iff the action declares a filter. */
128
140
  insight?: ConditionInsight;
141
+ /** Derived state of a cascade-fired action's `when` trigger — what would
142
+ * fire it. Present iff the action declares `when`. */
143
+ whenInsight?: ConditionInsight;
129
144
  }
130
145
 
131
146
  /** Type-mirror of {@link actionFields}, parameterised over the op and
@@ -135,10 +150,12 @@ declare type ActionFields<TOp, TGroup> = {
135
150
  title?: string | undefined;
136
151
  description?: string | undefined;
137
152
  group?: TGroup | undefined;
153
+ when?: string | undefined;
138
154
  filter?: string | undefined;
139
155
  params?: ActionParam[] | undefined;
140
156
  ops?: TOp[] | undefined;
141
157
  effects?: Effect[] | undefined;
158
+ spawn?: Subworkflows | undefined;
142
159
  };
143
160
 
144
161
  export declare type ActionName = string;
@@ -205,6 +222,26 @@ export declare class ActionParamsInvalidError extends WorkflowError<"action-para
205
222
  });
206
223
  }
207
224
 
225
+ /**
226
+ * The three-way rendering verdict every surface applies to an action:
227
+ * `absent` — its `filter` failed, so it does not exist for this actor/visit
228
+ * (GROQ existence semantics; never even a disabled affordance). `automation` —
229
+ * cascade-fired, so the engine's cascade is its only firing path (narrate it,
230
+ * never render a fire affordance). `button` — fireable: enabled when
231
+ * `allowed`, otherwise disabled WITH its {@link DisabledReason} shown.
232
+ * Structural over the shared verdict fields so {@link ActionEvaluation} and
233
+ * `AvailableAction` both qualify; filter-existence dominates automation, the
234
+ * same order the engine's own verdicts apply.
235
+ */
236
+ export declare function actionRendering(action: {
237
+ triggered?: true;
238
+ disabledReason?:
239
+ | {
240
+ kind: DisabledReason["kind"];
241
+ }
242
+ | undefined;
243
+ }): "absent" | "automation" | "button";
244
+
208
245
  /** The fireable-action verdict for one action on an activity — its `allowed`
209
246
  * state, structured `disabledReason`, and declared params, tagged with the
210
247
  * owning activity. The per-action atom both projections share:
@@ -219,7 +256,6 @@ export declare function actionVerdict(
219
256
  export declare type Activity = ActivityFields<
220
257
  FieldEntry,
221
258
  Action,
222
- Op,
223
259
  ManualTarget,
224
260
  string[]
225
261
  >;
@@ -271,19 +307,23 @@ export declare const ACTIVITY_KINDS: readonly [
271
307
  * `schema.ts → enums.ts` terminates here.
272
308
  */
273
309
  declare const ACTIVITY_STATUSES: readonly [
274
- "pending",
275
310
  "active",
276
311
  "done",
277
312
  "skipped",
278
313
  "failed",
279
314
  ];
280
315
 
281
- /** An activity's four gates, each describedabsent keys mirror undeclared gates. */
316
+ /** The activity slice of a stage rollup total by construction, like
317
+ * {@link stageAutonomyOf}. */
318
+ export declare function activityAutonomyOf(
319
+ stageAutonomy: StageAutonomy,
320
+ activityName: string,
321
+ ): AutonomyAnswer;
322
+
323
+ /** An activity's gates, each described — absent keys mirror undeclared gates. */
282
324
  export declare interface ActivityDescription {
283
325
  requirements?: Record<string, ConditionDescription>;
284
326
  filter?: ConditionDescription;
285
- completeWhen?: ConditionDescription;
286
- failWhen?: ConditionDescription;
287
327
  }
288
328
 
289
329
  export declare interface ActivityEntry {
@@ -294,6 +334,14 @@ export declare interface ActivityEntry {
294
334
  startedAt?: string;
295
335
  completedAt?: string;
296
336
  completedBy?: string;
337
+ /**
338
+ * Names of this activity's cascade-fired actions that have fired during
339
+ * THIS stage visit — the once-per-visit ledger. Level-triggered: a name
340
+ * lands here on the first evaluation that finds its `when` true and never
341
+ * re-fires within the visit; stage re-entry rebuilds entries, so re-arming
342
+ * on a new visit is structural. Absent until the first trigger fires.
343
+ */
344
+ firedActions?: string[];
297
345
  filterEvaluation?: {
298
346
  at: string;
299
347
  truthy: boolean;
@@ -314,13 +362,35 @@ export declare interface ActivityEvaluation {
314
362
  activity: Activity;
315
363
  status: ActivityStatus;
316
364
  /**
317
- * The activity's effective {@link ActivityKind} the explicit {@link Activity.kind}, or
318
- * the shape-derived default when omitted. Advisory: a label so a consumer can
319
- * render each activity as what it is (`user` / `service` / `script` / `manual` / `receive`).
365
+ * The activity's shape-derived {@link ActivityKind}. Advisory: a label so a
366
+ * consumer can render each activity as what it is (`user` / `service` /
367
+ * `script` / `manual` / `receive`).
320
368
  */
321
369
  kind: ActivityKind;
370
+ /**
371
+ * Who, if anyone, fires the activity's actions — derived from its shape
372
+ * alone: `autonomous` (every action cascade-fired), `interactive` (only
373
+ * fireAction-fired actions), `off-system` (`target` present), or `hybrid`.
374
+ */
375
+ classification: ExecutorClassification;
376
+ /**
377
+ * The causal refinement of {@link ActivityEvaluation.classification}:
378
+ * whether this activity completes without a caller, derived by
379
+ * {@link deriveWorkflowAutonomy}'s dataflow over the definition — a
380
+ * mechanically `autonomous` activity whose triggers only read
381
+ * caller-written state still reports the caller it waits on.
382
+ */
383
+ autonomy: AutonomyAnswer;
322
384
  /** Whether this activity is the current actor's responsibility right now. */
323
385
  pendingOnActor: boolean;
386
+ /**
387
+ * The stage-entry `filter` scoped this activity out of the current visit:
388
+ * its entry is `skipped` and never started, so it does not exist for this
389
+ * visit — surfaces hide it entirely (an action-resolved `skipped` was real,
390
+ * started work and stays visible). Derived via {@link isFilterScopedOut},
391
+ * stamped here so consumers never re-join entry state to compute it.
392
+ */
393
+ scopedOut: boolean;
324
394
  /**
325
395
  * The activity's unmet {@link Activity.requirements}, by name — present iff at least
326
396
  * one is unmet. The activity's own readiness summary: a consumer can explain why
@@ -332,45 +402,28 @@ export declare interface ActivityEvaluation {
332
402
  * Present iff the activity declares requirements; `unmetRequirements`
333
403
  * is exactly the keys whose insight isn't satisfied. */
334
404
  requirementInsights?: Record<string, ConditionInsight>;
335
- /** Derived state of the activity's `filter` skip-gate. Present iff declared.
336
- * Advisory read: the engine's cascade owns the gate itself. */
405
+ /** Derived state of the activity's `filter` existence gate. Present iff
406
+ * declared. Advisory read: the engine's stage entry owns the gate itself. */
337
407
  filterInsight?: ConditionInsight;
338
- /** Derived state of `completeWhen` — what would auto-complete this activity.
339
- * Present iff declared. */
340
- completeWhenInsight?: ConditionInsight;
341
- /** Derived state of `failWhen` — what would auto-fail this activity.
342
- * Present iff declared. */
343
- failWhenInsight?: ConditionInsight;
344
408
  actions: ActionEvaluation[];
345
409
  }
346
410
 
347
- /** Type-mirror of {@link activityFields}, parameterised over field/action/op/target/group. */
348
- declare type ActivityFields<TField, TAction, TOp, TTarget, TGroup> = {
411
+ /** Type-mirror of {@link activityFields}, parameterised over field/action/target/group. */
412
+ declare type ActivityFields<TField, TAction, TTarget, TGroup> = {
349
413
  name: string;
350
414
  title?: string | undefined;
351
415
  description?: string | undefined;
352
416
  groups?: Group[] | undefined;
353
417
  group?: TGroup | undefined;
354
- kind?: ActivityKind | undefined;
355
418
  target?: TTarget | undefined;
356
- activation?: "auto" | "manual" | undefined;
357
419
  filter?: string | undefined;
358
420
  requirements?: Record<string, string> | undefined;
359
- completeWhen?: string | undefined;
360
- failWhen?: string | undefined;
361
- ops?: TOp[] | undefined;
362
- effects?: Effect[] | undefined;
363
421
  actions?: TAction[] | undefined;
364
- subworkflows?: Subworkflows | undefined;
365
422
  fields?: TField[] | undefined;
366
423
  };
367
424
 
368
425
  export declare type ActivityKind = (typeof ACTIVITY_KINDS)[number];
369
426
 
370
- /** The effective kind of an activity: the explicitly declared {@link Activity.kind}, or
371
- * the shape-derived default when omitted. */
372
- export declare function activityKind(activity: Activity): ActivityKind;
373
-
374
427
  export declare type ActivityName = string;
375
428
 
376
429
  export declare type ActivityStatus = (typeof ACTIVITY_STATUSES)[number];
@@ -408,14 +461,20 @@ export declare type ActorKind = (typeof ACTOR_KINDS)[number];
408
461
 
409
462
  export { analyzeCondition };
410
463
 
411
- /** The definition surface applicability reads — structural, so authored,
412
- * stored, and deployed definition shapes all fit. `name` is error context
413
- * only: a broken `applicableWhen` fails naming its definition. */
464
+ /** The definition surface the start contexts read — structural, so authored,
465
+ * stored, and deployed definition shapes all fit. `name` binds
466
+ * `$definition` and names the definition in a broken-predicate error. */
414
467
  export declare interface ApplicabilitySource {
415
468
  name?: string | undefined;
416
469
  lifecycle?: WorkflowLifecycle | undefined;
417
470
  fields?: FieldEntry[] | undefined;
418
- applicableWhen?: string | undefined;
471
+ start?:
472
+ | {
473
+ kind?: StartKind | undefined;
474
+ filter?: string | undefined;
475
+ allowed?: string | undefined;
476
+ }
477
+ | undefined;
419
478
  }
420
479
 
421
480
  /**
@@ -427,6 +486,8 @@ export declare function applicableDefinitions<
427
486
  >(args: {
428
487
  definitions: readonly T[];
429
488
  document: CandidateDocument;
489
+ /** Caller-side filter bindings, shared by every definition's evaluation. */
490
+ scope?: StartScope;
430
491
  }): Promise<T[]>;
431
492
 
432
493
  /**
@@ -467,6 +528,8 @@ export declare type Assignee =
467
528
 
468
529
  export { AtomInsight };
469
530
 
531
+ export { atomReadsDataset };
532
+
470
533
  export { AtomRequirement };
471
534
 
472
535
  /**
@@ -496,7 +559,6 @@ export declare type AuthoringAction = AuthoringRawAction | ClaimAction;
496
559
  export declare type AuthoringActivity = ActivityFields<
497
560
  AuthoringFieldEntry,
498
561
  AuthoringAction,
499
- AuthoringOp,
500
562
  AuthoringManualTarget,
501
563
  GroupMembership
502
564
  >;
@@ -564,6 +626,11 @@ export declare type AuthoringGuard = v.InferOutput<typeof AuthoringGuardSchema>;
564
626
 
565
627
  declare const AuthoringGuardSchema: v.StrictObjectSchema<
566
628
  {
629
+ /**
630
+ * Lake-id-segment grammar (`^[a-z0-9][a-z0-9-]*$`, deploy-enforced): the
631
+ * guard's lake `_id` derives from `(instanceId, name)` at stage entry.
632
+ * Unique per definition.
633
+ */
567
634
  name: v.SchemaWithPipe<
568
635
  readonly [
569
636
  v.StringSchema<undefined>,
@@ -725,8 +792,9 @@ declare const AuthoringGuardSchema: v.StrictObjectSchema<
725
792
  /**
726
793
  * Lake GROQ predicate — a distinct eval context: delta-mode GROQ
727
794
  * reading the `before()`/`after()` natives, `mutation`, `guard`, and
728
- * `identity()`. Bare ids/fields only. Omitted or empty means
729
- * UNCONDITIONAL DENY.
795
+ * `identity()`. Bare ids/fields only. Polarity: a result of strictly
796
+ * `true` ALLOWS the matched mutation; anything else (false, null, an
797
+ * evaluation error) DENIES. Omitted or empty means UNCONDITIONAL DENY.
730
798
  */
731
799
  predicate: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
732
800
  /**
@@ -905,7 +973,7 @@ declare const AuthoringManualTargetSchema: v.VariantSchema<
905
973
  undefined
906
974
  >;
907
975
 
908
- declare type AuthoringOp = v.InferOutput<typeof AuthoringOpSchema>;
976
+ export declare type AuthoringOp = v.InferOutput<typeof AuthoringOpSchema>;
909
977
 
910
978
  declare const AuthoringOpSchema: v.VariantSchema<
911
979
  "type",
@@ -1058,7 +1126,7 @@ declare const AuthoringOpSchema: v.VariantSchema<
1058
1126
  undefined
1059
1127
  >;
1060
1128
  readonly status: v.PicklistSchema<
1061
- readonly ["pending", "active", "done", "skipped", "failed"],
1129
+ readonly ["active", "done", "skipped", "failed"],
1062
1130
  `Invalid option: expected one of ${string}`
1063
1131
  >;
1064
1132
  },
@@ -1123,15 +1191,21 @@ declare const AuthoringOpSchema: v.VariantSchema<
1123
1191
  * Authoring action — the stored fields plus two field sugars with one
1124
1192
  * defined expansion each:
1125
1193
  *
1126
- * - `roles` a `count($actor.roles[@ in [...]]) > 0` membership condition
1127
- * ANDed with the authored `filter`. The definition's `roleAliases`
1128
- * ({@link RoleAliasesSchema}) widen that membership at desugar time.
1194
+ * - `roles` on a fireAction-fired action (no `when`) it desugars to a
1195
+ * `count($actor.roles[@ in [...]]) > 0` membership condition ANDed with
1196
+ * the authored `filter` (for a caller, "not yours to fire" and "doesn't
1197
+ * exist for you" are the same advisory answer). On a CASCADE-FIRED
1198
+ * action it stores VERBATIM — the pin on which identities may execute
1199
+ * the trigger; folding it into `filter` would make the action's
1200
+ * existence depend on whose token happens to cascade. The definition's
1201
+ * `roleAliases` ({@link RoleAliasesSchema}) widen the membership either
1202
+ * way.
1129
1203
  * - `status` → a `status.set` op on the firing activity, appended **after**
1130
1204
  * the authored ops (deliberately never implied: a forgotten explicit
1131
1205
  * `status` is a visible stall, an implied default silently completes
1132
1206
  * claim-like actions). Status is the health axis: a decision action
1133
1207
  * (decline, send back) resolves `done` and writes the decision into a
1134
- * field the transition filter reads — `failed` is for work that
1208
+ * field the transition trigger reads — `failed` is for work that
1135
1209
  * genuinely could not complete.
1136
1210
  */
1137
1211
  declare type AuthoringRawAction = ActionFields<AuthoringOp, GroupMembership> & {
@@ -1152,219 +1226,105 @@ export declare type AuthoringStage = StageFields<
1152
1226
  AuthoringEditable
1153
1227
  >;
1154
1228
 
1155
- export declare type AuthoringTransition =
1156
- TransitionFields<AuthoringTransitionOp> & {
1157
- filter?: string | undefined;
1158
- };
1159
-
1160
- declare type AuthoringTransitionOp = v.InferOutput<
1161
- typeof AuthoringTransitionOpSchema
1162
- >;
1229
+ export declare type AuthoringStartBlock = StartFields & {
1230
+ kind?: StartKind | undefined;
1231
+ };
1163
1232
 
1164
1233
  /**
1165
- * Authoring transitions may omit `filter`; desugar fills the safe,
1166
- * overwhelmingly-common gate `"$allActivitiesDone"`. "Fire unconditionally"
1167
- * stays spellable as an explicit `filter: "true"`.
1234
+ * Authoring transitions may omit `when`; desugar fills the safe,
1235
+ * overwhelmingly-common trigger `"$allActivitiesDone"`. "Fire unconditionally"
1236
+ * stays spellable as an explicit `when: "true"`.
1168
1237
  */
1169
- declare const AuthoringTransitionOpSchema: v.VariantSchema<
1170
- "type",
1171
- [
1172
- v.StrictObjectSchema<
1173
- {
1174
- readonly type: v.LiteralSchema<"field.set", undefined>;
1175
- readonly target: v.StrictObjectSchema<
1176
- {
1177
- readonly scope: v.OptionalSchema<
1178
- v.PicklistSchema<
1179
- readonly ["workflow", "stage", "activity"],
1180
- `Invalid option: expected one of ${string}`
1181
- >,
1182
- undefined
1183
- >;
1184
- readonly field: v.SchemaWithPipe<
1185
- readonly [
1186
- v.StringSchema<undefined>,
1187
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1188
- ]
1189
- >;
1190
- },
1191
- undefined
1192
- >;
1193
- readonly value: v.GenericSchema<ValueExprInternal>;
1194
- },
1195
- undefined
1196
- >,
1197
- v.StrictObjectSchema<
1198
- {
1199
- readonly type: v.LiteralSchema<"field.unset", undefined>;
1200
- readonly target: v.StrictObjectSchema<
1201
- {
1202
- readonly scope: v.OptionalSchema<
1203
- v.PicklistSchema<
1204
- readonly ["workflow", "stage", "activity"],
1205
- `Invalid option: expected one of ${string}`
1206
- >,
1207
- undefined
1208
- >;
1209
- readonly field: v.SchemaWithPipe<
1210
- readonly [
1211
- v.StringSchema<undefined>,
1212
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1213
- ]
1214
- >;
1215
- },
1216
- undefined
1217
- >;
1218
- },
1219
- undefined
1220
- >,
1221
- v.StrictObjectSchema<
1222
- {
1223
- readonly type: v.LiteralSchema<"field.append", undefined>;
1224
- readonly target: v.StrictObjectSchema<
1225
- {
1226
- readonly scope: v.OptionalSchema<
1227
- v.PicklistSchema<
1228
- readonly ["workflow", "stage", "activity"],
1229
- `Invalid option: expected one of ${string}`
1230
- >,
1231
- undefined
1232
- >;
1233
- readonly field: v.SchemaWithPipe<
1234
- readonly [
1235
- v.StringSchema<undefined>,
1236
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1237
- ]
1238
- >;
1239
- },
1240
- undefined
1241
- >;
1242
- readonly value: v.GenericSchema<ValueExprInternal>;
1243
- },
1244
- undefined
1245
- >,
1246
- v.StrictObjectSchema<
1247
- {
1248
- readonly type: v.LiteralSchema<"field.updateWhere", undefined>;
1249
- readonly target: v.StrictObjectSchema<
1250
- {
1251
- readonly scope: v.OptionalSchema<
1252
- v.PicklistSchema<
1253
- readonly ["workflow", "stage", "activity"],
1254
- `Invalid option: expected one of ${string}`
1255
- >,
1256
- undefined
1257
- >;
1258
- readonly field: v.SchemaWithPipe<
1259
- readonly [
1260
- v.StringSchema<undefined>,
1261
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1262
- ]
1263
- >;
1264
- },
1265
- undefined
1266
- >;
1267
- readonly where: v.SchemaWithPipe<
1268
- readonly [
1269
- v.StringSchema<undefined>,
1270
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1271
- ]
1272
- >;
1273
- readonly value: v.GenericSchema<ValueExprInternal>;
1274
- },
1275
- undefined
1276
- >,
1277
- v.StrictObjectSchema<
1278
- {
1279
- readonly type: v.LiteralSchema<"field.removeWhere", undefined>;
1280
- readonly target: v.StrictObjectSchema<
1281
- {
1282
- readonly scope: v.OptionalSchema<
1283
- v.PicklistSchema<
1284
- readonly ["workflow", "stage", "activity"],
1285
- `Invalid option: expected one of ${string}`
1286
- >,
1287
- undefined
1288
- >;
1289
- readonly field: v.SchemaWithPipe<
1290
- readonly [
1291
- v.StringSchema<undefined>,
1292
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1293
- ]
1294
- >;
1295
- },
1296
- undefined
1297
- >;
1298
- readonly where: v.SchemaWithPipe<
1299
- readonly [
1300
- v.StringSchema<undefined>,
1301
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1302
- ]
1303
- >;
1304
- },
1305
- undefined
1306
- >,
1307
- v.StrictObjectSchema<
1308
- {
1309
- readonly type: v.LiteralSchema<"audit", undefined>;
1310
- readonly target: v.StrictObjectSchema<
1311
- {
1312
- readonly scope: v.OptionalSchema<
1313
- v.PicklistSchema<
1314
- readonly ["workflow", "stage", "activity"],
1315
- `Invalid option: expected one of ${string}`
1316
- >,
1317
- undefined
1318
- >;
1319
- readonly field: v.SchemaWithPipe<
1320
- readonly [
1321
- v.StringSchema<undefined>,
1322
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1323
- ]
1324
- >;
1325
- },
1326
- undefined
1327
- >;
1328
- readonly value: v.GenericSchema<ValueExprInternal>;
1329
- readonly stampFields: v.OptionalSchema<
1330
- v.StrictObjectSchema<
1331
- {
1332
- readonly actor: v.OptionalSchema<
1333
- v.SchemaWithPipe<
1334
- readonly [
1335
- v.StringSchema<undefined>,
1336
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1337
- ]
1338
- >,
1339
- undefined
1340
- >;
1341
- readonly at: v.OptionalSchema<
1342
- v.SchemaWithPipe<
1343
- readonly [
1344
- v.StringSchema<undefined>,
1345
- v.MinLengthAction<string, 1, "must be a non-empty string">,
1346
- ]
1347
- >,
1348
- undefined
1349
- >;
1350
- },
1351
- undefined
1352
- >,
1353
- undefined
1354
- >;
1355
- },
1356
- undefined
1357
- >,
1358
- ],
1359
- undefined
1360
- >;
1238
+ export declare type AuthoringTransition = TransitionFields & {
1239
+ when?: string | undefined;
1240
+ };
1361
1241
 
1362
1242
  /** The authoring surface: stored primitives plus the define-time sugar. */
1363
1243
  export declare type AuthoringWorkflow = WorkflowFields<
1364
1244
  AuthoringFieldEntry,
1365
- AuthoringStage
1245
+ AuthoringStage,
1246
+ AuthoringStartBlock
1366
1247
  >;
1367
1248
 
1249
+ /** The narratable answer at one rollup level: the verdict plus what it waits
1250
+ * on. `waitsOn` may be non-empty on a `yes` verdict (an effect settle is a
1251
+ * wait the engine resolves itself) — consumers choose what to surface. */
1252
+ export declare interface AutonomyAnswer {
1253
+ completesWithoutCaller: AutonomyVerdict;
1254
+ waitsOn: AutonomyWait[];
1255
+ }
1256
+
1257
+ /** The workflow-level autonomy narrative: fully self-running, or the stages
1258
+ * where a run is held. */
1259
+ export declare function autonomySummary(
1260
+ autonomy: WorkflowAutonomy,
1261
+ ctx: DescribeContext,
1262
+ ): InsightPhrase;
1263
+
1264
+ /**
1265
+ * Whether a node resolves without a caller: `yes` — engine machinery alone
1266
+ * (the cascade plus the host's effect drain/ticker) gets it there; `no` —
1267
+ * someone outside the engine must act (a caller fires an action, an editor
1268
+ * writes a field or content); `conditional` — it may resolve either way
1269
+ * depending on values the analysis can't see (start-time seeds, mixed
1270
+ * alternative routes, unresolvable children, cyclic gates).
1271
+ *
1272
+ * `yes` still leans on the host: effects settle only if a drain runs, and
1273
+ * time only passes for a ticker. A cascade-fired action's `roles` pin is a
1274
+ * DEPENDENCY, not an assumption — the runtime leaves a pinned trigger armed
1275
+ * until a capable token cascades, and whether the deployment's tokens fulfil
1276
+ * the pin is configuration the analysis can't see, so a pinned trigger
1277
+ * reports `conditional` with the pin as its wait.
1278
+ */
1279
+ export declare type AutonomyVerdict = "yes" | "no" | "conditional";
1280
+
1281
+ /** One narratable dependency a non-`yes` verdict rests on. */
1282
+ export declare type AutonomyWait =
1283
+ | {
1284
+ kind: "caller-action";
1285
+ stage: string;
1286
+ activity: string;
1287
+ action: string;
1288
+ roles?: string[];
1289
+ }
1290
+ | {
1291
+ kind: "execute-pin";
1292
+ stage: string;
1293
+ activity: string;
1294
+ action: string;
1295
+ roles: string[];
1296
+ }
1297
+ | {
1298
+ kind: "editor-field";
1299
+ field: string;
1300
+ }
1301
+ | {
1302
+ kind: "editor-content";
1303
+ condition: Condition;
1304
+ }
1305
+ | {
1306
+ kind: "effect";
1307
+ effect: string;
1308
+ }
1309
+ | {
1310
+ kind: "start-input";
1311
+ field: string;
1312
+ }
1313
+ | {
1314
+ kind: "context";
1315
+ key?: string;
1316
+ }
1317
+ | {
1318
+ kind: "subworkflow";
1319
+ definition: string;
1320
+ resolved: boolean;
1321
+ }
1322
+ | {
1323
+ kind: "unwritten-field";
1324
+ field: string;
1325
+ handlerPossible: boolean;
1326
+ };
1327
+
1368
1328
  export declare interface AvailableAction {
1369
1329
  activity: string;
1370
1330
  activityStatus: ActivityStatus;
@@ -1376,7 +1336,10 @@ export declare interface AvailableAction {
1376
1336
  }
1377
1337
 
1378
1338
  /** Flatten an evaluation's current-stage activities into the actions the actor
1379
- * could fire, each carrying whether it's allowed (and why not). */
1339
+ * could fire, each carrying whether it's allowed (and why not). A
1340
+ * filter-scoped-out activity does not exist for this visit, so its actions
1341
+ * never list — the same {@link ActivityEvaluation.scopedOut} split every
1342
+ * other surface applies. */
1380
1343
  export declare function availableActions(
1381
1344
  activities: ActivityEvaluation[],
1382
1345
  ): AvailableAction[];
@@ -1389,6 +1352,22 @@ export declare interface AvailableActionsResult {
1389
1352
  actions: AvailableAction[];
1390
1353
  }
1391
1354
 
1355
+ /**
1356
+ * Type each caller-supplied name→value against the workflow's declared field
1357
+ * entries — the engine takes typed {@link InitialFieldValue}s, and a value's
1358
+ * type IS its declared entry's kind. Only `input`-sourced entries read
1359
+ * caller values (the engine silently ignores the rest), so anything else
1360
+ * fails here, naming the fields that ARE settable; value validation stays
1361
+ * in the engine.
1362
+ */
1363
+ export declare function buildInitialFields({
1364
+ declared,
1365
+ values,
1366
+ }: {
1367
+ declared: Pick<FieldEntry, "type" | "name" | "initialValue">[];
1368
+ values: Record<string, unknown>;
1369
+ }): InitialFieldValue[];
1370
+
1392
1371
  /**
1393
1372
  * Build a snapshot from a set of loaded docs. Pure transform.
1394
1373
  *
@@ -1410,7 +1389,7 @@ export declare function buildSnapshot(args: {
1410
1389
  /**
1411
1390
  * A LOADED candidate document. Applicability evaluates content, so a ref or
1412
1391
  * bare id is not enough: the doc must carry its schema `_type` plus whatever
1413
- * attributes `applicableWhen` predicates read — under whatever perspective
1392
+ * attributes `start.filter` predicates read — under whatever perspective
1414
1393
  * the caller loaded it with.
1415
1394
  */
1416
1395
  export declare interface CandidateDocument {
@@ -1419,12 +1398,12 @@ export declare interface CandidateDocument {
1419
1398
  }
1420
1399
 
1421
1400
  /**
1422
- * Thrown when auto-transitions on an instance fail to stabilise within
1423
- * {@link CascadeLimitError.limit} passes — the signature of a runaway
1424
- * cascade. The classic trigger is two stages whose transition guards
1401
+ * Thrown when hops on an instance fail to stabilise within
1402
+ * {@link CascadeLimitError.limit} moves — the signature of a runaway
1403
+ * cascade. The classic cause is two stages whose transition `when`s
1425
1404
  * stay simultaneously satisfied (e.g. an `op.field.set` lands a value
1426
- * that trips the very guard that fired, and nothing ever clears it), so
1427
- * the engine flip-flops and would otherwise write revisions forever.
1405
+ * that trips the very trigger that fired, and nothing ever clears it),
1406
+ * so the engine flip-flops and would otherwise write revisions forever.
1428
1407
  *
1429
1408
  * The cascade aborts at the limit rather than the engine hanging; the
1430
1409
  * instance is left at whatever stage the last completed pass reached.
@@ -1575,13 +1554,13 @@ export declare interface CompleteEffectArgs extends DedupableOperationArgs {
1575
1554
  effectKey: string;
1576
1555
  status: EffectCompletionStatus;
1577
1556
  /**
1578
- * Named values produced by the effect. If supplied, these are merged
1579
- * into `effectsContext` (replace-by-key) and become available to
1580
- * downstream effect bindings on the same instance. Each value may be any
1581
- * JSON scalar, {@link GlobalDocumentReference}, or an arbitrary
1582
- * object/array (the whole record stores as one `effectsContext.json` entry).
1557
+ * Named values produced by the effect recorded on the run's
1558
+ * `effectHistory` row and read downstream as
1559
+ * `$effects['<effect name>'].<output>` (the latest completed run per
1560
+ * effect name wins). Validated against the effect's declared `outputs`
1561
+ * allowlist; each value may be any JSON.
1583
1562
  */
1584
- outputs?: EffectsContext;
1563
+ outputs?: Record<string, unknown>;
1585
1564
  /**
1586
1565
  * The state half of the effect: `field.*` ops to apply in the completion
1587
1566
  * commit, computed from the effect's real result (e.g. a `field.set` of a
@@ -1616,6 +1595,26 @@ export declare function computeDiffEntries<
1616
1595
  target: DeployTarget;
1617
1596
  }): Promise<DiffEntry[]>;
1618
1597
 
1598
+ /**
1599
+ * Completion twin of {@link ConcurrentFireActionError}: a `completeEffect`
1600
+ * commit lost the optimistic-locking race on every attempt. Same contract —
1601
+ * nothing committed on the final try, so the pending entry (and its claim)
1602
+ * is untouched: the completion can be retried, and a claim whose lease
1603
+ * lapses is recovered by a later drain. The handler's reported outcome is
1604
+ * NOT applied — a caller that retries must not re-run the side effect,
1605
+ * only re-report it.
1606
+ */
1607
+ export declare class ConcurrentCompleteEffectError extends WorkflowError<"concurrent-complete-effect"> {
1608
+ readonly instanceId: string;
1609
+ readonly effectKey: string;
1610
+ readonly attempts: number;
1611
+ constructor(args: {
1612
+ instanceId: string;
1613
+ effectKey: string;
1614
+ attempts: number;
1615
+ });
1616
+ }
1617
+
1619
1618
  /**
1620
1619
  * Edit-seam twin of {@link ConcurrentFireActionError}: an `editField` commit
1621
1620
  * lost the optimistic-locking race on every attempt. Same contract — re-read
@@ -1642,7 +1641,7 @@ export declare class ConcurrentEditFieldError extends WorkflowError<"concurrent-
1642
1641
 
1643
1642
  /**
1644
1643
  * Thrown when a `fireAction` commit loses the optimistic-locking race on
1645
- * all {@link CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS} attempts — every reload +
1644
+ * all {@link CONCURRENT_COMMIT_MAX_ATTEMPTS} attempts — every reload +
1646
1645
  * `ifRevisionId` retry was beaten by another writer committing first.
1647
1646
  * Surfacing it (rather than silently overwriting) lets the caller decide
1648
1647
  * whether to retry later or report a write storm; nothing was committed
@@ -1689,6 +1688,17 @@ export { ConditionClause };
1689
1688
 
1690
1689
  export { ConditionDescription };
1691
1690
 
1691
+ /**
1692
+ * Every STATIC `$fields.<name>` (or `$fields['<name>']` — groq-js normalises
1693
+ * both to `AccessAttribute`) read in a condition, from the AST. Dynamic access
1694
+ * (`$fields[$var]`) carries no static name and is not collected. A malformed
1695
+ * condition reads nothing here ({@link conditionSyntaxIssues} owns the parse
1696
+ * error).
1697
+ */
1698
+ export declare function conditionFieldReadNames(
1699
+ groq: string,
1700
+ ): ReadonlySet<string>;
1701
+
1692
1702
  export { ConditionInsight };
1693
1703
 
1694
1704
  export { ConditionOutcome };
@@ -1697,8 +1707,8 @@ export { ConditionRead };
1697
1707
 
1698
1708
  /**
1699
1709
  * Every condition a definition declares, in stable order: author predicates,
1700
- * then per stage — transitions, activities (filter, requirements,
1701
- * completeWhen, failWhen, action filters), editable gates, tighten-overrides.
1710
+ * then per stage — transitions, activities (filter, requirements, action
1711
+ * filters and `when` triggers), editable gates, tighten-overrides.
1702
1712
  * Editable gates resolve through the same helper the runtime projection uses
1703
1713
  * ({@link editableFieldsInStage}: entry `editable` ANDed with the stage
1704
1714
  * tighten-override), so every live editable-field address exists here with
@@ -1724,25 +1734,33 @@ export declare interface ConditionVar {
1724
1734
  * The condition-variable inventory — the single source of truth for every
1725
1735
  * `$var` the engine binds when it evaluates a {@link Condition}.
1726
1736
  *
1727
- * Three evaluation contexts read a definition's GROQ:
1737
+ * Four evaluation contexts read a definition's GROQ:
1728
1738
  *
1729
1739
  * 1. **Rendered condition scope** — every condition site in a definition
1730
- * (transition filters, `completeWhen`/`failWhen`, action filters, effect
1731
- * bindings, `subworkflows` reads, where-op `where`s, editability
1732
- * predicates, author predicates). {@link CONDITION_VARS} is its inventory;
1733
- * each entry's `binding` says when the var actually holds a value. The
1740
+ * (transition `when`s, activity filters, action `when`s/filters, effect
1741
+ * bindings, `spawn` reads, where-op `where`s, editability predicates,
1742
+ * author predicates). {@link CONDITION_VARS} is its inventory; each
1743
+ * entry's `binding` says when the var actually holds a value. The
1734
1744
  * where-op context is the one closed subset — its bound set is statically
1735
1745
  * fixed and deploy-enforced (see the op-where scope's param-name list in
1736
1746
  * the op applier).
1737
- * 2. **Filter evaluation without a caller** — the engine's cascade evaluates
1738
- * transition filters and the activity gates (`filter`, `completeWhen`,
1739
- * `failWhen`) with no caller, so only the `'always'`-bound subset carries
1740
- * values there ({@link FILTER_SCOPE_VARS}). Caller-bound vars fail closed —
1741
- * `$assigned` binds its caller-free constant `false`, the rest evaluate to
1742
- * `undefined` — and deploy rejects them at these sites: routing and gate
1743
- * resolution must resolve identically no matter whose token triggers the
1744
- * evaluation.
1745
- * 3. **Guard predicates** — NOT conditions. A lake mutation guard's
1747
+ * 2. **Cascade gates** — transition `when`s, activity filters, and a
1748
+ * cascade-fired action's `when`/`filter` must resolve identically no
1749
+ * matter whose token drives the cascade, so only the `'always'`-bound
1750
+ * subset carries values there ({@link FILTER_SCOPE_VARS}). Caller-bound
1751
+ * vars fail closed — `$assigned` binds its caller-free constant `false`,
1752
+ * the rest evaluate to `undefined` — and deploy rejects them at these
1753
+ * sites; a cascade-fired action's per-token gate is `roles`, never its
1754
+ * conditions.
1755
+ * 3. **The start contexts** — a definition's `start.filter` and
1756
+ * `start.allowed` evaluate against a CANDIDATE (no instance exists yet):
1757
+ * `*[...]` reads the WORKFLOW resource's dataset and none of the rendered
1758
+ * condition vars exist. The two split on what a surface can know:
1759
+ * `filter` is browse-time-pure (candidate document as root,
1760
+ * {@link START_FILTER_VARS} — no `$fields`, which cannot exist before
1761
+ * inputs do), `allowed` is the start-time permission predicate
1762
+ * ({@link START_ALLOWED_VARS} — `$fields` bound, never a root).
1763
+ * 4. **Guard predicates** — NOT conditions. A lake mutation guard's
1746
1764
  * `predicate` is groq-js **delta-mode** GROQ over a document mutation:
1747
1765
  * `before()`/`after()`/`identity()` are dialect natives, and the wire
1748
1766
  * format binds the identifiers in {@link GUARD_PREDICATE_VARS}. None of
@@ -1755,11 +1773,43 @@ export declare interface ConditionVar {
1755
1773
  * instance and its snapshot.
1756
1774
  * - `'caller'` — rides the acting caller; without one the var is `undefined`
1757
1775
  * (conditions referencing it fail closed). Author predicates may not read
1758
- * these — they pre-evaluate once per instance, caller-free.
1759
- * - `'spawn'` — bound only at `subworkflows` sites (the per-row `$row`).
1776
+ * these — they pre-evaluate caller-free, per evaluation context.
1777
+ * - `'spawn'` — bound only at spawn sites (the per-row `$row`).
1760
1778
  */
1761
1779
  export declare type ConditionVarBinding = "always" | "caller" | "spawn";
1762
1780
 
1781
+ /**
1782
+ * The one-doc read a reactive adapter subscribes with to observe a content doc
1783
+ * under a perspective stack — the same GROQ as the engine's own hydration read
1784
+ * ({@link hydrateSnapshot}), so the adapter and the engine resolve identical
1785
+ * content: draft/version content projected onto the published id, a doc that
1786
+ * exists only as a draft (or only inside a release) still visible.
1787
+ */
1788
+ export declare function contentDocQuery(documentId: string): CompiledQuery;
1789
+
1790
+ /**
1791
+ * Whether a watched content doc's reactive read may fall back to the DRAFT —
1792
+ * the fallback half of the per-doc resolution rule {@link contentReleaseName}
1793
+ * starts. Mirrors the engine's own hydration under the instance's effective
1794
+ * perspective (`instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE`): a draft
1795
+ * is visible only when that perspective names `'drafts'` — the drafts default,
1796
+ * `'drafts'` itself, or a stack like `[release, 'drafts']`. Under `'published'`
1797
+ * / `'raw'` and under a stack without a `'drafts'` entry (`[release]`,
1798
+ * `['published']`) drafts are invisible, and an adapter feeding one would
1799
+ * evaluate content the engine's read can never see.
1800
+ *
1801
+ * Raw refs ({@link readsRaw}) answer `true` by contract, and it is
1802
+ * load-bearing: instance / ancestor / `system.release` docs have no draft
1803
+ * form, and the `true` keeps them on the adapters' raw per-doc read path —
1804
+ * never a perspective-scoped content read.
1805
+ */
1806
+ export declare function contentDraftFallback(args: {
1807
+ ref: {
1808
+ type: string;
1809
+ };
1810
+ perspective: WorkflowPerspective | undefined;
1811
+ }): boolean;
1812
+
1763
1813
  /**
1764
1814
  * The Content Release a watched doc resolves under, or `undefined` for a raw
1765
1815
  * read — how a reactive adapter turns the watch-set's perspective into the
@@ -1772,9 +1822,10 @@ export declare type ConditionVarBinding = "always" | "caller" | "spawn";
1772
1822
  *
1773
1823
  * Note: this resolves a **single** release — the documented `instance.perspective`
1774
1824
  * shapes (`[release]` / `[release, "drafts"]`). The stores' per-doc reads take
1775
- * one release, so a multi-release stack can't be observed reactively here; the
1776
- * engine's own fetch path ({@link hydrateSnapshot}) honours the full stack via
1777
- * `client.fetch({perspective})`.
1825
+ * one release, so a multi-release stack can't be observed through them; the
1826
+ * engine's own fetch path ({@link hydrateSnapshot}) and the SDK adapter's
1827
+ * query-store route for stacks without a `'drafts'` entry — honour the full
1828
+ * stack via `client.fetch({perspective})`.
1778
1829
  */
1779
1830
  export declare function contentReleaseName(args: {
1780
1831
  ref: {
@@ -1783,12 +1834,92 @@ export declare function contentReleaseName(args: {
1783
1834
  perspective: WorkflowPerspective | undefined;
1784
1835
  }): string | undefined;
1785
1836
 
1837
+ /**
1838
+ * Context-bag entry kinds.
1839
+ */
1840
+ export declare const CONTEXT_ENTRY_DISPLAY: {
1841
+ "context.string": {
1842
+ title: string;
1843
+ description: string;
1844
+ };
1845
+ "context.number": {
1846
+ title: string;
1847
+ description: string;
1848
+ };
1849
+ "context.boolean": {
1850
+ title: string;
1851
+ description: string;
1852
+ };
1853
+ "context.ref": {
1854
+ title: string;
1855
+ description: string;
1856
+ };
1857
+ "context.json": {
1858
+ title: string;
1859
+ description: string;
1860
+ };
1861
+ };
1862
+
1863
+ /**
1864
+ * One entry of the instance's `context` bag, rendered to conditions as
1865
+ * `$context.<name>`. **Written exactly twice, both at start**: the
1866
+ * `startInstance` seed and a parent's `spawn.context` handoff. Never
1867
+ * mutated afterwards — completed effects' outputs live on
1868
+ * `effectHistory[].outputs` and render as `$effects`, a separate bag.
1869
+ *
1870
+ * Critical: `context` is NOT a workflow field container. Transition
1871
+ * triggers must NOT read it as fields. The workflow's fields live on
1872
+ * `fields[]`; external state lives in the lake (queryable via conditions).
1873
+ */
1874
+ export declare type ContextEntry =
1875
+ | {
1876
+ _key: string;
1877
+ _type: "context.string";
1878
+ name: string;
1879
+ value: string;
1880
+ }
1881
+ | {
1882
+ _key: string;
1883
+ _type: "context.number";
1884
+ name: string;
1885
+ value: number;
1886
+ }
1887
+ | {
1888
+ _key: string;
1889
+ _type: "context.boolean";
1890
+ name: string;
1891
+ value: boolean;
1892
+ }
1893
+ | {
1894
+ _key: string;
1895
+ _type: "context.ref";
1896
+ name: string;
1897
+ /** GDR — see `../core/refs.ts` */
1898
+ value: GlobalDocumentReference;
1899
+ }
1900
+ | {
1901
+ _key: string;
1902
+ _type: "context.json";
1903
+ name: string;
1904
+ value: string;
1905
+ };
1906
+
1907
+ /**
1908
+ * Render the instance's `context` bag as the `$context` map — the
1909
+ * start-time seed plus a parent's spawn handoff. `json` entries decode to
1910
+ * their object form.
1911
+ */
1912
+ export declare function contextMap(
1913
+ instance: Pick<WorkflowInstance, "context">,
1914
+ ): Record<string, unknown>;
1915
+
1786
1916
  /**
1787
1917
  * A caller broke a public-API contract — a call the engine rejects
1788
- * before touching the lake: an invalid `tag`, a `workflow.query` GROQ that
1918
+ * without writing anything: an invalid `tag`, a `workflow.query` GROQ that
1789
1919
  * never binds `$tag`, a bare id where a GDR URI is required, an action,
1790
- * activity, or field the definition never declared. Fix the call site;
1791
- * retrying cannot succeed.
1920
+ * activity, or field the definition never declared, or a `startInstance`
1921
+ * `instanceId` reused for a different start (or a discarded one). Fix the
1922
+ * call site; retrying cannot succeed.
1792
1923
  */
1793
1924
  export declare class ContractViolationError extends WorkflowError<"contract-violation"> {
1794
1925
  constructor(message: string);
@@ -1968,6 +2099,11 @@ export declare const DEFAULT_EFFECT_LEASE_MS: number;
1968
2099
  * configured `idempotencyTtlMs`. */
1969
2100
  export declare const DEFAULT_IDEMPOTENCY_TTL_MS: number;
1970
2101
 
2102
+ /** The condition a transition gets when the author declares none — fire once
2103
+ * the stage's own work settles. Exported so consumers describing transition
2104
+ * conditions can recognize the default's spelling without copying it. */
2105
+ export declare const DEFAULT_TRANSITION_WHEN = "$allActivitiesDone";
2106
+
1971
2107
  /**
1972
2108
  * Default LoggerFactory — writes through the global `console`. Exposed
1973
2109
  * so callers outside `createEngine` (drive scripts, the
@@ -2071,7 +2207,7 @@ export declare interface DefinitionsForDocumentArgs {
2071
2207
  /**
2072
2208
  * The LOADED candidate document — unlike {@link InstancesForDocumentArgs},
2073
2209
  * a ref won't do: applicability evaluates content (`_type` against subject
2074
- * entry `types`, `applicableWhen` against the attributes), under whatever
2210
+ * entry `types`, `start.filter` against the attributes), under whatever
2075
2211
  * perspective the caller loaded the document with.
2076
2212
  */
2077
2213
  document: CandidateDocument;
@@ -2201,11 +2337,13 @@ export declare interface DeployDefinitionsArgs<
2201
2337
  > {
2202
2338
  /**
2203
2339
  * Resource-alias bindings for this deploy (alias name → physical resource).
2204
- * Every `@<alias>:` reference in a definition is expanded to its bound
2205
- * physical resource at deploy, so the deployed definition holds only physical
2206
- * references. Deploy fails closed when a definition references an alias this
2207
- * map doesn't bind. Omit for single-resource workflows (definitions that
2208
- * reference no aliases).
2340
+ * A deploy-time abstraction ONLY: every `@<alias>:` reference in a
2341
+ * definition is expanded to its bound physical resource before the
2342
+ * deployed definition is fingerprinted and stored, and deploy fails closed
2343
+ * on an unbound alias. Aliases never exist past deploy — no stored
2344
+ * document carries one, the runtime never sees the map, and an
2345
+ * alias-shaped ref reaching a runtime boundary is rejected as malformed.
2346
+ * Omit for single-resource workflows (definitions referencing no aliases).
2209
2347
  */
2210
2348
  resourceAliases?: ResourceAliases;
2211
2349
  /** Authored definitions (the default — full compile-time checking) or
@@ -2272,16 +2410,45 @@ export declare interface DeployTarget {
2272
2410
  }
2273
2411
 
2274
2412
  /**
2275
- * Classify an activity from its shape, in precedence order: an activity a person acts on
2276
- * (`actions`) is `user`; otherwise an automated effect step is `service`;
2277
- * otherwise an activity that waits on a condition (`completeWhen` or a `subworkflows`
2278
- * fan-out) is `receive`; otherwise an inline machine step is `script`.
2279
- *
2280
- * Never returns `manual` — off-system work reads identically to a `user` or
2281
- * `receive` activity by shape, so it is only ever an EXPLICIT {@link Activity.kind}.
2413
+ * Classify an activity from its shape, BPMN-aligned: `target` marks off-system
2414
+ * work (`manual`); otherwise any fireAction-fired action means a person (or
2415
+ * robot caller) acts on it (`user`); otherwise cascade-fired effects make it a
2416
+ * `service` step; otherwise cascade-fired status flips make it a `receive`
2417
+ * wait; anything left is an inline `script` step.
2282
2418
  */
2283
2419
  export declare function deriveActivityKind(activity: Activity): ActivityKind;
2284
2420
 
2421
+ export declare interface DeriveAutonomyOptions {
2422
+ /** Child definitions for `spawn` recursion, by definition `name` — the
2423
+ * deploy batch or previously deployed set. An unresolvable child reports
2424
+ * `conditional` with an unresolved wait. */
2425
+ children?: ReadonlyMap<string, WorkflowDefinition>;
2426
+ }
2427
+
2428
+ /**
2429
+ * Who, if anyone, fires the activity's actions — derived ahead-of-time from
2430
+ * the activity's shape alone: `off-system` when `target` is present; else
2431
+ * `autonomous` (every action cascade-fired — no caller fires anything),
2432
+ * `interactive` (only fireAction-fired actions), or `hybrid` (mixed). An
2433
+ * actionless activity classifies `autonomous` vacuously (deploy's
2434
+ * terminal-reachability invariant rejects it anyway). Shape-only: whether
2435
+ * the activity actually RESOLVES without a caller is the causal question
2436
+ * `deriveWorkflowAutonomy` answers.
2437
+ */
2438
+ export declare function deriveExecutorClassification(
2439
+ activity: Activity,
2440
+ ): ExecutorClassification;
2441
+
2442
+ /**
2443
+ * Derive the causal autonomy of a stored definition. Pure and instance-free;
2444
+ * conditions must parse (they did at define/deploy time — an unparseable one
2445
+ * throws groq-js's syntax error rather than guessing).
2446
+ */
2447
+ export declare function deriveWorkflowAutonomy(
2448
+ definition: WorkflowDefinition,
2449
+ options?: DeriveAutonomyOptions,
2450
+ ): WorkflowAutonomy;
2451
+
2285
2452
  export declare type Describable =
2286
2453
  | ConditionInsight
2287
2454
  | FieldInsight
@@ -2299,6 +2466,12 @@ export declare function describeAtom(
2299
2466
  fallback: boolean;
2300
2467
  };
2301
2468
 
2469
+ /** One {@link AutonomyWait} as a phrase in the workflow vocabulary. */
2470
+ export declare function describeAutonomyWait(
2471
+ wait: AutonomyWait,
2472
+ ctx: DescribeContext,
2473
+ ): InsightPhrase;
2474
+
2302
2475
  /** Render one condition's insight as a checklist + frontier summary, in the
2303
2476
  * workflow vocabulary. */
2304
2477
  export declare function describeCondition(
@@ -2348,8 +2521,10 @@ export declare function describeFieldInsight(
2348
2521
  /**
2349
2522
  * The one front door: feed it any insight-bearing node off an evaluation and
2350
2523
  * get its description — a checklist for gate nodes (undefined when the node
2351
- * declares no gate), all four gates for an activity, involvement + proposals
2352
- * for a field. Dispatch is by each node's defining property, which is
2524
+ * declares no gate), every declared gate for an activity, involvement +
2525
+ * proposals for a field. For an action node the firing gate wins: a
2526
+ * cascade-fired action describes its `when` trigger; a fireAction action
2527
+ * its `filter`. Dispatch is by each node's defining property, which is
2353
2528
  * unambiguous across the union.
2354
2529
  */
2355
2530
  export declare function describeNode<T extends Describable>(
@@ -2379,7 +2554,7 @@ export declare function describeSiteHeading(
2379
2554
  * so a hand-crafted stuck permutation doesn't have to fabricate insights. */
2380
2555
  export declare type DiagnosedTransition = Pick<
2381
2556
  TransitionEvaluation,
2382
- "transition" | "filterSatisfied" | "unevaluable"
2557
+ "transition" | "whenSatisfied" | "unevaluable"
2383
2558
  >;
2384
2559
 
2385
2560
  /**
@@ -2513,12 +2688,19 @@ export declare type DisabledReason =
2513
2688
  * The action's condition evaluated falsy for this actor — the ONE
2514
2689
  * engine-side gate (advisory, like every engine check). Sugar like
2515
2690
  * `roles` desugared into this condition, so a role miss surfaces
2516
- * here too.
2691
+ * here too. Under filter-existence semantics a consumer renders the
2692
+ * action ABSENT, never disabled.
2517
2693
  */
2518
2694
  kind: "filter-failed";
2519
2695
  filter: string;
2520
2696
  detail?: string;
2521
2697
  }
2698
+ | {
2699
+ /** The action is cascade-fired (`when`) — the engine's cascade is its
2700
+ * only firing path; `fireAction` rejects it for every caller. */
2701
+ kind: "cascade-fired";
2702
+ when: string;
2703
+ }
2522
2704
  | {
2523
2705
  kind: "activity-not-active";
2524
2706
  status: TerminalActivityStatus;
@@ -2657,6 +2839,20 @@ declare type DocumentEnvelopeKey =
2657
2839
  | "modelVersion"
2658
2840
  | "minReaderModel";
2659
2841
 
2842
+ /**
2843
+ * The document-prefilter GROQ arm — matches instances that MAY reference any
2844
+ * of `documents`. A deliberate lake-side SUPERSET (it also matches
2845
+ * exited-stage refs); narrow fetched rows to the exact watch-set with
2846
+ * {@link instanceWatchesDocument}. Binds `$documents` + `$bareIds` into
2847
+ * `params`. Exposed for list builders that compose their own conditions
2848
+ * (e.g. the CLI's cross-partition list); {@link instancesQuery} consumers get
2849
+ * it via the `document`/`documents` filter fields instead.
2850
+ */
2851
+ export declare function documentPrefilter(
2852
+ documents: readonly GdrUri[],
2853
+ params: Record<string, string | string[]>,
2854
+ ): string;
2855
+
2660
2856
  export declare type DocumentValuePermission =
2661
2857
  (typeof DOCUMENT_VALUE_PERMISSIONS)[number];
2662
2858
 
@@ -2859,6 +3055,15 @@ export declare type EditMode = "set" | "append" | "unset";
2859
3055
 
2860
3056
  export declare type Effect = v.InferOutput<typeof EffectSchema>;
2861
3057
 
3058
+ /**
3059
+ * Every terminal state an effect run can record. `done` and `failed` are
3060
+ * reported through completion ({@link EffectCompletionStatus}); `cancelled`
3061
+ * is engine-stamped only — an abort cancelling the entry before dispatch.
3062
+ * A cancellation is not a failure: anything counting failures (dashboards,
3063
+ * retry tooling) must not count aborted-away effects among them.
3064
+ */
3065
+ declare const EFFECT_RUN_STATUSES: readonly ["done", "failed", "cancelled"];
3066
+
2862
3067
  /** The outcomes a completer may report through `completeEffect` — a run
2863
3068
  * either succeeded or failed. `cancelled` is not reportable: only an abort
2864
3069
  * stamps it, on entries that never dispatched. */
@@ -2869,8 +3074,9 @@ export declare type EffectCompletionStatus = Exclude<
2869
3074
 
2870
3075
  /**
2871
3076
  * External effect handler — invoked at drain time with the resolved
2872
- * `params` and a context. Returning `outputs` upserts them into
2873
- * `effectsContext` (replace-by-key) so downstream effects can bind.
3077
+ * `params` and a context. Returning `outputs` records them on the run's
3078
+ * `effectHistory` row, where downstream bindings and conditions read them
3079
+ * as `$effects['<effect name>'].<output>`.
2874
3080
  * Returning `ops` applies the state half of the effect in the completion
2875
3081
  * commit — `field.*` computed from the real result, run through the same op
2876
3082
  * applier as an action's field ops (so a created doc's ref enters `$fields`, or
@@ -2924,7 +3130,7 @@ export declare type EffectHandler = (
2924
3130
  log: (message: string, extra?: Record<string, unknown>) => void;
2925
3131
  },
2926
3132
  ) => Promise<{
2927
- outputs?: EffectsContext;
3133
+ outputs?: Record<string, unknown>;
2928
3134
  ops?: FieldOp[];
2929
3135
  } | void>;
2930
3136
 
@@ -2954,9 +3160,9 @@ export declare interface EffectHistoryEntry {
2954
3160
  stack?: string;
2955
3161
  };
2956
3162
  /**
2957
- * Outputs the runtime reported back. The engine merges these into
2958
- * `effectsContext` (replace-by-name) so downstream effect bindings and
2959
- * `$effects.<name>` reads can reference them. Recorded here for audit.
3163
+ * Outputs the runtime reported back the source the `$effects` read
3164
+ * derives from (`$effects['<effect name>'].<output>` is the latest
3165
+ * completed run's outputs, by name). Recorded here for audit.
2960
3166
  */
2961
3167
  outputs?: Record<string, unknown>;
2962
3168
  }
@@ -3004,14 +3210,16 @@ export declare interface EffectOrigin {
3004
3210
  name: string;
3005
3211
  }
3006
3212
 
3007
- /** The lifecycle boundary that queued an effect — the node IS the moment. */
3008
- export declare type EffectOriginKind = "activity" | "transition" | "action";
3213
+ /** The boundary that queued an effect — actions are the only effect
3214
+ * carriers, so the kind is structural (kept on the row for
3215
+ * self-description). */
3216
+ export declare type EffectOriginKind = "action";
3009
3217
 
3010
3218
  /**
3011
3219
  * Thrown when an effect completes with a returned output its declaration doesn't
3012
3220
  * allow — an undeclared key or a value that doesn't fit the declared shape. The
3013
3221
  * declared `outputs` are a strict allowlist bounding what an effect may persist
3014
- * into `effectsContext`, so the completion does NOT commit rather than silently
3222
+ * onto its `effectHistory` entry, so the completion does NOT commit rather than silently
3015
3223
  * storing off-contract data. An effect that declares no `outputs` has an EMPTY
3016
3224
  * allowlist (produces nothing), so any returned output fails here.
3017
3225
  */
@@ -3022,39 +3230,16 @@ export declare class EffectOutputsInvalidError extends WorkflowError<"effect-out
3022
3230
  }
3023
3231
 
3024
3232
  /**
3025
- * Every terminal state an effect run can record. `done` and `failed` are
3026
- * reported through completion ({@link EffectCompletionStatus}); `cancelled`
3027
- * is engine-stamped only an abort cancelling the entry before dispatch.
3028
- * A cancellation is not a failure: anything counting failures (dashboards,
3029
- * retry tooling) must not count aborted-away effects among them.
3233
+ * Render completed effects' outputs as the `$effects` map each effect's
3234
+ * LATEST completed run with outputs wins, by name, across the whole
3235
+ * history (outputs are workflow-scope handler results; the per-visit
3236
+ * signal is `$effectStatus`).
3030
3237
  */
3031
- export declare type EffectRunStatus = "done" | "failed" | "cancelled";
3238
+ export declare function effectOutputsMap(
3239
+ instance: Pick<WorkflowInstance, "effectHistory">,
3240
+ ): Record<string, unknown>;
3032
3241
 
3033
- /**
3034
- * EffectsContext entry kinds.
3035
- */
3036
- export declare const EFFECTS_CONTEXT_DISPLAY: {
3037
- "effectsContext.string": {
3038
- title: string;
3039
- description: string;
3040
- };
3041
- "effectsContext.number": {
3042
- title: string;
3043
- description: string;
3044
- };
3045
- "effectsContext.boolean": {
3046
- title: string;
3047
- description: string;
3048
- };
3049
- "effectsContext.ref": {
3050
- title: string;
3051
- description: string;
3052
- };
3053
- "effectsContext.json": {
3054
- title: string;
3055
- description: string;
3056
- };
3057
- };
3242
+ export declare type EffectRunStatus = (typeof EFFECT_RUN_STATUSES)[number];
3058
3243
 
3059
3244
  declare const EffectSchema: v.StrictObjectSchema<
3060
3245
  {
@@ -3098,85 +3283,20 @@ declare const EffectSchema: v.StrictObjectSchema<
3098
3283
  * doesn't fit its shape — fails the completion (nothing is stored). Omitting
3099
3284
  * `outputs` is an EMPTY allowlist: the effect produces nothing, so any returned
3100
3285
  * output is rejected — the bound is universal, not opt-in.
3101
- * Why strict: `effectsContext` lives on the instance document, so the allowlist
3102
- * keeps it bounded — a handler can't accidentally spread a whole API response
3103
- * into the instance — and the declared shapes let tooling (e.g. the simulator's
3104
- * drain UI) suggest an effect's exact output keys. Declaring outputs also
3105
- * powers the advisory deploy-time producer/consumer lint.
3106
- */
3107
- readonly outputs: v.OptionalSchema<
3108
- v.ArraySchema<v.GenericSchema<FieldShape>, undefined>,
3109
- undefined
3110
- >;
3111
- },
3112
- undefined
3113
- >;
3114
-
3115
- /**
3116
- * The named-params record effects bind against: what a caller supplies at
3117
- * `startInstance` (stored as {@link WorkflowInstance.effectsContext} entries)
3118
- * and what a handler's `outputs` merges back in (replace-by-key).
3119
- *
3120
- * Each value may be any JSON — a scalar, a {@link GlobalDocumentReference}, or
3121
- * an arbitrary object/array. Scalars and GDRs store as their typed
3122
- * `effectsContext` entries; anything else stores as one `effectsContext.json`
3123
- * entry, so all forms read back the same under `$effects.<name>`.
3124
- */
3125
- export declare type EffectsContext = Record<string, unknown>;
3126
-
3127
- /**
3128
- * Each entry is a named param the runtime hands to effect handlers via
3129
- * binding resolution, rendered to conditions as `$effects.<name>`. **Set at
3130
- * start (including a parent's `subworkflows.context` handoff); appended to
3131
- * only by completed effects reporting `outputs`** — those land as one
3132
- * `json` entry per effect, read as `$effects['<effect name>'].<output>`.
3133
- * Never mutated by user-facing API.
3134
- *
3135
- * Critical: `effectsContext` is NOT a workflow field container.
3136
- * Transition filters must NOT read it as fields. The workflow's fields live
3137
- * on `fields[]`; external state lives in the lake (queryable via filters).
3138
- */
3139
- export declare type EffectsContextEntry =
3140
- | {
3141
- _key: string;
3142
- _type: "effectsContext.string";
3143
- name: string;
3144
- value: string;
3145
- }
3146
- | {
3147
- _key: string;
3148
- _type: "effectsContext.number";
3149
- name: string;
3150
- value: number;
3151
- }
3152
- | {
3153
- _key: string;
3154
- _type: "effectsContext.boolean";
3155
- name: string;
3156
- value: boolean;
3157
- }
3158
- | {
3159
- _key: string;
3160
- _type: "effectsContext.ref";
3161
- name: string;
3162
- /** GDR — see `../core/refs.ts` */
3163
- value: GlobalDocumentReference;
3164
- }
3165
- | {
3166
- _key: string;
3167
- _type: "effectsContext.json";
3168
- name: string;
3169
- value: string;
3170
- };
3171
-
3172
- /**
3173
- * Render `effectsContext` as the `$effects` map. `json` entries decode to
3174
- * their object form so a completed effect's outputs read namespaced:
3175
- * `$effects['<effect name>'].<output>`.
3176
- */
3177
- export declare function effectsContextMap(
3178
- instance: Pick<WorkflowInstance, "effectsContext">,
3179
- ): Record<string, unknown>;
3286
+ * Why strict: outputs land on the instance document's `effectHistory`, so
3287
+ * the allowlist keeps it bounded — a handler can't accidentally spread a
3288
+ * whole API response
3289
+ * into the instance and the declared shapes let tooling (e.g. the simulator's
3290
+ * drain UI) suggest an effect's exact output keys. Declaring outputs also
3291
+ * powers the advisory deploy-time producer/consumer lint.
3292
+ */
3293
+ readonly outputs: v.OptionalSchema<
3294
+ v.ArraySchema<v.GenericSchema<FieldShape>, undefined>,
3295
+ undefined
3296
+ >;
3297
+ },
3298
+ undefined
3299
+ >;
3180
3300
 
3181
3301
  /** The `effectHistory` outcome {@link EffectNotFoundError} reports when the
3182
3302
  * missing key belongs to a settled run. `detail` is the row's recorded
@@ -3274,18 +3394,30 @@ export declare interface Engine {
3274
3394
  /** The startable half of {@link Engine.instancesForDocument}: the latest
3275
3395
  * deployed version of every definition that applies to the LOADED candidate
3276
3396
  * document — startable, a required subject entry accepts its `_type`, and
3277
- * `applicableWhen` passes. All matches, name ascending; advisory — a start
3278
- * picker's filter, never enforcement. */
3397
+ * `start.filter` (browse-time-pure `$fields` never binds) passes. All
3398
+ * matches, name ascending; advisory — a start picker's filter, never
3399
+ * enforcement. */
3279
3400
  definitionsForDocument: (
3280
3401
  args: DefinitionsForDocumentArgs,
3281
3402
  ) => Promise<DeployedDefinition[]>;
3403
+ /** Pre-flight `startInstance`'s gates for a definition + the
3404
+ * `initialFields` gathered so far: the `start.allowed` verdict with its
3405
+ * insight (disable the start affordance on a definitive false AND say
3406
+ * why) plus the still-missing required inputs. Bindability-aware — a
3407
+ * predicate reading a not-yet-supplied entry reports `'unevaluable'`
3408
+ * with the entries named in `unboundReads`, never a collapsed verdict.
3409
+ * Pure read; the enforcement moment is `startInstance` itself. */
3410
+ evaluateStart: (args: EvaluateStartArgs) => Promise<StartEvaluation>;
3282
3411
  /** GROQ query against the engine's workflow resource. `$tag`
3283
3412
  * is bound for tag-scoped filtering. */
3284
3413
  query: <T = unknown>(args: QueryArgs) => Promise<T>;
3285
3414
  /** Snapshot-aware GROQ — runs against the same in-memory view the
3286
- * engine's filters see for the supplied instance. The rendered scope's
3287
- * instance-derived vars ({@link FILTER_SCOPE_VARS}) are bound, ids in
3288
- * GDR URI form to match the snapshot's keying. */
3415
+ * engine's filters see for the supplied instance. The caller-free
3416
+ * rendered scope cascade gates evaluate in is bound the
3417
+ * instance-derived vars ({@link FILTER_SCOPE_VARS}) with the open
3418
+ * stage's overlay merged into `$fields`, `$assigned` at its caller-free
3419
+ * `false`, plus the author's pre-evaluated `$<predicate>` booleans —
3420
+ * ids in GDR URI form to match the snapshot's keying. */
3289
3421
  queryInScope: <T = unknown>(args: QueryInScopeArgs) => Promise<T>;
3290
3422
  /** List every pending effect on an instance. */
3291
3423
  listPendingEffects: (args: InstanceRefArgs) => Promise<PendingEffect[]>;
@@ -3364,8 +3496,9 @@ export declare interface EngineScopeArgs {
3364
3496
  * docs that live in a different Sanity resource than the workflow). Called
3365
3497
  * with a parsed GDR; return a client for that resource, or undefined to let
3366
3498
  * the engine route it — `client` for the workflow resource itself, a
3367
- * sibling derived from `client`'s credentials for anything else. Engine-scope
3368
- * configuration see `CreateEngineArgs`.
3499
+ * sibling derived from `client`'s credentials for anything else. A served
3500
+ * resource is also part of the declared surface for runtime-supplied refs.
3501
+ * Engine-scope configuration — see `CreateEngineArgs`.
3369
3502
  */
3370
3503
  resourceClients?: ResourceClientResolver;
3371
3504
  /**
@@ -3385,6 +3518,16 @@ export declare interface EngineScopeArgs {
3385
3518
  idempotencyTtlMs?: number;
3386
3519
  }
3387
3520
 
3521
+ /**
3522
+ * Pull the {@link GlobalDocumentReference} values out of `doc.ref` /
3523
+ * `doc.refs` (content) field entries. Content only — release field entries come
3524
+ * from {@link entryReleaseRefs}, so guard discovery (which reads this via
3525
+ * `collectEntryDocUris`) stays scoped to content docs.
3526
+ */
3527
+ export declare function entryDocRefs(
3528
+ entries: unknown,
3529
+ ): GlobalDocumentReference[];
3530
+
3388
3531
  /**
3389
3532
  * Pure error helpers — no client, no I/O. The one home for the engine's
3390
3533
  * "coerce an unknown caught value" + "rethrow with context" idioms, so the
@@ -3482,6 +3625,46 @@ export declare function evaluateMutationGuard(args: {
3482
3625
  context: MutationContext;
3483
3626
  }): Promise<boolean>;
3484
3627
 
3628
+ /** Args for `evaluateStart` — the pre-flight read of `startInstance`'s gates
3629
+ * for a definition + the `initialFields` gathered so far (possibly none). */
3630
+ export declare interface EvaluateStartArgs {
3631
+ /** The definition's `name` — which deployed workflow would be started. */
3632
+ definition: string;
3633
+ /** Optional explicit version. Defaults to the highest deployed version. */
3634
+ version?: number;
3635
+ /**
3636
+ * The candidate seed, in `StartInstanceArgs.initialFields` shape. Partial
3637
+ * mid-form input is expected and safe: the verdict is BINDABILITY-AWARE —
3638
+ * when the predicate reads a declared entry these inputs don't supply,
3639
+ * `outcome` reports `'unevaluable'` and {@link StartEvaluation.unboundReads}
3640
+ * names the entries, instead of the collapsed answer GROQ equality would
3641
+ * otherwise produce (`null == x` is false, not null — a definitive-looking
3642
+ * verdict one more input could overturn, in either direction).
3643
+ */
3644
+ initialFields?: InitialFieldValue[];
3645
+ }
3646
+
3647
+ /**
3648
+ * Evaluate one `start.filter` in the start-filter context: `document` (may
3649
+ * be absent — a root read is then GROQ null, fail-closed or vacuous-pass by
3650
+ * shape) as the GROQ root, the {@link StartScope} bindings plus
3651
+ * `$definition`, and — only when
3652
+ * `analyzeCondition` says the filter reads the dataset — the scope's fetched
3653
+ * slice as `*`. Cheap pure evaluation otherwise: no I/O rides a filter that
3654
+ * never scans. GROQ null ("can't decide") is `false` — every consumer of
3655
+ * this verdict fails closed; a parse/evaluation THROW is a malformed
3656
+ * predicate, not an unevaluable one — rethrown loud, naming the definition
3657
+ * (only writable by bypassing deploy validation), so every read surface that
3658
+ * evaluates the filter reports the same context. A failed slice FETCH
3659
+ * propagates as itself: transport trouble, never definition blame.
3660
+ */
3661
+ export declare function evaluateStartFilter(args: {
3662
+ filter: string;
3663
+ definition: Pick<ApplicabilitySource, "name">;
3664
+ document?: CandidateDocument | undefined;
3665
+ scope?: StartScope | undefined;
3666
+ }): Promise<boolean>;
3667
+
3485
3668
  /**
3486
3669
  * Well-known {@link ExecutionContext.kind} values. The field is a free
3487
3670
  * string — these are the shipped vocabulary, not a closed set.
@@ -3528,10 +3711,72 @@ export declare interface ExecutionContext {
3528
3711
  id?: string;
3529
3712
  }
3530
3713
 
3714
+ /**
3715
+ * Executor classifications — who, if anyone, fires an activity's actions
3716
+ * (shape-derived; see `./activity-kind.ts`).
3717
+ */
3718
+ export declare const EXECUTOR_CLASSIFICATION_DISPLAY: {
3719
+ autonomous: {
3720
+ title: string;
3721
+ description: string;
3722
+ };
3723
+ interactive: {
3724
+ title: string;
3725
+ description: string;
3726
+ };
3727
+ "off-system": {
3728
+ title: string;
3729
+ description: string;
3730
+ };
3731
+ hybrid: {
3732
+ title: string;
3733
+ description: string;
3734
+ };
3735
+ };
3736
+
3737
+ export declare const EXECUTOR_CLASSIFICATIONS: readonly [
3738
+ "autonomous",
3739
+ "interactive",
3740
+ "off-system",
3741
+ "hybrid",
3742
+ ];
3743
+
3744
+ export declare type ExecutorClassification =
3745
+ (typeof EXECUTOR_CLASSIFICATIONS)[number];
3746
+
3531
3747
  export { explainCondition };
3532
3748
 
3533
3749
  export { ExplainConditionArgs };
3534
3750
 
3751
+ /**
3752
+ * Explain one `start.allowed` in the start-allowed context: no root (the
3753
+ * subject rides `$fields.<entry>.id` — deploy rejects a root read), the
3754
+ * {@link StartScope} bindings plus `$definition` and the caller's `$fields`
3755
+ * map, and the scope's fetched slice as `*` when the predicate reads the
3756
+ * dataset. Returns the full {@link ConditionInsight} — `outcome` is the
3757
+ * verdict (`'satisfied'` allows; `'unsatisfied'`/`'unevaluable'` refuse,
3758
+ * fail-closed) and the atom breakdown is what lets a surface disable its
3759
+ * start affordance AND say why. One evaluator serves both moments:
3760
+ * `startInstance` enforces the outcome, the `evaluateStart` verb pre-flights
3761
+ * it.
3762
+ *
3763
+ * A dataset-reading predicate with no slice provider THROWS instead of
3764
+ * failing closed: unlike a picker that merely hides a row, this verdict
3765
+ * refuses starts — evaluating over an empty `*` would let a `count()`-style
3766
+ * rule pass vacuously, and a silent "not allowed" would misreport a caller
3767
+ * bug as an author verdict. Parse/evaluation throws rethrow naming the
3768
+ * definition, like the filter's; a failed slice FETCH propagates as itself.
3769
+ */
3770
+ export declare function explainStartAllowed(args: {
3771
+ allowed: string;
3772
+ definition: Pick<ApplicabilitySource, "name">;
3773
+ /** The caller's input entries as a `$fields` map — the engine verbs build
3774
+ * it with `startFieldsParam` (field resolution's projection), so the
3775
+ * predicate can only see values the resolution would persist. */
3776
+ fields: Record<string, unknown>;
3777
+ scope?: StartScope | undefined;
3778
+ }): Promise<ConditionInsight>;
3779
+
3535
3780
  /**
3536
3781
  * Extract the bare document `_id` from a GDR URI — what
3537
3782
  * `*[_id == $docId]` in GROQ needs.
@@ -3759,10 +4004,11 @@ declare type FieldSourceInternal =
3759
4004
 
3760
4005
  /**
3761
4006
  * The field tree of a persisted value — every leaf replaced by its type name
3762
- * (`null` kept distinct from `object`), object keys sorted, arrays
3763
- * element-wise so discriminated variants stay visible. The comparison form
3764
- * the model-surface gates pin per model version, and the form ops tooling
3765
- * can diff a live document against.
4007
+ * (`null` kept distinct from `object`), object keys sorted by UTF-16 code
4008
+ * unit (locale-independent, so the pinned ledgers canonicalise identically
4009
+ * on every machine), arrays element-wise so discriminated variants stay
4010
+ * visible. The comparison form the model-surface gates pin per model
4011
+ * version, and the form ops tooling can diff a live document against.
3766
4012
  */
3767
4013
  export declare function fieldTreeShape(value: unknown): unknown;
3768
4014
 
@@ -3806,9 +4052,8 @@ export declare class FieldValueShapeError extends WorkflowError<"field-value-sha
3806
4052
  }
3807
4053
 
3808
4054
  /**
3809
- * The subset that holds a value when the engine evaluates filters without a
3810
- * caller (the cascade path: transition filters, activity `filter`/
3811
- * `completeWhen`/`failWhen`).
4055
+ * The subset that holds a value in the cascade gates (transition `when`s,
4056
+ * activity filters, a cascade-fired action's `when`/`filter`).
3812
4057
  */
3813
4058
  export declare const FILTER_SCOPE_VARS: readonly string[];
3814
4059
 
@@ -3951,6 +4196,35 @@ export declare function gdrUri(
3951
4196
  ): GdrUri;
3952
4197
 
3953
4198
  /**
4199
+ * The engine's reference value — deliberately NOT a lake reference, and
4200
+ * spelled differently on purpose (`id`/`type`, never `_ref`/`_id`):
4201
+ *
4202
+ * - **`id` is a URI, not a bare doc id**, because workflow references cross
4203
+ * resources (another dataset, a media library) and a bare `_id` is only
4204
+ * meaningful inside one dataset. Everything resource-aware — client
4205
+ * routing, spawn discovery, guard target resolution — reads the resource
4206
+ * out of this URI. It is stripped to a bare lake id at exactly one
4207
+ * boundary: `paramsForLake` (core/params.ts), when a query actually runs
4208
+ * against a lake.
4209
+ * - **`type` carries the target's schema type so consumers don't need a
4210
+ * fetch — as stamped by the ref's constructor, never verified against the
4211
+ * target.** Engine-built refs stamp it from real data (a projection row's
4212
+ * `_type`, the `releaseRef` constructor); a caller-supplied ref's `type`
4213
+ * is a trusted claim. Three advisory surfaces read it: the declared-subject-type
4214
+ * contract (an entry's `types: [...]` accepts/rejects incoming refs —
4215
+ * `refTypeIssues` at start `initialFields` and spawn `with`), start
4216
+ * applicability ("which definitions accept a doc of this `_type`" —
4217
+ * `acceptsDocumentType`), and guard `match.types` inference from resolved
4218
+ * targets (`resolveMatchTypes`). All must work when the target lives in a
4219
+ * resource the reader can't (or shouldn't) fetch from.
4220
+ *
4221
+ * In rendered GROQ this shape is what a reference-held value exposes
4222
+ * (`$fields.<entry>.id` / `.type`) — whereas `_id`/`_type` belong to
4223
+ * documents you hold (lake rows, `$row`, a dereferenced singular `doc.ref`).
4224
+ * The spelling is not a namespace guarantee: a content document can carry
4225
+ * its own `id`/`type` fields — what you're reading follows what the entry's
4226
+ * declared kind puts in your hand, never the key's spelling.
4227
+ *
3954
4228
  * `TType` preserves the `type` literal the constructors ({@link refDataset},
3955
4229
  * {@link gdrRef}, …) were called with, so a constructed ref satisfies
3956
4230
  * literal-typed targets (e.g. a `release.ref` value's `"system.release"`).
@@ -4207,6 +4481,11 @@ declare const GuardReadSchema: v.VariantSchema<
4207
4481
  /** Stored guards carry the printed string reads (the deploy resolver's input). */
4208
4482
  declare const GuardSchema: v.StrictObjectSchema<
4209
4483
  {
4484
+ /**
4485
+ * Lake-id-segment grammar (`^[a-z0-9][a-z0-9-]*$`, deploy-enforced): the
4486
+ * guard's lake `_id` derives from `(instanceId, name)` at stage entry.
4487
+ * Unique per definition.
4488
+ */
4210
4489
  name: v.SchemaWithPipe<
4211
4490
  readonly [
4212
4491
  v.StringSchema<undefined>,
@@ -4278,8 +4557,9 @@ declare const GuardSchema: v.StrictObjectSchema<
4278
4557
  /**
4279
4558
  * Lake GROQ predicate — a distinct eval context: delta-mode GROQ
4280
4559
  * reading the `before()`/`after()` natives, `mutation`, `guard`, and
4281
- * `identity()`. Bare ids/fields only. Omitted or empty means
4282
- * UNCONDITIONAL DENY.
4560
+ * `identity()`. Bare ids/fields only. Polarity: a result of strictly
4561
+ * `true` ALLOWS the matched mutation; anything else (false, null, an
4562
+ * evaluation error) DENIES. Omitted or empty means UNCONDITIONAL DENY.
4283
4563
  */
4284
4564
  predicate: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
4285
4565
  /**
@@ -4380,10 +4660,6 @@ export declare const HISTORY_DISPLAY: {
4380
4660
  title: string;
4381
4661
  description: string;
4382
4662
  };
4383
- activityActivated: {
4384
- title: string;
4385
- description: string;
4386
- };
4387
4663
  activityStatusChanged: {
4388
4664
  title: string;
4389
4665
  description: string;
@@ -4475,14 +4751,6 @@ declare type HistoryEvent =
4475
4751
  reason?: string;
4476
4752
  actor?: Actor;
4477
4753
  }
4478
- | {
4479
- _key: string;
4480
- _type: "activityActivated";
4481
- at: string;
4482
- stage: StageName;
4483
- activity: ActivityName;
4484
- actor?: Actor;
4485
- }
4486
4754
  | {
4487
4755
  _key: string;
4488
4756
  _type: "activityStatusChanged";
@@ -4507,6 +4775,12 @@ declare type HistoryEvent =
4507
4775
  * person/agent/service/engine axis. Absent when no actor was supplied.
4508
4776
  */
4509
4777
  driverKind?: DriverKind;
4778
+ /**
4779
+ * The engine fired this action in a cascade (its `when` turned true) —
4780
+ * {@link actor} is the cascading token that happened to execute it,
4781
+ * not a caller who invoked `fireAction`.
4782
+ */
4783
+ triggered?: true;
4510
4784
  }
4511
4785
  | {
4512
4786
  _key: string;
@@ -4617,16 +4891,15 @@ declare type HistoryEvent =
4617
4891
  _type: "opApplied";
4618
4892
  at: string;
4619
4893
  stage: StageName;
4620
- /** The boundary that ran the op. For an action/transition/activation,
4621
- * exactly one of activity/action/transition is set; for a direct edit
4894
+ /** The boundary that ran the op. For an action fire (caller- or
4895
+ * cascade-fired), `activity` + `action` are set; for a direct edit
4622
4896
  * (the edit seam), `edit` is set and `activity` carries the field's activity when
4623
4897
  * the edited field is activity-scope; for an effect's completion ops,
4624
4898
  * `effect` names the effect. */
4625
4899
  activity?: ActivityName;
4626
4900
  action?: ActionName;
4627
- transition?: string;
4628
4901
  /** Set when the op was a direct edit through the edit seam (`editField`),
4629
- * not an action/transition/activation. */
4902
+ * not an action. */
4630
4903
  edit?: true;
4631
4904
  /** Set when the op came from an effect handler's completion (the state
4632
4905
  * half of an effect); names the effect. */
@@ -4673,6 +4946,15 @@ export declare interface HydratedSnapshot {
4673
4946
  knownIds: Set<string>;
4674
4947
  }
4675
4948
 
4949
+ /**
4950
+ * The in-flight arm — the one spelling of "not completed/aborted" every
4951
+ * list surface's `includeCompleted` filter negates (the engine stamps
4952
+ * `completedAt` on entry into any terminal stage, aborts included). A
4953
+ * sibling of {@link tagScopeFilter} for list builders that compose their
4954
+ * own conditions.
4955
+ */
4956
+ export declare function inFlightFilter(): string;
4957
+
4676
4958
  /**
4677
4959
  * Initial value for an `input`-sourced entry — what a caller supplies at
4678
4960
  * `startInstance` (or `setStage`). Same discriminator shape as
@@ -4706,15 +4988,12 @@ export declare type InsightSite =
4706
4988
  requirement: string;
4707
4989
  }
4708
4990
  | {
4709
- kind: "complete-when";
4710
- activity: string;
4711
- }
4712
- | {
4713
- kind: "fail-when";
4991
+ kind: "action";
4714
4992
  activity: string;
4993
+ action: string;
4715
4994
  }
4716
4995
  | {
4717
- kind: "action";
4996
+ kind: "action-when";
4718
4997
  activity: string;
4719
4998
  action: string;
4720
4999
  }
@@ -4725,6 +5004,16 @@ export declare type InsightSite =
4725
5004
  activity?: string;
4726
5005
  };
4727
5006
 
5007
+ /**
5008
+ * Mint the Sanity document `_id` for a workflow instance — a fresh
5009
+ * {@link randomKey} suffix, so every instance (root or spawned child) gets a
5010
+ * unique doc id under its tag. Lives in the shell, not `core/`, because it
5011
+ * draws randomness; the deterministic {@link definitionDocId} is the pure-core
5012
+ * counterpart. Bare form — Sanity rejects `:` in document IDs, so this is
5013
+ * never a GDR URI.
5014
+ */
5015
+ export declare function instanceDocId(tag: string): string;
5016
+
4728
5017
  /**
4729
5018
  * The per-instance guard filter — the single definition of "this instance's
4730
5019
  * guards in one datasource". The engine's verdict load
@@ -4780,7 +5069,10 @@ export declare interface InstanceSession {
4780
5069
  /** Replace the held content with the current values the consumer observed
4781
5070
  * (last-write-wins, scoped). A self-doc updates the held instance only when
4782
5071
  * its `_updatedAt` is strictly newer — an older or timestamp-equal echo is
4783
- * ignored. Buffered if a commit is in flight. */
5072
+ * ignored. Buffered if a commit is in flight; a BUFFERED push that fails
5073
+ * validation surfaces on the NEXT `update` call (after that call's own
5074
+ * docs are processed), never from the commit itself — the commit's
5075
+ * outcome is never masked by a store echo's failure. */
4784
5076
  update(docs: LoadedDoc[]): void;
4785
5077
  /** Replace the held live guards (the consumer's guard stream,
4786
5078
  * last-write-wins). Guards are a separate stream from the watch-set:
@@ -4823,10 +5115,11 @@ export declare interface InstanceSession {
4823
5115
  * replace the target's staged rows (last write wins); appends accumulate.
4824
5116
  * Tolerant where commits are loud: a target that doesn't resolve in the
4825
5117
  * current stage (it moved under a mounted editor), a closed edit window
4826
- * (a pending activity's field before activation), or a value that doesn't
4827
- * fit the field's shape yet (a half-typed date) stages an INERT preview
4828
- * no echo, no throw; the commit at the semantic boundary is the loud
4829
- * surface. Never persisted — commit via {@link InstanceSession.editField}. */
5118
+ * (a skipped activity's field), a value that doesn't fit the field's
5119
+ * shape yet (a half-typed date), or a ref outside the declared resource
5120
+ * surface stages an INERT preview — no echo, no throw; the commit at the
5121
+ * semantic boundary is the loud surface. Never persisted — commit via
5122
+ * {@link InstanceSession.editField}. */
4830
5123
  previewField(args: {
4831
5124
  target: EditFieldTarget;
4832
5125
  mode?: EditMode;
@@ -4844,8 +5137,10 @@ export declare interface InstancesForDocumentArgs {
4844
5137
 
4845
5138
  /**
4846
5139
  * The instance-list GROQ for a {@link InstancesQueryFilter}, ordered by
4847
- * `startedAt` ascending. Adapters subscribe to it; {@link workflow.query}
4848
- * -style one-shot reads fetch it — both see the same rows.
5140
+ * `startedAt` ascending (descending + sliced under
5141
+ * {@link InstancesQueryFilter.limit}). Adapters subscribe to it;
5142
+ * {@link workflow.query}-style one-shot reads fetch it — both see the same
5143
+ * rows.
4849
5144
  */
4850
5145
  export declare function instancesQuery(args: {
4851
5146
  tag: string;
@@ -4889,6 +5184,14 @@ export declare interface InstancesQueryFilter {
4889
5184
  stage?: string;
4890
5185
  /** Include completed/aborted instances (default: in-flight only). */
4891
5186
  includeCompleted?: boolean;
5187
+ /**
5188
+ * Cap the read to the NEWEST `limit` instances — the query flips to
5189
+ * `startedAt desc` and slices, so a bounded consumer (a dashboard over an
5190
+ * unbounded dataset) reads the most recent rows instead of the oldest.
5191
+ * Unlimited reads keep the ascending order adapters index by. Must be a
5192
+ * positive integer.
5193
+ */
5194
+ limit?: number;
4892
5195
  }
4893
5196
 
4894
5197
  /**
@@ -4908,6 +5211,13 @@ export declare function instanceWatchesDocument(
4908
5211
  document: GdrUri,
4909
5212
  ): boolean;
4910
5213
 
5214
+ /** The trigger split: a `when` action is cascade-fired; without one it is
5215
+ * fireAction-fired. The single spelling of that test — structural over
5216
+ * `when` so authoring and stored shapes both qualify. */
5217
+ export declare function isCascadeFired(action: {
5218
+ when?: string | undefined;
5219
+ }): boolean;
5220
+
4911
5221
  /**
4912
5222
  * A claim whose lease has lapsed no longer protects the entry. A claim
4913
5223
  * without `leaseExpiresAt` (persisted before leases existed) counts as
@@ -4922,11 +5232,11 @@ export { isComparisonOp };
4922
5232
 
4923
5233
  /**
4924
5234
  * The full applicability derivation for one definition against one loaded
4925
- * document: startable ∧ {@link acceptsDocumentType} ∧ `applicableWhen`.
5235
+ * document: startable ∧ {@link acceptsDocumentType} ∧ `start.filter`.
4926
5236
  * "Deployed" is the caller's premise — feed it deployed definitions (the
4927
5237
  * engine verb `definitionsForDocument` does). Throws when `document` carries
4928
- * no `_type` — that's a ref or a projection, not a loaded document. An
4929
- * `applicableWhen` that doesn't PARSE also throws, naming the definition —
5238
+ * no `_type` — that's a ref or a projection, not a loaded document. A
5239
+ * `start.filter` that doesn't PARSE also throws, naming the definition —
4930
5240
  * that's a malformed definition (only writable by bypassing deploy
4931
5241
  * validation), and silently excluding it would make it vanish from every
4932
5242
  * picker with nothing screaming; only an evaluation result of GROQ null
@@ -4935,8 +5245,22 @@ export { isComparisonOp };
4935
5245
  export declare function isDefinitionApplicable(args: {
4936
5246
  definition: ApplicabilitySource;
4937
5247
  document: CandidateDocument;
5248
+ /** Caller-side filter bindings — omit for the bare candidate-doc context. */
5249
+ scope?: StartScope;
4938
5250
  }): Promise<boolean>;
4939
5251
 
5252
+ /**
5253
+ * Whether an entry was scoped OUT at stage entry — the `filter` existence
5254
+ * gate skipped it before it ever started. Distinct from an action-resolved
5255
+ * `skipped` (real, started work whose entry carries `startedAt`): under
5256
+ * filter-existence semantics render surfaces hide the former and keep the
5257
+ * latter. The ONE spelling of that split.
5258
+ */
5259
+ export declare function isFilterScopedOut(entry: {
5260
+ status: ActivityStatus;
5261
+ startedAt?: string | undefined;
5262
+ }): boolean;
5263
+
4940
5264
  /** Type filter for GDR shape. */
4941
5265
  export declare function isGdr(value: unknown): value is GlobalDocumentReference;
4942
5266
 
@@ -4974,7 +5298,7 @@ export declare function isNotesEntry(
4974
5298
  /**
4975
5299
  * Whether a human may start this definition standalone (the default). A
4976
5300
  * `lifecycle: 'child'` definition is spawn-only — instantiated by a parent via
4977
- * `activity.subworkflows`, so consumers exclude it from top-level start pickers.
5301
+ * an action's `spawn`, so consumers exclude it from top-level start pickers.
4978
5302
  * Advisory: the engine does not enforce it (see the `required`-field backstop
4979
5303
  * for load-bearing input fields). Accepts any definition-shaped value (authored,
4980
5304
  * stored, deployed, or a projected list row).
@@ -4983,6 +5307,18 @@ export declare function isStartableDefinition(definition: {
4983
5307
  lifecycle?: WorkflowLifecycle | undefined;
4984
5308
  }): boolean;
4985
5309
 
5310
+ /**
5311
+ * The SUBJECT-ENTRY RULE: a required `doc.ref` / `doc.refs` input entry is
5312
+ * what makes a definition "about" a subject document. One predicate shared by
5313
+ * applicability's type matching (`acceptsDocumentType`), the autonomous-start
5314
+ * deploy invariant, and the root-read deploy check (a root-reading
5315
+ * `start.filter` needs a subject entry to bind a candidate root) — so
5316
+ * "counts as a subject" can't drift between them.
5317
+ */
5318
+ export declare function isSubjectEntry(
5319
+ entry: Pick<FieldEntry, "required" | "type">,
5320
+ ): boolean;
5321
+
4986
5322
  /**
4987
5323
  * The environment denial process shells (CLI, MCP server) feed
4988
5324
  * {@link createTelemetryIntake}: CI and `DO_NOT_TRACK` are trueish
@@ -5030,6 +5366,19 @@ export declare function isTodoListEntry(
5030
5366
  * as null). The remaining columns stay loose (field validation owns them). */
5031
5367
  export declare function isTodoListItem(row: unknown): row is TodoListItem;
5032
5368
 
5369
+ /**
5370
+ * True when the instance document exists but its start never finished: the
5371
+ * `create` committed, yet priming (stage entry, activities, guard deploy)
5372
+ * did not. The shape is legal and resumable — `startInstance` with the same
5373
+ * `instanceId` completes the outstanding commits. Readers should present it
5374
+ * as an incomplete start, never as a normal run sitting at its initial
5375
+ * stage. Terminal instances are excluded: an aborted never-primed husk
5376
+ * reads as aborted, not as still-resumable.
5377
+ */
5378
+ export declare function isUnprimed(
5379
+ instance: Pick<WorkflowInstance, "stages" | "completedAt" | "abortedAt">,
5380
+ ): boolean;
5381
+
5033
5382
  /**
5034
5383
  * Deterministic id so deploy/retract are query-free and idempotent. Derived
5035
5384
  * from the guard's authored `name` — an author-controlled handle that stays
@@ -5058,18 +5407,18 @@ export declare function latestDeployedDefinitions<
5058
5407
 
5059
5408
  /**
5060
5409
  * Advisory producer/consumer check for a deployed definition: every
5061
- * `$effects['<name>'].<key>` read whose `<name>` is a declared effect that
5062
- * declares its `outputs` but does not list `<key>`. Returns human-readable
5063
- * warning strings (empty when clean) — deploy surfaces them, it never rejects.
5064
- * (Runtime completion is where declared `outputs` are ENFORCED; this only warns
5065
- * the author at deploy about a consumer read of a key no producer declares.)
5410
+ * `$effects['<name>'].<key>` read whose `<name>` is not a declared effect,
5411
+ * or is one whose declared `outputs` do not list `<key>`. Returns
5412
+ * human-readable warning strings (empty when clean) — deploy surfaces them,
5413
+ * it never rejects. (Runtime completion is where declared `outputs` are
5414
+ * ENFORCED; this only warns the author at deploy about a consumer read no
5415
+ * producer declares.)
5066
5416
  *
5067
- * Advisory-strength by design: it flags reads against DECLARED effects only. A
5068
- * name that is not a declared effect is left alone it may be a `startInstance`
5069
- * `effectsContext` seed key or a `subworkflows.context` handoff key, runtime
5070
- * producers the static definition cannot see. An effect that declares no
5071
- * `outputs` produces nothing (an empty allowlist), so a keyed read of it is
5072
- * flagged like any undeclared key.
5417
+ * `$effects` derives solely from completed effects' outputs the start
5418
+ * seed and spawn handoff live in `$context` so every producer IS a
5419
+ * declared effect and an unknown name is warnable, not tolerated. An
5420
+ * effect that declares no `outputs` produces nothing (an empty allowlist),
5421
+ * so a keyed read of it is flagged like any undeclared key.
5073
5422
  *
5074
5423
  * Reads are collected from the primary consumer sites: condition GROQ
5075
5424
  * ({@link conditionSitesOf}), effect binding GROQ, and guard reads (the
@@ -5171,8 +5520,8 @@ export declare type MissingHandlerPolicy =
5171
5520
  * read in {@link resolveInputValue}, but is stricter: a present-but-null/
5172
5521
  * undefined value counts as absent here (a required field needs a real
5173
5522
  * value), whereas {@link resolveInputValue} passes a null fill straight
5174
- * through. Exported so pre-flight validators (e.g. a start gate) share the
5175
- * engine's own rule instead of mirroring it.
5523
+ * through. Exported so pre-flight validators (e.g. mapping validation) share
5524
+ * the engine's own rule instead of mirroring it.
5176
5525
  */
5177
5526
  export declare function missingRequiredInputs(args: {
5178
5527
  entryDefs: readonly FieldEntry[];
@@ -5328,6 +5677,8 @@ export declare interface MutationGuardDoc extends MutationGuardBody {
5328
5677
  _updatedAt?: string;
5329
5678
  }
5330
5679
 
5680
+ export declare const MutationGuardDocSchema: v.GenericSchema<MutationGuardDoc>;
5681
+
5331
5682
  export declare interface MutationGuardMatch {
5332
5683
  types?: string[];
5333
5684
  /** Bare document ids (resource-local; both published and `drafts.` forms). */
@@ -5336,6 +5687,14 @@ export declare interface MutationGuardMatch {
5336
5687
  actions: MutationGuardAction[];
5337
5688
  }
5338
5689
 
5690
+ /** Every wait narrated, de-duplicated by phrase: same-named actions on
5691
+ * sibling activities are distinct waits that tell one story — consumers
5692
+ * want one line per story, not one per site. */
5693
+ export declare function narrateAutonomyWaits(
5694
+ waits: readonly AutonomyWait[],
5695
+ ctx: DescribeContext,
5696
+ ): string[];
5697
+
5339
5698
  /** The default logger: emit nothing. The engine's twin of `@sanity/telemetry`'s `noopLogger`. */
5340
5699
  export declare const noopTelemetry: WorkflowTelemetryLogger;
5341
5700
 
@@ -5446,6 +5805,19 @@ export declare function parentRef(
5446
5805
  instance: Pick<WorkflowInstance, "ancestors">,
5447
5806
  ): GlobalDocumentReference | undefined;
5448
5807
 
5808
+ /**
5809
+ * Parse one incoming definition at the deploy/diff boundary (`caller` prefixes
5810
+ * the error). Accepts authored content or a fetched definition document — the
5811
+ * document envelope is stripped (the inverse of the deploy serialisation, so a
5812
+ * fetched document round-trips to `unchanged` instead of fingerprinting its
5813
+ * envelope as content), then the remainder strict-parses against the stored
5814
+ * schema: any unknown key anywhere in the tree fails loud.
5815
+ */
5816
+ export declare function parseDefinitionInput(
5817
+ def: Record<string, unknown>,
5818
+ caller: string,
5819
+ ): WorkflowDefinition;
5820
+
5449
5821
  /**
5450
5822
  * The ONE way to read an instance's frozen {@link WorkflowInstance.definitionSnapshot}
5451
5823
  * back into a {@link WorkflowDefinition} — wraps the parse so a corrupt
@@ -5474,6 +5846,23 @@ export declare interface ParsedGdr {
5474
5846
  */
5475
5847
  export declare function parseGdr(uri: string): ParsedGdr;
5476
5848
 
5849
+ /**
5850
+ * Parse a fetched guard document, failing hard with a
5851
+ * {@link PersistedDocShapeError} naming the document and every offending
5852
+ * field. No model-version gate: the guard doc carries no stamp by design —
5853
+ * its `_type` cutover is its versioning event.
5854
+ */
5855
+ export declare function parseGuardDocument(doc: unknown): MutationGuardDoc;
5856
+
5857
+ /**
5858
+ * Parse a fetched `sanity.workflow.instance` document, failing hard with a
5859
+ * {@link PersistedDocShapeError} naming the document and every offending
5860
+ * field. The caller's model-version gate (`assertReadableModel`) comes
5861
+ * FIRST — a doc from beyond the reader floor is a governed
5862
+ * `ModelVersionAheadError`, not a shape error.
5863
+ */
5864
+ export declare function parseInstanceDocument(doc: unknown): WorkflowInstance;
5865
+
5477
5866
  /**
5478
5867
  * Parse a resource-shaped GDR (`<type>:<id>`, no document part) into the
5479
5868
  * {@link WorkflowResource} it names. The one grammar for resource addresses —
@@ -5562,6 +5951,26 @@ export declare interface PendingEffectClaim {
5562
5951
  leaseExpiresAt?: string;
5563
5952
  }
5564
5953
 
5954
+ /**
5955
+ * A persisted engine-owned document failed its shape parse — the stored
5956
+ * field tree does not match what this engine's data model declares for the
5957
+ * doc type. Distinct from {@link ModelVersionAheadError} (a declared,
5958
+ * governed fence: upgrade the engine); this is UNGOVERNED corruption — a
5959
+ * document no conforming engine wrote — and the read fails hard instead of
5960
+ * letting the malformed value corrupt behavior downstream. The issues name
5961
+ * every offending field path.
5962
+ */
5963
+ export declare class PersistedDocShapeError extends WorkflowError<"persisted-doc-shape"> {
5964
+ readonly documentId: string;
5965
+ readonly documentType: string;
5966
+ readonly issues: ValidationIssue[];
5967
+ constructor(args: {
5968
+ documentId: string;
5969
+ documentType: string;
5970
+ issues: ValidationIssue[];
5971
+ });
5972
+ }
5973
+
5565
5974
  /**
5566
5975
  * One row of the instance's idempotency ledger — a caller-supplied
5567
5976
  * `idempotencyKey` a state-changing verb already committed under. Recorded in
@@ -5601,6 +6010,26 @@ export declare function processShellUserProperties<
5601
6010
  runtimeVersion: string;
5602
6011
  };
5603
6012
 
6013
+ /**
6014
+ * Project a store-resolved doc onto its watch ref's identity, the way the
6015
+ * lake's perspective reads do: the published-form id becomes `_id` (the form
6016
+ * the session keys its overlay and snapshot by), and a draft/version
6017
+ * representation's stored id rides along as `_originalId` — session-side
6018
+ * conditions (including the `_originalId in path("versions.**")` shape) then
6019
+ * read exactly what the engine's own hydration ({@link contentDocQuery})
6020
+ * returns. Strict by construction: only the ids in
6021
+ * {@link watchRefRepresentations} are accepted — an id that merely *ends* in
6022
+ * the watched id (another doc's dotted id under a version prefix), a release
6023
+ * the perspective doesn't read, or a draft the perspective makes invisible is
6024
+ * a store routing bug, and projecting it would make content the engine's read
6025
+ * can never see impersonate the watched doc silently, so it throws instead.
6026
+ */
6027
+ export declare function projectToWatchRef(args: {
6028
+ doc: SanityDocument;
6029
+ ref: SubscriptionDocument;
6030
+ perspective: WorkflowPerspective | undefined;
6031
+ }): SanityDocument;
6032
+
5604
6033
  export declare interface QueryArgs {
5605
6034
  groq: string;
5606
6035
  params?: Record<string, unknown>;
@@ -5610,6 +6039,14 @@ export declare interface QueryInScopeArgs extends InstanceRefArgs, QueryArgs {}
5610
6039
 
5611
6040
  export { quoted };
5612
6041
 
6042
+ /**
6043
+ * The one spelling of the instance read discipline — model gate
6044
+ * ({@link assertReadableModel}) first, shape parse
6045
+ * ({@link parseInstanceDocument}) second — shared by the point-read funnel
6046
+ * above and the list-read sites that fetch instance rows in bulk.
6047
+ */
6048
+ export declare function readInstanceDoc(doc: SanityDocument): WorkflowInstance;
6049
+
5613
6050
  /**
5614
6051
  * Whether a watched ref reads RAW — never perspective-scoped. The
5615
6052
  * instance, its ancestors, and its spawned children (all instance docs), and
@@ -5621,6 +6058,18 @@ export { quoted };
5621
6058
  */
5622
6059
  export declare function readsRaw(ref: { type: string }): boolean;
5623
6060
 
6061
+ /**
6062
+ * Whether a GROQ expression reads its ROOT document — the candidate document
6063
+ * a `start.filter` binds as root. A bare attribute access (`_type == 'task'`)
6064
+ * or `@` in the OUTER scope reads the root; the same inside a construct that
6065
+ * rebinds the implicit `this` per element (`*[...]` filters, projections,
6066
+ * `map`/pipe traversals) addresses THOSE items, so it doesn't. `^` (Parent)
6067
+ * climbs scopes — one that escapes past the outermost scope lands back on the
6068
+ * root and counts, wherever it is nested. A malformed expression reads
6069
+ * nothing here ({@link conditionSyntaxIssues} owns the parse error).
6070
+ */
6071
+ export declare function readsRootDocument(groq: string): boolean;
6072
+
5624
6073
  /** Make a GDR pointer to a Canvas-resource doc. */
5625
6074
  export declare function refCanvas<TType extends string = string>({
5626
6075
  resourceId,
@@ -5655,6 +6104,18 @@ export declare function refMediaLibrary<TType extends string = string>({
5655
6104
  type,
5656
6105
  }: ResourceRefArgs<TType>): GlobalDocumentReference<TType>;
5657
6106
 
6107
+ /**
6108
+ * Thrown when a field write carries a ref to a resource outside the
6109
+ * declared surface. The write does not commit; the engine has not mutated
6110
+ * state.
6111
+ */
6112
+ export declare class RefResourceUndeclaredError extends WorkflowError<"ref-resource-undeclared"> {
6113
+ readonly entryType: string;
6114
+ readonly entryName: string;
6115
+ readonly issues: string[];
6116
+ constructor(args: { entryName: string; entryType: string; issues: string[] });
6117
+ }
6118
+
5658
6119
  /**
5659
6120
  * The GDR `type`s in a `doc.ref` / `doc.refs` value that the entry's declared
5660
6121
  * accepted `types` reject — empty when the value conforms, the entry declares
@@ -5720,8 +6181,8 @@ export declare function remediationsFor(
5720
6181
  /**
5721
6182
  * A verb that would unstick a {@link StuckCause} — the *what to do about it*
5722
6183
  * half of a diagnosis. Surface-neutral identifiers; a consumer maps each to
5723
- * its own command or button. `retry-effect`, `drain-effects` and `reset-activity`
5724
- * are not yet callable engine operations — see {@link SuggestedRemediation.available}.
6184
+ * its own command or button. `retry-effect` and `reset-activity` are not yet
6185
+ * callable engine operations — see {@link SuggestedRemediation.available}.
5725
6186
  */
5726
6187
  export declare type RemediationVerb =
5727
6188
  | "retry-effect"
@@ -5846,9 +6307,10 @@ export declare function resolveFieldEntry(
5846
6307
  export declare type ResourceAliases = Record<string, WorkflowResource>;
5847
6308
 
5848
6309
  /**
5849
- * Collapse a deployment's `resourceAliases` bindings into the engine's
5850
- * {@link ResourceAliases} map (handle name → physical resource) — the shape
5851
- * {@link deployDefinitions} consumes.
6310
+ * Collapse a deployment's `resourceAliases` bindings into the
6311
+ * {@link ResourceAliases} map (handle name → physical resource) that
6312
+ * `deployDefinitions` expands `@<handle>:` references against. Deploy-time
6313
+ * only — the map never reaches any other verb.
5852
6314
  */
5853
6315
  export declare function resourceAliasesToMap(
5854
6316
  resourceAliases: WorkflowDeployment["resourceAliases"],
@@ -5861,6 +6323,10 @@ export declare function resourceAliasesToMap(
5861
6323
  * engine route it — its own client for the workflow resource, a derived
5862
6324
  * sibling ({@link WorkflowClient.withConfig}) for anything else.
5863
6325
  *
6326
+ * Serving a resource also DECLARES it on the written-ref surface: the write
6327
+ * boundaries probe this resolver per ref, so narrowing a resolver rejects
6328
+ * refs to the resources it stops serving.
6329
+ *
5864
6330
  * Resolved clients ride the engine's API version like every other engine
5865
6331
  * client: the verb scope rebinds them onto `ENGINE_API_VERSION` when they
5866
6332
  * carry {@link WorkflowClient.withConfig} (one without it is used as
@@ -5895,6 +6361,13 @@ declare interface ResourceRefArgs<TType extends string> {
5895
6361
  type: TType;
5896
6362
  }
5897
6363
 
6364
+ export declare interface ResourceSurface {
6365
+ /** True when the parsed GDR targets a declared resource. */
6366
+ allows: (parsed: ParsedGdr) => boolean;
6367
+ /** Prose rendering of the declared surface, for rejection messages. */
6368
+ description: string;
6369
+ }
6370
+
5898
6371
  /**
5899
6372
  * Retract every guard for a stage being exited (lift predicate to allow), but
5900
6373
  * only once the instance has genuinely moved off that stage — or was aborted
@@ -5966,6 +6439,23 @@ declare const RoleAliasesSchema: v.RecordSchema<
5966
6439
  undefined
5967
6440
  >;
5968
6441
 
6442
+ /** Whether two workflow resources address the same place. */
6443
+ export declare function sameResource(
6444
+ a: WorkflowResource,
6445
+ b: WorkflowResource,
6446
+ ): boolean;
6447
+
6448
+ /**
6449
+ * The DECLARED field tree of a runtime schema — every entry key (optional
6450
+ * keys marked `key?`), every enum vocabulary, every variant arm — walked out
6451
+ * of valibot's runtime shape. The declared twin of `fieldTreeShape` (which
6452
+ * walks a CONCRETE document): the model-surface gate pins persisted doc
6453
+ * types by what their schema declares, so an optional field no fixture
6454
+ * reaches is still ledger-visible. Recursive grammars (a field shape nesting
6455
+ * field shapes) close with a `'(circular)'` marker.
6456
+ */
6457
+ export declare function schemaTreeShape(schema: v.GenericSchema): unknown;
6458
+
5969
6459
  export { ScopeAssignment };
5970
6460
 
5971
6461
  export { sentenceCase };
@@ -6010,6 +6500,21 @@ export declare type Stage = StageFields<
6010
6500
  Editable
6011
6501
  >;
6012
6502
 
6503
+ /** A stage's rollup: progress through any exit transition, plus each
6504
+ * activity's own completion answer. */
6505
+ export declare interface StageAutonomy extends AutonomyAnswer {
6506
+ stage: string;
6507
+ activities: Record<string, AutonomyAnswer>;
6508
+ }
6509
+
6510
+ /** The stage slice of a rollup. Total by construction — the rollup derives
6511
+ * from the same `definition.stages` consumers iterate, so a miss is an
6512
+ * engine bug worth failing loud on. */
6513
+ export declare function stageAutonomyOf(
6514
+ autonomy: WorkflowAutonomy,
6515
+ stageName: string,
6516
+ ): StageAutonomy;
6517
+
6013
6518
  export declare interface StageEntry {
6014
6519
  _key: string;
6015
6520
  name: StageName;
@@ -6025,6 +6530,9 @@ export declare interface StageEvaluation {
6025
6530
  stage: Stage;
6026
6531
  activities: ActivityEvaluation[];
6027
6532
  transitions: TransitionEvaluation[];
6533
+ /** The stage's causal-autonomy rollup — will it progress without a caller,
6534
+ * and what does it wait on. Definition-derived, identical for every actor. */
6535
+ autonomy: StageAutonomy;
6028
6536
  }
6029
6537
 
6030
6538
  /** Type-mirror of {@link stageFields}, parameterised over field/activity/transition/guard/editable. */
@@ -6057,6 +6565,118 @@ declare interface StageGuardArgs {
6057
6565
 
6058
6566
  export declare type StageName = string;
6059
6567
 
6568
+ /**
6569
+ * The vars a definition's `start.allowed` reads — the start-time permission
6570
+ * dialect: everything the filter context binds ({@link START_FILTER_VARS})
6571
+ * plus `$fields`, which start time always has. No candidate root ever binds
6572
+ * here (the subject rides `$fields.<entry>.id`; a root read is
6573
+ * deploy-rejected), so a false/GROQ-null verdict is genuinely fail-closed —
6574
+ * there is no unbound-binding ambiguity to caveat. Bound in one place:
6575
+ * `startContextParams` in the applicability evaluator.
6576
+ */
6577
+ export declare const START_ALLOWED_VARS: readonly {
6578
+ name: string;
6579
+ description: string;
6580
+ }[];
6581
+
6582
+ /**
6583
+ * The vars a definition's `start.filter` reads — the start-filter dialect,
6584
+ * not the rendered condition scope (no {@link ConditionVarBinding}: these
6585
+ * bind only while the filter is evaluated on the READ side — the
6586
+ * `definitionsForDocument` derivation and the Studio start control).
6587
+ * `startInstance` never evaluates the filter. BROWSE-TIME-PURE: every var is
6588
+ * knowable before any inputs exist, so `$fields` is deliberately absent — a
6589
+ * `$fields` read in a filter is deploy-rejected with a pointer to
6590
+ * `start.allowed`, the context that binds it. An absent binding evaluates to
6591
+ * GROQ null; the VERDICT-level coercion (null result ⇒ not applicable) is
6592
+ * what fails closed — what a null read does inside the expression depends on
6593
+ * its shape. Bound in one place: `startContextParams` in the applicability
6594
+ * evaluator.
6595
+ */
6596
+ export declare const START_FILTER_VARS: readonly {
6597
+ name: string;
6598
+ description: string;
6599
+ }[];
6600
+
6601
+ declare const START_KINDS: readonly ["interactive", "autonomous"];
6602
+
6603
+ export declare type StartBlock = StartFields & {
6604
+ kind: StartKind;
6605
+ };
6606
+
6607
+ /**
6608
+ * The start-time seed a caller supplies at `startInstance`, stored as
6609
+ * {@link WorkflowInstance.context} entries and read back as
6610
+ * `$context.<name>`. Written only at start — a completed effect's
6611
+ * `outputs` live on the run row and read as `$effects`, a separate bag.
6612
+ *
6613
+ * Each value may be any JSON — a scalar, a {@link GlobalDocumentReference}, or
6614
+ * an arbitrary object/array. Scalars and GDRs store as their typed
6615
+ * `context` entries; anything else stores as one `context.json` entry.
6616
+ */
6617
+ export declare type StartContext = Record<string, unknown>;
6618
+
6619
+ /**
6620
+ * What `evaluateStart` projects — the same gates `startInstance` enforces,
6621
+ * as renderable state. `allowed` and `missingRequired` are deliberately
6622
+ * orthogonal: a missing required input is a caller mistake (the verb throws
6623
+ * `RequiredFieldNotProvidedError` for it), never a `start.allowed` verdict.
6624
+ */
6625
+ export declare interface StartEvaluation {
6626
+ /** The `start.allowed` verdict — `outcome === 'satisfied'`. Vacuously true
6627
+ * when the definition declares no `allowed`, exactly like the verb. */
6628
+ allowed: boolean;
6629
+ /**
6630
+ * Three-valued, BINDABILITY-AWARE verdict: `'unsatisfied'` is a definitive
6631
+ * no for these inputs (disable and explain), `'unevaluable'` means the
6632
+ * predicate can't be decided yet — either a null operand reached an
6633
+ * ordered comparison, or the predicate reads an entry `initialFields`
6634
+ * doesn't supply ({@link StartEvaluation.unboundReads} non-empty). Keep
6635
+ * the affordance enabled on `'unevaluable'`; `startInstance` still
6636
+ * enforces the final verdict, where absence is final rather than
6637
+ * provisional.
6638
+ */
6639
+ outcome: ConditionOutcome;
6640
+ /** The `start.allowed` atom breakdown over the SUPPLIED inputs — what to
6641
+ * render next to a disabled start affordance. Present iff the definition
6642
+ * declares `allowed`. With `unboundReads` non-empty its own `outcome` may
6643
+ * be a collapsed provisional answer — the top-level `outcome` is the
6644
+ * trustworthy one. */
6645
+ insight?: ConditionInsight;
6646
+ /** The `$fields` entries the predicate reads that `initialFields` doesn't
6647
+ * supply — "fill these to decide". Non-empty forces `outcome:
6648
+ * 'unevaluable'`. Empty when the definition declares no `allowed`. */
6649
+ unboundReads: string[];
6650
+ /** Required input entries `initialFields` doesn't fill yet — the rows a
6651
+ * `startInstance` now would throw `RequiredFieldNotProvidedError` about. */
6652
+ missingRequired: {
6653
+ name: string;
6654
+ type: FieldEntry["type"];
6655
+ }[];
6656
+ }
6657
+
6658
+ /** Type-mirror of {@link startFields}: how standalone runs of this workflow
6659
+ * begin. Stored requires `kind` (desugar fills the `'interactive'` default);
6660
+ * authoring may omit it — so each variant declares it. */
6661
+ declare type StartFields = {
6662
+ filter?: string | undefined;
6663
+ allowed?: string | undefined;
6664
+ };
6665
+
6666
+ /**
6667
+ * Project caller-supplied `initialFields` into the `$fields` map the
6668
+ * start-allowed context binds: one key per declared `input`-sourced entry,
6669
+ * resolved through {@link suppliedFieldFor} — the predicate can only ever see
6670
+ * a value the input resolution would persist, and an undeclared supplied name
6671
+ * never leaks in. Unsupplied (or null-supplied) entries stay unbound, so a
6672
+ * read of one is GROQ null. Document references bind as their GDR envelopes
6673
+ * (`$fields.<entry>.id` is the GDR URI).
6674
+ */
6675
+ export declare function startFieldsParam(args: {
6676
+ entryDefs: readonly FieldEntry[];
6677
+ initialFields: readonly InitialFieldValue[];
6678
+ }): Record<string, unknown>;
6679
+
6060
6680
  export declare interface StartInstanceArgs {
6061
6681
  /** The definition's `name` — which deployed workflow to instantiate. */
6062
6682
  definition: string;
@@ -6079,17 +6699,25 @@ export declare interface StartInstanceArgs {
6079
6699
  initialFields?: InitialFieldValue[];
6080
6700
  ancestors?: GlobalDocumentReference[];
6081
6701
  /**
6082
- * Stable named params for effect handlers, set at start time. The
6083
- * runtime resolves effect bindings against these to materialise
6084
- * handler params at queue time. **Not read by transition filters.**
6702
+ * The instance's start seed stable named values set once at start (or
6703
+ * handed down by a parent's `spawn.context`) and never mutated after.
6704
+ * Effect bindings and conditions read them as `$context.<name>`; the
6705
+ * `$effects` bag is separate (completed effects' outputs only).
6085
6706
  *
6086
6707
  * Each value may be any JSON — a scalar, a {@link GlobalDocumentReference},
6087
6708
  * or an arbitrary object/array. Scalars and GDRs store as their typed
6088
- * `effectsContext` entries; anything else stores as one `effectsContext.json`
6089
- * entry, so all forms read back the same under `$effects.<name>`.
6709
+ * `context` entries; anything else stores as one `context.json`
6710
+ * entry, so all forms read back the same under `$context.<name>`.
6711
+ */
6712
+ context?: StartContext;
6713
+ /**
6714
+ * Caller-supplied id; auto-generated otherwise (mint one with
6715
+ * `instanceDocId`). This is start's idempotency key: a retry carrying the
6716
+ * same id resumes the earlier attempt's outstanding commits instead of
6717
+ * creating a duplicate — the per-instance request ledger can't cover the
6718
+ * create (it lives on the instance document), so id identity is the rail.
6719
+ * Inputs bind on the create only; a resume never re-reads them.
6090
6720
  */
6091
- effectsContext?: EffectsContext;
6092
- /** Caller-supplied id; auto-generated otherwise. */
6093
6721
  instanceId?: string;
6094
6722
  /**
6095
6723
  * URL path on the supplied client where the engine fetches the
@@ -6118,6 +6746,122 @@ export declare interface StartInstanceArgs {
6118
6746
  perspective?: WorkflowPerspective;
6119
6747
  }
6120
6748
 
6749
+ /**
6750
+ * Who initiates a standalone run of this workflow: a person picking it from a
6751
+ * start surface (`'interactive'`, the default) or a system reacting to a
6752
+ * document event (`'autonomous'`). PURE CLASSIFICATION — consumers use it to
6753
+ * shape their surfaces (hide autonomous workflows from human pickers, list
6754
+ * interactive ones for editors); `startInstance` is one verb and one code
6755
+ * path for every kind, and an interactive start of an autonomous workflow is
6756
+ * legal.
6757
+ */
6758
+ export declare type StartKind = (typeof START_KINDS)[number];
6759
+
6760
+ /**
6761
+ * The declared {@link StartKind} of a definition, defaulting the absent
6762
+ * `start` block to `'interactive'` — the one reading rule for every consumer,
6763
+ * so definitions deployed before the block existed classify unchanged.
6764
+ * Classification only: the engine never gates on it.
6765
+ */
6766
+ export declare function startKindOf(definition: {
6767
+ start?:
6768
+ | {
6769
+ kind?: StartKind | undefined;
6770
+ }
6771
+ | undefined;
6772
+ }): StartKind;
6773
+
6774
+ /**
6775
+ * Thrown by `startInstance` when the definition's `start.allowed` predicate
6776
+ * is not satisfied for the supplied `initialFields` — evaluated false, or
6777
+ * GROQ null ("can't decide"; fail-closed, `insight.outcome` tells the two
6778
+ * apart). Carries the full {@link ConditionInsight} so a consumer renders
6779
+ * the same explanation the `evaluateStart` pre-flight would have shown.
6780
+ * There is no override arg — like `fireAction`, you don't bypass a verdict,
6781
+ * you change what produces it. Advisory under races like every engine-side
6782
+ * check.
6783
+ */
6784
+ export declare class StartNotAllowedError extends WorkflowError<"start-not-allowed"> {
6785
+ readonly definition: string;
6786
+ readonly insight: ConditionInsight;
6787
+ constructor(args: { definition: string; insight: ConditionInsight });
6788
+ }
6789
+
6790
+ /**
6791
+ * Thrown when a start fails AFTER its create committed but BEFORE priming:
6792
+ * the instance document exists, unprimed (see {@link isUnprimed}) — a
6793
+ * genuinely failed start, but a RESUMABLE one. The error names the id so
6794
+ * every surface's failure message can point at the retry rail: retrying
6795
+ * `startInstance` with this `instanceId` finishes the start instead of
6796
+ * creating a duplicate; aborting the instance discards it.
6797
+ */
6798
+ export declare class StartNotPrimedError extends WorkflowError<"start-not-primed"> {
6799
+ readonly instanceId: string;
6800
+ constructor(args: { instanceId: string; cause: unknown });
6801
+ }
6802
+
6803
+ /**
6804
+ * Thrown when a start's first cascade fails AFTER the instance was created
6805
+ * and primed: the run exists — stage entered, activities active, guards
6806
+ * deployed — it just hasn't auto-advanced to a stable stage yet. Callers
6807
+ * must not present this as a failed start: the workflow IS running, and a
6808
+ * `tick` (or retrying `startInstance` with the same `instanceId`) resumes
6809
+ * the settling.
6810
+ */
6811
+ export declare class StartNotSettledError extends WorkflowError<"start-not-settled"> {
6812
+ readonly instanceId: string;
6813
+ /** The primed instance re-read after the cascade failure — absent when
6814
+ * that best-effort read also failed. */
6815
+ readonly instance?: WorkflowInstance;
6816
+ constructor(args: {
6817
+ instanceId: string;
6818
+ instance?: WorkflowInstance;
6819
+ cause: unknown;
6820
+ });
6821
+ }
6822
+
6823
+ /**
6824
+ * Why a definition can't be started standalone, or `undefined` when it can.
6825
+ * Advisory — the engine itself does not refuse ({@link isStartableDefinition})
6826
+ * — but a start surface has no way to supply the parent context a spawn-only
6827
+ * child expects, so it fails fast with this message instead.
6828
+ */
6829
+ export declare function startRefusal(definition: {
6830
+ lifecycle?: WorkflowLifecycle | undefined;
6831
+ }): string | undefined;
6832
+
6833
+ /**
6834
+ * The caller-side half of the start contexts — everything the evaluating
6835
+ * surface knows that the definition doesn't. Every member is optional
6836
+ * because the surfaces genuinely differ (a pure consumer may hold no clock):
6837
+ * an absent binding evaluates each read of it to GROQ null, and where that
6838
+ * null lands decides the verdict — a predicate that can't decide without
6839
+ * the binding fails closed, while a count-of-matches clause over values
6840
+ * every row stores passes vacuously (in GROQ null equals only null, so the
6841
+ * unbound read matches no stored value). The vars themselves are
6842
+ * inventoried in `START_FILTER_VARS` / `START_ALLOWED_VARS`; the caller's
6843
+ * `$fields` map is NOT scope — `start.filter` never binds it, and
6844
+ * `explainStartAllowed` takes it as its own argument.
6845
+ */
6846
+ export declare interface StartScope {
6847
+ /** The engine's tag partition — binds `$tag`. */
6848
+ tag?: string | undefined;
6849
+ /** ISO clock reading — binds `$now`. */
6850
+ now?: string | undefined;
6851
+ /**
6852
+ * The WORKFLOW resource's dataset, for predicates that read it (`*[...]` or
6853
+ * a deref) — invoked lazily, only when `analyzeCondition` says the
6854
+ * predicate needs it. A slice is fine as long as it covers what predicates
6855
+ * scan; the engine verbs supply EVERY instance of the tag, completed
6856
+ * included — `*` carries no hidden predicate, so authors qualify in-flight
6857
+ * themselves (`!defined(completedAt)`). Absent ⇒ a dataset-reading filter
6858
+ * fails closed (this surface cannot see the dataset, so it cannot decide),
6859
+ * while a dataset-reading `allowed` THROWS — see
6860
+ * {@link explainStartAllowed}.
6861
+ */
6862
+ fetchDataset?: (() => Promise<unknown[]>) | undefined;
6863
+ }
6864
+
6121
6865
  /**
6122
6866
  * Declared editability of a field — the generic edit seam's gate. Default
6123
6867
  * (absent) is NOT editable: a field is op-only engine working memory unless the
@@ -6468,7 +7212,7 @@ declare const StoredOpSchema: v.VariantSchema<
6468
7212
  ]
6469
7213
  >;
6470
7214
  readonly status: v.PicklistSchema<
6471
- readonly ["pending", "active", "done", "skipped", "failed"],
7215
+ readonly ["active", "done", "skipped", "failed"],
6472
7216
  `Invalid option: expected one of ${string}`
6473
7217
  >;
6474
7218
  },
@@ -6507,7 +7251,7 @@ export declare type StuckCause =
6507
7251
  kind: "no-transition-fires";
6508
7252
  }
6509
7253
  /**
6510
- * Every activity resolved, but an exit transition's filter is *unevaluable*
7254
+ * Every activity resolved, but an exit transition's `when` is *unevaluable*
6511
7255
  * (GROQ `null` — a referenced operand is missing/unreadable), so selection
6512
7256
  * halts. Unlike {@link StuckCause} `no-transition-fires` this is recoverable:
6513
7257
  * the cascade re-fires once the operand resolves (e.g. the subject is
@@ -6582,6 +7326,12 @@ export declare interface SubworkflowEntry {
6582
7326
  _key: string;
6583
7327
  /** The spawning activity's name. */
6584
7328
  activity: ActivityName;
7329
+ /**
7330
+ * The spawning ACTION's name — rows key per (activity entry, spawning
7331
+ * action), so two spawn actions on one activity can never cross-adopt
7332
+ * each other's children.
7333
+ */
7334
+ action: string;
6585
7335
  /** The child definition's `name` — adoption never crosses definitions. */
6586
7336
  definition: string;
6587
7337
  /**
@@ -6705,8 +7455,8 @@ declare const SubworkflowsSchema: v.StrictObjectSchema<
6705
7455
  >;
6706
7456
  /**
6707
7457
  * Extra values evaluated in the parent's rendered scope at spawn time and
6708
- * delivered into each subworkflow's `$effects` bag — the parent→child
6709
- * handoff, read exactly like an effect output.
7458
+ * delivered into each subworkflow's `$context` bag — the parent→child
7459
+ * handoff.
6710
7460
  */
6711
7461
  readonly context: v.OptionalSchema<
6712
7462
  v.RecordSchema<
@@ -6728,13 +7478,13 @@ declare const SubworkflowsSchema: v.StrictObjectSchema<
6728
7478
  >;
6729
7479
  /**
6730
7480
  * What happens to still-live children when their cohort's scope stops
6731
- * applying — the spawning stage exits, or a re-entry's `forEach` no longer
7481
+ * applying — the spawning stage exits, or a re-fire's `forEach` no longer
6732
7482
  * discovers their row. `'detach'` (the default) lets them run to
6733
7483
  * completion outside the gate; `'abort'` kills them (recursively). The
6734
7484
  * engine never destroys in-flight work implicitly — `'abort'` is always
6735
7485
  * an authored choice. Note this governs only the CHILDREN's fate; whether
6736
- * the parent may move at all is what gates (`completeWhen`, transition
6737
- * filters over `$subworkflows`) decide.
7486
+ * the parent may move at all is what gates (conditions over
7487
+ * `$subworkflows`) decide.
6738
7488
  */
6739
7489
  readonly onExit: v.OptionalSchema<
6740
7490
  v.PicklistSchema<
@@ -6910,46 +7660,42 @@ export declare interface TodoListItem {
6910
7660
  dueDate?: string | null;
6911
7661
  }
6912
7662
 
6913
- export declare type Transition = TransitionFields<TransitionOp> & {
6914
- filter: string;
7663
+ export declare type Transition = TransitionFields & {
7664
+ when: string;
6915
7665
  };
6916
7666
 
6917
7667
  export declare interface TransitionEvaluation {
6918
7668
  transition: Transition;
6919
7669
  /**
6920
- * Whether the transition's filter is definitively satisfied at read time.
6921
- * Projected caller-free, exactly as the engine's cascade evaluates it — the
6922
- * evaluation's actor and grants never participate.
7670
+ * Whether the transition's `when` trigger is definitively satisfied at read
7671
+ * time. Projected caller-free, exactly as the engine's cascade evaluates
7672
+ * it — the evaluation's actor and grants never participate.
6923
7673
  */
6924
- filterSatisfied: boolean;
7674
+ whenSatisfied: boolean;
6925
7675
  /**
6926
- * The filter evaluated to GROQ `null` — a referenced operand was missing or
6927
- * incomparable, so it couldn't be decided. `filterSatisfied` is `false`, but
7676
+ * The trigger evaluated to GROQ `null` — a referenced operand was missing or
7677
+ * incomparable, so it couldn't be decided. `whenSatisfied` is `false`, but
6928
7678
  * this is a *recoverable hold*, not a deliberate `false`: the engine halts
6929
7679
  * selection (it won't fall through to a later transition) and the cascade
6930
7680
  * re-fires once the operand resolves. Lets a diagnosis tell an undecidable
6931
7681
  * hold apart from a genuine routing dead-end.
6932
7682
  */
6933
7683
  unevaluable: boolean;
6934
- /** Derived state of the transition's filter — the atoms standing between
6935
- * here and the next stage. `filterSatisfied`/`unevaluable` are projections
7684
+ /** Derived state of the transition's `when` trigger — the atoms standing between
7685
+ * here and the next stage. `whenSatisfied`/`unevaluable` are projections
6936
7686
  * of `insight.outcome`; one evaluation feeds all three. */
6937
7687
  insight: ConditionInsight;
6938
7688
  }
6939
7689
 
6940
- /** Type-mirror of {@link transitionFields} minus `filter` — stored requires it,
7690
+ /** Type-mirror of {@link transitionFields} minus `when` — stored requires it,
6941
7691
  * authoring omits it (desugar fills the default), so each variant declares it. */
6942
- declare type TransitionFields<TOp> = {
7692
+ declare type TransitionFields = {
6943
7693
  name: string;
6944
7694
  title?: string | undefined;
6945
7695
  description?: string | undefined;
6946
7696
  to: string;
6947
- ops?: TOp[] | undefined;
6948
- effects?: Effect[] | undefined;
6949
7697
  };
6950
7698
 
6951
- export declare type TransitionOp = FieldOp;
6952
-
6953
7699
  /**
6954
7700
  * Parse a GDR URI, returning `undefined` instead of throwing when the string
6955
7701
  * is not a GDR (or is malformed). The "is this a GDR, and if so its parts"
@@ -6958,6 +7704,29 @@ export declare type TransitionOp = FieldOp;
6958
7704
  */
6959
7705
  export declare function tryParseGdr(uri: string): ParsedGdr | undefined;
6960
7706
 
7707
+ /**
7708
+ * The `$fields` entries a `start.allowed` predicate reads that `fields` does
7709
+ * not bind — the BINDABILITY rule every pre-flight surface applies before
7710
+ * trusting a verdict over possibly-incomplete inputs. GROQ equality against
7711
+ * a missing operand collapses to a definitive-looking answer (`null == x` is
7712
+ * false, not null), so a verdict that read an unbound entry is PROVISIONAL in
7713
+ * both directions: supplying the entry could overturn a refusal AND an
7714
+ * approval. Non-empty ⇒ report "can't decide yet" instead of the collapsed
7715
+ * verdict (the `evaluateStart` verb does; the Studio start control does).
7716
+ *
7717
+ * The GATE never consults this: at `startInstance` the supplied inputs are
7718
+ * final, so evaluating over genuine absence — including rules that WANT it,
7719
+ * like `!defined($fields.rush)` — is the correct semantics there.
7720
+ *
7721
+ * `fields` is a produced `$fields` map (`startFieldsParam`, or a surface's
7722
+ * seed map) — producers bind only own keys with real values, so key presence
7723
+ * IS the supplied-ness rule.
7724
+ */
7725
+ export declare function unboundAllowedReads(
7726
+ allowed: string,
7727
+ fields: Record<string, unknown>,
7728
+ ): string[];
7729
+
6961
7730
  /**
6962
7731
  * The diagnose idiom shared by every consumer: each unsatisfied exit
6963
7732
  * transition with its insight summary. Consumers shape the result (the CLI
@@ -6973,8 +7742,8 @@ export declare function unsatisfiedTransitionSummaries(
6973
7742
 
6974
7743
  /**
6975
7744
  * Validate a (stored, desugared) definition before deploy: parse-check every
6976
- * GROQ string (malformed predicates, conditions, bindings, `completeWhen`/
6977
- * `failWhen`) and re-run the cross-field invariants. `defineWorkflow` already
7745
+ * GROQ string (malformed predicates, conditions, bindings, triggers) and
7746
+ * re-run the cross-field invariants. `defineWorkflow` already
6978
7747
  * ran the invariants at authoring time, but deploy must reject what authoring
6979
7748
  * rejects — a stored-shape definition handed straight to the verb (raw JSON,
6980
7749
  * a foreign tool) would otherwise bypass every invariant.
@@ -6998,27 +7767,13 @@ export declare function validateDefinition(
6998
7767
  definition: WorkflowDefinition,
6999
7768
  ): void;
7000
7769
 
7001
- /**
7002
- * Engine-scope tag — the environment partition primitive.
7003
- *
7004
- * Every engine operates against exactly one `tag` ("test", "prod", …).
7005
- * The tag partitions definitions and instances within a single workflow
7006
- * resource: `deploy(def, tag: "test")` and `deploy(def, tag: "prod")` are
7007
- * separate deployed workflows with independent lifecycles, and every read
7008
- * is scoped to one tag so test runs never surface in prod.
7009
- *
7010
- * The tag must be Sanity-ID-compatible since it's joined into IDs with
7011
- * `.` — ASCII lowercase + digits + dashes, no leading dash, no dots. It
7012
- * is also the ID prefix for every definition/instance the engine writes.
7013
- *
7014
- * There is no default. The tag selects which environment the engine reads
7015
- * and writes, and the engine enforces nothing — so the partition is the
7016
- * only thing keeping test runs out of prod. Every entry point
7017
- * (`createEngine`, the CLI, the MCP server) requires it explicitly and
7018
- * fails when it's absent rather than guessing an environment.
7019
- */
7020
7770
  export declare function validateTag(tag: string): void;
7021
7771
 
7772
+ declare interface ValidationIssue {
7773
+ path: ReadonlyArray<PropertyKey>;
7774
+ message: string;
7775
+ }
7776
+
7022
7777
  export declare type ValueExpr = ValueExprInternal;
7023
7778
 
7024
7779
  declare type ValueExprInternal =
@@ -7096,16 +7851,18 @@ export declare interface WatchSet {
7096
7851
  /**
7097
7852
  * The instance's effective read perspective (a release stack like
7098
7853
  * `[releaseName]` or `[releaseName, "drafts"]`), already resolved at
7099
- * `startInstance`. `undefined` means raw/published.
7854
+ * `startInstance`. `undefined` means content reads use the
7855
+ * drafts-preferring {@link DEFAULT_CONTENT_PERSPECTIVE}.
7100
7856
  *
7101
7857
  * The engine does not form `versions.<release>.<id>` ids — it has no
7102
7858
  * such concept (it reads content through `client.fetch({perspective})`
7103
7859
  * for queries). A reactive consumer applies this perspective when it
7104
- * subscribes: a **content** doc must be resolved to its version /
7105
- * draft / published form (e.g. Studio `editState`'s version param, or
7106
- * the SDK's `getVersionId`), while instance / ancestor / `system.release`
7107
- * docs are always raw. The `type` on each {@link SubscriptionDocument}
7108
- * tells the consumer which is which.
7860
+ * subscribes: a **content** doc resolves under the release
7861
+ * {@link contentReleaseName} names (e.g. Studio `editState`'s version
7862
+ * param, or the SDK's `getVersionId`), falling back to the draft only
7863
+ * when {@link contentDraftFallback} allows it, then published while
7864
+ * instance / ancestor / `system.release` docs are always raw. The `type`
7865
+ * on each {@link SubscriptionDocument} tells the consumer which is which.
7109
7866
  */
7110
7867
  perspective?: WorkflowPerspective;
7111
7868
  }
@@ -7131,7 +7888,7 @@ export declare const workflow: {
7131
7888
  * never patches a deployed version, so a definition can't change out from
7132
7889
  * under the instances pinned to it. `startInstance` picks the highest
7133
7890
  * version by default. The engine figures out the dependency order itself
7134
- * (children before parents that spawn them via `subworkflows.definition`)
7891
+ * (children before parents that spawn them via `action.spawn.definition`)
7135
7892
  * and reports a per-definition outcome (`created` / `unchanged`).
7136
7893
  *
7137
7894
  * Refs may point inside the batch OR at already-deployed definitions
@@ -7161,20 +7918,44 @@ export declare const workflow: {
7161
7918
  /**
7162
7919
  * Spawn a new workflow instance from a deployed definition.
7163
7920
  *
7164
- * Pins the snapshot at start-time, seeds `effectsContext`, enters
7165
- * the initial stage (which builds activityStatus, queues onEnter
7166
- * effects, auto-activates activities). Then cascades auto-transitions
7167
- * until stable. Starting always changes state (`changed: true`) —
7168
- * `cascaded` reports how far the new instance auto-advanced.
7921
+ * Two gates run before anything is written, in order: the required-input
7922
+ * check a missing `required` input is a CALLER mistake
7923
+ * ({@link RequiredFieldNotProvidedError}) then the definition's
7924
+ * `start.allowed` permission predicate, whose false/GROQ-null VERDICT
7925
+ * throws {@link StartNotAllowedError} (declaring the expression is the
7926
+ * opt-in; there is no override arg — pre-flight with `evaluateStart`).
7927
+ * Per-value SHAPE validation fires during field resolution, after the
7928
+ * verdict but still before any write. It does NOT evaluate `start.filter`:
7929
+ * that is a read-side visibility rule (see `definitionsForDocument`).
7930
+ *
7931
+ * Pins the snapshot at start-time, seeds the `context` bag, and enters
7932
+ * the initial stage — fields resolve and every in-scope activity is
7933
+ * born active. Then cascades until stable, so the initial stage's
7934
+ * `when: 'true'` triggers have fired by the time this returns.
7935
+ *
7936
+ * Start is three commits — create, prime, first cascade — and a supplied
7937
+ * `instanceId` is its idempotency key across them. When that id already
7938
+ * exists under this tag for the same start, the call RESUMES: input gates
7939
+ * and field resolution are skipped (those values were pinned at create)
7940
+ * and the outstanding commits run — the retry path for a start that
7941
+ * failed after its create landed (see `isUnprimed`). Reusing an id for a
7942
+ * DIFFERENT start (definition or explicit version mismatch) — or for an
7943
+ * unfinished start that was aborted (a discarded start) — throws
7944
+ * {@link ContractViolationError}. The mid-sequence failures are typed and
7945
+ * carry the retry id: a prime failure after the create committed throws
7946
+ * `StartNotPrimedError` (failed but resumable), and a cascade failure
7947
+ * after a successful prime throws `StartNotSettledError` — the run exists
7948
+ * by then and must not be reported as a failed start. `changed` is `true`
7949
+ * on a fresh start and rev-derived on a resume; `cascaded` reports how far
7950
+ * the instance auto-advanced.
7169
7951
  */
7170
7952
  startInstance: (
7171
7953
  rawArgs: Clocked<Telemetered<StartInstanceArgs & EngineScopeArgs>>,
7172
7954
  ) => Promise<OperationResult>;
7173
7955
  /**
7174
- * Fire an action against an activity. If the activity is pending, it is
7175
- * auto-invoked first (pending active) so callers don't have to know
7176
- * the lifecycle. Cascades auto-transitions and propagates to ancestors
7177
- * after the action commits.
7956
+ * Fire an action against an active activity. Cascades and propagates to
7957
+ * ancestors after the action commits. A cascade-fired (`when`) action is
7958
+ * rejected — the cascade is its only firing path.
7178
7959
  *
7179
7960
  * This is the universal "something happened" call. Editors fire it.
7180
7961
  * Runtimes fire it in response to webhooks, effect completions, and
@@ -7202,11 +7983,11 @@ export declare const workflow: {
7202
7983
  rawArgs: Clocked<Telemetered<EditFieldArgs & EngineScopeArgs>>,
7203
7984
  ) => Promise<OperationResult>;
7204
7985
  /**
7205
- * Report a queued effect's outcome. Drains it from `pendingEffects`,
7206
- * appends an `effectHistory` entry, and (if `outputs` are supplied
7207
- * and the effect succeeded) upserts those values into
7208
- * `effectsContext` by keyso downstream effect bindings can
7209
- * reference them. Any `ops` the handler returned (`field.*`) are
7986
+ * Report a queued effect's outcome. Drains it from `pendingEffects` and
7987
+ * appends an `effectHistory` entry whose `outputs` (when supplied on a
7988
+ * successful run) are what downstream effect bindings and conditions
7989
+ * read as `$effects['<name>'].<output>`the start-only `context` bag
7990
+ * is never touched. Any `ops` the handler returned (`field.*`) are
7210
7991
  * validated and applied to the instance in the same commit, through the
7211
7992
  * same op applier an action's field ops use. Cascades after. A completion
7212
7993
  * that applies always changes state (the effect drains + history is
@@ -7222,17 +8003,16 @@ export declare const workflow: {
7222
8003
  rawArgs: Clocked<Telemetered<CompleteEffectArgs & EngineScopeArgs>>,
7223
8004
  ) => Promise<OperationResult>;
7224
8005
  /**
7225
- * Re-evaluate auto-transitions and resolve due waits until stable.
8006
+ * Run the cascade until stable triggered actions fire, transitions move.
7226
8007
  *
7227
8008
  * Used by the runtime after any event that might affect the workflow
7228
- * but isn't itself an activity action: a subject doc was patched, a sibling
7229
- * workflow completed, the clock crossed a `completeWhen` deadline, etc.
8009
+ * but isn't itself an action fire: a subject doc was patched, a sibling
8010
+ * workflow completed, the clock crossed a deadline a `when` reads, etc.
7230
8011
  * The runtime doesn't need to know what changed — it just nudges
7231
8012
  * affected instances and the engine re-evaluates. `changed` reports
7232
8013
  * whether the nudge wrote anything: a fired transition, but also a
7233
- * persisted activity-gate flip (`completeWhen`/`failWhen`) that didn't
7234
- * unlock a transition yet — so it's derived from the instance's `_rev`,
7235
- * not from `cascaded` alone.
8014
+ * hop that fired triggered actions without unlocking a transition yet —
8015
+ * so it's derived from the instance's `_rev`, not from `cascaded` alone.
7236
8016
  */
7237
8017
  tick: (
7238
8018
  rawArgs: Clocked<Telemetered<OperationArgs & EngineScopeArgs>>,
@@ -7289,8 +8069,11 @@ export declare const workflow: {
7289
8069
  * Hydrates the instance's snapshot (instance + ancestors + every doc
7290
8070
  * declared by a `doc.ref` / `doc.refs` entry in scope), then
7291
8071
  * evaluates the supplied GROQ in groq-js against that dataset. The
7292
- * rendered scope's instance-derived vars ({@link FILTER_SCOPE_VARS}) are
7293
- * auto-bound ids in GDR URI form to match the snapshot's keying.
8072
+ * caller-free rendered scope cascade gates evaluate in is auto-bound —
8073
+ * the instance-derived vars ({@link FILTER_SCOPE_VARS}) with the open
8074
+ * stage's overlay merged into `$fields`, `$assigned` at its caller-free
8075
+ * `false`, plus the author's pre-evaluated `$<predicate>` booleans —
8076
+ * ids in GDR URI form to match the snapshot's keying.
7294
8077
  *
7295
8078
  * Use when an external consumer (a UI, a debug pane, a test) wants
7296
8079
  * to ask "what does the engine see for this workflow right now?"
@@ -7398,9 +8181,13 @@ export declare const workflow: {
7398
8181
  * The startable half of {@link workflow.instancesForDocument}: every
7399
8182
  * deployed definition that APPLIES to `document` — what a start picker for
7400
8183
  * it should offer. Loads the latest deployed version of each definition
7401
- * visible to the engine's tag and filters it through the pure derivation
8184
+ * visible to the engine's tag and filters it through the derivation
7402
8185
  * ({@link applicableDefinitions}): startable ∧ a required subject entry
7403
- * accepts the doc's `_type` ∧ `applicableWhen` passes.
8186
+ * accepts the doc's `_type` ∧ `start.filter` passes — evaluated in the
8187
+ * browse-time-pure start-filter context with `$tag`/`$definition`/`$now`
8188
+ * bound and the tag's instance slice (completed included) backing dataset
8189
+ * reads. `start.allowed` never participates — permission is a start-time
8190
+ * question; pre-flight it with {@link workflow.evaluateStart}.
7404
8191
  *
7405
8192
  * Takes the LOADED candidate document, not a ref — applicability evaluates
7406
8193
  * its content, under whatever perspective the caller read it with. Surfaces
@@ -7408,8 +8195,28 @@ export declare const workflow: {
7408
8195
  * auto-picking is consumer policy. Advisory like every engine-side check.
7409
8196
  */
7410
8197
  definitionsForDocument: (
7411
- rawArgs: DefinitionsForDocumentArgs & EngineScopeArgs,
8198
+ rawArgs: Clocked<DefinitionsForDocumentArgs & EngineScopeArgs>,
7412
8199
  ) => Promise<DeployedDefinition[]>;
8200
+ /**
8201
+ * Pre-flight the start gates for a definition + candidate `initialFields` —
8202
+ * the read `startInstance` enforces, as a {@link StartEvaluation} a surface
8203
+ * can render: `missingRequired` mirrors the input contract
8204
+ * ({@link RequiredFieldNotProvidedError}'s rows), and `allowed` /
8205
+ * `outcome` / `insight` carry the `start.allowed` verdict with its atom
8206
+ * breakdown — disable the start affordance on a definitive `false` and say
8207
+ * why. BINDABILITY-AWARE for partial mid-form inputs: when the predicate
8208
+ * reads an entry `initialFields` doesn't supply, `outcome` is
8209
+ * `'unevaluable'` and `unboundReads` names the entries ("fill these to
8210
+ * decide") instead of the collapsed answer GROQ equality would give —
8211
+ * this is the ONE deliberate divergence from the gate, where absence is
8212
+ * final, not provisional (a rule like `!defined($fields.rush)` genuinely
8213
+ * passes there when `rush` is absent). A definition with no
8214
+ * `start.allowed` is vacuously allowed, exactly like the verb. Pure read;
8215
+ * advisory under races — the enforcement moment is `startInstance` itself.
8216
+ */
8217
+ evaluateStart: (
8218
+ rawArgs: Clocked<EvaluateStartArgs & EngineScopeArgs>,
8219
+ ) => Promise<StartEvaluation>;
7413
8220
  /**
7414
8221
  * Permission helpers — Sanity ACL grants evaluated against documents
7415
8222
  * via GROQ. Used by `workflow.evaluate` to soft-gate actions when the
@@ -7471,6 +8278,12 @@ export declare interface WorkflowAdminOverrideData extends InstanceScopedEventDa
7471
8278
  changed: boolean;
7472
8279
  }
7473
8280
 
8281
+ /** The whole-definition rollup: every stage must progress, so the workflow
8282
+ * verdict conjoins the stage verdicts. */
8283
+ export declare interface WorkflowAutonomy extends AutonomyAnswer {
8284
+ stages: StageAutonomy[];
8285
+ }
8286
+
7474
8287
  export declare interface WorkflowClient {
7475
8288
  fetch: <T = unknown>(
7476
8289
  query: string,
@@ -7858,7 +8671,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
7858
8671
  description?: string | undefined;
7859
8672
  groups?: Group[] | undefined;
7860
8673
  lifecycle?: WorkflowLifecycle | undefined;
7861
- applicableWhen?: string | undefined;
8674
+ start?: StartBlock | undefined;
7862
8675
  initialStage: string;
7863
8676
  fields?: FieldEntry[] | undefined;
7864
8677
  stages: Stage[];
@@ -7876,7 +8689,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
7876
8689
  description?: string | undefined;
7877
8690
  groups?: Group[] | undefined;
7878
8691
  lifecycle?: WorkflowLifecycle | undefined;
7879
- applicableWhen?: string | undefined;
8692
+ start?: StartBlock | undefined;
7880
8693
  initialStage: string;
7881
8694
  fields?: FieldEntry[] | undefined;
7882
8695
  stages: Stage[];
@@ -7942,7 +8755,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
7942
8755
  description?: string | undefined;
7943
8756
  groups?: Group[] | undefined;
7944
8757
  lifecycle?: WorkflowLifecycle | undefined;
7945
- applicableWhen?: string | undefined;
8758
+ start?: StartBlock | undefined;
7946
8759
  initialStage: string;
7947
8760
  fields?: FieldEntry[] | undefined;
7948
8761
  stages: Stage[];
@@ -8002,7 +8815,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
8002
8815
  description?: string | undefined;
8003
8816
  groups?: Group[] | undefined;
8004
8817
  lifecycle?: WorkflowLifecycle | undefined;
8005
- applicableWhen?: string | undefined;
8818
+ start?: StartBlock | undefined;
8006
8819
  initialStage: string;
8007
8820
  fields?: FieldEntry[] | undefined;
8008
8821
  stages: Stage[];
@@ -8073,9 +8886,9 @@ export declare interface WorkflowDefinitionDeployedData {
8073
8886
  * scopes, sorted. */
8074
8887
  fieldKinds: FieldKind[];
8075
8888
  guardCount: number;
8076
- /** Effects declared across activities, actions, and transitions. */
8889
+ /** Effects declared across the definition's actions. */
8077
8890
  effectCount: number;
8078
- /** Activities carrying a `subworkflows` block. */
8891
+ /** Actions carrying a `spawn` block. */
8079
8892
  subworkflowCount: number;
8080
8893
  lifecycle: WorkflowLifecycle;
8081
8894
  }
@@ -8114,7 +8927,7 @@ export declare type WorkflowDefinitionInput<T> = string extends keyof T
8114
8927
  * mints the next version.
8115
8928
  */
8116
8929
  declare const WorkflowDefinitionSchema: v.GenericSchema<
8117
- WorkflowFields<FieldEntry, Stage>
8930
+ WorkflowFields<FieldEntry, Stage, StartBlock>
8118
8931
  >;
8119
8932
 
8120
8933
  export declare type WorkflowDeployment = WorkflowConfig["deployments"][number];
@@ -8165,6 +8978,7 @@ export declare abstract class WorkflowError<
8165
8978
 
8166
8979
  export declare type WorkflowErrorKind =
8167
8980
  | "action-disabled"
8981
+ | "start-not-allowed"
8168
8982
  | "edit-field-denied"
8169
8983
  | "mutation-guard-denied"
8170
8984
  | "action-params-invalid"
@@ -8173,11 +8987,16 @@ export declare type WorkflowErrorKind =
8173
8987
  | "required-field-not-provided"
8174
8988
  | "workflow-state-diverged"
8175
8989
  | "partial-guard-deploy"
8990
+ | "start-not-primed"
8991
+ | "start-not-settled"
8176
8992
  | "concurrent-fire-action"
8177
8993
  | "concurrent-edit-field"
8994
+ | "concurrent-complete-effect"
8178
8995
  | "cascade-limit"
8179
8996
  | "field-value-shape"
8997
+ | "ref-resource-undeclared"
8180
8998
  | "model-version-ahead"
8999
+ | "persisted-doc-shape"
8181
9000
  | "instance-not-found"
8182
9001
  | "definition-not-found"
8183
9002
  | "definition-in-use"
@@ -8208,6 +9027,14 @@ export declare interface WorkflowEvaluation {
8208
9027
  * Empty when no condition reads a field.
8209
9028
  */
8210
9029
  fieldInsights: FieldInsight[];
9030
+ /**
9031
+ * The whole definition's causal-autonomy rollup: per activity, per stage,
9032
+ * and workflow-wide — will each level resolve without a caller, and what
9033
+ * does it wait on. Static (no instance state), stamped here so every
9034
+ * consumer reads one derivation. Spawn children are not resolved at
9035
+ * evaluation time, so their legs report `conditional`.
9036
+ */
9037
+ autonomy: WorkflowAutonomy;
8211
9038
  }
8212
9039
 
8213
9040
  export declare interface WorkflowFetchOptions {
@@ -8235,14 +9062,20 @@ export declare interface WorkflowFieldEditedData extends InstanceScopedEventData
8235
9062
  mode: EditMode;
8236
9063
  }
8237
9064
 
8238
- /** Type-mirror of {@link workflowFields}, parameterised over field/stage. */
8239
- declare type WorkflowFields<TField, TStage> = {
9065
+ /** Type-mirror of {@link workflowFields}, parameterised over field/stage/start. */
9066
+ declare type WorkflowFields<TField, TStage, TStart> = {
9067
+ /**
9068
+ * Lake-id-segment grammar (`^[a-z0-9][a-z0-9-]*$`, deploy-enforced): the
9069
+ * name interpolates into every deployed document id
9070
+ * (`<tag>.<name>.v<version>`). Stable identity — instances pin to it,
9071
+ * subworkflows resolve by it.
9072
+ */
8240
9073
  name: string;
8241
9074
  title: string;
8242
9075
  description?: string | undefined;
8243
9076
  groups?: Group[] | undefined;
8244
9077
  lifecycle?: WorkflowLifecycle | undefined;
8245
- applicableWhen?: string | undefined;
9078
+ start?: TStart | undefined;
8246
9079
  initialStage: string;
8247
9080
  fields?: TField[] | undefined;
8248
9081
  stages: TStage[];
@@ -8307,10 +9140,11 @@ export declare interface WorkflowInstance extends SanityDocument {
8307
9140
  */
8308
9141
  fields: ResolvedFieldEntry[];
8309
9142
  /**
8310
- * Stable named params the runtime hands to effect handlers via binding
8311
- * resolution. Set at `startInstance`. **Not read by transition filters.**
9143
+ * The start seed: named values set once at `startInstance` (or by a
9144
+ * parent's `spawn.context`) and never mutated after. Conditions and
9145
+ * effect bindings read them as `$context.<name>`.
8312
9146
  */
8313
- effectsContext: EffectsContextEntry[];
9147
+ context: ContextEntry[];
8314
9148
  /**
8315
9149
  * Chain of ancestor workflow instances, root-first. Each entry is a
8316
9150
  * GDR pointing at a {@link WORKFLOW_INSTANCE_TYPE} document in the
@@ -8370,6 +9204,13 @@ export declare interface WorkflowInstance extends SanityDocument {
8370
9204
 
8371
9205
  export declare const WorkflowInstanceAborted: WorkflowTelemetryEvent<WorkflowAdminOverrideData>;
8372
9206
 
9207
+ /**
9208
+ * The persisted instance document, root to leaf. Reused verbatim by every
9209
+ * engine read boundary through {@link parseInstanceDocument}; the
9210
+ * model-surface gate derives its shape ledger from this schema.
9211
+ */
9212
+ export declare const WorkflowInstanceSchema: v.GenericSchema<WorkflowInstance>;
9213
+
8373
9214
  export declare const WorkflowInstanceStarted: WorkflowTelemetryEvent<WorkflowInstanceStartedData>;
8374
9215
 
8375
9216
  export declare interface WorkflowInstanceStartedData extends InstanceScopedEventData {
@@ -8564,10 +9405,11 @@ export declare interface WorkflowTransaction {
8564
9405
  /**
8565
9406
  * Queue a document delete. Deliberately a transaction-only capability —
8566
9407
  * the top-level client surface stays delete-free so no engine code path
8567
- * can casually remove documents; the sole consumer is `deleteDefinition`
8568
- * housekeeping (definition docs + orphaned guard docs). Both the real
8569
- * `@sanity/client` Transaction and the test fake's TransactionHandle
8570
- * carry this shape.
9408
+ * can casually remove documents; engine-side the sole consumer is
9409
+ * `deleteDefinition` housekeeping (definition docs + orphaned guard docs),
9410
+ * and outside the engine the CLI's `nuke` reset deletes raw engine-owned
9411
+ * docs through it. Both the real `@sanity/client` Transaction and the test
9412
+ * fake's TransactionHandle carry this shape.
8571
9413
  */
8572
9414
  delete: (id: string) => WorkflowTransaction;
8573
9415
  /**