@sanity/workflow-engine 0.14.0 → 0.15.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.cts CHANGED
@@ -1,7 +1,34 @@
1
+ import { analyzeCondition } from "@sanity/groq-condition-describe";
2
+ import { AtomInsight } from "@sanity/groq-condition-describe";
3
+ import { AtomRequirement } from "@sanity/groq-condition-describe";
4
+ import { checklistLines as checklistLines_2 } from "@sanity/groq-condition-describe";
5
+ import { ComparisonOp } from "@sanity/groq-condition-describe";
6
+ import { ConditionAnalysis } from "@sanity/groq-condition-describe";
7
+ import { ConditionAtom } from "@sanity/groq-condition-describe";
8
+ import { ConditionClause } from "@sanity/groq-condition-describe";
9
+ import { ConditionDescription } from "@sanity/groq-condition-describe";
10
+ import { ConditionInsight } from "@sanity/groq-condition-describe";
11
+ import { ConditionOutcome } from "@sanity/groq-condition-describe";
12
+ import { ConditionRead } from "@sanity/groq-condition-describe";
13
+ import { DescribedClause } from "@sanity/groq-condition-describe";
14
+ import { explainCondition } from "@sanity/groq-condition-describe";
15
+ import { ExplainConditionArgs } from "@sanity/groq-condition-describe";
16
+ import { formatRead } from "@sanity/groq-condition-describe";
17
+ import { guillemets } from "@sanity/groq-condition-describe";
18
+ import { humanize } from "@sanity/groq-condition-describe";
19
+ import { InsightPhrase } from "@sanity/groq-condition-describe";
20
+ import { isComparisonOp } from "@sanity/groq-condition-describe";
21
+ import { MAX_COUNTERFACTUAL_INDEX } from "@sanity/groq-condition-describe";
22
+ import { quoted } from "@sanity/groq-condition-describe";
1
23
  import type { SanityDocument } from "@sanity/types";
24
+ import { ScopeAssignment } from "@sanity/groq-condition-describe";
25
+ import { sentenceCase } from "@sanity/groq-condition-describe";
2
26
  import * as v from "valibot";
27
+ import { whatIfCondition } from "@sanity/groq-condition-describe";
28
+ import { WhatIfOutcome } from "@sanity/groq-condition-describe";
29
+ import { withAssignment } from "@sanity/groq-condition-describe";
3
30
 
4
- export declare interface AbortInstanceArgs extends OperationArgs {
31
+ export declare interface AbortInstanceArgs extends DedupableOperationArgs {
5
32
  /** Free-text reason — surfaces on the `workflow.history.aborted` entry for audit. */
6
33
  reason?: string;
7
34
  }
@@ -11,7 +38,58 @@ export declare function abortReason(
11
38
  instance: Pick<WorkflowInstance, "history">,
12
39
  ): string | undefined;
13
40
 
14
- export declare type Action = ActionFields<Op>;
41
+ /**
42
+ * 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.)
52
+ */
53
+ export declare function acceptsDocumentType(
54
+ definition: Pick<ApplicabilitySource, "fields">,
55
+ documentType: string,
56
+ ): boolean;
57
+
58
+ /**
59
+ * The URL path a resource's ACL is served at — where the engine fetches an
60
+ * actor's grants for that resource (and the canonical encoding of the shape
61
+ * a caller passes as `grantsFromPath`). Known for `dataset`
62
+ * (`/projects/<id>/datasets/<ds>/acl`) and `canvas`
63
+ * (`/canvases/<resourceId>/acl`); `undefined` for resource types without a
64
+ * known grants endpoint, so a caller can degrade open (no grants → no
65
+ * advisory forecast) instead of guessing a path.
66
+ */
67
+ export declare function aclPathForResource(
68
+ res: Extract<
69
+ WorkflowResource,
70
+ {
71
+ type: "dataset" | "canvas";
72
+ }
73
+ >,
74
+ ): string;
75
+
76
+ export declare function aclPathForResource(
77
+ res: WorkflowResource,
78
+ ): string | undefined;
79
+
80
+ export declare type Action = ActionFields<Op, string[]>;
81
+
82
+ /**
83
+ * The engine's per-reason detail fragment — the same wording
84
+ * {@link ActionDisabledError} embeds in its message. Exported so a dev-facing
85
+ * host (the CLI) renders the one canonical detail instead of maintaining a
86
+ * parallel per-kind copy. Covers every reason but `mutation-guard-denied`,
87
+ * which escalates to {@link MutationGuardDeniedError} and renders through
88
+ * {@link deniedGuardLabels}.
89
+ */
90
+ export declare function actionDisabledDetail(
91
+ reason: ActionDisabledReason,
92
+ ): string;
15
93
 
16
94
  /**
17
95
  * Thrown by `fireAction` when the requested action's verdict is
@@ -45,13 +123,18 @@ export declare interface ActionEvaluation {
45
123
  allowed: boolean;
46
124
  /** Present iff `allowed === false`. The first failing gate wins. */
47
125
  disabledReason?: DisabledReason;
126
+ /** Derived state of the action's `filter` gate — why it holds or fails,
127
+ * atom by atom. Present iff the action declares a filter. */
128
+ insight?: ConditionInsight;
48
129
  }
49
130
 
50
- /** Type-mirror of {@link actionFields}, parameterised over the op grammar. */
51
- declare type ActionFields<TOp> = {
131
+ /** Type-mirror of {@link actionFields}, parameterised over the op and
132
+ * group-membership grammars. */
133
+ declare type ActionFields<TOp, TGroup> = {
52
134
  name: string;
53
135
  title?: string | undefined;
54
136
  description?: string | undefined;
137
+ group?: TGroup | undefined;
55
138
  filter?: string | undefined;
56
139
  params?: ActionParam[] | undefined;
57
140
  ops?: TOp[] | undefined;
@@ -137,7 +220,8 @@ export declare type Activity = ActivityFields<
137
220
  FieldEntry,
138
221
  Action,
139
222
  Op,
140
- ManualTarget
223
+ ManualTarget,
224
+ string[]
141
225
  >;
142
226
 
143
227
  /**
@@ -194,6 +278,14 @@ declare const ACTIVITY_STATUSES: readonly [
194
278
  "failed",
195
279
  ];
196
280
 
281
+ /** An activity's four gates, each described — absent keys mirror undeclared gates. */
282
+ export declare interface ActivityDescription {
283
+ requirements?: Record<string, ConditionDescription>;
284
+ filter?: ConditionDescription;
285
+ completeWhen?: ConditionDescription;
286
+ failWhen?: ConditionDescription;
287
+ }
288
+
197
289
  export declare interface ActivityEntry {
198
290
  _key: string;
199
291
  /** Matches the authoring `Activity.name`. */
@@ -214,13 +306,6 @@ export declare interface ActivityEntry {
214
306
  unevaluable?: boolean;
215
307
  detail?: string;
216
308
  };
217
- /**
218
- * Subworkflows started by this activity (via `activity.subworkflows`). Each entry
219
- * is a GDR pointing at an instance document. Used by ancestor propagation
220
- * to walk back up the tree, and rendered as `$subworkflows` for the activity's
221
- * `completeWhen` / `failWhen`.
222
- */
223
- spawnedInstances?: GlobalDocumentReference[];
224
309
  /** Activity-scoped resolved field entries. Absent until the engine writes to it. */
225
310
  fields?: ResolvedFieldEntry[];
226
311
  }
@@ -243,14 +328,29 @@ export declare interface ActivityEvaluation {
243
328
  * carries the matching `requirements-unmet` `disabledReason`).
244
329
  */
245
330
  unmetRequirements?: string[];
331
+ /** Derived state per declared requirement, keyed by requirement name.
332
+ * Present iff the activity declares requirements; `unmetRequirements`
333
+ * is exactly the keys whose insight isn't satisfied. */
334
+ 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. */
337
+ 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;
246
344
  actions: ActionEvaluation[];
247
345
  }
248
346
 
249
- /** Type-mirror of {@link activityFields}, parameterised over field/action/op/target. */
250
- declare type ActivityFields<TField, TAction, TOp, TTarget> = {
347
+ /** Type-mirror of {@link activityFields}, parameterised over field/action/op/target/group. */
348
+ declare type ActivityFields<TField, TAction, TOp, TTarget, TGroup> = {
251
349
  name: string;
252
350
  title?: string | undefined;
253
351
  description?: string | undefined;
352
+ groups?: Group[] | undefined;
353
+ group?: TGroup | undefined;
254
354
  kind?: ActivityKind | undefined;
255
355
  target?: TTarget | undefined;
256
356
  activation?: "auto" | "manual" | undefined;
@@ -276,12 +376,11 @@ export declare type ActivityName = string;
276
376
  export declare type ActivityStatus = (typeof ACTIVITY_STATUSES)[number];
277
377
 
278
378
  /**
279
- * Who is acting, as asserted by the caller — advisory provenance, not an
280
- * authenticated principal. The engine resolves it from the client's token by
281
- * default (`/users/me`), but every acting verb accepts an `access.actor`
282
- * override and nothing verifies the asserted id against the token performing
283
- * the writes. Only the lake's own token identity is authenticated; hard
284
- * enforcement lives there.
379
+ * Who is acting — advisory provenance, not an authenticated principal. The
380
+ * engine always resolves it from the client's token (`/users/me`); there is
381
+ * no way to pass or synthesize one. The stamp is still advisory: nothing
382
+ * re-verifies it at read time, and only the lake's own token identity is
383
+ * authenticated hard enforcement lives there.
285
384
  */
286
385
  export declare interface Actor {
287
386
  kind: ActorKind;
@@ -307,6 +406,47 @@ export declare const ACTOR_KINDS: readonly ["person", "agent", "system"];
307
406
 
308
407
  export declare type ActorKind = (typeof ACTOR_KINDS)[number];
309
408
 
409
+ export { analyzeCondition };
410
+
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. */
414
+ export declare interface ApplicabilitySource {
415
+ name?: string | undefined;
416
+ lifecycle?: WorkflowLifecycle | undefined;
417
+ fields?: FieldEntry[] | undefined;
418
+ applicableWhen?: string | undefined;
419
+ }
420
+
421
+ /**
422
+ * Filter `definitions` to the ones applicable to `document`, preserving
423
+ * input order — surface ALL matches; the engine ranks nothing.
424
+ */
425
+ export declare function applicableDefinitions<
426
+ T extends ApplicabilitySource,
427
+ >(args: {
428
+ definitions: readonly T[];
429
+ document: CandidateDocument;
430
+ }): Promise<T[]>;
431
+
432
+ /**
433
+ * The data-model gate every engine read of an engine-owned document passes
434
+ * through — point reads, list queries, projections, and snapshot hydration's
435
+ * raw reads (the structural exceptions — existence-only probes and lake-side
436
+ * GROQ filters — are declared in DATAMODEL.md). Throws
437
+ * {@link ModelVersionAheadError} only when the document's
438
+ * {@link minReaderModelOf reader floor} is ahead of
439
+ * {@link DATA_MODEL_VERSION}; a doc stamped by a NEWER model whose changes
440
+ * were additive keeps its old floor and reads fine — mixed-version fleets
441
+ * interoperate across additive evolution. A missing stamp is model 0 and
442
+ * always readable.
443
+ */
444
+ export declare function assertReadableModel<
445
+ T extends {
446
+ _id: string;
447
+ },
448
+ >(doc: T): T;
449
+
310
450
  /**
311
451
  * One member of an `assignees`-kind entry's value — and the value of the
312
452
  * singular `assignee` kind. The WHO-FOR spec the inbox reverse-query and the
@@ -325,6 +465,10 @@ export declare type Assignee =
325
465
  role: string;
326
466
  };
327
467
 
468
+ export { AtomInsight };
469
+
470
+ export { AtomRequirement };
471
+
328
472
  /**
329
473
  * Persisted document and effect-queue `_type` discriminators.
330
474
  */
@@ -353,7 +497,8 @@ export declare type AuthoringActivity = ActivityFields<
353
497
  AuthoringFieldEntry,
354
498
  AuthoringAction,
355
499
  AuthoringOp,
356
- AuthoringManualTarget
500
+ AuthoringManualTarget,
501
+ GroupMembership
357
502
  >;
358
503
 
359
504
  export declare type AuthoringEditable = v.InferOutput<
@@ -578,8 +723,8 @@ declare const AuthoringGuardSchema: v.StrictObjectSchema<
578
723
  undefined
579
724
  >;
580
725
  /**
581
- * Lake GROQ predicate — a distinct eval context reading
582
- * `document.before`/`document.after`, `mutation`, `guard`, and
726
+ * Lake GROQ predicate — a distinct eval context: delta-mode GROQ
727
+ * reading the `before()`/`after()` natives, `mutation`, `guard`, and
583
728
  * `identity()`. Bare ids/fields only. Omitted or empty means
584
729
  * UNCONDITIONAL DENY.
585
730
  */
@@ -989,12 +1134,15 @@ declare const AuthoringOpSchema: v.VariantSchema<
989
1134
  * field the transition filter reads — `failed` is for work that
990
1135
  * genuinely could not complete.
991
1136
  */
992
- declare type AuthoringRawAction = ActionFields<AuthoringOp> & {
1137
+ declare type AuthoringRawAction = ActionFields<AuthoringOp, GroupMembership> & {
993
1138
  roles?: string[] | undefined;
994
1139
  status?: TerminalActivityStatus | undefined;
995
1140
  };
996
1141
 
997
- declare type AuthoringRawFieldEntry = FieldEntryFields<AuthoringEditable>;
1142
+ declare type AuthoringRawFieldEntry = FieldEntryFields<
1143
+ AuthoringEditable,
1144
+ GroupMembership
1145
+ >;
998
1146
 
999
1147
  export declare type AuthoringStage = StageFields<
1000
1148
  AuthoringFieldEntry,
@@ -1259,6 +1407,17 @@ export declare function buildSnapshot(args: {
1259
1407
  docs: LoadedDoc[];
1260
1408
  }): HydratedSnapshot;
1261
1409
 
1410
+ /**
1411
+ * A LOADED candidate document. Applicability evaluates content, so a ref or
1412
+ * bare id is not enough: the doc must carry its schema `_type` plus whatever
1413
+ * attributes `applicableWhen` predicates read — under whatever perspective
1414
+ * the caller loaded it with.
1415
+ */
1416
+ export declare interface CandidateDocument {
1417
+ _type: string;
1418
+ [key: string]: unknown;
1419
+ }
1420
+
1262
1421
  /**
1263
1422
  * Thrown when auto-transitions on an instance fail to stabilise within
1264
1423
  * {@link CascadeLimitError.limit} passes — the signature of a runaway
@@ -1276,6 +1435,9 @@ export declare class CascadeLimitError extends WorkflowError<"cascade-limit"> {
1276
1435
  constructor(args: { instanceId: string; limit: number });
1277
1436
  }
1278
1437
 
1438
+ /** Flatten a description to indented English lines (✓ / ✗ / ? per outcome). */
1439
+ export declare const checklistLines: typeof checklistLines_2;
1440
+
1279
1441
  export declare interface ChildrenArgs extends InstanceRefArgs {
1280
1442
  /** Restrict to children spawned by this activity on the parent. */
1281
1443
  activity?: string;
@@ -1294,6 +1456,7 @@ declare type ClaimAction = {
1294
1456
  name: string;
1295
1457
  title?: string | undefined;
1296
1458
  description?: string | undefined;
1459
+ group?: GroupMembership | undefined;
1297
1460
  field: string | AuthoringFieldRef;
1298
1461
  roles?: string[] | undefined;
1299
1462
  filter?: string | undefined;
@@ -1312,8 +1475,34 @@ declare type ClaimField = {
1312
1475
  name: string;
1313
1476
  title?: string | undefined;
1314
1477
  description?: string | undefined;
1478
+ group?: GroupMembership | undefined;
1315
1479
  };
1316
1480
 
1481
+ /**
1482
+ * The `@sanity/client`-config fragment that addresses a {@link WorkflowResource}
1483
+ * — the one home for the dataset-vs-resource branch every client-building host
1484
+ * (CLI, MCP) would otherwise reimplement. A dataset addresses via the classic
1485
+ * `{projectId, dataset}` config (the proven path, and it keeps
1486
+ * `client.listen()` on well-trodden ground); the other resource types have no
1487
+ * projectId/dataset split, so they pass through as a `resource` directly.
1488
+ *
1489
+ * Returns a plain fragment (not a client) so the engine stays free of a
1490
+ * runtime `@sanity/client` dependency — callers spread it into their own
1491
+ * `createClient` call alongside their auth/host params.
1492
+ */
1493
+ export declare function clientConfigFromResource(res: WorkflowResource):
1494
+ | {
1495
+ projectId: string;
1496
+ dataset: string;
1497
+ }
1498
+ | {
1499
+ resource: WorkflowResource;
1500
+ };
1501
+
1502
+ /** The router {@link buildClientForGdr} builds — total: every parsed GDR
1503
+ * resolves to a client, or the router throws. */
1504
+ declare type ClientForGdr = (parsed: ParsedGdr) => WorkflowClient;
1505
+
1317
1506
  /**
1318
1507
  * The engine's single source of "now".
1319
1508
  *
@@ -1331,10 +1520,9 @@ declare type ClaimField = {
1331
1520
  * accept it only through the internal {@link Clocked} seam, so prod code
1332
1521
  * can't trivially override engine time by accident.
1333
1522
  *
1334
- * Out of scope: `drainEffects` (the external runtime worker claiming and
1335
- * executing effects) still stamps `claimedAt` from wall-clock — it is not
1336
- * a verb and the bench never drives it. Thread a clock there too if the
1337
- * simulator ever needs deterministic drain timing.
1523
+ * `drainEffects` and `sweepStaleClaims` read the same engine clock for
1524
+ * their claim / lease-expiry stamps, so lease timing is deterministic
1525
+ * under a pinned clock too.
1338
1526
  */
1339
1527
  export declare type Clock = () => string;
1340
1528
 
@@ -1351,6 +1539,8 @@ declare type Clocked<T> = T & {
1351
1539
  clock?: Clock;
1352
1540
  };
1353
1541
 
1542
+ export { ComparisonOp };
1543
+
1354
1544
  /**
1355
1545
  * A compiled lake read — what the engine's query builders return and what a
1356
1546
  * reactive adapter's live-query store subscribes with, so the stateless and
@@ -1358,26 +1548,40 @@ declare type Clocked<T> = T & {
1358
1548
  */
1359
1549
  export declare interface CompiledQuery {
1360
1550
  query: string;
1361
- params: Record<string, string>;
1551
+ params: Record<string, string | string[]>;
1362
1552
  }
1363
1553
 
1364
- /** Assemble a persisted guard doc from resolved pieces. */
1554
+ /** Assemble a persisted guard doc from resolved pieces. Deliberately carries
1555
+ * NO engine data-model stamp: the guard doc format is the lake's forthcoming
1556
+ * contract, not ours to grow fields on — its versioning event is the
1557
+ * {@link GUARD_DOC_TYPE} cutover itself (see DATAMODEL.md). */
1365
1558
  export declare function compileGuard(args: CompileGuardArgs): MutationGuardDoc;
1366
1559
 
1367
1560
  declare interface CompileGuardArgs extends MutationGuardBody {
1368
1561
  id: string;
1369
1562
  }
1370
1563
 
1371
- export declare interface CompleteEffectArgs extends OperationArgs {
1564
+ /**
1565
+ * Completion is FIRST-WRITER-WINS: reporting on an entry that already
1566
+ * settled throws `EffectNotFoundError`, whose `settled` field says how it
1567
+ * settled — another party's completion (a lease takeover, a manual
1568
+ * recovery), or an abort cancelling the entry before dispatch. A completer
1569
+ * on a retrying transport (webhook redelivery, queue, cron) should pass
1570
+ * {@link DedupableOperationArgs.idempotencyKey} — its own lost-response
1571
+ * retries then replay as `changed: false` instead of throwing.
1572
+ */
1573
+ export declare interface CompleteEffectArgs extends DedupableOperationArgs {
1372
1574
  /** The `_key` of the pending effect being reported on. */
1373
1575
  effectKey: string;
1374
- status: "done" | "failed";
1576
+ status: EffectCompletionStatus;
1375
1577
  /**
1376
1578
  * Named values produced by the effect. If supplied, these are merged
1377
1579
  * into `effectsContext` (replace-by-key) and become available to
1378
- * downstream effect bindings on the same instance.
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).
1379
1583
  */
1380
- outputs?: Record<string, string | number | boolean | GlobalDocumentReference>;
1584
+ outputs?: EffectsContext;
1381
1585
  /**
1382
1586
  * The state half of the effect: `field.*` ops to apply in the completion
1383
1587
  * commit, computed from the effect's real result (e.g. a `field.set` of a
@@ -1400,13 +1604,15 @@ export declare interface CompleteEffectArgs extends OperationArgs {
1400
1604
  }
1401
1605
 
1402
1606
  /** Compare each definition against the latest deployed version of its name. */
1403
- export declare function computeDiffEntries({
1607
+ export declare function computeDiffEntries<
1608
+ T extends WorkflowDefinitionInput<T>,
1609
+ >({
1404
1610
  client,
1405
1611
  defs,
1406
1612
  target,
1407
1613
  }: {
1408
1614
  client: Pick<WorkflowClient, "fetch">;
1409
- defs: WorkflowDefinition[];
1615
+ defs: T[];
1410
1616
  target: DeployTarget;
1411
1617
  }): Promise<DiffEntry[]>;
1412
1618
 
@@ -1475,11 +1681,43 @@ export declare type Condition = string;
1475
1681
  */
1476
1682
  export declare const CONDITION_VARS: readonly ConditionVar[];
1477
1683
 
1684
+ export { ConditionAnalysis };
1685
+
1686
+ export { ConditionAtom };
1687
+
1688
+ export { ConditionClause };
1689
+
1690
+ export { ConditionDescription };
1691
+
1692
+ export { ConditionInsight };
1693
+
1694
+ export { ConditionOutcome };
1695
+
1696
+ export { ConditionRead };
1697
+
1698
+ /**
1699
+ * 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.
1702
+ * Editable gates resolve through the same helper the runtime projection uses
1703
+ * ({@link editableFieldsInStage}: entry `editable` ANDed with the stage
1704
+ * tighten-override), so every live editable-field address exists here with
1705
+ * the same EFFECTIVE predicate the edit insight explains — including
1706
+ * override-only gates on `editable: true` entries, and workflow-scope fields
1707
+ * once per stage (the effective predicate is per-stage).
1708
+ */
1709
+ export declare function conditionSitesOf(
1710
+ definition: WorkflowDefinition,
1711
+ ): DefinitionConditionSite[];
1712
+
1478
1713
  export declare interface ConditionVar {
1479
1714
  /** The GROQ param name, read as `$<name>`. */
1480
1715
  name: string;
1481
1716
  binding: ConditionVarBinding;
1482
1717
  description: string;
1718
+ /** Short human phrase for rendered explanations ("all activities in this
1719
+ * stage are finished") — consumer-facing, unlike `description`. */
1720
+ label: string;
1483
1721
  }
1484
1722
 
1485
1723
  /**
@@ -1497,9 +1735,13 @@ export declare interface ConditionVar {
1497
1735
  * fixed and deploy-enforced (see the op-where scope's param-name list in
1498
1736
  * the op applier).
1499
1737
  * 2. **Filter evaluation without a caller** — the engine's cascade evaluates
1500
- * transition filters and `completeWhen`/`failWhen` with no caller, so only
1501
- * the `'always'`-bound subset carries values there ({@link FILTER_SCOPE_VARS}).
1502
- * Caller-bound vars evaluate to `undefined` and fail closed.
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.
1503
1745
  * 3. **Guard predicates** — NOT conditions. A lake mutation guard's
1504
1746
  * `predicate` is groq-js **delta-mode** GROQ over a document mutation:
1505
1747
  * `before()`/`after()`/`identity()` are dialect natives, and the wire
@@ -1514,7 +1756,7 @@ export declare interface ConditionVar {
1514
1756
  * - `'caller'` — rides the acting caller; without one the var is `undefined`
1515
1757
  * (conditions referencing it fail closed). Author predicates may not read
1516
1758
  * these — they pre-evaluate once per instance, caller-free.
1517
- * - `'spawn'` — bound only at `subworkflows` sites.
1759
+ * - `'spawn'` — bound only at `subworkflows` sites (the per-row `$row`).
1518
1760
  */
1519
1761
  export declare type ConditionVarBinding = "always" | "caller" | "spawn";
1520
1762
 
@@ -1590,6 +1832,15 @@ export declare interface CreateEngineArgs extends EngineScopeArgs {
1590
1832
  effectHandlers?: Record<string, EffectHandler>;
1591
1833
  missingHandler?: MissingHandlerPolicy;
1592
1834
  loggerFactory?: LoggerFactory;
1835
+ /**
1836
+ * Lease duration `drainEffects` stamps on each pending-effect claim.
1837
+ * Past the lease the claimer is presumed dead: the entry becomes
1838
+ * recoverable by another drain's takeover or by
1839
+ * the standalone `sweepStaleClaims` export. Default 5 minutes — size it well above
1840
+ * the slowest handler's honest runtime, since a live-but-slow dispatch
1841
+ * that outlives its lease can be redispatched (see {@link EffectHandler}).
1842
+ */
1843
+ effectLeaseMs?: number;
1593
1844
  /**
1594
1845
  * Deterministic-time seam, pinned once for this engine and threaded
1595
1846
  * into every verb it drives — `$now`, the `now` op-source, and the
@@ -1600,8 +1851,61 @@ export declare interface CreateEngineArgs extends EngineScopeArgs {
1600
1851
  * option.
1601
1852
  */
1602
1853
  clock?: Clock;
1854
+ /**
1855
+ * Product-telemetry seam, pinned once for this engine and threaded into
1856
+ * every verb it drives. The core engine ships no metrics pipeline — it
1857
+ * cannot know the environment's output rules (consent, transport, data
1858
+ * governance), so it only DEFINES events (exported from the package
1859
+ * root) and logs them through this logger; the app shell that does know
1860
+ * those rules owns store creation, consent resolution, and transport.
1861
+ * Omit (the default) to emit nothing. Any `@sanity/telemetry`
1862
+ * `TelemetryLogger` satisfies this seam — the engine mirrors the shape
1863
+ * rather than depending on the package. Same rule as the clock:
1864
+ * engine-construction config, never a per-verb option.
1865
+ */
1866
+ telemetry?: WorkflowTelemetryLogger;
1603
1867
  }
1604
1868
 
1869
+ /**
1870
+ * Build the consent/transport pair a shell's batched store runs on.
1871
+ * Consent is the account-level status from the authenticated client —
1872
+ * anything but `granted` (including a fetch error) means the store sends
1873
+ * nothing. `denied` short-circuits without a request: process shells
1874
+ * resolve their environment denial (see {@link isTelemetryEnvDenied})
1875
+ * once and pass it here; browser shells omit it.
1876
+ */
1877
+ export declare function createTelemetryIntake(args: {
1878
+ client: TelemetryIntakeClient;
1879
+ projectId: string;
1880
+ denied?: boolean;
1881
+ }): TelemetryIntake;
1882
+
1883
+ /**
1884
+ * The reader floor the current model imposes — the compatibility half of
1885
+ * {@link MODEL_STAMP}, stamped as `minReaderModel`: the oldest engine model
1886
+ * that can safely interpret a document written at {@link DATA_MODEL_VERSION}.
1887
+ * An additive change leaves it untouched (older engines read newer docs
1888
+ * fine — mixed fleets of Functions, Studios, and CLIs are the steady state);
1889
+ * ONLY a breaking shape change raises it, and doing so is a declared,
1890
+ * DATAMODEL.md-logged decision to fence out older readers.
1891
+ */
1892
+ export declare const DATA_MODEL_MIN_READER = 0;
1893
+
1894
+ /**
1895
+ * The engine's persisted data-model version — the provenance half of
1896
+ * {@link MODEL_STAMP}, stamped as `modelVersion` on every engine-owned
1897
+ * document at its construction and persist choke-points. Conforms-to
1898
+ * semantics: the stamp means "this document conforms to model N now" —
1899
+ * instances are re-stamped on every full persist, so mixed-version fleets
1900
+ * honestly record whichever engine last shaped a doc.
1901
+ *
1902
+ * Bump on every declared shape change (additive included). Bumping does NOT
1903
+ * by itself lock out older engines — that is {@link DATA_MODEL_MIN_READER}'s
1904
+ * job. Declare every bump in `DATAMODEL.md`; the model-surface snapshot test
1905
+ * keeps undeclared drift red.
1906
+ */
1907
+ export declare const DATA_MODEL_VERSION = 1;
1908
+
1605
1909
  /**
1606
1910
  * Split a dataset resource id (`<projectId>.<dataset>`) into its parts — the
1607
1911
  * one parser for the format, shared by {@link gdrFromResource} and the React
@@ -1613,6 +1917,39 @@ export declare function datasetResourceParts(id: string): {
1613
1917
  dataset: string;
1614
1918
  };
1615
1919
 
1920
+ /** The declarable half of {@link ExecutionContext} — what `createEngine` accepts. */
1921
+ export declare type DeclaredExecutionContext = Pick<
1922
+ ExecutionContext,
1923
+ "kind" | "id"
1924
+ >;
1925
+
1926
+ /**
1927
+ * The {@link OperationArgs} of the verbs that own a discrete commit —
1928
+ * `fireAction`, `editField`, `completeEffect`, `setStage`, `abortInstance`.
1929
+ * `tick` deliberately takes the plain base: it owns no commit of its own and
1930
+ * re-derives everything, so retrying it blind is already safe.
1931
+ */
1932
+ export declare interface DedupableOperationArgs extends OperationArgs {
1933
+ /**
1934
+ * Caller-supplied key that makes this call safe to retry blind (a network
1935
+ * failure ate the response; a queue redelivered the message). The first
1936
+ * commit records the key on the instance's `processedRequests[]` ledger —
1937
+ * in the same CAS write as the operation itself — and a retry carrying the
1938
+ * same key replays instead of double-applying (or, for a completed effect,
1939
+ * throwing `EffectNotFoundError`): the operation is NOT re-applied, but
1940
+ * the cascade runs to convergence (the original may have died before its
1941
+ * own cascade), so `changed` is `false` unless that cascade repaired
1942
+ * something. Keys live for the engine's `idempotencyTtlMs` (default 24h);
1943
+ * reusing a live key on a different verb throws. The caller owns key
1944
+ * uniqueness per logical request.
1945
+ *
1946
+ * Only a commit records the key: a keyed call that no-ops (a `setStage`
1947
+ * already at its target, an abort of a terminal instance) leaves no ledger
1948
+ * row, so retrying it later re-evaluates fresh rather than replaying.
1949
+ */
1950
+ idempotencyKey?: string;
1951
+ }
1952
+
1616
1953
  /**
1617
1954
  * The perspective a content read falls back to when the instance has no
1618
1955
  * explicit release perspective: `drafts` overlay published (Sanity
@@ -1623,6 +1960,14 @@ export declare function datasetResourceParts(id: string): {
1623
1960
  */
1624
1961
  export declare const DEFAULT_CONTENT_PERSPECTIVE = "drafts";
1625
1962
 
1963
+ /** How long a fresh claim's lease runs unless the engine configured
1964
+ * `effectLeaseMs`. */
1965
+ export declare const DEFAULT_EFFECT_LEASE_MS: number;
1966
+
1967
+ /** How long a recorded idempotency key dedupes retries, unless the caller
1968
+ * configured `idempotencyTtlMs`. */
1969
+ export declare const DEFAULT_IDEMPOTENCY_TTL_MS: number;
1970
+
1626
1971
  /**
1627
1972
  * Default LoggerFactory — writes through the global `console`. Exposed
1628
1973
  * so callers outside `createEngine` (drive scripts, the
@@ -1632,9 +1977,36 @@ export declare const DEFAULT_CONTENT_PERSPECTIVE = "drafts";
1632
1977
  */
1633
1978
  export declare const defaultLoggerFactory: LoggerFactory;
1634
1979
 
1980
+ export declare interface DefinitionConditionSite {
1981
+ /** The stage the condition applies in; absent only for author predicates. */
1982
+ stage?: string;
1983
+ address: DefinitionSiteAddress;
1984
+ condition: Condition;
1985
+ }
1986
+
1987
+ /** Derive the deploy event payload from a definition's structure. */
1988
+ export declare function definitionDeployedData(
1989
+ definition: WorkflowDefinition,
1990
+ outcome: {
1991
+ contentHash: string;
1992
+ status: WorkflowDefinitionDeployedData["status"];
1993
+ deployId: string;
1994
+ },
1995
+ ): WorkflowDefinitionDeployedData;
1996
+
1997
+ /** One declared group at one level, with the members lexically attributed to it. */
1998
+ export declare interface DefinitionGroupSite {
1999
+ group: Group;
2000
+ /** The declaration level; `stage`/`activity` name the declaring node. */
2001
+ level: FieldScope;
2002
+ stage?: string;
2003
+ activity?: string;
2004
+ members: GroupMember[];
2005
+ }
2006
+
1635
2007
  declare interface DefinitionGuardsQueryArgs {
1636
2008
  client: WorkflowClient;
1637
- clientForGdr: (parsed: ParsedGdr) => WorkflowClient;
2009
+ clientForGdr: ClientForGdr;
1638
2010
  workflowResource: WorkflowResource;
1639
2011
  definition: string;
1640
2012
  /** Every deployed version of {@link DefinitionGuardsQueryArgs.definition}. */
@@ -1669,6 +2041,24 @@ export declare class DefinitionInUseError extends WorkflowError<"definition-in-u
1669
2041
  constructor(args: { definition: string; blockedBy: DefinitionInUseBlocker });
1670
2042
  }
1671
2043
 
2044
+ /**
2045
+ * Pure GROQ builder for resolving a deployed workflow definition. Composes the
2046
+ * doc-type constant and the tag-scope predicate so the engine's internal
2047
+ * lookups and the CLI share one definition of "tag-scoped, latest-or-pinned
2048
+ * `workflow.definition`" instead of hand-writing the query at each call site.
2049
+ */
2050
+ /**
2051
+ * GROQ resolving a single deployed {@link WORKFLOW_DEFINITION_TYPE} visible to
2052
+ * the caller's tag. With {@link explicit} the query pins `$version`; otherwise
2053
+ * it returns the highest deployed version.
2054
+ *
2055
+ * Params: `$definition`, `$tag`, plus `$version` when {@link explicit}.
2056
+ *
2057
+ * @param explicit - whether the caller wants a specific version (`$version`)
2058
+ * rather than the latest.
2059
+ */
2060
+ export declare function definitionLookupGroq(explicit: boolean): string;
2061
+
1672
2062
  /** No deployed workflow definition matches the requested name (and version,
1673
2063
  * when given) under this engine's tag. */
1674
2064
  export declare class DefinitionNotFoundError extends WorkflowError<"definition-not-found"> {
@@ -1677,6 +2067,44 @@ export declare class DefinitionNotFoundError extends WorkflowError<"definition-n
1677
2067
  constructor(args: { definition: string; version?: number });
1678
2068
  }
1679
2069
 
2070
+ export declare interface DefinitionsForDocumentArgs {
2071
+ /**
2072
+ * The LOADED candidate document — unlike {@link InstancesForDocumentArgs},
2073
+ * a ref won't do: applicability evaluates content (`_type` against subject
2074
+ * entry `types`, `applicableWhen` against the attributes), under whatever
2075
+ * perspective the caller loaded the document with.
2076
+ */
2077
+ document: CandidateDocument;
2078
+ }
2079
+
2080
+ /** A definition-level site address: every runtime {@link InsightSite}, plus
2081
+ * the two shapes that only exist at the definition level — author
2082
+ * predicates, and stage `editable` tighten-overrides (runtime folds an
2083
+ * override into the field's effective predicate; statically it is also its
2084
+ * own authored gate). */
2085
+ export declare type DefinitionSiteAddress =
2086
+ | InsightSite
2087
+ | {
2088
+ kind: "predicate";
2089
+ predicate: string;
2090
+ }
2091
+ | {
2092
+ kind: "editable-override";
2093
+ name: string;
2094
+ };
2095
+
2096
+ /**
2097
+ * GROQ listing EVERY deployed {@link WORKFLOW_DEFINITION_TYPE} visible to the
2098
+ * caller's tag, grouped by name. `versionOrder` picks the in-group direction:
2099
+ * `'desc'` puts each name's latest first (discovery keeps the head per name),
2100
+ * `'asc'` walks history oldest-first (handler verification reads them all).
2101
+ *
2102
+ * Params: `$tag`.
2103
+ */
2104
+ export declare function definitionsListGroq(
2105
+ versionOrder: "asc" | "desc",
2106
+ ): string;
2107
+
1680
2108
  export declare interface DeleteDefinitionArgs {
1681
2109
  /** The definition's `name` — delete addresses the workflow, not a doc id. */
1682
2110
  definition: string;
@@ -1691,8 +2119,6 @@ export declare interface DeleteDefinitionArgs {
1691
2119
  cascade?: boolean;
1692
2120
  /** Free-text reason stamped on each cascade-abort history entry. */
1693
2121
  reason?: string;
1694
- /** Optional access-state override — see `StartInstanceArgs.access`. */
1695
- access?: WorkflowAccessOverride;
1696
2122
  /** URL path for grant resolution — see `StartInstanceArgs.grantsFromPath`. */
1697
2123
  grantsFromPath?: string;
1698
2124
  }
@@ -1733,6 +2159,15 @@ export declare interface DeniedGuardRef {
1733
2159
  name?: string;
1734
2160
  }
1735
2161
 
2162
+ /**
2163
+ * The one mapping from {@link MutationGuardDoc} to the structured
2164
+ * {@link DeniedGuardRef} payload — shared by the thrown error and the
2165
+ * read-side `mutation-guard-denied` verdict, so the two can't drift.
2166
+ */
2167
+ export declare function deniedGuardRefs(
2168
+ guards: readonly MutationGuardDoc[],
2169
+ ): DeniedGuardRef[];
2170
+
1736
2171
  /**
1737
2172
  * Run every guard whose `match` applies and collect those that DENY. Empty
1738
2173
  * result ⇒ allowed (optimistically).
@@ -1752,9 +2187,18 @@ export declare interface DeployDefinitionResult {
1752
2187
  /** The deploy-assigned version this deploy created or matched. */
1753
2188
  version: number;
1754
2189
  status: "created" | "unchanged";
2190
+ /**
2191
+ * Advisory producer/consumer warnings from the effect-output lint — a
2192
+ * `$effects['<name>'].<key>` read of a key a declared effect does not produce.
2193
+ * Non-blocking: deploy still succeeds. Absent when the definition is clean.
2194
+ * See {@link lintEffectOutputs}.
2195
+ */
2196
+ warnings?: string[];
1755
2197
  }
1756
2198
 
1757
- export declare interface DeployDefinitionsArgs {
2199
+ export declare interface DeployDefinitionsArgs<
2200
+ T extends WorkflowDefinitionInput<T> = WorkflowDefinition,
2201
+ > {
1758
2202
  /**
1759
2203
  * Resource-alias bindings for this deploy (alias name → physical resource).
1760
2204
  * Every `@<alias>:` reference in a definition is expanded to its bound
@@ -1764,12 +2208,21 @@ export declare interface DeployDefinitionsArgs {
1764
2208
  * reference no aliases).
1765
2209
  */
1766
2210
  resourceAliases?: ResourceAliases;
1767
- definitions: WorkflowDefinition[];
2211
+ /** Authored definitions (the default — full compile-time checking) or
2212
+ * loosely-typed fetched definition documents — the boundary parse
2213
+ * normalizes either ({@link WorkflowDefinitionInput}). */
2214
+ definitions: T[];
1768
2215
  }
1769
2216
 
1770
2217
  export declare interface DeployDefinitionsResult {
1771
2218
  /** Per-definition outcome, ordered by the resolved deploy order (children first). */
1772
2219
  results: DeployDefinitionResult[];
2220
+ /**
2221
+ * Random id grouping this call's per-definition deploy telemetry events.
2222
+ * Returned so a caller reporting on the same deploy (e.g. definition
2223
+ * sharing) can carry the id the events already minted.
2224
+ */
2225
+ deployId: string;
1773
2226
  }
1774
2227
 
1775
2228
  /** A definition as fetched back from the lake. The document carries `_id` plus
@@ -1784,6 +2237,12 @@ export declare type DeployedDefinition = WorkflowDefinition & {
1784
2237
  /** Optional: a document deployed before content-addressing has none — see
1785
2238
  * {@link LatestDeployed}. Every version this engine deploys carries one. */
1786
2239
  contentHash?: string;
2240
+ /** Engine data-model stamp (see {@link DATA_MODEL_VERSION}) — absent on
2241
+ * documents deployed before the stamp existed (model 0). */
2242
+ modelVersion?: number;
2243
+ /** Reader floor (see {@link DATA_MODEL_MIN_READER}) — the oldest engine
2244
+ * data model that can safely interpret this document. */
2245
+ minReaderModel?: number;
1787
2246
  };
1788
2247
 
1789
2248
  /**
@@ -1823,6 +2282,106 @@ export declare interface DeployTarget {
1823
2282
  */
1824
2283
  export declare function deriveActivityKind(activity: Activity): ActivityKind;
1825
2284
 
2285
+ export declare type Describable =
2286
+ | ConditionInsight
2287
+ | FieldInsight
2288
+ | TransitionEvaluation
2289
+ | ActionEvaluation
2290
+ | ActivityEvaluation
2291
+ | EditableFieldEvaluation;
2292
+
2293
+ /** One atom as a phrase in the workflow vocabulary. */
2294
+ export declare function describeAtom(
2295
+ atom: AtomInsight,
2296
+ ctx: DescribeContext,
2297
+ ): {
2298
+ phrase: InsightPhrase;
2299
+ fallback: boolean;
2300
+ };
2301
+
2302
+ /** Render one condition's insight as a checklist + frontier summary, in the
2303
+ * workflow vocabulary. */
2304
+ export declare function describeCondition(
2305
+ insight: ConditionInsight,
2306
+ ctx: DescribeContext,
2307
+ ): ConditionDescription;
2308
+
2309
+ /** The engine's rendering context: everything phrases resolve titles and
2310
+ * vocabulary from is on the definition. */
2311
+ export declare interface DescribeContext {
2312
+ definition: WorkflowDefinition;
2313
+ }
2314
+
2315
+ /** The return shape {@link describeNode} owes each input — conditional on the node. */
2316
+ export declare type Described<T extends Describable> = T extends FieldInsight
2317
+ ? FieldDescription
2318
+ : T extends ActivityEvaluation
2319
+ ? ActivityDescription
2320
+ : T extends ConditionInsight | TransitionEvaluation
2321
+ ? ConditionDescription
2322
+ : ConditionDescription | undefined;
2323
+
2324
+ export { DescribedClause };
2325
+
2326
+ /** One described definition site: the address plus its phrased checklist. */
2327
+ export declare type DescribedDefinitionSite = DefinitionConditionSite & {
2328
+ description: ConditionDescription;
2329
+ };
2330
+
2331
+ /**
2332
+ * Explain the whole workflow in human language, without an instance: every
2333
+ * condition site phrased against an EMPTY scope, in the workflow vocabulary.
2334
+ * Outcome marks are placeholders (no values to judge) — the checklists and
2335
+ * summaries carry the "what this needs" story; live verdicts come from
2336
+ * evaluation insight where a run's current stage overlaps these addresses.
2337
+ */
2338
+ export declare function describeDefinition(
2339
+ definition: WorkflowDefinition,
2340
+ ): Promise<DescribedDefinitionSite[]>;
2341
+
2342
+ /** A field's insight as phrases: involvement plus verified proposals. */
2343
+ export declare function describeFieldInsight(
2344
+ field: FieldInsight,
2345
+ ctx: DescribeContext,
2346
+ ): FieldDescription;
2347
+
2348
+ /**
2349
+ * The one front door: feed it any insight-bearing node off an evaluation and
2350
+ * 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
2353
+ * unambiguous across the union.
2354
+ */
2355
+ export declare function describeNode<T extends Describable>(
2356
+ node: T,
2357
+ ctx: DescribeContext,
2358
+ ): Described<T>;
2359
+
2360
+ /** A site address as a noun phrase, using declared titles where present. */
2361
+ export declare function describeSite(
2362
+ site: InsightSite,
2363
+ ctx: DescribeContext,
2364
+ ): InsightPhrase;
2365
+
2366
+ /**
2367
+ * A site address as a checklist lead-in that flows into the atom lines —
2368
+ * "«Approval» completes automatically when:" over "✗ «Approved» must be
2369
+ * true". The connective mood for tooltips/panels; {@link describeSite} is
2370
+ * the noun-phrase mood for mid-sentence composition. Accepts the
2371
+ * definition-level address superset so the static tier heads the same way.
2372
+ */
2373
+ export declare function describeSiteHeading(
2374
+ address: DefinitionSiteAddress,
2375
+ ctx: DescribeContext,
2376
+ ): InsightPhrase;
2377
+
2378
+ /** The slice of a {@link TransitionEvaluation} the classifier reads — narrowed
2379
+ * so a hand-crafted stuck permutation doesn't have to fabricate insights. */
2380
+ export declare type DiagnosedTransition = Pick<
2381
+ TransitionEvaluation,
2382
+ "transition" | "filterSatisfied" | "unevaluable"
2383
+ >;
2384
+
1826
2385
  /**
1827
2386
  * The slice of an instance + its {@link WorkflowEvaluation} the classifier
1828
2387
  * reads. Narrowed so callers (and tests) can craft an exact stuck permutation
@@ -1838,9 +2397,10 @@ export declare interface DiagnoseInput {
1838
2397
  | "effectHistory"
1839
2398
  | "pendingEffects"
1840
2399
  | "history"
2400
+ | "subworkflows"
1841
2401
  >;
1842
2402
  activities: ActivityEvaluation[];
1843
- transitions: TransitionEvaluation[];
2403
+ transitions: DiagnosedTransition[];
1844
2404
  assignees: Record<string, Assignee[]>;
1845
2405
  }
1846
2406
 
@@ -1888,11 +2448,13 @@ export declare type Diagnosis =
1888
2448
  | {
1889
2449
  state: "completed";
1890
2450
  at: string;
2451
+ liveChildren?: number;
1891
2452
  }
1892
2453
  | {
1893
2454
  state: "aborted";
1894
2455
  at: string;
1895
2456
  reason?: string;
2457
+ liveChildren?: number;
1896
2458
  }
1897
2459
  | {
1898
2460
  state: "stuck";
@@ -1918,14 +2480,19 @@ export declare interface DiffEntry {
1918
2480
  * name (or its absence), classifying it via the engine's own content-addressed
1919
2481
  * planner ({@link planDefinitionDeploy}). Create-only: a content change is a
1920
2482
  * NEW version, never an in-place update — so there is no `update` verdict.
1921
- * Pure — the caller owns the fetch.
1922
- */
1923
- export declare function diffEntry({
1924
- def,
2483
+ * Pure — the caller owns the fetch. `def` passes the same boundary parse as
2484
+ * deploy ({@link parseDefinitionInput}): a fetched document's envelope is
2485
+ * stripped and unknown keys fail loud, so diff and deploy accept and reject
2486
+ * the same input shape. Deploy's further checks (GROQ + invariants via
2487
+ * `validateDefinition`, batch ordering) stay the caller's job, as they always
2488
+ * were.
2489
+ */
2490
+ export declare function diffEntry<T extends WorkflowDefinitionInput<T>>({
2491
+ def: rawDef,
1925
2492
  latestRaw,
1926
2493
  target,
1927
2494
  }: {
1928
- def: WorkflowDefinition;
2495
+ def: T;
1929
2496
  latestRaw: Record<string, unknown> | undefined;
1930
2497
  target: DeployTarget;
1931
2498
  }): DiffEntry;
@@ -1974,6 +2541,13 @@ export declare type DisabledReason =
1974
2541
  kind: "instance-completed";
1975
2542
  completedAt: string;
1976
2543
  }
2544
+ | {
2545
+ /** The instance was hard-stopped via `abortInstance`. Aborted instances
2546
+ * carry `completedAt` too, so this arm wins over `instance-completed`
2547
+ * (the {@link terminalState} precedence). */
2548
+ kind: "instance-aborted";
2549
+ abortedAt: string;
2550
+ }
1977
2551
  | {
1978
2552
  /**
1979
2553
  * The activity's declared {@link Activity.requirements} aren't all satisfied —
@@ -1985,6 +2559,19 @@ export declare type DisabledReason =
1985
2559
  */
1986
2560
  kind: "requirements-unmet";
1987
2561
  unmetRequirements: string[];
2562
+ }
2563
+ | {
2564
+ /**
2565
+ * The actor's grants on an involved subject's resource don't allow the
2566
+ * content write the action's effects would perform there. Forecast
2567
+ * per-resource: each subject named by a `doc.ref`/`doc.refs`/`release.ref`
2568
+ * field entry outside the workflow's own resource is checked against
2569
+ * THAT resource's ACL. Advisory like every engine verdict — the subject's
2570
+ * lake still enforces; and it degrades open: a resource whose grants
2571
+ * can't be fetched is simply not forecast.
2572
+ */
2573
+ kind: "subject-permission-denied";
2574
+ denied: SubjectPermissionDenial[];
1988
2575
  };
1989
2576
 
1990
2577
  /**
@@ -2029,20 +2616,69 @@ export declare function displayTitle(typeKey: string | undefined): string;
2029
2616
 
2030
2617
  declare const DOCUMENT_VALUE_PERMISSIONS: readonly ["create", "read", "update"];
2031
2618
 
2619
+ /**
2620
+ * The guards that would deny a PROSPECTIVE lake action on a document — the
2621
+ * disable-this-button pre-flight, evaluated before any concrete mutation
2622
+ * exists. Advisory, like every guard verdict: the check explains and
2623
+ * disables, it never enforces.
2624
+ */
2625
+ export declare function documentActionDenials(
2626
+ args: DocumentActionDenialsArgs,
2627
+ ): Promise<MutationGuardDoc[]>;
2628
+
2629
+ export declare interface DocumentActionDenialsArgs {
2630
+ /** The document's current value — both `before` and `after` of the
2631
+ * pre-flighted action (a prospective check has no concrete mutation, so
2632
+ * delta predicates see "no change"). */
2633
+ doc: {
2634
+ _id: string;
2635
+ _type: string;
2636
+ } & Record<string, unknown>;
2637
+ /** The datasource the document lives in. A guard applies solely within the
2638
+ * datasource it is registered in, so a same-id match from a foreign
2639
+ * datasource can never gate this action and must not flip verdicts. */
2640
+ resource: {
2641
+ type: string;
2642
+ id: string;
2643
+ };
2644
+ action: MutationGuardAction;
2645
+ guards: readonly MutationGuardDoc[];
2646
+ /** Actor id, resolved as `identity()` in predicates. */
2647
+ identity?: string;
2648
+ }
2649
+
2650
+ /** Compile-time mirror of {@link isDocumentEnvelopeKey}: the keys the
2651
+ * boundary strips rather than rejects. */
2652
+ declare type DocumentEnvelopeKey =
2653
+ | `_${string}`
2654
+ | "tag"
2655
+ | "version"
2656
+ | "contentHash"
2657
+ | "modelVersion"
2658
+ | "minReaderModel";
2659
+
2032
2660
  export declare type DocumentValuePermission =
2033
2661
  (typeof DOCUMENT_VALUE_PERMISSIONS)[number];
2034
2662
 
2035
- export declare interface DrainEffectsArgs extends InstanceRefArgs {
2036
- /** Override the drainer's identity (e.g. impersonate a real user whose
2037
- * grants the dispatch should respect). Defaults to a
2038
- * `{ kind: "system", id: "engine.drainEffects" }` actor. */
2039
- access?: WorkflowAccessOverride;
2040
- }
2663
+ /** Args for {@link Engine.drainEffects} the drainer's identity is the
2664
+ * engine client's token, like every other verb. */
2665
+ export declare type DrainEffectsArgs = InstanceRefArgs;
2041
2666
 
2042
2667
  export declare interface DrainEffectsResult {
2668
+ /** The drained instance's pinned definition fingerprint — carried so the
2669
+ * drain telemetry event can slice per definition. Absent only for
2670
+ * instances pinned before content fingerprinting. */
2671
+ definitionContentHash?: string;
2043
2672
  drained: PendingEffect[];
2044
2673
  failed: PendingEffect[];
2045
2674
  skipped: PendingEffect[];
2675
+ /**
2676
+ * Entries this drainer dispatched whose completion lost to another party
2677
+ * (a lease-expiry takeover finishing first, or a manual recovery). The
2678
+ * handler's side effect ran here too — the at-least-once overlap the
2679
+ * {@link EffectHandler} contract tells handlers to tolerate.
2680
+ */
2681
+ lost: PendingEffect[];
2046
2682
  }
2047
2683
 
2048
2684
  /**
@@ -2080,8 +2716,12 @@ export declare type DriverKind = (typeof DRIVER_KINDS)[number];
2080
2716
 
2081
2717
  /**
2082
2718
  * Classify the actor that drove an action for the audit-trail glyph. `person`
2083
- * and `agent` pass through from {@link Actor.kind}; a `system` actor is the
2084
- * `engine` itself ({@link isEngineActor}) or otherwise an external `service`.
2719
+ * and `agent` pass through from {@link Actor.kind}; `system` maps to
2720
+ * `service`. Today every token identity resolves as a person (`/users/me`
2721
+ * carries no kind), so new entries stamp `person` and the `agent`/`service`/
2722
+ * `engine` values are read vocabulary for stored entries — a service's
2723
+ * environment lives on the entry's `executionContext`, and agent delegation
2724
+ * is a future designed feature, not an actor kind the engine can mint.
2085
2725
  *
2086
2726
  * Advisory and only as trustworthy as the {@link Actor} it reads — actor
2087
2727
  * identity is itself advisory in this engine (the lake/token is authoritative),
@@ -2115,6 +2755,9 @@ export declare interface EditableFieldEvaluation {
2115
2755
  setBy?: Actor;
2116
2756
  /** When the field was last written (ISO); absent if never written. */
2117
2757
  setAt?: string;
2758
+ /** Derived state of the who-may-edit predicate. Present iff the effective
2759
+ * editability is a predicate (not the unconditional `true`). */
2760
+ insight?: ConditionInsight;
2118
2761
  }
2119
2762
 
2120
2763
  /**
@@ -2133,6 +2776,12 @@ export declare type EditDisabledReason =
2133
2776
  kind: "instance-completed";
2134
2777
  completedAt: string;
2135
2778
  }
2779
+ | {
2780
+ /** The instance was hard-stopped via `abortInstance` — wins over
2781
+ * `instance-completed` (aborted instances carry `completedAt` too). */
2782
+ kind: "instance-aborted";
2783
+ abortedAt: string;
2784
+ }
2136
2785
  | {
2137
2786
  /** The field's scope window is closed: an activity-scope field whose activity isn't
2138
2787
  * active, or otherwise out of its editable window. */
@@ -2147,11 +2796,12 @@ export declare type EditDisabledReason =
2147
2796
  }
2148
2797
  | MutationGuardDenial;
2149
2798
 
2150
- export declare interface EditFieldArgs extends OperationArgs {
2799
+ export declare interface EditFieldArgs extends DedupableOperationArgs {
2151
2800
  /**
2152
2801
  * Which declared-editable field to write. Omit `scope` to resolve lexically
2153
- * (a `activity` implies activity scope; otherwise the nearest declaring scope, stage
2154
- * before workflow). `activity` is required to address an activity-scope field.
2802
+ * the nearest declaring scope wins (activity, then stage, then workflow);
2803
+ * an explicit `activity` pins the match to that activity's field when the
2804
+ * bare name is ambiguous.
2155
2805
  */
2156
2806
  target: EditFieldTarget;
2157
2807
  /**
@@ -2198,30 +2848,25 @@ export declare type EditFieldDeniedReason = Exclude<
2198
2848
  >;
2199
2849
 
2200
2850
  export declare interface EditFieldTarget {
2201
- /** Omit to resolve lexically (activity field if `activity` set, else stage then workflow). */
2851
+ /** Omit to resolve lexically the nearest declaring scope wins (activity, then stage, then workflow). */
2202
2852
  scope?: FieldScope;
2203
2853
  field: string;
2204
- /** Required to address an activity-scope field. */
2854
+ /** Pins the match to that activity's field. A bare name already resolves activity-first; set this to disambiguate. */
2205
2855
  activity?: string;
2206
2856
  }
2207
2857
 
2208
- /**
2209
- * The generic edit seam. `editField` writes a declared-editable
2210
- * field directly — reassign, reschedule, claim-by-hand, append to a log —
2211
- * without a bespoke action per field. It is NOT a raw patch: the edit applies
2212
- * as a `field.*` op through the shared op path (so it stamps `opApplied`
2213
- * provenance + history), gates on the field's declared editability the same way
2214
- * an action gates on its filter, then refreshes the stage's guards and lets the
2215
- * caller cascade. Editing a value a transition reads can and should move the
2216
- * instance — that rippling is intended.
2217
- *
2218
- * Gating is ONE mechanism (advisory, like every engine check): the field's
2219
- * effective `editable` predicate over the rendered scope.
2220
- */
2221
2858
  export declare type EditMode = "set" | "append" | "unset";
2222
2859
 
2223
2860
  export declare type Effect = v.InferOutput<typeof EffectSchema>;
2224
2861
 
2862
+ /** The outcomes a completer may report through `completeEffect` — a run
2863
+ * either succeeded or failed. `cancelled` is not reportable: only an abort
2864
+ * stamps it, on entries that never dispatched. */
2865
+ export declare type EffectCompletionStatus = Exclude<
2866
+ EffectRunStatus,
2867
+ "cancelled"
2868
+ >;
2869
+
2225
2870
  /**
2226
2871
  * External effect handler — invoked at drain time with the resolved
2227
2872
  * `params` and a context. Returning `outputs` upserts them into
@@ -2233,6 +2878,22 @@ export declare type Effect = v.InferOutput<typeof EffectSchema>;
2233
2878
  * report results as field state, never by flipping an activity status, so `ops`
2234
2879
  * excludes `status.set`. Throwing marks the effect as failed (and returns no
2235
2880
  * `ops`).
2881
+ *
2882
+ * **Delivery is at-least-once — a handler MAY run more than once for the
2883
+ * same effect.** Two overlaps produce a double-run: the dispatching process
2884
+ * dies after the handler's side effect but before `completeEffect` commits,
2885
+ * and a slow dispatch outliving its claim's lease (the engine's
2886
+ * `effectLeaseMs`, default 5 minutes) — an expired claim is taken over by
2887
+ * the next drain, or force-released by `sweepStaleClaims`, and redispatched
2888
+ * while the original handler may still be running. Completion is
2889
+ * first-writer-wins: the losing drainer's dispatch already ran, its
2890
+ * completion is reported as `lost`.
2891
+ *
2892
+ * Write handlers to tolerate that: before irreversible work, check
2893
+ * `effectHistory[]` for a row with this run's `ctx.effectKey` (the stable
2894
+ * `_key` an entry keeps from queue to history) and skip work a prior run
2895
+ * already completed; derive external-system identifiers from `ctx.effectKey`
2896
+ * so the receiving system can dedupe the overlap the ledger can't see.
2236
2897
  */
2237
2898
  export declare type EffectHandler = (
2238
2899
  params: Record<string, unknown>,
@@ -2251,11 +2912,11 @@ export declare type EffectHandler = (
2251
2912
  *
2252
2913
  * Pass the subject's GDR — the URI a binding like `$fields.subject._id`
2253
2914
  * resolves to (the hydrated doc's `_id`), or a full
2254
- * {@link GlobalDocumentReference}. Returns the resolver's client for
2255
- * that resource, falling back to {@link client} when the engine has no
2256
- * `resourceClients` mapping for it. Throws if `ref` isn't a GDR — a
2257
- * bare id can't be routed, so failing loud beats silently patching the
2258
- * wrong dataset.
2915
+ * {@link GlobalDocumentReference}. Returns the `resourceClients` client
2916
+ * for that resource when one is mapped, {@link client} for the workflow
2917
+ * resource itself, and a sibling derived from {@link client}'s
2918
+ * credentials otherwise. Throws if `ref` isn't a GDR a bare id can't
2919
+ * be routed, so failing loud beats silently patching the wrong dataset.
2259
2920
  */
2260
2921
  clientFor: (ref: GdrUri | GlobalDocumentReference) => WorkflowClient;
2261
2922
  instanceId: string;
@@ -2263,7 +2924,7 @@ export declare type EffectHandler = (
2263
2924
  log: (message: string, extra?: Record<string, unknown>) => void;
2264
2925
  },
2265
2926
  ) => Promise<{
2266
- outputs?: Record<string, string | number | boolean | GlobalDocumentReference>;
2927
+ outputs?: EffectsContext;
2267
2928
  ops?: FieldOp[];
2268
2929
  } | void>;
2269
2930
 
@@ -2286,7 +2947,7 @@ export declare interface EffectHistoryEntry {
2286
2947
  stageEntryKey?: string;
2287
2948
  ranAt: string;
2288
2949
  durationMs?: number;
2289
- status: "done" | "failed";
2950
+ status: EffectRunStatus;
2290
2951
  detail?: string;
2291
2952
  error?: {
2292
2953
  message: string;
@@ -2303,15 +2964,25 @@ export declare interface EffectHistoryEntry {
2303
2964
  export declare type EffectName = string;
2304
2965
 
2305
2966
  /**
2306
- * The effect key does not match any pending effect on the instance. Besides a
2307
- * typo'd key, an at-least-once runtime hits this on a double delivery — the
2308
- * first completion drained the effect — so a consumer can branch on this kind
2309
- * to treat the second attempt as already-done rather than as a failure.
2967
+ * The effect key does not match any pending effect on the instance. When the
2968
+ * key matches an `effectHistory` row instead, `settled` carries that row's
2969
+ * outcome so a consumer can branch on WHY the entry is gone: a double
2970
+ * delivery on an at-least-once runtime (`done`/`failed` the first
2971
+ * completion drained it, treat the retry as already-done), or an abort that
2972
+ * cancelled the entry before dispatch (`cancelled` — the reported work was
2973
+ * discarded, not recorded). `settled` absent means the key never existed.
2310
2974
  */
2311
2975
  export declare class EffectNotFoundError extends WorkflowError<"effect-not-found"> {
2312
2976
  readonly instanceId: string;
2313
2977
  readonly effectKey: string;
2314
- constructor(args: { instanceId: string; effectKey: string });
2978
+ /** The matching `effectHistory` outcome, when the effect settled rather
2979
+ * than never existed. */
2980
+ readonly settled?: EffectSettledInfo;
2981
+ constructor(args: {
2982
+ instanceId: string;
2983
+ effectKey: string;
2984
+ settled?: EffectSettledInfo;
2985
+ });
2315
2986
  }
2316
2987
 
2317
2988
  /**
@@ -2336,6 +3007,29 @@ export declare interface EffectOrigin {
2336
3007
  /** The lifecycle boundary that queued an effect — the node IS the moment. */
2337
3008
  export declare type EffectOriginKind = "activity" | "transition" | "action";
2338
3009
 
3010
+ /**
3011
+ * Thrown when an effect completes with a returned output its declaration doesn't
3012
+ * allow — an undeclared key or a value that doesn't fit the declared shape. The
3013
+ * declared `outputs` are a strict allowlist bounding what an effect may persist
3014
+ * into `effectsContext`, so the completion does NOT commit rather than silently
3015
+ * storing off-contract data. An effect that declares no `outputs` has an EMPTY
3016
+ * allowlist (produces nothing), so any returned output fails here.
3017
+ */
3018
+ export declare class EffectOutputsInvalidError extends WorkflowError<"effect-outputs-invalid"> {
3019
+ readonly effect: string;
3020
+ readonly issues: string[];
3021
+ constructor(args: { effect: string; issues: string[] });
3022
+ }
3023
+
3024
+ /**
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.
3030
+ */
3031
+ export declare type EffectRunStatus = "done" | "failed" | "cancelled";
3032
+
2339
3033
  /**
2340
3034
  * EffectsContext entry kinds.
2341
3035
  */
@@ -2394,10 +3088,42 @@ declare const EffectSchema: v.StrictObjectSchema<
2394
3088
  v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>,
2395
3089
  undefined
2396
3090
  >;
3091
+ /**
3092
+ * The outputs this effect is allowed to produce, as typed {@link FieldShape}s
3093
+ * (each `name` is an output key, read downstream as `$effects['<name>'].<key>`;
3094
+ * an `array` output is an array of objects shaped by `of`).
3095
+ *
3096
+ * A STRICT allowlist: at completion the handler's returned `outputs` are
3097
+ * validated against these shapes and an undeclared key — or a value that
3098
+ * doesn't fit its shape — fails the completion (nothing is stored). Omitting
3099
+ * `outputs` is an EMPTY allowlist: the effect produces nothing, so any returned
3100
+ * 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
+ >;
2397
3111
  },
2398
3112
  undefined
2399
3113
  >;
2400
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
+
2401
3127
  /**
2402
3128
  * Each entry is a named param the runtime hands to effect handlers via
2403
3129
  * binding resolution, rendered to conditions as `$effects.<name>`. **Set at
@@ -2452,6 +3178,15 @@ export declare function effectsContextMap(
2452
3178
  instance: Pick<WorkflowInstance, "effectsContext">,
2453
3179
  ): Record<string, unknown>;
2454
3180
 
3181
+ /** The `effectHistory` outcome {@link EffectNotFoundError} reports when the
3182
+ * missing key belongs to a settled run. `detail` is the row's recorded
3183
+ * detail — for a cancellation, the cause the canceller stamped. */
3184
+ export declare interface EffectSettledInfo {
3185
+ status: EffectRunStatus;
3186
+ ranAt: string;
3187
+ detail?: string;
3188
+ }
3189
+
2455
3190
  export declare interface Engine {
2456
3191
  /** Engine-scoped bindings — exposed for the few advanced consumers
2457
3192
  * (e.g. test bench, drain workers) that need them; the verbs already
@@ -2462,8 +3197,11 @@ export declare interface Engine {
2462
3197
  readonly effectHandlers: Readonly<Record<string, EffectHandler>>;
2463
3198
  readonly missingHandler: MissingHandlerPolicy;
2464
3199
  readonly logger: LoggerFactory;
2465
- deployDefinitions: (
2466
- args: DeployDefinitionsArgs,
3200
+ /** The resolved telemetry logger ({@link noopTelemetry} unless injected) —
3201
+ * exposed so adapters built on the engine log through the same seam. */
3202
+ readonly telemetry: WorkflowTelemetryLogger;
3203
+ deployDefinitions: <T extends WorkflowDefinitionInput<T>>(
3204
+ args: DeployDefinitionsArgs<T>,
2467
3205
  ) => Promise<DeployDefinitionsResult>;
2468
3206
  startInstance: (args: StartInstanceArgs) => Promise<OperationResult>;
2469
3207
  fireAction: (args: FireActionArgs) => Promise<OperationResult>;
@@ -2533,6 +3271,14 @@ export declare interface Engine {
2533
3271
  instancesForDocument: (
2534
3272
  args: InstancesForDocumentArgs,
2535
3273
  ) => Promise<WorkflowInstance[]>;
3274
+ /** The startable half of {@link Engine.instancesForDocument}: the latest
3275
+ * deployed version of every definition that applies to the LOADED candidate
3276
+ * 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. */
3279
+ definitionsForDocument: (
3280
+ args: DefinitionsForDocumentArgs,
3281
+ ) => Promise<DeployedDefinition[]>;
2536
3282
  /** GROQ query against the engine's workflow resource. `$tag`
2537
3283
  * is bound for tag-scoped filtering. */
2538
3284
  query: <T = unknown>(args: QueryArgs) => Promise<T>;
@@ -2547,10 +3293,13 @@ export declare interface Engine {
2547
3293
  findPendingEffects: (
2548
3294
  args: FindPendingEffectsArgs,
2549
3295
  ) => Promise<PendingEffect[]>;
2550
- /** Dispatch unclaimed effects through registered handlers. The
2551
- * drainer's identity defaults to a `{ kind: "system", id: "engine.drainEffects" }`
2552
- * actor; pass `access` to override (e.g. impersonate a real user
2553
- * whose grants you want the dispatch to respect). */
3296
+ /** Dispatch unclaimed effects through registered handlers — including
3297
+ * entries whose claim's lease expired, which are taken over (audit row)
3298
+ * and redispatched. The drainer's identity is the engine client's token —
3299
+ * a drain runtime supplies its own token-bearing client and declares
3300
+ * itself via `executionContext`. Release-without-dispatch housekeeping is
3301
+ * the standalone {@link sweepStaleClaims} export, deliberately off the
3302
+ * engine surface. */
2554
3303
  drainEffects: (args: DrainEffectsArgs) => Promise<DrainEffectsResult>;
2555
3304
  /**
2556
3305
  * Inspect every deployed definition in the engine's tag and apply
@@ -2566,8 +3315,15 @@ export declare interface Engine {
2566
3315
  }
2567
3316
 
2568
3317
  /**
2569
- * The API surface the engine's reads and writes assume. Every client bound to
2570
- * the engine pins this one version so they cannot drift apart.
3318
+ * The API surface the engine's reads and writes assume. The engine owns this
3319
+ * pin: every entry point `createEngine`, each raw `workflow.*` verb, the
3320
+ * housekeeping exports — derives its working client onto this version via
3321
+ * {@link WorkflowClient.withConfig}, and `resourceClients`-resolved clients
3322
+ * are rebound the same way. A caller's configured `apiVersion` therefore
3323
+ * never reaches engine traffic (the caller's own client instance is
3324
+ * untouched). The one exception is a client that lacks `withConfig` and so
3325
+ * cannot be rebound — it must be built to serve this version; see
3326
+ * {@link WorkflowClient.withConfig}.
2571
3327
  */
2572
3328
  export declare const ENGINE_API_VERSION = "2026-04-29";
2573
3329
 
@@ -2583,6 +3339,17 @@ export declare interface EngineLogger {
2583
3339
  * these — they were supplied at construction.
2584
3340
  */
2585
3341
  export declare interface EngineScopeArgs {
3342
+ /**
3343
+ * The engine resolves its acting identity from this client's token and
3344
+ * caches the resolution per client object — the client's auth must be
3345
+ * stable for the engine's lifetime. To act as someone else, construct
3346
+ * an engine over a client bound to their token (`withConfig({token})`),
3347
+ * never mutate this client's auth in place.
3348
+ *
3349
+ * Any configured `apiVersion` works: every verb derives its working
3350
+ * client onto `ENGINE_API_VERSION`, on the raw namespace as much as
3351
+ * through `createEngine` — see {@link WorkflowClient.withConfig}.
3352
+ */
2586
3353
  client: WorkflowClient;
2587
3354
  /** Engine-scope environment partition — required. See {@link validateTag} + `tags.ts`. */
2588
3355
  tag: string;
@@ -2593,29 +3360,52 @@ export declare interface EngineScopeArgs {
2593
3360
  */
2594
3361
  workflowResource: WorkflowResource;
2595
3362
  /**
2596
- * Optional routing for cross-resource reads (subject + ancestor docs that
2597
- * live in a different Sanity resource than the workflow). Called with a
2598
- * parsed GDR; return a client for that resource, or undefined to fall back
2599
- * to `client`. Engine-scope configuration see `CreateEngineArgs`.
3363
+ * Optional routing override for cross-resource reads (subject + ancestor
3364
+ * docs that live in a different Sanity resource than the workflow). Called
3365
+ * with a parsed GDR; return a client for that resource, or undefined to let
3366
+ * 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`.
2600
3369
  */
2601
3370
  resourceClients?: ResourceClientResolver;
3371
+ /**
3372
+ * Declared execution context — the advisory "via what" stamped on every
3373
+ * history entry alongside the token-resolved actor. Construction-scope
3374
+ * configuration, never per-call: the runtime half is always inferred, and
3375
+ * the declared `{kind, id}` half labels the host (see `EXECUTION_KINDS`).
3376
+ * Omitted ⇒ runtime-only stamps.
3377
+ */
3378
+ executionContext?: DeclaredExecutionContext;
3379
+ /**
3380
+ * How long a recorded `idempotencyKey` dedupes retries (the
3381
+ * `processedRequests[]` row's lifetime). Default 24 hours. Size it to the
3382
+ * caller's longest retry horizon — a retry arriving after the row expired
3383
+ * re-executes the operation.
3384
+ */
3385
+ idempotencyTtlMs?: number;
2602
3386
  }
2603
3387
 
3388
+ /**
3389
+ * Pure error helpers — no client, no I/O. The one home for the engine's
3390
+ * "coerce an unknown caught value" + "rethrow with context" idioms, so the
3391
+ * message shape and cause-chaining can't drift across call sites.
3392
+ */
3393
+ /**
3394
+ * A human-readable message for an unknown caught value: `.message` for an
3395
+ * {@link Error}, otherwise `String(value)`. Strips control characters
3396
+ * (keeping newlines and tabs) so server-derived error text can't smuggle
3397
+ * terminal escape sequences into consumer output.
3398
+ */
3399
+ export declare function errorMessage(err: unknown): string;
3400
+
2604
3401
  export declare interface EvaluateArgs {
2605
3402
  instanceId: string;
2606
- /**
2607
- * Optional access-state override. By default the engine resolves
2608
- * `(actor, grants)` from the supplied client's token via
2609
- * `/users/me` (+ `grantsFromPath`). The bench supplies its
2610
- * `ALL_ACCESS` default here; admin tooling can use this for
2611
- * "view as another user" or "preview with restricted grants" flows.
2612
- */
2613
- access?: WorkflowAccessOverride;
2614
3403
  /**
2615
3404
  * URL path on the supplied client where the engine should fetch
2616
- * grants when `access.grants` isn't supplied. Without grants the
2617
- * rendered `$can` is undefined, so conditions referencing it fail
2618
- * closed — and the real Sanity write boundary still enforces.
3405
+ * grants. The actor always token-resolves via `/users/me`; without
3406
+ * a grants path the rendered `$can` is undefined, so conditions
3407
+ * referencing it fail closed — and the real Sanity write boundary
3408
+ * still enforces.
2619
3409
  *
2620
3410
  * Canvas resource example: `/canvases/<resourceId>/acl`.
2621
3411
  * Project/dataset example: `/projects/<id>/datasets/<dataset>/acl`.
@@ -2671,6 +3461,15 @@ export declare interface EvaluateFromSnapshotArgs {
2671
3461
  * commit time ({@link evaluateInstance} fetches them itself).
2672
3462
  */
2673
3463
  guards?: readonly MutationGuardDoc[];
3464
+ /**
3465
+ * The actor's ACL grants per FOREIGN subject resource, keyed by
3466
+ * resource-shaped GDR (`<type>:<id>`) — feeds the subject-write forecast on
3467
+ * effects-bearing actions ({@link SubjectPermissionDenial}). Omit, or omit a
3468
+ * resource, to skip that resource's forecast (degrade open — the subject's
3469
+ * lake still enforces). {@link evaluateInstance} resolves this through each
3470
+ * resource's own client via {@link subjectResourceGrants}.
3471
+ */
3472
+ resourceGrants?: ReadonlyMap<string, Grant[]>;
2674
3473
  }
2675
3474
 
2676
3475
  /**
@@ -2683,6 +3482,56 @@ export declare function evaluateMutationGuard(args: {
2683
3482
  context: MutationContext;
2684
3483
  }): Promise<boolean>;
2685
3484
 
3485
+ /**
3486
+ * Well-known {@link ExecutionContext.kind} values. The field is a free
3487
+ * string — these are the shipped vocabulary, not a closed set.
3488
+ */
3489
+ export declare const EXECUTION_KINDS: {
3490
+ /** A human-facing session (generic — prefer `studio`/`sdkApp` when known). */
3491
+ readonly interactive: "interactive";
3492
+ /** A server process acting with a user's token (e.g. a publish proxy). */
3493
+ readonly server: "server";
3494
+ readonly cli: "cli";
3495
+ readonly mcp: "mcp";
3496
+ /** An effect-drain runtime. */
3497
+ readonly drainer: "drainer";
3498
+ readonly script: "script";
3499
+ readonly test: "test";
3500
+ readonly studio: "studio";
3501
+ readonly sdkApp: "sdk-app";
3502
+ };
3503
+
3504
+ /**
3505
+ * Execution context — advisory "via what" provenance, stamped on every
3506
+ * history entry alongside the actor's "who".
3507
+ *
3508
+ * Identity is always the token behind the client (`/users/me`); the
3509
+ * execution context records the ENVIRONMENT that drove the commit: a
3510
+ * studio session, the CLI, an MCP host, a server proxy acting with a
3511
+ * user's token. It is self-asserted advisory metadata, configured once at
3512
+ * `createEngine` — never per call — and never an authorization input.
3513
+ *
3514
+ * The `runtime` half is always inferred at construction (best-effort,
3515
+ * zero dependencies); the declared `{kind, id}` half is optional. An
3516
+ * unlabeled `runtime: "node"` is itself a signal (a script or proxy that
3517
+ * didn't declare itself), and a declared/inferred mismatch (`kind:
3518
+ * "studio"` on `runtime: "node"`) surfaces SSR passes or lying labels
3519
+ * for free.
3520
+ */
3521
+ /** The stored stamp — `runtime` is always present, the declared half optional. */
3522
+ export declare interface ExecutionContext {
3523
+ /** Inferred JavaScript runtime: `browser` | `node` | `worker` | `edge` | `deno` | `bun` | `unknown`. */
3524
+ runtime: string;
3525
+ /** Declared role — free string; see {@link EXECUTION_KINDS} for the shipped vocabulary. */
3526
+ kind?: string;
3527
+ /** Declared instance label, e.g. `workflow-cli` or `publish-proxy`. */
3528
+ id?: string;
3529
+ }
3530
+
3531
+ export { explainCondition };
3532
+
3533
+ export { ExplainConditionArgs };
3534
+
2686
3535
  /**
2687
3536
  * Extract the bare document `_id` from a GDR URI — what
2688
3537
  * `*[_id == $docId]` in GROQ needs.
@@ -2702,7 +3551,11 @@ export declare function extractDocumentId(gdrUriString: string): string;
2702
3551
  */
2703
3552
  declare function fetchGrants(args: {
2704
3553
  client: {
2705
- request: <T>(opts: { url: string; signal?: AbortSignal }) => Promise<T>;
3554
+ request: <T>(opts: {
3555
+ url: string;
3556
+ signal?: AbortSignal;
3557
+ tag?: string;
3558
+ }) => Promise<T>;
2706
3559
  };
2707
3560
  resourcePath: string;
2708
3561
  signal?: AbortSignal;
@@ -2782,6 +3635,9 @@ declare const FIELD_SCOPES: readonly ["workflow", "stage", "activity"];
2782
3635
  * reference kinds, the actor/assignee identities, and the two compositional
2783
3636
  * kinds (`object` with named `fields`, `array` of objects shaped by `of`).
2784
3637
  * This is also the set a nested {@link FieldShape} sub-field may use.
3638
+ *
3639
+ * Exported (module-level, not package API) for the model-surface gate's
3640
+ * enum-value coverage test.
2785
3641
  */
2786
3642
  declare const FIELD_VALUE_KINDS: readonly [
2787
3643
  "doc.ref",
@@ -2801,30 +3657,75 @@ declare const FIELD_VALUE_KINDS: readonly [
2801
3657
  "array",
2802
3658
  ];
2803
3659
 
2804
- /** Type-mirror of {@link fieldBase}, parameterised over the `editable` grammar. */
2805
- declare type FieldBase<TEditable> = {
3660
+ /** Type-mirror of {@link fieldBase}, parameterised over the `editable` and
3661
+ * `group` grammars (stored membership is the canonical list form). */
3662
+ declare type FieldBase<TEditable, TGroup> = {
2806
3663
  name: string;
2807
3664
  title?: string | undefined;
2808
3665
  description?: string | undefined;
3666
+ group?: TGroup | undefined;
2809
3667
  required?: boolean | undefined;
2810
3668
  initialValue?: FieldSource | undefined;
2811
3669
  editable?: TEditable | undefined;
2812
3670
  };
2813
3671
 
2814
- export declare type FieldEntry = FieldEntryFields<Editable>;
3672
+ export declare interface FieldDescription {
3673
+ /** The field's footprint — present even when nothing is proposable. */
3674
+ involvement: InsightPhrase;
3675
+ /** One phrase per verified proposal. */
3676
+ proposals: InsightPhrase[];
3677
+ }
3678
+
3679
+ export declare type FieldEntry = FieldEntryFields<Editable, string[]>;
2815
3680
 
2816
3681
  /** Type-mirror of {@link fieldEntryFields}: a raw field entry of the given
2817
- * editability grammar. */
2818
- declare type FieldEntryFields<TEditable> = FieldBase<TEditable> & {
3682
+ * editability and group-membership grammars. */
3683
+ declare type FieldEntryFields<TEditable, TGroup> = FieldBase<
3684
+ TEditable,
3685
+ TGroup
3686
+ > & {
2819
3687
  type: FieldValueKind;
3688
+ types?: string[] | undefined;
2820
3689
  fields?: FieldShape[] | undefined;
2821
3690
  of?: FieldShape[] | undefined;
2822
3691
  };
2823
3692
 
3693
+ /**
3694
+ * Everything the evaluation derived about one `$fields` entry. `involvedIn`
3695
+ * is the field's full footprint — every gate whose condition reads it —
3696
+ * independent of whether any single value flips anything. Scope caveat: the
3697
+ * name is the RENDERED scope name; a stage-/activity-scope declaration
3698
+ * shadows a workflow-scope one lexically, so the same name in two sites'
3699
+ * scopes can denote two declarations.
3700
+ */
3701
+ export declare interface FieldInsight {
3702
+ /** The field entry name — the head segment of every read below. */
3703
+ field: string;
3704
+ /** The distinct `$fields` reads naming this field across all sites. */
3705
+ reads: ConditionRead[];
3706
+ /** Every site whose condition reads this field. */
3707
+ involvedIn: InsightSite[];
3708
+ /** Verified single-value proposals harvested from blocking atoms; empty
3709
+ * when nothing solvable blocks on this field. */
3710
+ proposals: FieldProposal[];
3711
+ }
3712
+
2824
3713
  export declare type FieldKind = keyof FieldValueMap;
2825
3714
 
2826
3715
  export declare type FieldOp = v.InferOutput<typeof StoredFieldOpSchema>;
2827
3716
 
3717
+ /**
3718
+ * A concrete value a blocking atom pins for this field, with the verified
3719
+ * consequences of assigning it — "setting `legalApproved` to `true` flips
3720
+ * transition `to-publish` to satisfied". Verified by re-evaluating every
3721
+ * site that reads the field, so a listed consequence is what the engine
3722
+ * would actually conclude, not a syntactic guess.
3723
+ */
3724
+ export declare interface FieldProposal {
3725
+ assign: ScopeAssignment;
3726
+ consequences: SiteConsequence[];
3727
+ }
3728
+
2828
3729
  declare type FieldReadExpr = {
2829
3730
  type: "fieldRead";
2830
3731
  scope?: "workflow" | "stage" | undefined;
@@ -2856,6 +3757,15 @@ declare type FieldSourceInternal =
2856
3757
  | LiteralExpr
2857
3758
  | FieldReadExpr;
2858
3759
 
3760
+ /**
3761
+ * 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.
3766
+ */
3767
+ export declare function fieldTreeShape(value: unknown): unknown;
3768
+
2859
3769
  /** The union a value (or nested {@link FieldShape}) kind may take — see {@link FIELD_VALUE_KINDS}. */
2860
3770
  declare type FieldValueKind = (typeof FIELD_VALUE_KINDS)[number];
2861
3771
 
@@ -2897,20 +3807,54 @@ export declare class FieldValueShapeError extends WorkflowError<"field-value-sha
2897
3807
 
2898
3808
  /**
2899
3809
  * The subset that holds a value when the engine evaluates filters without a
2900
- * caller (the cascade path: transition filters, `completeWhen`/`failWhen`).
3810
+ * caller (the cascade path: transition filters, activity `filter`/
3811
+ * `completeWhen`/`failWhen`).
2901
3812
  */
2902
3813
  export declare const FILTER_SCOPE_VARS: readonly string[];
2903
3814
 
2904
- export declare interface FindPendingEffectsArgs extends InstanceRefArgs {
2905
- /** Filter on claim presence. */
2906
- claimed?: boolean;
2907
- /** Restrict to specific effect names. */
2908
- names?: string[];
2909
- }
3815
+ /**
3816
+ * The current stage's {@link ActivityEntry} named `activityName`, or
3817
+ * `undefined` when the open stage holds no such activity (including when
3818
+ * `activityName` is itself `undefined`). Composes {@link findOpenStageEntry}
3819
+ * with the by-name lookup that activity ops, editability, params, and the
3820
+ * operation pre-flights all need — callers keep their own `.status` / `.fields`
3821
+ * chain off the result.
3822
+ */
3823
+ export declare function findCurrentActivityEntry(
3824
+ host: {
3825
+ stages: StageEntry[];
3826
+ currentStage: StageName;
3827
+ },
3828
+ activityName: ActivityName | undefined,
3829
+ ): ActivityEntry | undefined;
2910
3830
 
2911
- export declare interface FireActionArgs extends OperationArgs {
2912
- activity: string;
2913
- action: string;
3831
+ /**
3832
+ * The open (un-exited) {@link StageEntry} matching `currentStage`, or
3833
+ * `undefined`. On loop-backs (re-entering a stage) several entries share
3834
+ * a name — the prior ones have `exitedAt` set, the current one does not.
3835
+ * Reads the `stages` + `currentStage` shape shared by the persisted
3836
+ * `WorkflowInstance` and the engine's mutable copy.
3837
+ */
3838
+ export declare function findOpenStageEntry(host: {
3839
+ stages: StageEntry[];
3840
+ currentStage: StageName;
3841
+ }): StageEntry | undefined;
3842
+
3843
+ export declare interface FindPendingEffectsArgs extends InstanceRefArgs {
3844
+ /**
3845
+ * Filter on claim PRESENCE — not protection. An entry whose claim's lease
3846
+ * expired still reports `claimed: true` here, yet a drain may take it
3847
+ * over; mirror the drainable set with {@link isClaimExpired} over the
3848
+ * returned entries' claims.
3849
+ */
3850
+ claimed?: boolean;
3851
+ /** Restrict to specific effect names. */
3852
+ names?: string[];
3853
+ }
3854
+
3855
+ export declare interface FireActionArgs extends DedupableOperationArgs {
3856
+ activity: string;
3857
+ action: string;
2914
3858
  /**
2915
3859
  * Caller-supplied params for the action. Validated against
2916
3860
  * `Action.params[]` before commit; missing/required mismatch throws
@@ -2921,13 +3865,18 @@ export declare interface FireActionArgs extends OperationArgs {
2921
3865
  /**
2922
3866
  * If true, no-op (instead of throwing) when the activity is missing from
2923
3867
  * the current stage's `activityStatus` — typically because the workflow
2924
- * has already advanced past it. Useful for drive scripts and
2925
- * at-least-once delivery semantics: the second call returns the
2926
- * unchanged instance with `changed: false` rather than blowing up.
3868
+ * has already advanced past it. Distinct from `idempotencyKey`: this flag
3869
+ * tolerates a *state* the fire no longer applies to (however it got
3870
+ * there, including someone else's action), while the key dedupes a retry
3871
+ * of *this specific request*. A drive script tolerating advanced
3872
+ * workflows wants the flag; a queue consumer redelivering a message
3873
+ * wants the key.
2927
3874
  */
2928
3875
  idempotent?: boolean;
2929
3876
  }
2930
3877
 
3878
+ export { formatRead };
3879
+
2931
3880
  /**
2932
3881
  * Build a GDR URI from a workflow resource config + a document id
2933
3882
  * within that resource. This is how the engine mints `_id`s for
@@ -2939,15 +3888,15 @@ export declare function gdrFromResource(
2939
3888
  ): GdrUri;
2940
3889
 
2941
3890
  /** Build a GDR (id + type) pointing at a document within a workflow resource. */
2942
- export declare function gdrRef({
3891
+ export declare function gdrRef<TType extends string = string>({
2943
3892
  res,
2944
3893
  documentId,
2945
3894
  type,
2946
3895
  }: {
2947
3896
  res: WorkflowResource;
2948
3897
  documentId: string;
2949
- type: string;
2950
- }): GlobalDocumentReference;
3898
+ type: TType;
3899
+ }): GlobalDocumentReference<TType>;
2951
3900
 
2952
3901
  /**
2953
3902
  * Global Document References (GDR).
@@ -3001,11 +3950,18 @@ export declare function gdrUri(
3001
3950
  },
3002
3951
  ): GdrUri;
3003
3952
 
3004
- export declare interface GlobalDocumentReference {
3953
+ /**
3954
+ * `TType` preserves the `type` literal the constructors ({@link refDataset},
3955
+ * {@link gdrRef}, …) were called with, so a constructed ref satisfies
3956
+ * literal-typed targets (e.g. a `release.ref` value's `"system.release"`).
3957
+ */
3958
+ export declare interface GlobalDocumentReference<
3959
+ TType extends string = string,
3960
+ > {
3005
3961
  /** URI: `<scheme>:<...id-parts>` */
3006
3962
  id: GdrUri;
3007
3963
  /** Document `_type` (schema name) */
3008
- type: string;
3964
+ type: TType;
3009
3965
  }
3010
3966
 
3011
3967
  export declare interface Grant {
@@ -3029,6 +3985,84 @@ declare function grantsPermissionOn(args: {
3029
3985
  userId?: string;
3030
3986
  }): Promise<boolean>;
3031
3987
 
3988
+ /** Type-mirror of {@link GroupSchema} — one declared group. */
3989
+ export declare type Group = {
3990
+ name: string;
3991
+ title?: string | undefined;
3992
+ description?: string | undefined;
3993
+ kind?: GroupKind | undefined;
3994
+ };
3995
+
3996
+ /**
3997
+ * Group-kind labels — the informational role of a declared group. Standalone
3998
+ * for the same reason as {@link ACTIVITY_KIND_DISPLAY}. A consumer reading a
3999
+ * definition as data and meeting an unknown kind (deployed by a newer engine)
4000
+ * must treat it as unset — a plain group; the strict engine parse itself
4001
+ * rejects unknown kinds, like every stored enum.
4002
+ */
4003
+ export declare const GROUP_KIND_DISPLAY: {
4004
+ core: {
4005
+ title: string;
4006
+ description: string;
4007
+ };
4008
+ details: {
4009
+ title: string;
4010
+ description: string;
4011
+ };
4012
+ };
4013
+
4014
+ export declare const GROUP_KINDS: readonly ["core", "details"];
4015
+
4016
+ export declare type GroupKind = (typeof GROUP_KINDS)[number];
4017
+
4018
+ /** A group member's address — enough to locate the node without the definition. */
4019
+ export declare type GroupMember =
4020
+ | {
4021
+ kind: "field";
4022
+ scope: FieldScope;
4023
+ stage?: string;
4024
+ activity?: string;
4025
+ name: string;
4026
+ }
4027
+ | {
4028
+ kind: "activity";
4029
+ stage: string;
4030
+ name: string;
4031
+ }
4032
+ | {
4033
+ kind: "action";
4034
+ stage: string;
4035
+ activity: string;
4036
+ name: string;
4037
+ };
4038
+
4039
+ /**
4040
+ * Group membership — the declared group(s) a node belongs to. A reference
4041
+ * resolves lexically up the node's enclosing chain (host activity → stage →
4042
+ * workflow; an activity's OWN membership sees stage → workflow — a node is
4043
+ * never a member of a group only it declares). An empty list is an author
4044
+ * error (it would name no groups) — omit `group` instead, and each group is
4045
+ * listed once. Authoring accepts a bare name as convenience; the STORED form
4046
+ * is always a list, so `'seo'` and `['seo']` hash identically under content
4047
+ * addressing and lake-side reads see one shape.
4048
+ */
4049
+ export declare type GroupMembership = string | string[];
4050
+
4051
+ /** The names in a `group` membership, normalised to a fresh list ([] when absent). */
4052
+ export declare function groupMembershipNames(
4053
+ group: GroupMembership | undefined,
4054
+ ): string[];
4055
+
4056
+ /**
4057
+ * Every group a definition declares, in stable order: workflow groups, then
4058
+ * per stage — the stage's groups, then per activity — the activity's groups.
4059
+ * A membership that resolves to no declaration (rejected at deploy) is
4060
+ * omitted — no site is invented for it.
4061
+ */
4062
+ export declare function groupSitesOf(
4063
+ definition: WorkflowDefinition,
4064
+ ): DefinitionGroupSite[];
4065
+
3032
4066
  export declare type Guard = v.InferOutput<typeof GuardSchema>;
3033
4067
 
3034
4068
  /**
@@ -3242,8 +4276,8 @@ declare const GuardSchema: v.StrictObjectSchema<
3242
4276
  undefined
3243
4277
  >;
3244
4278
  /**
3245
- * Lake GROQ predicate — a distinct eval context reading
3246
- * `document.before`/`document.after`, `mutation`, `guard`, and
4279
+ * Lake GROQ predicate — a distinct eval context: delta-mode GROQ
4280
+ * reading the `before()`/`after()` natives, `mutation`, `guard`, and
3247
4281
  * `identity()`. Bare ids/fields only. Omitted or empty means
3248
4282
  * UNCONDITIONAL DENY.
3249
4283
  */
@@ -3318,6 +4352,8 @@ export declare function guardsForResource(
3318
4352
  client: WorkflowClient,
3319
4353
  ): Promise<MutationGuardDoc[]>;
3320
4354
 
4355
+ export { guillemets };
4356
+
3321
4357
  /**
3322
4358
  * Content fingerprint of an authored definition: the canonical JSON of its
3323
4359
  * content, hashed. Stamped on the deployed document ({@link DeployedDefinition})
@@ -3368,10 +4404,26 @@ export declare const HISTORY_DISPLAY: {
3368
4404
  title: string;
3369
4405
  description: string;
3370
4406
  };
4407
+ effectClaimReleased: {
4408
+ title: string;
4409
+ description: string;
4410
+ };
3371
4411
  spawned: {
3372
4412
  title: string;
3373
4413
  description: string;
3374
4414
  };
4415
+ subworkflowAdopted: {
4416
+ title: string;
4417
+ description: string;
4418
+ };
4419
+ subworkflowResolved: {
4420
+ title: string;
4421
+ description: string;
4422
+ };
4423
+ subworkflowOrphaned: {
4424
+ title: string;
4425
+ description: string;
4426
+ };
3375
4427
  aborted: {
3376
4428
  title: string;
3377
4429
  description: string;
@@ -3386,7 +4438,18 @@ export declare const HISTORY_DISPLAY: {
3386
4438
  };
3387
4439
  };
3388
4440
 
3389
- export declare type HistoryEntry =
4441
+ export declare type HistoryEntry = HistoryEvent & {
4442
+ /**
4443
+ * The execution environment that committed this entry — inferred
4444
+ * runtime plus the host's declared `{kind, id}` (see
4445
+ * {@link ExecutionContext}). Stamped centrally at persist time on every
4446
+ * entry the engine appends; absent only on entries stored before the
4447
+ * stamp existed.
4448
+ */
4449
+ executionContext?: ExecutionContext;
4450
+ };
4451
+
4452
+ declare type HistoryEvent =
3390
4453
  | {
3391
4454
  _key: string;
3392
4455
  _type: "stageEntered";
@@ -3465,16 +4528,33 @@ export declare type HistoryEntry =
3465
4528
  origin: EffectOrigin;
3466
4529
  }
3467
4530
  | {
4531
+ /** A queued effect settled — a reported completion (`done`/`failed`),
4532
+ * or an abort cancelling it before dispatch (`cancelled`). One entry
4533
+ * per `effectHistory` row, sharing its `effectKey`. */
3468
4534
  _key: string;
3469
4535
  _type: "effectCompleted";
3470
4536
  at: string;
3471
4537
  effectKey: string;
3472
4538
  effect: EffectName;
3473
- status: "done" | "failed";
4539
+ status: EffectRunStatus;
3474
4540
  outputs?: Record<string, unknown>;
3475
4541
  detail?: string;
3476
4542
  actor?: Actor;
3477
4543
  }
4544
+ | {
4545
+ _key: string;
4546
+ _type: "effectClaimReleased";
4547
+ at: string;
4548
+ effectKey: string;
4549
+ effect: EffectName;
4550
+ /** The expired claim that was released — who abandoned it and when. */
4551
+ claim: PendingEffectClaim;
4552
+ /** How the stale claim was recovered: a drain taking it over for
4553
+ * redispatch, or `sweepStaleClaims` force-releasing it. */
4554
+ via: "drain-takeover" | "sweep";
4555
+ /** The drainer/sweeper that released it. */
4556
+ actor?: Actor;
4557
+ }
3478
4558
  | {
3479
4559
  _key: string;
3480
4560
  _type: "spawned";
@@ -3483,12 +4563,45 @@ export declare type HistoryEntry =
3483
4563
  /**
3484
4564
  * GDR pointer at the spawned child workflow instance. Stored as
3485
4565
  * the typed `{id, type}` envelope — same shape every other GDR
3486
- * field in the engine uses (`ancestors[]`, `spawnedInstances[]`,
4566
+ * field in the engine uses (`ancestors[]`, the subworkflow registry,
3487
4567
  * any `doc.ref` field entry). The `id` is the full GDR URI.
3488
4568
  */
3489
4569
  instanceRef: GlobalDocumentReference;
4570
+ /** Identity of the `forEach` row that produced this child. */
4571
+ rowKey?: string;
3490
4572
  actor?: Actor;
3491
4573
  }
4574
+ | {
4575
+ _key: string;
4576
+ _type: "subworkflowAdopted";
4577
+ at: string;
4578
+ stage: StageName;
4579
+ activity: ActivityName;
4580
+ /** The live child re-bound to the entering stage's cohort instead of
4581
+ * being duplicated — its `forEach` row was rediscovered on re-entry. */
4582
+ instanceRef: GlobalDocumentReference;
4583
+ rowKey: string;
4584
+ }
4585
+ | {
4586
+ _key: string;
4587
+ _type: "subworkflowResolved";
4588
+ at: string;
4589
+ activity: ActivityName;
4590
+ /** The child observed terminal; its registry row now carries the
4591
+ * terminal cache. */
4592
+ instanceRef: GlobalDocumentReference;
4593
+ status: "done" | "aborted";
4594
+ }
4595
+ | {
4596
+ _key: string;
4597
+ _type: "subworkflowOrphaned";
4598
+ at: string;
4599
+ /** A child that names this instance in its `ancestors` reached terminal,
4600
+ * but no registry row matches it — its completion cannot drive any
4601
+ * gate. Loud audit record of a propagation dead-end. */
4602
+ instanceRef: GlobalDocumentReference;
4603
+ detail: string;
4604
+ }
3492
4605
  | {
3493
4606
  _key: string;
3494
4607
  _type: "aborted";
@@ -3542,6 +4655,8 @@ export declare type HistoryEntry =
3542
4655
  detail: string;
3543
4656
  };
3544
4657
 
4658
+ export { humanize };
4659
+
3545
4660
  /**
3546
4661
  * The in-memory groq-js dataset filters evaluate against. Built once
3547
4662
  * per cascade entry (in the shell), passed through to `evaluateFilter`
@@ -3572,6 +4687,44 @@ export declare type InitialFieldValue = {
3572
4687
  };
3573
4688
  }[FieldKind];
3574
4689
 
4690
+ export { InsightPhrase };
4691
+
4692
+ /** Where a condition lives in the current stage — the address a consumer
4693
+ * joins back onto the matching evaluation node. */
4694
+ export declare type InsightSite =
4695
+ | {
4696
+ kind: "transition";
4697
+ transition: string;
4698
+ }
4699
+ | {
4700
+ kind: "activity-filter";
4701
+ activity: string;
4702
+ }
4703
+ | {
4704
+ kind: "requirement";
4705
+ activity: string;
4706
+ requirement: string;
4707
+ }
4708
+ | {
4709
+ kind: "complete-when";
4710
+ activity: string;
4711
+ }
4712
+ | {
4713
+ kind: "fail-when";
4714
+ activity: string;
4715
+ }
4716
+ | {
4717
+ kind: "action";
4718
+ activity: string;
4719
+ action: string;
4720
+ }
4721
+ | {
4722
+ kind: "editable-field";
4723
+ scope: FieldScope;
4724
+ name: string;
4725
+ activity?: string;
4726
+ };
4727
+
3575
4728
  /**
3576
4729
  * The per-instance guard filter — the single definition of "this instance's
3577
4730
  * guards in one datasource". The engine's verdict load
@@ -3585,7 +4738,7 @@ export declare function instanceGuardQuery(instanceId: string): CompiledQuery;
3585
4738
 
3586
4739
  declare interface InstanceGuardsQueryArgs {
3587
4740
  client: WorkflowClient;
3588
- clientForGdr: (parsed: ParsedGdr) => WorkflowClient;
4741
+ clientForGdr: ClientForGdr;
3589
4742
  instance: WorkflowInstance;
3590
4743
  }
3591
4744
 
@@ -3605,6 +4758,21 @@ export declare interface InstanceRefArgs {
3605
4758
  instanceId: string;
3606
4759
  }
3607
4760
 
4761
+ /**
4762
+ * The two join keys every instance-scoped event carries — one home for the
4763
+ * fields, their docs, and the policy, so a new event can't forget half the
4764
+ * pair or drift its wording.
4765
+ */
4766
+ declare interface InstanceScopedEventData {
4767
+ /** Absent only when the definition predates content fingerprinting. */
4768
+ definitionContentHash?: string;
4769
+ /** The instance document `_id` — the per-instance join key (engine-minted
4770
+ * `<tag>.wf-instance.<random>`; caller-supplied ids ride verbatim). One of
4771
+ * the payload policy's two deliberate customer-string exceptions — see the
4772
+ * module doc. */
4773
+ instanceId: string;
4774
+ }
4775
+
3608
4776
  export declare interface InstanceSession {
3609
4777
  /** The watch-set to feed via {@link InstanceSession.update}, derived from the
3610
4778
  * held instance. Recompute after the instance changes (a new stage changes it). */
@@ -3622,8 +4790,11 @@ export declare interface InstanceSession {
3622
4790
  * behind an in-flight commit — guards aren't part of the held-snapshot
3623
4791
  * consistency story, and tick's pre-flight reads them at commit start. */
3624
4792
  updateGuards(guards: readonly MutationGuardDoc[]): void;
3625
- /** Best-effort projection against the held content + held guards. No network
3626
- * (async only because filter evaluation runs on groq-js). */
4793
+ /** Best-effort projection against the held content + held guards. The first
4794
+ * call resolves the caller's identity/grants from the client's token over
4795
+ * the network (cached per client) — plus each foreign subject resource's
4796
+ * grants through its own client, for the subject-write forecast;
4797
+ * evaluation itself runs in-memory on groq-js. */
3627
4798
  evaluate(): Promise<WorkflowEvaluation>;
3628
4799
  /** Advance the instance against the held content: cascade auto-transitions,
3629
4800
  * deploy guards, queue effects, commit with `ifRevisionId`. */
@@ -3635,12 +4806,35 @@ export declare interface InstanceSession {
3635
4806
  params?: Record<string, unknown>;
3636
4807
  }): Promise<OperationResult>;
3637
4808
  /** Edit a declared-editable field against the held content (the generic edit
3638
- * seam), gated on the held projection, then cascade. */
4809
+ * seam), gated on the held projection, then cascade. Stages its own
4810
+ * optimistic preview on entry and drops the target's previews when the
4811
+ * commit settles — success lands the reloaded value, failure reverts the
4812
+ * projection to the held committed state. */
3639
4813
  editField(args: {
3640
4814
  target: EditFieldTarget;
3641
4815
  mode?: EditMode;
3642
4816
  value?: unknown;
3643
4817
  }): Promise<OperationResult>;
4818
+ /** Stage an optimistic preview of a field edit: {@link InstanceSession.evaluate}
4819
+ * projects against the held instance with staged previews applied, so field
4820
+ * values and the advisory verdicts derived from them move instantly, while
4821
+ * everything the engine writes (stage, activity statuses, history) stays
4822
+ * committed by construction — previews apply `field.*` ops only. Set/unset
4823
+ * replace the target's staged rows (last write wins); appends accumulate.
4824
+ * Tolerant where commits are loud: a target that doesn't resolve in the
4825
+ * 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}. */
4830
+ previewField(args: {
4831
+ target: EditFieldTarget;
4832
+ mode?: EditMode;
4833
+ value?: unknown;
4834
+ }): void;
4835
+ /** Drop staged previews for one target — or all, when omitted — reverting
4836
+ * the projection to the held committed state on the next evaluate. */
4837
+ discardFieldPreview(target?: EditFieldTarget): void;
3644
4838
  }
3645
4839
 
3646
4840
  export declare interface InstancesForDocumentArgs {
@@ -3670,6 +4864,25 @@ export declare interface InstancesQueryFilter {
3670
4864
  * watch-set with {@link instanceWatchesDocument}.
3671
4865
  */
3672
4866
  document?: GdrUri;
4867
+ /**
4868
+ * The multi-document form of {@link InstancesQueryFilter.document}: one
4869
+ * prefilter matching instances that may reference ANY of the given docs,
4870
+ * for consumers discovering instances across many open documents at once.
4871
+ * Merged with `document` when both are set; per-document narrowing is the
4872
+ * caller's ({@link instanceWatchesDocument} again). A DEFINED-but-EMPTY
4873
+ * array matches nothing (the GROQ-natural reading of membership in an
4874
+ * empty set) — omit the field for the unconstrained every-in-flight read.
4875
+ */
4876
+ documents?: readonly GdrUri[];
4877
+ /**
4878
+ * Specific instances by bare doc id — OR'd with the document prefilter, so
4879
+ * a consumer tracking freshly-started instances (not yet referencing any
4880
+ * registered doc) sees them in the same live read. Bare ids only: an
4881
+ * instance's `_id` is never a GDR URI, so a URI here is a caller bug and
4882
+ * is rejected. A DEFINED-but-EMPTY array matches nothing, exactly like
4883
+ * {@link InstancesQueryFilter.documents}.
4884
+ */
4885
+ ids?: readonly string[];
3673
4886
  /** Version-less definition `name` the instances were started from. */
3674
4887
  definition?: string;
3675
4888
  /** Current stage name. */
@@ -3695,6 +4908,35 @@ export declare function instanceWatchesDocument(
3695
4908
  document: GdrUri,
3696
4909
  ): boolean;
3697
4910
 
4911
+ /**
4912
+ * A claim whose lease has lapsed no longer protects the entry. A claim
4913
+ * without `leaseExpiresAt` (persisted before leases existed) counts as
4914
+ * expired — those are exactly the stuck-forever entries leases recover.
4915
+ */
4916
+ export declare function isClaimExpired(
4917
+ claim: PendingEffectClaim,
4918
+ now: string,
4919
+ ): boolean;
4920
+
4921
+ export { isComparisonOp };
4922
+
4923
+ /**
4924
+ * The full applicability derivation for one definition against one loaded
4925
+ * document: startable ∧ {@link acceptsDocumentType} ∧ `applicableWhen`.
4926
+ * "Deployed" is the caller's premise — feed it deployed definitions (the
4927
+ * 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 —
4930
+ * that's a malformed definition (only writable by bypassing deploy
4931
+ * validation), and silently excluding it would make it vanish from every
4932
+ * picker with nothing screaming; only an evaluation result of GROQ null
4933
+ * ("can't decide") fails closed.
4934
+ */
4935
+ export declare function isDefinitionApplicable(args: {
4936
+ definition: ApplicabilitySource;
4937
+ document: CandidateDocument;
4938
+ }): Promise<boolean>;
4939
+
3698
4940
  /** Type filter for GDR shape. */
3699
4941
  export declare function isGdr(value: unknown): value is GlobalDocumentReference;
3700
4942
 
@@ -3708,6 +4950,27 @@ export declare function isGuardLifted(
3708
4950
  guard: Pick<MutationGuardDoc, "predicate">,
3709
4951
  ): boolean;
3710
4952
 
4953
+ /**
4954
+ * A notes-shaped `array` entry: the sugar's fixed `of` declares
4955
+ * `{body, actor, at}` rows ({@link NoteItem}), which tells the audit log
4956
+ * apart from other array kinds (e.g. the `todoList` sugar's
4957
+ * `{label, status}`). Accepts both vocabularies — a deployed definition
4958
+ * entry (`type`) and a resolved instance entry (`_type`) — so start-time
4959
+ * and runtime surfaces share one verdict.
4960
+ */
4961
+ export declare function isNotesEntry(entry: FieldEntry): entry is FieldEntry & {
4962
+ type: "array";
4963
+ };
4964
+
4965
+ export declare function isNotesEntry(
4966
+ entry: ResolvedFieldEntry,
4967
+ ): entry is Extract<
4968
+ ResolvedFieldEntry,
4969
+ {
4970
+ _type: "array";
4971
+ }
4972
+ >;
4973
+
3711
4974
  /**
3712
4975
  * Whether a human may start this definition standalone (the default). A
3713
4976
  * `lifecycle: 'child'` definition is spawn-only — instantiated by a parent via
@@ -3720,12 +4983,53 @@ export declare function isStartableDefinition(definition: {
3720
4983
  lifecycle?: WorkflowLifecycle | undefined;
3721
4984
  }): boolean;
3722
4985
 
4986
+ /**
4987
+ * The environment denial process shells (CLI, MCP server) feed
4988
+ * {@link createTelemetryIntake}: CI and `DO_NOT_TRACK` are trueish
4989
+ * opt-outs — the standard Sanity CLI consent recipe.
4990
+ */
4991
+ export declare function isTelemetryEnvDenied(
4992
+ env: Record<string, string | undefined>,
4993
+ ): boolean;
4994
+
4995
+ export declare function isTerminalActivityStatus(
4996
+ status: ActivityStatus,
4997
+ ): status is TerminalActivityStatus;
4998
+
3723
4999
  /**
3724
5000
  * A stage with no transitions out IS terminal — structural, not declared.
3725
5001
  * Reaching one completes the instance.
3726
5002
  */
3727
5003
  export declare function isTerminalStage(stage: Stage): boolean;
3728
5004
 
5005
+ /**
5006
+ * A todoList-shaped `array` entry: the sugar's fixed `of` declares `label` +
5007
+ * `status` rows, which tells a todo list apart from other array kinds (e.g.
5008
+ * the `notes` sugar's `{body, actor, at}`). Accepts both vocabularies —
5009
+ * a deployed definition entry (`type`) and a resolved instance entry
5010
+ * (`_type`) — so start-time and runtime surfaces share one verdict.
5011
+ */
5012
+ export declare function isTodoListEntry(
5013
+ entry: FieldEntry,
5014
+ ): entry is FieldEntry & {
5015
+ type: "array";
5016
+ };
5017
+
5018
+ export declare function isTodoListEntry(
5019
+ entry: ResolvedFieldEntry,
5020
+ ): entry is Extract<
5021
+ ResolvedFieldEntry,
5022
+ {
5023
+ _type: "array";
5024
+ }
5025
+ >;
5026
+
5027
+ /** Per-row narrowing for {@link TodoListItem}: `_key` + `label` must be
5028
+ * strings; `status`, when present, must be a string — a non-string status is
5029
+ * rejected, while null counts as unset (GROQ materializes absent attributes
5030
+ * as null). The remaining columns stay loose (field validation owns them). */
5031
+ export declare function isTodoListItem(row: unknown): row is TodoListItem;
5032
+
3729
5033
  /**
3730
5034
  * Deterministic id so deploy/retract are query-free and idempotent. Derived
3731
5035
  * from the guard's authored `name` — an author-controlled handle that stays
@@ -3737,6 +5041,44 @@ export declare function lakeGuardId(args: {
3737
5041
  guardName: string;
3738
5042
  }): string;
3739
5043
 
5044
+ /**
5045
+ * The latest deployed version of each definition name — the reduction every
5046
+ * "what would a start load?" consumer applies over a
5047
+ * {@link definitionsListGroq} read (GROQ has no group-by, so it happens
5048
+ * client-side). Order-independent: the highest version per name wins under
5049
+ * either `versionOrder`; names keep first-appearance order, so a
5050
+ * name-ascending read stays name-ascending.
5051
+ */
5052
+ export declare function latestDeployedDefinitions<
5053
+ T extends {
5054
+ name: string;
5055
+ version: number;
5056
+ },
5057
+ >(rows: readonly T[]): T[];
5058
+
5059
+ /**
5060
+ * 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.)
5066
+ *
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.
5073
+ *
5074
+ * Reads are collected from the primary consumer sites: condition GROQ
5075
+ * ({@link conditionSitesOf}), effect binding GROQ, and guard reads (the
5076
+ * `$effects['<name>'].<path>` mini-language on `match.idRefs` / `metadata`).
5077
+ */
5078
+ export declare function lintEffectOutputs(
5079
+ definition: WorkflowDefinition,
5080
+ ): string[];
5081
+
3740
5082
  declare type LiteralExpr = {
3741
5083
  type: "literal";
3742
5084
  value: unknown;
@@ -3776,6 +5118,16 @@ declare function matchesFilter(args: {
3776
5118
  userId?: string;
3777
5119
  }): Promise<boolean>;
3778
5120
 
5121
+ export { MAX_COUNTERFACTUAL_INDEX };
5122
+
5123
+ /**
5124
+ * The oldest engine model that can safely read a persisted engine document.
5125
+ * The engine always writes the {@link MODEL_STAMP} pair, so a `modelVersion`
5126
+ * with no `minReaderModel` is malformed foreign data — read conservatively:
5127
+ * the stamp itself is the floor. A doc with neither is model 0 (floor 0).
5128
+ */
5129
+ export declare function minReaderModelOf(doc: object): number;
5130
+
3779
5131
  export declare interface MissingHandlerDeployInfo {
3780
5132
  phase: "deploy";
3781
5133
  name: string;
@@ -3810,6 +5162,52 @@ export declare type MissingHandlerPolicy =
3810
5162
  | "skip"
3811
5163
  | ((info: MissingHandlerInfo) => void | Promise<void>);
3812
5164
 
5165
+ /**
5166
+ * The `required` input entries `initialFields` fails to provide — the pure
5167
+ * predicate behind {@link RequiredFieldNotProvidedError}: an entry counts as
5168
+ * required when it is `required` AND `input`-sourced (the only combination
5169
+ * deploy admits), and as provided when a supplied value matches its name AND
5170
+ * kind with a real (non-null) value. Keys on the same name+type as the input
5171
+ * read in {@link resolveInputValue}, but is stricter: a present-but-null/
5172
+ * undefined value counts as absent here (a required field needs a real
5173
+ * 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.
5176
+ */
5177
+ export declare function missingRequiredInputs(args: {
5178
+ entryDefs: readonly FieldEntry[];
5179
+ initialFields: readonly InitialFieldValue[];
5180
+ }): {
5181
+ name: string;
5182
+ type: FieldEntry["type"];
5183
+ }[];
5184
+
5185
+ /**
5186
+ * A persisted engine document requires a NEWER reader than this engine: its
5187
+ * `minReaderModel` floor is above {@link DATA_MODEL_VERSION}. Reading it
5188
+ * could silently misinterpret whatever the newer model reshaped, so every
5189
+ * engine read path fails hard instead. The remediation is always the same:
5190
+ * upgrade `@sanity/workflow-engine`.
5191
+ */
5192
+ export declare class ModelVersionAheadError extends WorkflowError<"model-version-ahead"> {
5193
+ readonly documentId: string;
5194
+ readonly documentModelVersion: number;
5195
+ readonly requiredReaderModel: number;
5196
+ readonly engineModelVersion: number;
5197
+ constructor(args: {
5198
+ documentId: string;
5199
+ documentModelVersion: number;
5200
+ requiredReaderModel: number;
5201
+ });
5202
+ }
5203
+
5204
+ /** The data model a persisted engine document conforms to. A document with no
5205
+ * stamp was last written before governance existed — model 0. Reads the
5206
+ * stamp structurally (persisted docs reach the gate as typed docs,
5207
+ * projections, and raw snapshot reads alike); a non-number stamp is foreign
5208
+ * data the engine never wrote and also reads as model 0. */
5209
+ export declare function modelVersionOf(doc: object): number;
5210
+
3813
5211
  declare const MUTATION_GUARD_ACTIONS: readonly [
3814
5212
  "create",
3815
5213
  "update",
@@ -3819,9 +5217,9 @@ declare const MUTATION_GUARD_ACTIONS: readonly [
3819
5217
  ];
3820
5218
 
3821
5219
  /**
3822
- * Inputs to predicate evaluation: `document.before`/`after`, the `mutation`,
3823
- * the `guard`, and `identity()`. `before` is null on create, `after` null on
3824
- * delete.
5220
+ * Inputs to predicate evaluation: the delta-mode natives `before()`/`after()`,
5221
+ * the `mutation`, the `guard`, and `identity()`. `before` is null on create,
5222
+ * `after` null on delete.
3825
5223
  */
3826
5224
  export declare interface MutationContext {
3827
5225
  before:
@@ -3938,12 +5336,30 @@ export declare interface MutationGuardMatch {
3938
5336
  actions: MutationGuardAction[];
3939
5337
  }
3940
5338
 
5339
+ /** The default logger: emit nothing. The engine's twin of `@sanity/telemetry`'s `noopLogger`. */
5340
+ export declare const noopTelemetry: WorkflowTelemetryLogger;
5341
+
5342
+ /**
5343
+ * One row of a `notes`-sugar `array` entry, as the desugared `of` persists
5344
+ * it: `_key` + the domain `body`, with the `actor`/`at` audit stamps. Like
5345
+ * {@link TodoListItem}, the sugar guarantees the COLUMNS in the schema, not
5346
+ * values per row — `actor`/`at` are per-row optional (an appender that
5347
+ * doesn't know the author writes a stamp-less row, and rows are lake data
5348
+ * any client can write).
5349
+ */
5350
+ export declare interface NoteItem {
5351
+ _key: string;
5352
+ body: string;
5353
+ actor?: Actor | null;
5354
+ at?: string | null;
5355
+ }
5356
+
3941
5357
  /**
3942
5358
  * `notes` — an append-only audit/comment log. Sugar over `array of object
3943
5359
  * { body, actor, at }` (the `actor`/`at` sub-fields match the `audit` op's
3944
5360
  * stamp names, so it pairs with it). Never a stored kind.
3945
5361
  */
3946
- declare type NotesField = FieldBase<AuthoringEditable> & {
5362
+ declare type NotesField = FieldBase<AuthoringEditable, GroupMembership> & {
3947
5363
  type: "notes";
3948
5364
  };
3949
5365
 
@@ -3990,16 +5406,10 @@ export declare interface OpAppliedSummary {
3990
5406
 
3991
5407
  export declare interface OperationArgs {
3992
5408
  instanceId: string;
3993
- /**
3994
- * Optional access-state override. See `StartInstanceArgs.access`
3995
- * for the resolution contract.
3996
- */
3997
- access?: WorkflowAccessOverride;
3998
5409
  /**
3999
5410
  * URL path for grant resolution — see `StartInstanceArgs.grantsFromPath`.
4000
5411
  */
4001
5412
  grantsFromPath?: string;
4002
- idempotencyKey?: string;
4003
5413
  }
4004
5414
 
4005
5415
  /**
@@ -4017,7 +5427,8 @@ export declare interface OperationResult {
4017
5427
  * Whether this call changed the instance's workflow state. `false` means
4018
5428
  * the call succeeded but was a no-op: an idempotent re-fire of an already
4019
5429
  * resolved activity, a `tick` that wrote nothing, a `setStage` already at
4020
- * the target, an abort of an already-terminal instance. Failures throw
5430
+ * the target, an abort of an already-terminal instance, or a retry whose
5431
+ * `idempotencyKey` was already processed. Failures throw —
4021
5432
  * `changed: false` never signals an error.
4022
5433
  */
4023
5434
  changed: boolean;
@@ -4027,6 +5438,24 @@ export declare interface OperationResult {
4027
5438
  ranOps?: OpAppliedSummary[];
4028
5439
  }
4029
5440
 
5441
+ /**
5442
+ * The instance's direct parent — the last entry of the root-first
5443
+ * {@link WorkflowInstance.ancestors} chain; `undefined` for a root instance.
5444
+ */
5445
+ export declare function parentRef(
5446
+ instance: Pick<WorkflowInstance, "ancestors">,
5447
+ ): GlobalDocumentReference | undefined;
5448
+
5449
+ /**
5450
+ * The ONE way to read an instance's frozen {@link WorkflowInstance.definitionSnapshot}
5451
+ * back into a {@link WorkflowDefinition} — wraps the parse so a corrupt
5452
+ * snapshot fails with the instance id attached instead of a bare
5453
+ * `SyntaxError`.
5454
+ */
5455
+ export declare function parseDefinitionSnapshot(
5456
+ instance: WorkflowInstance,
5457
+ ): WorkflowDefinition;
5458
+
4030
5459
  export declare interface ParsedGdr {
4031
5460
  scheme: GdrScheme;
4032
5461
  /** For `dataset`: `<projectId>` */
@@ -4045,6 +5474,15 @@ export declare interface ParsedGdr {
4045
5474
  */
4046
5475
  export declare function parseGdr(uri: string): ParsedGdr;
4047
5476
 
5477
+ /**
5478
+ * Parse a resource-shaped GDR (`<type>:<id>`, no document part) into the
5479
+ * {@link WorkflowResource} it names. The one grammar for resource addresses —
5480
+ * hosts validate/parse through this instead of hand-rolling the split.
5481
+ * Throws on unknown type, extra `:` parts (that shape is a document GDR —
5482
+ * see {@link parseGdr}), or a malformed dataset id.
5483
+ */
5484
+ export declare function parseResourceGdr(uri: string): WorkflowResource;
5485
+
4048
5486
  /**
4049
5487
  * A stage's multi-guard deploy failed *after* at least one guard already landed.
4050
5488
  * Those partial locks can't be cleanly undone (guard docs have no transactional
@@ -4090,9 +5528,9 @@ export declare interface PendingEffect {
4090
5528
  stageEntryKey?: string;
4091
5529
  /**
4092
5530
  * Presence = a drainer is dispatching this entry. Absence = the entry
4093
- * is unclaimed and eligible for drain. Entries in terminal states
4094
- * (done / failed) don't live on `pendingEffects[]` at all; they move
4095
- * to `effectHistory[]`.
5531
+ * is unclaimed and eligible for drain. Settled entries (any
5532
+ * {@link EffectRunStatus}) don't live on `pendingEffects[]` at all; they
5533
+ * move to `effectHistory[]`.
4096
5534
  */
4097
5535
  claim?: PendingEffectClaim;
4098
5536
  }
@@ -4103,15 +5541,66 @@ export declare interface PendingEffect {
4103
5541
  * available. There is intentionally no `status` enum; presence of
4104
5542
  * `claim` is the sole marker.
4105
5543
  *
4106
- * Wire format includes `_type` so future fields (lease expiry, attempt
4107
- * counters, etc.) can land additively without parsing strings.
5544
+ * Wire format includes `_type` so future fields can land additively
5545
+ * without parsing strings.
4108
5546
  */
4109
5547
  export declare interface PendingEffectClaim {
4110
5548
  _type: "pendingEffect.claim";
4111
5549
  claimedAt: string;
4112
5550
  claimedBy: Actor;
5551
+ /**
5552
+ * When this claim's lease lapses. Past this instant the claimer is
5553
+ * presumed dead: a drain may take the claim over and `sweepStaleClaims`
5554
+ * may force-release it. Absent only on claims persisted before leases
5555
+ * existed — treated as already expired, so the exact entries leases
5556
+ * exist to recover (claimed-then-abandoned) stay recoverable.
5557
+ *
5558
+ * There is deliberately no attempt counter here: the retry count is the
5559
+ * `effectClaimReleased` history rows for the entry's `_key` (plus the
5560
+ * live claim), and a field would drift from that trail across releases.
5561
+ */
5562
+ leaseExpiresAt?: string;
4113
5563
  }
4114
5564
 
5565
+ /**
5566
+ * One row of the instance's idempotency ledger — a caller-supplied
5567
+ * `idempotencyKey` a state-changing verb already committed under. Recorded in
5568
+ * the same CAS commit as the operation it dedupes, so "the op applied" and
5569
+ * "the key is recorded" can never diverge. Rows expire at `expiresAt` and are
5570
+ * pruned lazily whenever a later keyed operation commits.
5571
+ */
5572
+ export declare interface ProcessedRequest {
5573
+ _key: string;
5574
+ _type: "processedRequest";
5575
+ /** The caller-supplied idempotency key. */
5576
+ key: string;
5577
+ /** The verb the key was processed under. Reusing a live key on a
5578
+ * different verb is a caller bug and throws. */
5579
+ op: string;
5580
+ processedAt: string;
5581
+ expiresAt: string;
5582
+ }
5583
+
5584
+ /**
5585
+ * The execution-environment user properties a process shell (CLI, MCP
5586
+ * server) attaches to its store — the sanity CLI's precedent set, joined
5587
+ * to events by sessionId downstream. The `surface` marker is what lets
5588
+ * intake data slice per shell; user identity is deliberately absent
5589
+ * (the intake service resolves the sender from the authenticated session
5590
+ * on every event).
5591
+ */
5592
+ export declare function processShellUserProperties<
5593
+ Surface extends "cli" | "mcp",
5594
+ >(
5595
+ surface: Surface,
5596
+ ): {
5597
+ surface: Surface;
5598
+ machinePlatform: string;
5599
+ cpuArchitecture: string;
5600
+ runtime: string;
5601
+ runtimeVersion: string;
5602
+ };
5603
+
4115
5604
  export declare interface QueryArgs {
4116
5605
  groq: string;
4117
5606
  params?: Record<string, unknown>;
@@ -4119,10 +5608,12 @@ export declare interface QueryArgs {
4119
5608
 
4120
5609
  export declare interface QueryInScopeArgs extends InstanceRefArgs, QueryArgs {}
4121
5610
 
5611
+ export { quoted };
5612
+
4122
5613
  /**
4123
5614
  * Whether a watched ref reads RAW — never perspective-scoped. The
4124
- * instance, its ancestors (both instance docs), and `release.ref`
4125
- * system docs (`system.release`) are not versioned by a release
5615
+ * instance, its ancestors, and its spawned children (all instance docs), and
5616
+ * `release.ref` system docs (`system.release`) are not versioned by a release
4126
5617
  * perspective; everything else is content and resolves to its
4127
5618
  * version/draft/published form. This is the single encoding of the rule
4128
5619
  * the {@link WatchSet} `perspective` contract describes to consumers, and
@@ -4131,21 +5622,21 @@ export declare interface QueryInScopeArgs extends InstanceRefArgs, QueryArgs {}
4131
5622
  export declare function readsRaw(ref: { type: string }): boolean;
4132
5623
 
4133
5624
  /** Make a GDR pointer to a Canvas-resource doc. */
4134
- export declare function refCanvas({
5625
+ export declare function refCanvas<TType extends string = string>({
4135
5626
  resourceId,
4136
5627
  documentId,
4137
5628
  type,
4138
- }: ResourceRefArgs): GlobalDocumentReference;
5629
+ }: ResourceRefArgs<TType>): GlobalDocumentReference<TType>;
4139
5630
 
4140
5631
  /** Make a GDR pointer to a Dashboard-resource doc. */
4141
- export declare function refDashboard({
5632
+ export declare function refDashboard<TType extends string = string>({
4142
5633
  resourceId,
4143
5634
  documentId,
4144
5635
  type,
4145
- }: ResourceRefArgs): GlobalDocumentReference;
5636
+ }: ResourceRefArgs<TType>): GlobalDocumentReference<TType>;
4146
5637
 
4147
5638
  /** Make a GDR pointer to a project-dataset doc. */
4148
- export declare function refDataset({
5639
+ export declare function refDataset<TType extends string = string>({
4149
5640
  projectId,
4150
5641
  dataset,
4151
5642
  documentId,
@@ -4154,15 +5645,39 @@ export declare function refDataset({
4154
5645
  projectId: string;
4155
5646
  dataset: string;
4156
5647
  documentId: string;
4157
- type: string;
4158
- }): GlobalDocumentReference;
5648
+ type: TType;
5649
+ }): GlobalDocumentReference<TType>;
4159
5650
 
4160
5651
  /** Make a GDR pointer to a Media-Library-resource doc. */
4161
- export declare function refMediaLibrary({
5652
+ export declare function refMediaLibrary<TType extends string = string>({
4162
5653
  resourceId,
4163
5654
  documentId,
4164
5655
  type,
4165
- }: ResourceRefArgs): GlobalDocumentReference;
5656
+ }: ResourceRefArgs<TType>): GlobalDocumentReference<TType>;
5657
+
5658
+ /**
5659
+ * The GDR `type`s in a `doc.ref` / `doc.refs` value that the entry's declared
5660
+ * accepted `types` reject — empty when the value conforms, the entry declares
5661
+ * no `types`, or the kind isn't a ref. A GDR's `type` names the target
5662
+ * document's schema type, so this is the declared-subject-type contract.
5663
+ * Skips anything that isn't GDR-shaped — the shape schemas scream about
5664
+ * those. Boundaries that need their own error framing (the spawn `with`
5665
+ * projection gates its remediation hint on the generic `"document"` type
5666
+ * being among the rejects) read this; everything else goes through
5667
+ * {@link refTypeIssues} / {@link checkFieldValue}.
5668
+ */
5669
+ export declare function rejectedRefTypes(args: {
5670
+ entryType: string;
5671
+ types: readonly string[] | undefined;
5672
+ value: unknown;
5673
+ }): string[];
5674
+
5675
+ /**
5676
+ * The `_id` of a release's system document — the one owner of the
5677
+ * `_.releases.<name>` spelling, shared by {@link releaseRef} and the
5678
+ * validation cross-check so the id scheme can't drift between them.
5679
+ */
5680
+ export declare function releaseDocId(releaseName: string): string;
4166
5681
 
4167
5682
  /**
4168
5683
  * A reference to a Content Release. Extends the GDR shape (so existing
@@ -4174,11 +5689,23 @@ export declare function refMediaLibrary({
4174
5689
  * workflow resource); `type` is always `"system.release"`;
4175
5690
  * `releaseName` is the final segment of the release id.
4176
5691
  */
4177
- export declare interface ReleaseRef extends GlobalDocumentReference {
4178
- type: "system.release";
5692
+ export declare interface ReleaseRef extends GlobalDocumentReference<"system.release"> {
4179
5693
  releaseName: string;
4180
5694
  }
4181
5695
 
5696
+ /**
5697
+ * Build a {@link ReleaseRef} — the one constructor for the id ↔
5698
+ * `releaseName` pairing, so the envelope can't be assembled out of step
5699
+ * (validation cross-checks the pairing, but only at instance-write time).
5700
+ */
5701
+ export declare function releaseRef({
5702
+ res,
5703
+ releaseName,
5704
+ }: {
5705
+ res: WorkflowResource;
5706
+ releaseName: string;
5707
+ }): ReleaseRef;
5708
+
4182
5709
  /**
4183
5710
  * The remediations that would unstick a diagnosis — the *what to do about it*
4184
5711
  * half. Empty unless the instance is `stuck`: a `waiting` instance advances on
@@ -4234,41 +5761,22 @@ export declare class RequiredFieldNotProvidedError extends WorkflowError<"requir
4234
5761
  export declare const RESERVED_CONDITION_VARS: readonly string[];
4235
5762
 
4236
5763
  /**
4237
- * Resolve the engine's `WorkflowAccess` for a client. Override wins
4238
- * outright; otherwise both halves are fetched in parallel and
4239
- * cached. Throws if neither path yields an actor — the engine
4240
- * refuses to operate without an identity.
5764
+ * Resolve the engine's `WorkflowAccess` for a client both halves
5765
+ * fetched from its token in parallel and cached. Throws if the client
5766
+ * can't yield an actor — the engine refuses to operate without an
5767
+ * identity.
4241
5768
  */
4242
5769
  export declare function resolveAccess(
4243
- client: WorkflowClient,
5770
+ taggedClient: WorkflowClient,
4244
5771
  args?: ResolveAccessArgs,
4245
5772
  ): Promise<WorkflowAccess>;
4246
5773
 
4247
5774
  export declare interface ResolveAccessArgs {
4248
5775
  /**
4249
- * Partial override. Any field set here wins outright; any field
4250
- * left unset falls through to token resolution.
4251
- *
4252
- * - `override.actor` set + `override.grants` set → returned as-is.
4253
- * - `override.actor` set + `override.grants` unset → actor from
4254
- * override; grants fetched from `grantsFromPath` (or undefined).
4255
- * - `override.actor` unset + `override.grants` set → actor from
4256
- * `/users/me`; grants from override.
4257
- * - `override` undefined entirely → both halves fetched.
4258
- *
4259
- * The test bench passes a fully-populated `{ actor, grants }` so
4260
- * the underlying TestClient (no `request` method) never hits the
4261
- * token path. Admin/preview tooling passes one or the other.
4262
- */
4263
- override?: {
4264
- actor?: Actor;
4265
- grants?: Grant[];
4266
- };
4267
- /**
4268
- * Where the engine should fetch grants from the supplied client
4269
- * when `override.grants` isn't given, e.g. a Canvas resource at
4270
- * `/canvases/<resourceId>/acl` or a dataset at
4271
- * `/projects/<id>/datasets/<ds>/acl`.
5776
+ * Where the engine should fetch grants from the supplied client,
5777
+ * e.g. a Canvas resource at `/canvases/<resourceId>/acl` or a
5778
+ * dataset at `/projects/<id>/datasets/<ds>/acl`. Omit to skip the
5779
+ * grants fetch (the rendered `$can` stays undefined).
4272
5780
  */
4273
5781
  grantsFromPath?: string;
4274
5782
  }
@@ -4280,7 +5788,8 @@ export declare interface ResolveAccessArgs {
4280
5788
  * `initialValue` is `{type:'query'}`, of any kind) additionally carries
4281
5789
  * `resolvedAt` — the lake-read time — so `resolvedAt` is provenance-driven,
4282
5790
  * not kind-specific. `object` / `array` entries carry their declared
4283
- * sub-field shape (`fields` / `of`) so the instance is self-describing for
5791
+ * sub-field shape (`fields` / `of`) and `doc.ref` / `doc.refs` entries
5792
+ * their declared accepted `types` — so the instance is self-describing for
4284
5793
  * op-time validation and rendering.
4285
5794
  */
4286
5795
  export declare type ResolvedFieldEntry = {
@@ -4302,9 +5811,31 @@ export declare type ResolvedFieldEntry = {
4302
5811
  ? {
4303
5812
  of: FieldShape[];
4304
5813
  }
5814
+ : Record<never, never>) &
5815
+ (K extends "doc.ref" | "doc.refs"
5816
+ ? {
5817
+ types?: string[];
5818
+ }
4305
5819
  : Record<never, never>);
4306
5820
  }[FieldKind];
4307
5821
 
5822
+ /**
5823
+ * The resolved entry a field site names, read from the instance's runtime
5824
+ * `fields[]` at the site's scope — the instance copy that carries what an
5825
+ * evaluation doesn't (the declared `of`/`fields` shape). Stage- and
5826
+ * activity-scope sites resolve against the OPEN stage. `undefined` when the
5827
+ * field hasn't been resolved yet (e.g. an activity-scope field before its
5828
+ * activity activated).
5829
+ */
5830
+ export declare function resolveFieldEntry(
5831
+ instance: WorkflowInstance,
5832
+ site: {
5833
+ scope: FieldScope;
5834
+ activity?: ActivityName;
5835
+ name: string;
5836
+ },
5837
+ ): ResolvedFieldEntry | undefined;
5838
+
4308
5839
  /**
4309
5840
  * A map binding each resource-alias name to the physical {@link WorkflowResource}
4310
5841
  * it resolves to at deploy. Parallels the role-alias map: where role aliases map
@@ -4324,15 +5855,16 @@ export declare function resourceAliasesToMap(
4324
5855
  ): ResourceAliases;
4325
5856
 
4326
5857
  /**
4327
- * Resolver for cross-resource reads. The engine has ONE `client` bound
4328
- * to its own resource (where definition / instance
4329
- * docs live). When the engine reads a doc in a different resource
4330
- * a subject in a project dataset while the workflow lives in canvas,
4331
- * say it consults this resolver with the parsed GDR. Return `undefined`
4332
- * to fall back to the engine's default client.
5858
+ * Routing override for cross-resource reads (subject + ancestor docs that
5859
+ * live in a different Sanity resource than the workflow). Called with a
5860
+ * parsed GDR; return a client for that resource, or `undefined` to let the
5861
+ * engine route it its own client for the workflow resource, a derived
5862
+ * sibling ({@link WorkflowClient.withConfig}) for anything else.
4333
5863
  *
4334
- * The resolver receives the parsed pieces, not the raw URI, so it can
4335
- * pattern-match on scheme + resource id without re-parsing.
5864
+ * Resolved clients ride the engine's API version like every other engine
5865
+ * client: the verb scope rebinds them onto `ENGINE_API_VERSION` when they
5866
+ * carry {@link WorkflowClient.withConfig} (one without it is used as
5867
+ * returned and must be built to serve that version).
4336
5868
  */
4337
5869
  export declare type ResourceClientResolver = (
4338
5870
  parsed: ParsedGdr,
@@ -4347,11 +5879,20 @@ export declare type ResourceClientResolver = (
4347
5879
  */
4348
5880
  export declare function resourceFromParsed(parsed: ParsedGdr): WorkflowResource;
4349
5881
 
5882
+ /**
5883
+ * Format a {@link WorkflowResource} as a resource-shaped GDR — `<type>:<id>`
5884
+ * with no document part (`dataset:abc123.production`, `media-library:mlXyz`).
5885
+ * The string form for naming a *resource* (not a document in it) across an
5886
+ * API boundary — e.g. the per-call workflow-environment address an MCP tool
5887
+ * takes. Inverse of {@link parseResourceGdr}.
5888
+ */
5889
+ export declare function resourceGdr(res: WorkflowResource): string;
5890
+
4350
5891
  /** Args for the single-resource ref constructors ({@link refCanvas}, {@link refMediaLibrary}, {@link refDashboard}). */
4351
- declare interface ResourceRefArgs {
5892
+ declare interface ResourceRefArgs<TType extends string> {
4352
5893
  resourceId: string;
4353
5894
  documentId: string;
4354
- type: string;
5895
+ type: TType;
4355
5896
  }
4356
5897
 
4357
5898
  /**
@@ -4425,16 +5966,18 @@ declare const RoleAliasesSchema: v.RecordSchema<
4425
5966
  undefined
4426
5967
  >;
4427
5968
 
5969
+ export { ScopeAssignment };
5970
+
5971
+ export { sentenceCase };
5972
+
4428
5973
  export declare interface SessionArgs {
4429
5974
  /** The instance document to bind — typically the consumer's live-store value. */
4430
5975
  instance: WorkflowInstance;
4431
- /** Optional access-state override — see `StartInstanceArgs.access`. */
4432
- access?: WorkflowAccessOverride | undefined;
4433
5976
  /** URL path for grant resolution — see `StartInstanceArgs.grantsFromPath`. */
4434
5977
  grantsFromPath?: string | undefined;
4435
5978
  }
4436
5979
 
4437
- export declare interface SetStageArgs extends OperationArgs {
5980
+ export declare interface SetStageArgs extends DedupableOperationArgs {
4438
5981
  /** Stage id to force the instance into. */
4439
5982
  targetStage: string;
4440
5983
  /** Free-text reason — surfaces on history entries for audit. */
@@ -4452,6 +5995,13 @@ export declare interface SetStageArgs extends OperationArgs {
4452
5995
  */
4453
5996
  export declare const silentLogger: EngineLogger;
4454
5997
 
5998
+ /** One site whose verdict a proposed assignment flips. */
5999
+ export declare interface SiteConsequence {
6000
+ site: InsightSite;
6001
+ before: ConditionOutcome;
6002
+ after: ConditionOutcome;
6003
+ }
6004
+
4455
6005
  export declare type Stage = StageFields<
4456
6006
  FieldEntry,
4457
6007
  Activity,
@@ -4482,6 +6032,7 @@ declare type StageFields<TField, TActivity, TTransition, TGuard, TEditable> = {
4482
6032
  name: string;
4483
6033
  title?: string | undefined;
4484
6034
  description?: string | undefined;
6035
+ groups?: Group[] | undefined;
4485
6036
  activities?: TActivity[] | undefined;
4486
6037
  transitions?: TTransition[] | undefined;
4487
6038
  guards?: TGuard[] | undefined;
@@ -4491,12 +6042,17 @@ declare type StageFields<TField, TActivity, TTransition, TGuard, TEditable> = {
4491
6042
 
4492
6043
  declare interface StageGuardArgs {
4493
6044
  client: WorkflowClient;
4494
- clientForGdr: (parsed: ParsedGdr) => WorkflowClient;
6045
+ clientForGdr: ClientForGdr;
4495
6046
  instance: WorkflowInstance;
4496
6047
  definition: WorkflowDefinition;
4497
6048
  stageName: string;
4498
6049
  /** Shell-supplied clock reading (ISO) backing guard `$now` reads. */
4499
6050
  now: string;
6051
+ /** The commit's hydrated snapshot, so a `metadata` read dereferences a
6052
+ * `doc.ref` into content. `undefined` → identity-only resolution (content
6053
+ * reads resolve `null`). Required (not optional) so a new call site can't
6054
+ * silently drop it — pass `undefined` deliberately. */
6055
+ snapshot: HydratedSnapshot | undefined;
4500
6056
  }
4501
6057
 
4502
6058
  export declare type StageName = string;
@@ -4526,30 +6082,19 @@ export declare interface StartInstanceArgs {
4526
6082
  * Stable named params for effect handlers, set at start time. The
4527
6083
  * runtime resolves effect bindings against these to materialise
4528
6084
  * handler params at queue time. **Not read by transition filters.**
6085
+ *
6086
+ * Each value may be any JSON — a scalar, a {@link GlobalDocumentReference},
6087
+ * 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>`.
4529
6090
  */
4530
- effectsContext?: Record<
4531
- string,
4532
- string | number | boolean | GlobalDocumentReference
4533
- >;
6091
+ effectsContext?: EffectsContext;
4534
6092
  /** Caller-supplied id; auto-generated otherwise. */
4535
6093
  instanceId?: string;
4536
- /**
4537
- * Optional access-state override. By default the engine resolves
4538
- * `(actor, grants)` from the supplied client's token:
4539
- *
4540
- * - `actor` ← `client.request({ url: "/users/me" })`
4541
- * - `grants` ← `client.request({ url: grantsFromPath })` (when path supplied)
4542
- *
4543
- * Pass `access` to override either or both — the test bench does
4544
- * this with its `ALL_ACCESS` default, admin tooling can do it for
4545
- * impersonation flows. If neither the explicit override nor a
4546
- * token-bearing client is available, the engine throws rather than
4547
- * fabricating an identity.
4548
- */
4549
- access?: WorkflowAccessOverride;
4550
6094
  /**
4551
6095
  * URL path on the supplied client where the engine fetches the
4552
- * actor's ACL grants when `access.grants` isn't supplied. Grants
6096
+ * caller's ACL grants. Identity is always token-resolved — `actor`
6097
+ * comes from `client.request({ uri: "/users/me" })` — and grants
4553
6098
  * feed the advisory `$can.*` params action conditions can read;
4554
6099
  * omitting this leaves the rendered `$can` undefined (conditions
4555
6100
  * referencing it fail closed, nothing else is gated engine-side —
@@ -4973,6 +6518,30 @@ export declare type StuckCause =
4973
6518
  transitions: string[];
4974
6519
  };
4975
6520
 
6521
+ /**
6522
+ * Display label per {@link SubjectPermissionDenial} —
6523
+ * `<permission> on <subject> (<resource>)`. The one display convention for
6524
+ * rendering the subject-write shortfall to a person (the subject-forecast
6525
+ * twin of {@link deniedGuardLabels}); consumers phrase their own sentence
6526
+ * around the labels instead of re-deriving them from the payload.
6527
+ */
6528
+ export declare function subjectDenialLabels(
6529
+ denied: SubjectPermissionDenial[],
6530
+ ): string[];
6531
+
6532
+ /**
6533
+ * One prospective subject write the actor's grants don't allow — the payload
6534
+ * of the `subject-permission-denied` {@link DisabledReason} arm.
6535
+ */
6536
+ export declare interface SubjectPermissionDenial {
6537
+ /** GDR URI of the subject document the write would target. */
6538
+ subject: GdrUri;
6539
+ /** Resource-shaped GDR (`<type>:<id>`) of the resource whose ACL denied. */
6540
+ resource: string;
6541
+ /** The forecast write permission the resource's grants don't confer. */
6542
+ permission: DocumentValuePermission;
6543
+ }
6544
+
4976
6545
  /**
4977
6546
  * A document the reactive layer should subscribe to, with its GDR
4978
6547
  * exploded so consumers never re-parse: the resource-addressing parts
@@ -5002,11 +6571,86 @@ export declare function subscriptionDocumentsForInstance(
5002
6571
  instance: WorkflowInstance,
5003
6572
  ): WatchSet;
5004
6573
 
6574
+ /**
6575
+ * One spawned child in the instance's workflow-scope registry
6576
+ * (`instance.subworkflows`). A child instance is a real lake document whose
6577
+ * lifetime is not bound to any stage, so its record lives at workflow scope —
6578
+ * rows are never deleted, and a child's status is always derived from its
6579
+ * document (or from {@link SubworkflowEntry.resolved} once it terminated).
6580
+ */
6581
+ export declare interface SubworkflowEntry {
6582
+ _key: string;
6583
+ /** The spawning activity's name. */
6584
+ activity: ActivityName;
6585
+ /** The child definition's `name` — adoption never crosses definitions. */
6586
+ definition: string;
6587
+ /**
6588
+ * `_key` of the {@link StageEntry} whose cohort this row currently belongs
6589
+ * to. Re-adoption on a stage re-entry re-binds the row to the new entry;
6590
+ * a row bound to an exited entry is a *detached* child — still tracked,
6591
+ * still propagating, just outside any activity's implicit gate.
6592
+ */
6593
+ stageEntry: string;
6594
+ /**
6595
+ * The cohort's stage NAME (denormalized from {@link SubworkflowEntry.stageEntry}
6596
+ * for lake queries/inspectors). Deliberately not called `stage`: the child's
6597
+ * own stage lives on `resolved.stage` and the `$subworkflows` var row.
6598
+ */
6599
+ cohortStage: StageName;
6600
+ /**
6601
+ * Identity of the `forEach` row that produced this child — what adopt
6602
+ * matching keys on: `_key` ?? `_id` ?? GDR `id` (a `{id, type}` row with
6603
+ * a GDR-URI `id`) for object rows, the value itself for scalar rows.
6604
+ */
6605
+ rowKey: string;
6606
+ /** GDR of the child instance document. */
6607
+ ref: GlobalDocumentReference;
6608
+ spawnedAt: string;
6609
+ /**
6610
+ * An abort the engine owes this child but could not perform inside the
6611
+ * condemning commit (children are separate documents — aborting one is its
6612
+ * own commit). Lives ON the row — a condemned child's whole story is one
6613
+ * record — and survives a crash because the row persists; the cascade
6614
+ * drains condemned rows (abort child → stamp {@link SubworkflowEntry.resolved})
6615
+ * idempotently, and the field stays afterwards as the audit of WHY. Set by
6616
+ * a parent abort or an `onExit: "abort"` stage exit; a condemned row is
6617
+ * never adopted into a fresh cohort.
6618
+ */
6619
+ abortPending?: {
6620
+ /** The condemning commit's clock reading. */
6621
+ at: string;
6622
+ /** Why — recorded on the child's own abort history when the drain runs. */
6623
+ reason: string;
6624
+ };
6625
+ /**
6626
+ * Terminal cache, stamped when the engine observes the child terminal.
6627
+ * Terminal state is immutable, so once stamped the child leaves the
6628
+ * watch-set and status derives from here instead of its document.
6629
+ */
6630
+ resolved?: {
6631
+ /** The child's `completedAt` — or the drain's clock reading when the
6632
+ * child document was already gone and only the disposition is known. */
6633
+ at: string;
6634
+ /** The child's final stage. Absent when the child document vanished
6635
+ * before the engine could observe it. */
6636
+ stage?: StageName;
6637
+ /** Set when the child was aborted rather than completing normally. */
6638
+ aborted?: true;
6639
+ };
6640
+ }
6641
+
5005
6642
  export declare type Subworkflows = v.InferOutput<typeof SubworkflowsSchema>;
5006
6643
 
5007
6644
  declare const SubworkflowsSchema: v.StrictObjectSchema<
5008
6645
  {
5009
- /** GROQ producing one row per subworkflow; each row binds as `$row`. */
6646
+ /**
6647
+ * GROQ producing one row per subworkflow; each row binds as `$row`. Every
6648
+ * row must carry an identity the engine can adopt against on re-entry:
6649
+ * `_key` ?? `_id` ?? GDR `id` for object rows (a GDR value — `{id, type}`
6650
+ * with a GDR-URI `id`, the rows a `doc.refs` field stores — keys on its
6651
+ * `id`; mint a `_key` in the projection for synthetic rows), the value
6652
+ * itself for scalar rows. A row without an identity fails the spawn.
6653
+ */
5010
6654
  readonly forEach: v.SchemaWithPipe<
5011
6655
  readonly [
5012
6656
  v.StringSchema<undefined>,
@@ -5082,6 +6726,23 @@ declare const SubworkflowsSchema: v.StrictObjectSchema<
5082
6726
  >,
5083
6727
  undefined
5084
6728
  >;
6729
+ /**
6730
+ * 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
6732
+ * discovers their row. `'detach'` (the default) lets them run to
6733
+ * completion outside the gate; `'abort'` kills them (recursively). The
6734
+ * engine never destroys in-flight work implicitly — `'abort'` is always
6735
+ * 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.
6738
+ */
6739
+ readonly onExit: v.OptionalSchema<
6740
+ v.PicklistSchema<
6741
+ readonly ["detach", "abort"],
6742
+ `Invalid option: expected one of ${string}`
6743
+ >,
6744
+ undefined
6745
+ >;
5085
6746
  },
5086
6747
  undefined
5087
6748
  >;
@@ -5097,6 +6758,33 @@ export declare interface SuggestedRemediation {
5097
6758
  rationale: string;
5098
6759
  }
5099
6760
 
6761
+ /**
6762
+ * Force-release every pending-effect claim whose lease has expired, so the
6763
+ * entries become drainable again after their claimer died between claim and
6764
+ * complete. One CAS write per pass (all expired claims at once) with an
6765
+ * `effectClaimReleased` audit row per release; the `ifRevisionId` guard is
6766
+ * the sweeper election — concurrent sweepers or drainers race safely, the
6767
+ * loser re-reads (bounded, then fails loud). Release only: dispatching the
6768
+ * recovered entries is the next `drainEffects` call's job. To kill a stuck
6769
+ * entry WITHOUT redispatching, report it via
6770
+ * `completeEffect({status: 'failed'})` instead.
6771
+ */
6772
+ export declare function sweepStaleClaims(args: {
6773
+ client: WorkflowClient;
6774
+ tag: string;
6775
+ instanceId: string;
6776
+ clock?: Clock;
6777
+ /** Declared execution context stamped on the release audit rows —
6778
+ * a sweeper runtime declares itself here (identity is its token). */
6779
+ executionContext?: DeclaredExecutionContext;
6780
+ }): Promise<SweepStaleClaimsResult>;
6781
+
6782
+ export declare interface SweepStaleClaimsResult {
6783
+ /** The entries whose expired claims were force-released, as they stood
6784
+ * before the release (each still carries the expired `claim`). */
6785
+ released: PendingEffect[];
6786
+ }
6787
+
5100
6788
  /**
5101
6789
  * The engine's read-partition invariant as a GROQ predicate: a document is
5102
6790
  * visible when its `tag` equals the caller's `$tag` param. The single
@@ -5105,6 +6793,67 @@ export declare interface SuggestedRemediation {
5105
6793
  */
5106
6794
  export declare function tagScopeFilter(): string;
5107
6795
 
6796
+ /**
6797
+ * Internal telemetry-injection seam for the raw `workflow.*` verbs — the
6798
+ * telemetry twin of the `Clocked` seam in `clock.ts`, deliberately not
6799
+ * re-exported from the package root. Production injects a logger ONCE via
6800
+ * `createEngine({ telemetry })`; a raw-namespace caller that omits it
6801
+ * emits nothing (the verbs default to {@link noopTelemetry}).
6802
+ */
6803
+ declare type Telemetered<T> = T & {
6804
+ telemetry?: WorkflowTelemetryLogger;
6805
+ };
6806
+
6807
+ /**
6808
+ * The standard Sanity-intake recipe every workflow app shell shares —
6809
+ * consent from `GET /intake/telemetry-status`, transport via
6810
+ * `POST /intake/batch` `{projectId, batch}`, and the request tags that
6811
+ * keep shell telemetry traffic identifiable in request logs. The shells
6812
+ * (App SDK provider, MCP server, CLI) each own their store and
6813
+ * environment gates; this module is the one home for the recipe those
6814
+ * stores run on, so a change to an endpoint, body shape, or tag lands
6815
+ * everywhere at once.
6816
+ *
6817
+ * Like the event vocabulary (`engine.telemetry.ts`), this module keeps
6818
+ * the engine free of a runtime `@sanity/telemetry` dependency: the
6819
+ * result is a structural mirror of that package's batched-store options,
6820
+ * so a shell passes it straight to `createBatchedStore`. Drift against
6821
+ * the real package is pinned by a type-level test (dev dependency).
6822
+ */
6823
+ /** Mirror of `@sanity/telemetry`'s `ConsentStatus`. */
6824
+ export declare type TelemetryConsentStatus =
6825
+ | "undetermined"
6826
+ | "unset"
6827
+ | "granted"
6828
+ | "denied";
6829
+
6830
+ /**
6831
+ * Structural mirror of the `resolveConsent`/`sendEvents` pair in
6832
+ * `@sanity/telemetry`'s `CreateBatchedStoreOptions` — assignable to it
6833
+ * verbatim.
6834
+ */
6835
+ export declare interface TelemetryIntake {
6836
+ resolveConsent: () => Promise<{
6837
+ status: TelemetryConsentStatus;
6838
+ }>;
6839
+ sendEvents: (batch: unknown[]) => Promise<unknown>;
6840
+ }
6841
+
6842
+ /** The slice of a `SanityClient` the intake recipe needs — narrow so
6843
+ * shells can hand in any authenticated client and tests fake exactly
6844
+ * what the recipe reads. `timeout` is the per-request cap in ms — the
6845
+ * real client honors it by aborting the request, which is what actually
6846
+ * closes the socket (a `Promise.race` alone cannot). */
6847
+ export declare interface TelemetryIntakeClient {
6848
+ request: <T>(opts: {
6849
+ uri: string;
6850
+ method?: string;
6851
+ body?: unknown;
6852
+ tag?: string;
6853
+ timeout?: number;
6854
+ }) => Promise<T>;
6855
+ }
6856
+
5108
6857
  declare const TERMINAL_ACTIVITY_STATUSES: readonly [
5109
6858
  "done",
5110
6859
  "skipped",
@@ -5114,22 +6863,64 @@ declare const TERMINAL_ACTIVITY_STATUSES: readonly [
5114
6863
  export declare type TerminalActivityStatus =
5115
6864
  (typeof TERMINAL_ACTIVITY_STATUSES)[number];
5116
6865
 
6866
+ /** See {@link terminalState}. */
6867
+ export declare type TerminalState = "aborted" | "completed" | "in-flight";
6868
+
6869
+ /**
6870
+ * Classify an instance by its terminal stamps. The ONE home for the
6871
+ * precedence rule: aborted instances carry `completedAt` too (stamped at the
6872
+ * abort, so in-flight queries treat both terminals uniformly), so `abortedAt`
6873
+ * must be checked first — `'completed'` means completed *without* an abort.
6874
+ */
6875
+ export declare function terminalState(
6876
+ instance: Pick<WorkflowInstance, "completedAt" | "abortedAt">,
6877
+ ): TerminalState;
6878
+
6879
+ /**
6880
+ * A GDR URI stripped back to its bare document `_id`; a non-GDR string passes
6881
+ * through unchanged. The one canonical "URI → bare id" transform — use it
6882
+ * wherever a stored ref (which may be a full GDR URI, or already bare) must
6883
+ * become a lake-queryable `_id`.
6884
+ */
6885
+ export declare function toBareId(id: string): string;
6886
+
5117
6887
  /**
5118
6888
  * `todoList` — ad-hoc, status-tracked work items. Sugar over `array of object
5119
6889
  * { label, status, assignee?, dueDate? }`; a plain checklist is this used with
5120
6890
  * `{label, status}` only (open ↔ done). Never a stored kind.
5121
6891
  */
5122
- declare type TodoListField = FieldBase<AuthoringEditable> & {
6892
+ declare type TodoListField = FieldBase<AuthoringEditable, GroupMembership> & {
5123
6893
  type: "todoList";
5124
6894
  };
5125
6895
 
6896
+ /**
6897
+ * One row of a `todoList`-sugar `array` entry, as the desugared `of`
6898
+ * persists it. The sugar guarantees the COLUMNS in the schema, not values
6899
+ * per row — field validation wraps every declared column in optional, so a
6900
+ * `status`-less row is valid lake data. `_key` + `label` identify a row;
6901
+ * `status` / `assignee` / `dueDate` are per-row optional. Rows are lake
6902
+ * data any client can write — read them through {@link isTodoListItem},
6903
+ * never a cast.
6904
+ */
6905
+ export declare interface TodoListItem {
6906
+ _key: string;
6907
+ label: string;
6908
+ status?: string | null;
6909
+ assignee?: Assignee | null;
6910
+ dueDate?: string | null;
6911
+ }
6912
+
5126
6913
  export declare type Transition = TransitionFields<TransitionOp> & {
5127
6914
  filter: string;
5128
6915
  };
5129
6916
 
5130
6917
  export declare interface TransitionEvaluation {
5131
6918
  transition: Transition;
5132
- /** Whether the transition's filter is definitively satisfied at read time. */
6919
+ /**
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.
6923
+ */
5133
6924
  filterSatisfied: boolean;
5134
6925
  /**
5135
6926
  * The filter evaluated to GROQ `null` — a referenced operand was missing or
@@ -5140,6 +6931,10 @@ export declare interface TransitionEvaluation {
5140
6931
  * hold apart from a genuine routing dead-end.
5141
6932
  */
5142
6933
  unevaluable: boolean;
6934
+ /** Derived state of the transition's filter — the atoms standing between
6935
+ * here and the next stage. `filterSatisfied`/`unevaluable` are projections
6936
+ * of `insight.outcome`; one evaluation feeds all three. */
6937
+ insight: ConditionInsight;
5143
6938
  }
5144
6939
 
5145
6940
  /** Type-mirror of {@link transitionFields} minus `filter` — stored requires it,
@@ -5156,14 +6951,47 @@ declare type TransitionFields<TOp> = {
5156
6951
  export declare type TransitionOp = FieldOp;
5157
6952
 
5158
6953
  /**
5159
- * Parse-validate every GROQ string in a (stored, desugared) definition
5160
- * before deploy. Surfaces malformed predicates, conditions, bindings, and
5161
- * `activity.completeWhen`/`failWhen` expressions early long before any
5162
- * instance runs.
6954
+ * Parse a GDR URI, returning `undefined` instead of throwing when the string
6955
+ * is not a GDR (or is malformed). The "is this a GDR, and if so its parts"
6956
+ * primitive every "parse a ref, treat unparseable as not-a-GDR" call site
6957
+ * routes through this rather than re-spelling the try/catch.
6958
+ */
6959
+ export declare function tryParseGdr(uri: string): ParsedGdr | undefined;
6960
+
6961
+ /**
6962
+ * The diagnose idiom shared by every consumer: each unsatisfied exit
6963
+ * transition with its insight summary. Consumers shape the result (the CLI
6964
+ * maps by name, the MCP prefixes the site phrase) — the selection lives here
6965
+ * so it can't drift between them.
6966
+ */
6967
+ export declare function unsatisfiedTransitionSummaries(
6968
+ evaluation: WorkflowEvaluation,
6969
+ ): {
6970
+ transition: string;
6971
+ summary: string;
6972
+ }[];
6973
+
6974
+ /**
6975
+ * 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
6978
+ * ran the invariants at authoring time, but deploy must reject what authoring
6979
+ * rejects — a stored-shape definition handed straight to the verb (raw JSON,
6980
+ * a foreign tool) would otherwise bypass every invariant.
5163
6981
  *
5164
- * Throws an `Error` with all problems aggregated. Doesn't try to
5165
- * type-check semantics (just syntax). Reserved params don't need to be
5166
- * declared here groq-js's parser doesn't know about runtime params,
6982
+ * Plain condition sites come from {@link conditionSitesOf} the same walker
6983
+ * the insight surface renders from so a condition the runtime will parse
6984
+ * cannot escape the deploy check. Editability is checked at its AUTHORED
6985
+ * sites: an entry's `editable` string alongside its other attributes (with
6986
+ * `initialValue.query`), a stage tighten-override at its walker site. The
6987
+ * walker's `editable-field` sites carry the derived per-stage EFFECTIVE
6988
+ * predicate instead — a composition of those authored parts, parseable
6989
+ * whenever they are — so they are the one site kind this check skips. GROQ
6990
+ * strings that are not condition sites (effect bindings, op `where`s,
6991
+ * subworkflow reads, guard reads) keep their own checks below.
6992
+ *
6993
+ * Throws an `Error` with all problems aggregated. Reserved params don't need
6994
+ * to be declared here — groq-js's parser doesn't know about runtime params,
5167
6995
  * just syntax.
5168
6996
  */
5169
6997
  export declare function validateDefinition(
@@ -5282,6 +7110,12 @@ export declare interface WatchSet {
5282
7110
  perspective?: WorkflowPerspective;
5283
7111
  }
5284
7112
 
7113
+ export { whatIfCondition };
7114
+
7115
+ export { WhatIfOutcome };
7116
+
7117
+ export { withAssignment };
7118
+
5285
7119
  /**
5286
7120
  * The workflow verbs, bound as one object — write path, admin
5287
7121
  * overrides, pure reads, and permission helpers. The module doc above
@@ -5305,9 +7139,14 @@ export declare const workflow: {
5305
7139
  * a clear message naming the missing target.
5306
7140
  *
5307
7141
  * Cycles in the dependency graph error before any write happens.
7142
+ *
7143
+ * Input is authored content or a fetched definition document — the document
7144
+ * envelope (`_*` system fields, `tag`, `version`, `contentHash`) is stripped
7145
+ * at the boundary, never fingerprinted, so a fetched document redeploys as
7146
+ * `unchanged`. Any other unknown key fails loud.
5308
7147
  */
5309
- deployDefinitions: (
5310
- args: DeployDefinitionsArgs & EngineScopeArgs,
7148
+ deployDefinitions: <T extends WorkflowDefinitionInput<T>>(
7149
+ rawArgs: Telemetered<DeployDefinitionsArgs<T> & EngineScopeArgs>,
5311
7150
  ) => Promise<DeployDefinitionsResult>;
5312
7151
  /**
5313
7152
  * Remove a deployed workflow definition (all versions, or one via
@@ -5317,7 +7156,7 @@ export declare const workflow: {
5317
7156
  * full contract (spawn-referrer check, guard-doc housekeeping).
5318
7157
  */
5319
7158
  deleteDefinition: (
5320
- args: Clocked<DeleteDefinitionArgs & EngineScopeArgs>,
7159
+ rawArgs: Clocked<Telemetered<DeleteDefinitionArgs & EngineScopeArgs>>,
5321
7160
  ) => Promise<DeleteDefinitionResult>;
5322
7161
  /**
5323
7162
  * Spawn a new workflow instance from a deployed definition.
@@ -5329,7 +7168,7 @@ export declare const workflow: {
5329
7168
  * `cascaded` reports how far the new instance auto-advanced.
5330
7169
  */
5331
7170
  startInstance: (
5332
- args: Clocked<StartInstanceArgs & EngineScopeArgs>,
7171
+ rawArgs: Clocked<Telemetered<StartInstanceArgs & EngineScopeArgs>>,
5333
7172
  ) => Promise<OperationResult>;
5334
7173
  /**
5335
7174
  * Fire an action against an activity. If the activity is pending, it is
@@ -5342,7 +7181,7 @@ export declare const workflow: {
5342
7181
  * timer firings. External signals never bypass this.
5343
7182
  */
5344
7183
  fireAction: (
5345
- args: Clocked<FireActionArgs & EngineScopeArgs>,
7184
+ rawArgs: Clocked<Telemetered<FireActionArgs & EngineScopeArgs>>,
5346
7185
  ) => Promise<OperationResult>;
5347
7186
  /**
5348
7187
  * Edit a declared-editable field directly — reassign, reschedule,
@@ -5360,7 +7199,7 @@ export declare const workflow: {
5360
7199
  * never an `onChange` per keystroke.
5361
7200
  */
5362
7201
  editField: (
5363
- args: Clocked<EditFieldArgs & EngineScopeArgs>,
7202
+ rawArgs: Clocked<Telemetered<EditFieldArgs & EngineScopeArgs>>,
5364
7203
  ) => Promise<OperationResult>;
5365
7204
  /**
5366
7205
  * Report a queued effect's outcome. Drains it from `pendingEffects`,
@@ -5370,11 +7209,17 @@ export declare const workflow: {
5370
7209
  * reference them. Any `ops` the handler returned (`field.*`) are
5371
7210
  * validated and applied to the instance in the same commit, through the
5372
7211
  * same op applier an action's field ops use. Cascades after. A completion
5373
- * always changes state (the effect drains + history is appended), so
5374
- * `changed` is always `true` — a bad `effectKey`/status throws instead.
7212
+ * that applies always changes state (the effect drains + history is
7213
+ * appended), so `changed` is `true` — a bad `effectKey`/status throws
7214
+ * instead, and a keyed retry of an already-applied completion replays as
7215
+ * `changed: false`.
7216
+ *
7217
+ * Completion is first-writer-wins — see {@link CompleteEffectArgs}. A
7218
+ * completer reporting over a retrying transport (webhook redelivery,
7219
+ * queue, cron) should pass `idempotencyKey`.
5375
7220
  */
5376
7221
  completeEffect: (
5377
- args: Clocked<CompleteEffectArgs & EngineScopeArgs>,
7222
+ rawArgs: Clocked<Telemetered<CompleteEffectArgs & EngineScopeArgs>>,
5378
7223
  ) => Promise<OperationResult>;
5379
7224
  /**
5380
7225
  * Re-evaluate auto-transitions and resolve due waits until stable.
@@ -5390,7 +7235,7 @@ export declare const workflow: {
5390
7235
  * not from `cascaded` alone.
5391
7236
  */
5392
7237
  tick: (
5393
- args: Clocked<OperationArgs & EngineScopeArgs>,
7238
+ rawArgs: Clocked<Telemetered<OperationArgs & EngineScopeArgs>>,
5394
7239
  ) => Promise<OperationResult>;
5395
7240
  /**
5396
7241
  * Admin override — force the instance into `targetStage` regardless
@@ -5399,7 +7244,7 @@ export declare const workflow: {
5399
7244
  * means the move was a no-op (already at the target / terminal).
5400
7245
  */
5401
7246
  setStage: (
5402
- args: Clocked<SetStageArgs & EngineScopeArgs>,
7247
+ rawArgs: Clocked<Telemetered<SetStageArgs & EngineScopeArgs>>,
5403
7248
  ) => Promise<OperationResult>;
5404
7249
  /**
5405
7250
  * Admin override — hard-stop an in-flight instance where it stands.
@@ -5411,7 +7256,7 @@ export declare const workflow: {
5411
7256
  * already terminal.
5412
7257
  */
5413
7258
  abortInstance: (
5414
- args: Clocked<AbortInstanceArgs & EngineScopeArgs>,
7259
+ rawArgs: Clocked<Telemetered<AbortInstanceArgs & EngineScopeArgs>>,
5415
7260
  ) => Promise<OperationResult>;
5416
7261
  /**
5417
7262
  * Fetch a workflow instance by id, scoped to the engine's tag.
@@ -5419,13 +7264,13 @@ export declare const workflow: {
5419
7264
  * engine.
5420
7265
  */
5421
7266
  getInstance: (
5422
- args: InstanceRefArgs & EngineScopeArgs,
7267
+ rawArgs: InstanceRefArgs & EngineScopeArgs,
5423
7268
  ) => Promise<WorkflowInstance>;
5424
7269
  guardsForInstance: (
5425
- args: InstanceRefArgs & EngineScopeArgs,
7270
+ rawArgs: InstanceRefArgs & EngineScopeArgs,
5426
7271
  ) => Promise<MutationGuardDoc[]>;
5427
7272
  guardsForDefinition: (
5428
- args: GuardsForDefinitionArgs & EngineScopeArgs,
7273
+ rawArgs: GuardsForDefinitionArgs & EngineScopeArgs,
5429
7274
  ) => Promise<MutationGuardDoc[]>;
5430
7275
  /**
5431
7276
  * Run a caller-supplied GROQ query with the engine's tag bound as
@@ -5436,7 +7281,7 @@ export declare const workflow: {
5436
7281
  * rejected before it reaches the lake. Caller is responsible for type
5437
7282
  * narrowing the result.
5438
7283
  */
5439
- query: <T = unknown>(args: QueryArgs & EngineScopeArgs) => Promise<T>;
7284
+ query: <T = unknown>(rawArgs: QueryArgs & EngineScopeArgs) => Promise<T>;
5440
7285
  /**
5441
7286
  * Snapshot-aware GROQ — runs against the same in-memory view that
5442
7287
  * filters see for a given instance.
@@ -5452,14 +7297,14 @@ export declare const workflow: {
5452
7297
  * without re-implementing hydration. Pure read — never writes.
5453
7298
  */
5454
7299
  queryInScope: <T = unknown>(
5455
- args: Clocked<QueryInScopeArgs & EngineScopeArgs>,
7300
+ rawArgs: Clocked<QueryInScopeArgs & EngineScopeArgs>,
5456
7301
  ) => Promise<T>;
5457
7302
  /**
5458
7303
  * List every pending effect on the instance. Returns the same entries
5459
7304
  * the runtime would see — claimed and unclaimed alike.
5460
7305
  */
5461
7306
  listPendingEffects: (
5462
- args: InstanceRefArgs & EngineScopeArgs,
7307
+ rawArgs: InstanceRefArgs & EngineScopeArgs,
5463
7308
  ) => Promise<PendingEffect[]>;
5464
7309
  /**
5465
7310
  * Filter pending effects on the instance by criteria. `claimed`
@@ -5467,7 +7312,7 @@ export declare const workflow: {
5467
7312
  * names. Both filters compose (AND).
5468
7313
  */
5469
7314
  findPendingEffects: (
5470
- args: FindPendingEffectsArgs & EngineScopeArgs,
7315
+ rawArgs: FindPendingEffectsArgs & EngineScopeArgs,
5471
7316
  ) => Promise<PendingEffect[]>;
5472
7317
  /**
5473
7318
  * Project the instance from a given actor's perspective. Returns a
@@ -5478,7 +7323,7 @@ export declare const workflow: {
5478
7323
  * `fireAction` to gate writes via the same logic.
5479
7324
  */
5480
7325
  evaluate: (
5481
- args: Clocked<EvaluateArgs & EngineScopeArgs>,
7326
+ rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>,
5482
7327
  ) => Promise<WorkflowEvaluation>;
5483
7328
  /**
5484
7329
  * Diagnose why an instance is or isn't progressing. Projects the
@@ -5490,7 +7335,7 @@ export declare const workflow: {
5490
7335
  * Pure read.
5491
7336
  */
5492
7337
  diagnose: (
5493
- args: Clocked<EvaluateArgs & EngineScopeArgs>,
7338
+ rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>,
5494
7339
  ) => Promise<DiagnoseResult>;
5495
7340
  /**
5496
7341
  * List the actions an actor could fire on an instance's current stage,
@@ -5500,14 +7345,14 @@ export declare const workflow: {
5500
7345
  * consumer can read the instance/stage context. Pure read.
5501
7346
  */
5502
7347
  availableActions: (
5503
- args: Clocked<EvaluateArgs & EngineScopeArgs>,
7348
+ rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>,
5504
7349
  ) => Promise<AvailableActionsResult>;
5505
7350
  /**
5506
7351
  * Materialised spawned children of a parent instance.
5507
7352
  *
5508
7353
  * Walks `history` for `spawned` events — the durable
5509
- * record. (The per-activity `spawnedInstances` field on the active stage
5510
- * only exists while that stage is current; history survives.) Strips
7354
+ * record predating the workflow-scope subworkflow registry, and still the
7355
+ * one place adoption/orphan events sit alongside spawns. Strips
5511
7356
  * the GDR URI on each `instanceRef` to a bare `_id`, fetches the
5512
7357
  * instances, drops any that aren't visible to this engine's tag, and
5513
7358
  * returns them sorted by `startedAt` ascending.
@@ -5515,7 +7360,7 @@ export declare const workflow: {
5515
7360
  * Pass `activity` to restrict to a single spawning activity on the parent.
5516
7361
  */
5517
7362
  children: (
5518
- args: ChildrenArgs & EngineScopeArgs,
7363
+ rawArgs: ChildrenArgs & EngineScopeArgs,
5519
7364
  ) => Promise<WorkflowInstance[]>;
5520
7365
  /**
5521
7366
  * Every in-flight instance whose reactive watch-set includes `document` —
@@ -5547,8 +7392,24 @@ export declare const workflow: {
5547
7392
  * `startedAt` ascending.
5548
7393
  */
5549
7394
  instancesForDocument: (
5550
- args: InstancesForDocumentArgs & EngineScopeArgs,
7395
+ rawArgs: InstancesForDocumentArgs & EngineScopeArgs,
5551
7396
  ) => Promise<WorkflowInstance[]>;
7397
+ /**
7398
+ * The startable half of {@link workflow.instancesForDocument}: every
7399
+ * deployed definition that APPLIES to `document` — what a start picker for
7400
+ * 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
7402
+ * ({@link applicableDefinitions}): startable ∧ a required subject entry
7403
+ * accepts the doc's `_type` ∧ `applicableWhen` passes.
7404
+ *
7405
+ * Takes the LOADED candidate document, not a ref — applicability evaluates
7406
+ * its content, under whatever perspective the caller read it with. Surfaces
7407
+ * ALL matches (name ascending), no engine ranking — presenting a picker or
7408
+ * auto-picking is consumer policy. Advisory like every engine-side check.
7409
+ */
7410
+ definitionsForDocument: (
7411
+ rawArgs: DefinitionsForDocumentArgs & EngineScopeArgs,
7412
+ ) => Promise<DeployedDefinition[]>;
5552
7413
  /**
5553
7414
  * Permission helpers — Sanity ACL grants evaluated against documents
5554
7415
  * via GROQ. Used by `workflow.evaluate` to soft-gate actions when the
@@ -5582,36 +7443,33 @@ declare const WORKFLOW_LIFECYCLES: readonly ["standalone", "child"];
5582
7443
  /**
5583
7444
  * The engine's view of "who am I, what can I do?". `actor` is who
5584
7445
  * the engine stamps onto history / `completedBy` / `ValueExpr.actor` —
5585
- * caller-asserted advisory provenance (see {@link Actor}), never an
5586
- * authenticated principal. `grants` (when present) feed the advisory
5587
- * `$can.*` params action conditions can read; there is no engine-side
5588
- * permission verdict. When grants are absent the rendered `$can` is
5589
- * undefined conditions referencing it fail closed, everything else
5590
- * is ungated engine-side — and the real Sanity write boundary takes
5591
- * over.
5592
- *
5593
- * This is the RESOLVED shape — `actor` is guaranteed. For the
5594
- * partial-override shape callers pass on verb args, see
5595
- * `WorkflowAccessOverride`.
7446
+ * token-resolved advisory provenance (see {@link Actor}). `grants`
7447
+ * (when present) feed the advisory `$can.*` params action conditions
7448
+ * can read; there is no engine-side permission verdict. When grants are
7449
+ * absent the rendered `$can` is undefined — conditions referencing it
7450
+ * fail closed, everything else is ungated engine-side and the real
7451
+ * Sanity write boundary takes over.
5596
7452
  */
5597
7453
  export declare interface WorkflowAccess {
5598
7454
  actor: Actor;
5599
7455
  grants?: Grant[];
5600
7456
  }
5601
7457
 
5602
- /**
5603
- * Partial override accepted by every acting verb's `access?:`
5604
- * argument. Any field present wins; missing halves token-resolve.
5605
- *
5606
- * `{ actor, grants }` — bench's all-access default; both halves
5607
- * injected, no token round-trip.
5608
- * `{ actor }` "act as another user"; grants come from
5609
- * `grantsFromPath` (or the rendered `$can` stays undefined).
5610
- * `{ grants }` — "preview with restricted permissions"; actor
5611
- * comes from `/users/me`.
5612
- * omitted entirelyboth halves token-resolved.
5613
- */
5614
- export declare type WorkflowAccessOverride = Partial<WorkflowAccess>;
7458
+ export declare const WorkflowActionFired: WorkflowTelemetryEvent<WorkflowActionFiredData>;
7459
+
7460
+ export declare interface WorkflowActionFiredData extends InstanceScopedEventData {
7461
+ /** Effective kind of the activity the action fired on. */
7462
+ activityKind?: ActivityKind;
7463
+ hasParams: boolean;
7464
+ /** Auto-transitions fired during the post-action cascade. */
7465
+ cascaded: number;
7466
+ }
7467
+
7468
+ /** Outcome flag for the admin-override events the attempt emits either way. */
7469
+ export declare interface WorkflowAdminOverrideData extends InstanceScopedEventData {
7470
+ /** `false` = the override was a no-op (already at the target / already terminal). */
7471
+ changed: boolean;
7472
+ }
5615
7473
 
5616
7474
  export declare interface WorkflowClient {
5617
7475
  fetch: <T = unknown>(
@@ -5621,6 +7479,9 @@ export declare interface WorkflowClient {
5621
7479
  ) => Promise<T>;
5622
7480
  getDocument: <T = SanityDocument>(
5623
7481
  id: string,
7482
+ options?: {
7483
+ tag?: string;
7484
+ },
5624
7485
  ) => Promise<T | null | undefined>;
5625
7486
  patch: (documentId: string) => WorkflowPatch;
5626
7487
  /**
@@ -5675,6 +7536,33 @@ export declare interface WorkflowClient {
5675
7536
  tag?: string;
5676
7537
  },
5677
7538
  ) => Promise<any>;
7539
+ /**
7540
+ * Optional — present on the real `@sanity/client` and the in-memory test
7541
+ * fake. Returns a sibling client with the given config overrides,
7542
+ * inheriting everything else from the source (token, apiHost). The engine
7543
+ * derives two things from it:
7544
+ *
7545
+ * - **Cross-resource routing** (`resource` + the dataset pair): a sibling
7546
+ * for a foreign GDR when `resourceClients` doesn't map it. A client
7547
+ * without `withConfig` makes foreign GDRs a loud routing error rather
7548
+ * than a silent read of the wrong resource.
7549
+ * - **API-version normalization** (`apiVersion`): every engine entry
7550
+ * derives its working client with
7551
+ * `withConfig({apiVersion: ENGINE_API_VERSION})`, so the caller's
7552
+ * configured version never reaches engine traffic (the caller's own
7553
+ * instance is untouched). A client without `withConfig` cannot be
7554
+ * rebound and is used as-is — it must be built to serve
7555
+ * {@link ENGINE_API_VERSION} already; older dated versions fail
7556
+ * silently incomplete, not loud.
7557
+ *
7558
+ * Dataset resources carry the `{projectId, dataset}` pair alongside
7559
+ * `resource`: the real client routes by `resource` (it takes precedence in
7560
+ * URL building), while the test fake keys its dataset registry by the pair.
7561
+ * Known gap: the fake ignores `resource`, so it derives DATASET siblings
7562
+ * only — a canvas/media-library derivation on the fake aliases the current
7563
+ * store until the fake learns `resource` upstream.
7564
+ */
7565
+ withConfig?: (config: WorkflowClientConfig) => WorkflowClient;
5678
7566
  /**
5679
7567
  * Optional — present on the real `@sanity/client`, absent on the
5680
7568
  * in-memory test client. The engine probes for it when auto-resolving
@@ -5697,6 +7585,19 @@ export declare interface WorkflowClient {
5697
7585
  }) => Promise<T>;
5698
7586
  }
5699
7587
 
7588
+ /**
7589
+ * The config bag {@link WorkflowClient.withConfig} accepts — all optional: a
7590
+ * resource rebind (`resource`, plus the dataset pair for dataset targets), an
7591
+ * `apiVersion` rebind, or both. Everything omitted is inherited from the
7592
+ * source client.
7593
+ */
7594
+ export declare interface WorkflowClientConfig {
7595
+ resource?: WorkflowResource;
7596
+ projectId?: string;
7597
+ dataset?: string;
7598
+ apiVersion?: string;
7599
+ }
7600
+
5700
7601
  export declare interface WorkflowCommitOptions {
5701
7602
  /**
5702
7603
  * When the mutation becomes visible to subsequent queries. The engine
@@ -5710,6 +7611,13 @@ export declare interface WorkflowCommitOptions {
5710
7611
  * {@link WorkflowTransaction.commit}.)
5711
7612
  */
5712
7613
  visibility?: WorkflowVisibility;
7614
+ /**
7615
+ * Request tag for request-log attribution — see
7616
+ * {@link WorkflowFetchOptions.tag}; not the workflow-domain deployment
7617
+ * tag. The engine stamps every write with an operation-scoped
7618
+ * `workflow.*` tag.
7619
+ */
7620
+ tag?: string;
5713
7621
  }
5714
7622
 
5715
7623
  export declare type WorkflowConfig = v.InferOutput<typeof WorkflowConfigSchema>;
@@ -5948,7 +7856,9 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
5948
7856
  name: string;
5949
7857
  title: string;
5950
7858
  description?: string | undefined;
7859
+ groups?: Group[] | undefined;
5951
7860
  lifecycle?: WorkflowLifecycle | undefined;
7861
+ applicableWhen?: string | undefined;
5952
7862
  initialStage: string;
5953
7863
  fields?: FieldEntry[] | undefined;
5954
7864
  stages: Stage[];
@@ -5964,7 +7874,9 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
5964
7874
  name: string;
5965
7875
  title: string;
5966
7876
  description?: string | undefined;
7877
+ groups?: Group[] | undefined;
5967
7878
  lifecycle?: WorkflowLifecycle | undefined;
7879
+ applicableWhen?: string | undefined;
5968
7880
  initialStage: string;
5969
7881
  fields?: FieldEntry[] | undefined;
5970
7882
  stages: Stage[];
@@ -6028,7 +7940,9 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
6028
7940
  name: string;
6029
7941
  title: string;
6030
7942
  description?: string | undefined;
7943
+ groups?: Group[] | undefined;
6031
7944
  lifecycle?: WorkflowLifecycle | undefined;
7945
+ applicableWhen?: string | undefined;
6032
7946
  initialStage: string;
6033
7947
  fields?: FieldEntry[] | undefined;
6034
7948
  stages: Stage[];
@@ -6086,7 +8000,9 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
6086
8000
  name: string;
6087
8001
  title: string;
6088
8002
  description?: string | undefined;
8003
+ groups?: Group[] | undefined;
6089
8004
  lifecycle?: WorkflowLifecycle | undefined;
8005
+ applicableWhen?: string | undefined;
6090
8006
  initialStage: string;
6091
8007
  fields?: FieldEntry[] | undefined;
6092
8008
  stages: Stage[];
@@ -6098,6 +8014,21 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
6098
8014
  >,
6099
8015
  ]
6100
8016
  >;
8017
+ /**
8018
+ * Custom telemetry destination for the CLI. When set, the CLI's built-in
8019
+ * Sanity-intake shell is not constructed and none of its policy applies:
8020
+ * every event — the command trace and the engine vocabulary — flows to
8021
+ * this logger unconditionally (CI and `DO_NOT_TRACK` environments
8022
+ * included). Consent, environment suppression, transport, and destination
8023
+ * are wholly this implementation's business.
8024
+ */
8025
+ readonly telemetry: v.OptionalSchema<
8026
+ v.CustomSchema<
8027
+ WorkflowTelemetryLogger,
8028
+ v.ErrorMessage<v.CustomIssue> | undefined
8029
+ >,
8030
+ undefined
8031
+ >;
6101
8032
  },
6102
8033
  undefined
6103
8034
  >;
@@ -6106,6 +8037,69 @@ export declare type WorkflowDefinition = v.InferOutput<
6106
8037
  typeof WorkflowDefinitionSchema
6107
8038
  >;
6108
8039
 
8040
+ export declare const WorkflowDefinitionDeleted: WorkflowTelemetryEvent<WorkflowDefinitionDeletedData>;
8041
+
8042
+ export declare interface WorkflowDefinitionDeletedData {
8043
+ /** Deployed versions removed by this delete. */
8044
+ deletedVersionCount: number;
8045
+ /** Non-terminal instances cascade-aborted before the delete. */
8046
+ cascadeAbortedCount: number;
8047
+ /** Orphaned guard docs removed (populated only when the last version goes). */
8048
+ deletedGuardCount: number;
8049
+ }
8050
+
8051
+ export declare const WorkflowDefinitionDeployed: WorkflowTelemetryEvent<WorkflowDefinitionDeployedData>;
8052
+
8053
+ export declare interface WorkflowDefinitionDeployedData {
8054
+ /** Content fingerprint of the deployed content. */
8055
+ contentHash: string;
8056
+ /** Whether this deploy minted a new version or matched the latest one.
8057
+ * Unchanged redeploys emit too — the attempt is the adoption signal. */
8058
+ status: "created" | "unchanged";
8059
+ /** Groups every definition event minted by one `deployDefinitions` call —
8060
+ * a random run id, never derived from names or resource coordinates, so
8061
+ * deployment-level rollups are a group-by without carrying customer
8062
+ * strings. Distinct from the workflow-domain deployment tag. */
8063
+ deployId: string;
8064
+ stageCount: number;
8065
+ activityCount: number;
8066
+ actionCount: number;
8067
+ transitionCount: number;
8068
+ /** Declared field entries across workflow, stage, and activity scopes. */
8069
+ fieldCount: number;
8070
+ /** Distinct effective activity kinds used, sorted. */
8071
+ activityKinds: ActivityKind[];
8072
+ /** Distinct declared field kinds across workflow, stage, and activity
8073
+ * scopes, sorted. */
8074
+ fieldKinds: FieldKind[];
8075
+ guardCount: number;
8076
+ /** Effects declared across activities, actions, and transitions. */
8077
+ effectCount: number;
8078
+ /** Activities carrying a `subworkflows` block. */
8079
+ subworkflowCount: number;
8080
+ lifecycle: WorkflowLifecycle;
8081
+ }
8082
+
8083
+ /**
8084
+ * What deploy/diff accept, conditioned on how the value is typed. The
8085
+ * `string extends keyof T` test matches ONLY index-signature types (`keyof`
8086
+ * of a literal is a finite key union that `string` never extends) — i.e. how
8087
+ * fetch results are typed (`Record<string, unknown>`), which pass through to
8088
+ * the runtime boundary parse. Everything precisely typed gets the exact
8089
+ * {@link WorkflowDefinition} contract: beyond the document envelope
8090
+ * ({@link DocumentEnvelopeKey}, so a typed {@link DeployedDefinition} passes
8091
+ * castless), unknown keys are `never` — a typo'd literal is a compile error,
8092
+ * never a silent collapse to the record arm.
8093
+ */
8094
+ export declare type WorkflowDefinitionInput<T> = string extends keyof T
8095
+ ? Record<string, unknown>
8096
+ : WorkflowDefinition & {
8097
+ [K in Exclude<
8098
+ keyof T,
8099
+ keyof WorkflowDefinition | DocumentEnvelopeKey
8100
+ >]?: never;
8101
+ };
8102
+
6109
8103
  /**
6110
8104
  * Structural schema for a STORED workflow definition — primitives only,
6111
8105
  * every reference scope resolved. Cross-field invariants (unique names,
@@ -6125,6 +8119,35 @@ declare const WorkflowDefinitionSchema: v.GenericSchema<
6125
8119
 
6126
8120
  export declare type WorkflowDeployment = WorkflowConfig["deployments"][number];
6127
8121
 
8122
+ export declare const WorkflowEffectCompleted: WorkflowTelemetryEvent<WorkflowEffectCompletedData>;
8123
+
8124
+ export declare interface WorkflowEffectCompletedData extends InstanceScopedEventData {
8125
+ /** The effect's author-chosen name — one of the payload policy's two
8126
+ * deliberate customer-string exceptions (module doc); unique per
8127
+ * definition. */
8128
+ effect: EffectName;
8129
+ /** The recorded outcome of the effect — `cancelled` when an abort
8130
+ * discarded it rather than a completer reporting. */
8131
+ status: EffectRunStatus;
8132
+ /** Which declaration boundary queued the effect. */
8133
+ origin: EffectOriginKind;
8134
+ /** Auto-transitions fired during the post-completion cascade — always 0
8135
+ * for a cancellation (the instance is terminal). */
8136
+ cascaded: number;
8137
+ }
8138
+
8139
+ export declare const WorkflowEffectsDrained: WorkflowTelemetryEvent<WorkflowEffectsDrainedData>;
8140
+
8141
+ export declare interface WorkflowEffectsDrainedData extends InstanceScopedEventData {
8142
+ /** Pending effects dispatched to a registered handler in this drain —
8143
+ * 0 for an empty poll (the pass itself is the signal). */
8144
+ drainedCount: number;
8145
+ /** The dispatched effects' author-chosen names, in drain order — one of
8146
+ * the payload policy's two deliberate customer-string exceptions (module
8147
+ * doc). */
8148
+ drainedEffects: EffectName[];
8149
+ }
8150
+
6128
8151
  /**
6129
8152
  * Base class of the engine's structured errors. `kind` is the stable
6130
8153
  * discriminant — render on it; `name` mirrors the concrete class for logs.
@@ -6140,28 +8163,13 @@ export declare abstract class WorkflowError<
6140
8163
  constructor(kind: K, message: string, options?: ErrorOptions);
6141
8164
  }
6142
8165
 
6143
- /**
6144
- * The engine's error model — one base class, one discriminant.
6145
- *
6146
- * Every structured error the engine throws at a caller extends
6147
- * {@link WorkflowError} and carries a {@link WorkflowErrorKind}, so a consumer
6148
- * can write ONE catch-and-render path: `instanceof WorkflowError`, then switch
6149
- * on `kind` (or `instanceof` a concrete class to read its typed payload). The
6150
- * model covers the runtime verbs' refusals, denials, races and lookups.
6151
- * Three things deliberately stay plain `Error`: deploy/authoring validation
6152
- * (a structured issue-report design of its own), transient transport
6153
- * failures (retryable, carrying `cause` — not engine verdicts), and internal
6154
- * invariant violations (the remediation is a bug report, not a catch branch).
6155
- *
6156
- * How thrown errors relate to the read-side verdicts is documented on
6157
- * {@link DisabledReason}.
6158
- */
6159
8166
  export declare type WorkflowErrorKind =
6160
8167
  | "action-disabled"
6161
8168
  | "edit-field-denied"
6162
8169
  | "mutation-guard-denied"
6163
8170
  | "action-params-invalid"
6164
8171
  | "effect-ops-invalid"
8172
+ | "effect-outputs-invalid"
6165
8173
  | "required-field-not-provided"
6166
8174
  | "workflow-state-diverged"
6167
8175
  | "partial-guard-deploy"
@@ -6169,6 +8177,7 @@ export declare type WorkflowErrorKind =
6169
8177
  | "concurrent-edit-field"
6170
8178
  | "cascade-limit"
6171
8179
  | "field-value-shape"
8180
+ | "model-version-ahead"
6172
8181
  | "instance-not-found"
6173
8182
  | "definition-not-found"
6174
8183
  | "definition-in-use"
@@ -6192,6 +8201,13 @@ export declare interface WorkflowEvaluation {
6192
8201
  * declares no editable fields.
6193
8202
  */
6194
8203
  editableFields: EditableFieldEvaluation[];
8204
+ /**
8205
+ * Per-field derived state across every condition site in the current stage:
8206
+ * which gates read each field (involvement) and the counterfactually
8207
+ * verified consequences of the values that would unblock blocking atoms.
8208
+ * Empty when no condition reads a field.
8209
+ */
8210
+ fieldInsights: FieldInsight[];
6195
8211
  }
6196
8212
 
6197
8213
  export declare interface WorkflowFetchOptions {
@@ -6201,6 +8217,22 @@ export declare interface WorkflowFetchOptions {
6201
8217
  * reads to the instance's `perspective` field.
6202
8218
  */
6203
8219
  perspective?: WorkflowPerspective;
8220
+ /**
8221
+ * Request tag for request-log attribution (`?tag=` — combined with any
8222
+ * client-configured `requestTagPrefix` as `prefix.tag`). Unrelated to
8223
+ * the workflow-domain deployment tag ({@link validateTag}'s `$tag`
8224
+ * partition): same word, disjoint concepts. The engine stamps every
8225
+ * read with an operation-scoped `workflow.*` tag.
8226
+ */
8227
+ tag?: string;
8228
+ }
8229
+
8230
+ export declare const WorkflowFieldEdited: WorkflowTelemetryEvent<WorkflowFieldEditedData>;
8231
+
8232
+ export declare interface WorkflowFieldEditedData extends InstanceScopedEventData {
8233
+ /** Declared kind of the edited field entry. */
8234
+ fieldKind?: FieldKind;
8235
+ mode: EditMode;
6204
8236
  }
6205
8237
 
6206
8238
  /** Type-mirror of {@link workflowFields}, parameterised over field/stage. */
@@ -6208,7 +8240,9 @@ declare type WorkflowFields<TField, TStage> = {
6208
8240
  name: string;
6209
8241
  title: string;
6210
8242
  description?: string | undefined;
8243
+ groups?: Group[] | undefined;
6211
8244
  lifecycle?: WorkflowLifecycle | undefined;
8245
+ applicableWhen?: string | undefined;
6212
8246
  initialStage: string;
6213
8247
  fields?: TField[] | undefined;
6214
8248
  stages: TStage[];
@@ -6218,6 +8252,21 @@ declare type WorkflowFields<TField, TStage> = {
6218
8252
 
6219
8253
  export declare interface WorkflowInstance extends SanityDocument {
6220
8254
  _type: typeof WORKFLOW_INSTANCE_TYPE;
8255
+ /**
8256
+ * Engine data-model stamp — the shape contract this document conforms to
8257
+ * (see {@link DATA_MODEL_VERSION}), orthogonal to the definition-content
8258
+ * pins (`pinnedVersion` / `pinnedContentHash`). Stamped at create and
8259
+ * re-asserted on every full persist; absent on documents last written
8260
+ * before the stamp existed (model 0).
8261
+ */
8262
+ modelVersion?: number;
8263
+ /**
8264
+ * Reader floor — the oldest engine data model that can safely interpret
8265
+ * this document (see {@link DATA_MODEL_MIN_READER}). Written alongside
8266
+ * {@link WorkflowInstance.modelVersion}; additive model changes leave it,
8267
+ * only breaking ones raise it.
8268
+ */
8269
+ minReaderModel?: number;
6221
8270
  /**
6222
8271
  * Engine-scope environment partition stamped on the instance at create
6223
8272
  * time. Reads are scoped to a single tag, so an engine only sees
@@ -6287,9 +8336,25 @@ export declare interface WorkflowInstance extends SanityDocument {
6287
8336
  * Each entry owns its activities.
6288
8337
  */
6289
8338
  stages: StageEntry[];
8339
+ /**
8340
+ * Workflow-scope registry of every child this instance ever spawned —
8341
+ * see {@link SubworkflowEntry}. Rows are never deleted; a row without
8342
+ * `resolved` is a LIVE child (watched, hydrated, propagating), one with
8343
+ * it is terminal, and a live row with `abortPending` is CONDEMNED — the
8344
+ * cascade owes it an abort. Rendered as the `$subworkflows` condition
8345
+ * var. Absent only on instances persisted before the registry existed.
8346
+ */
8347
+ subworkflows?: SubworkflowEntry[];
6290
8348
  pendingEffects: PendingEffect[];
6291
8349
  effectHistory: EffectHistoryEntry[];
6292
8350
  history: HistoryEntry[];
8351
+ /**
8352
+ * Idempotency ledger — see {@link ProcessedRequest}. Optional because
8353
+ * instances persisted before the ledger existed lack it (any commit
8354
+ * materialises it, empty). Only keyed operations add rows — it never
8355
+ * grows on instances whose callers pass no `idempotencyKey`.
8356
+ */
8357
+ processedRequests?: ProcessedRequest[];
6293
8358
  startedAt: string;
6294
8359
  lastChangedAt: string;
6295
8360
  completedAt?: string;
@@ -6303,6 +8368,27 @@ export declare interface WorkflowInstance extends SanityDocument {
6303
8368
  abortedAt?: string;
6304
8369
  }
6305
8370
 
8371
+ export declare const WorkflowInstanceAborted: WorkflowTelemetryEvent<WorkflowAdminOverrideData>;
8372
+
8373
+ export declare const WorkflowInstanceStarted: WorkflowTelemetryEvent<WorkflowInstanceStartedData>;
8374
+
8375
+ export declare interface WorkflowInstanceStartedData extends InstanceScopedEventData {
8376
+ /** Caller-supplied initial-field entries — `initialFields` for a direct
8377
+ * start, the parent's projected `with` bindings for a spawned child. */
8378
+ initialFieldCount: number;
8379
+ /** Started by a parent's subworkflow spawn rather than a direct
8380
+ * `startInstance` — automation vs. hands-on at instance creation. */
8381
+ viaSpawn: boolean;
8382
+ lifecycle: WorkflowLifecycle;
8383
+ }
8384
+
8385
+ export declare const WorkflowInstanceTicked: WorkflowTelemetryEvent<WorkflowInstanceTickedData>;
8386
+
8387
+ export declare interface WorkflowInstanceTickedData extends InstanceScopedEventData {
8388
+ /** Auto-transitions fired during the cascade. */
8389
+ cascaded: number;
8390
+ }
8391
+
6306
8392
  /** How instances of a definition come to exist: started standalone (the
6307
8393
  * default) or spawned by a parent. `'child'` is spawn-only — see
6308
8394
  * {@link isStartableDefinition}. */
@@ -6373,6 +8459,28 @@ export declare type WorkflowResource =
6373
8459
  id: string;
6374
8460
  };
6375
8461
 
8462
+ export declare const WorkflowStageSet: WorkflowTelemetryEvent<WorkflowAdminOverrideData>;
8463
+
8464
+ export declare const WorkflowStageTransitioned: WorkflowTelemetryEvent<WorkflowStageTransitionedData>;
8465
+
8466
+ export declare interface WorkflowStageTransitionedData extends InstanceScopedEventData {
8467
+ /** Positional index of the exited stage in the definition's `stages[]`
8468
+ * order — indexes, never names, so the payload stays content-free. */
8469
+ fromStageIndex: number;
8470
+ /** Positional index of the entered stage in the definition's `stages[]` order. */
8471
+ toStageIndex: number;
8472
+ /** The entered stage is terminal (no transitions out) — reaching it
8473
+ * completes the instance. */
8474
+ toIsTerminal: boolean;
8475
+ /** The entered stage was already visited on this instance — a loop-back. */
8476
+ isRevisit: boolean;
8477
+ /** Time spent in the exited stage (its entry's `enteredAt` to this exit,
8478
+ * on the engine clock). Omitted when the exited entry is unavailable. */
8479
+ dwellMs?: number;
8480
+ /** Condition-driven transition vs. the setStage admin override. */
8481
+ via: "transition" | "setStage";
8482
+ }
8483
+
6376
8484
  /**
6377
8485
  * Thrown when a guard deploy fails *after* its state move committed and the
6378
8486
  * engine could not cleanly undo the result — any of: the rollback write itself
@@ -6397,6 +8505,37 @@ export declare class WorkflowStateDivergedError extends WorkflowError<"workflow-
6397
8505
  });
6398
8506
  }
6399
8507
 
8508
+ /**
8509
+ * A telemetry event descriptor — a structural mirror of `@sanity/telemetry`'s
8510
+ * `DefinedTelemetryLog`, so shells hand these straight to their store logger.
8511
+ */
8512
+ export declare interface WorkflowTelemetryEvent<Data = void> {
8513
+ type: "log";
8514
+ /** Matched verbatim downstream — frozen once shipped. */
8515
+ name: string;
8516
+ /** Bumped on any payload shape change; the name never changes. */
8517
+ version: number;
8518
+ /** Optional to match the real package's event shape, so an event built
8519
+ * with the real `defineEvent` flows into this seam; the engine's own
8520
+ * vocabulary always sets it. */
8521
+ description?: string;
8522
+ /** Advisory volume cap enforced by the shell's store — at most one
8523
+ * submission per interval (ms). */
8524
+ maxSampleRate?: number;
8525
+ /** Type-level payload carrier — never set at runtime (the real package's
8526
+ * `defineEvent` leaves it unset the same way). */
8527
+ schema: Data;
8528
+ }
8529
+
8530
+ /**
8531
+ * The logger surface the engine emits through — the `log` subset of
8532
+ * `@sanity/telemetry`'s `TelemetryLogger`, so any real logger satisfies it.
8533
+ */
8534
+ export declare interface WorkflowTelemetryLogger {
8535
+ log<Data>(event: WorkflowTelemetryEvent<Data>, data: Data): void;
8536
+ log(event: WorkflowTelemetryEvent<void>): void;
8537
+ }
8538
+
6400
8539
  export declare interface WorkflowTransaction {
6401
8540
  /**
6402
8541
  * Queue a `create` mutation. Fails the transaction on `_id` collision,
@@ -6432,17 +8571,17 @@ export declare interface WorkflowTransaction {
6432
8571
  */
6433
8572
  delete: (id: string) => WorkflowTransaction;
6434
8573
  /**
6435
- * Commit the batch. No options a transaction rides the client's *default*
6436
- * `visibility`: `'sync'` in `@sanity/client` (so a post-spawn `listInstances`
6437
- * GROQ sees freshly-created children), immediate in the in-memory test fake.
6438
- * This is a standing engine assumption a consumer that configured an
6439
- * `async` default would make spawn read-backs racy. Single-document writes
6440
- * state `'sync'` explicitly via {@link WorkflowCommitOptions}; transactions
6441
- * can't (yet) the test fake's commit options don't carry `visibility`, and
6442
- * per repo convention that gap is closed upstream in the fake, not worked
6443
- * around here.
8574
+ * Commit the batch. The options bag carries only the request tag
8575
+ * ({@link WorkflowFetchOptions.tag} attribution, not the deployment
8576
+ * tag). Deliberately no `visibility`: a transaction rides the client's
8577
+ * *default* `visibility` `'sync'` in `@sanity/client` (so a post-spawn
8578
+ * `listInstances` GROQ sees freshly-created children), immediate in the
8579
+ * in-memory test fake. This is a standing engine assumption — a consumer
8580
+ * that configured an `async` default would make spawn read-backs racy.
8581
+ * Single-document writes state `'sync'` explicitly via
8582
+ * {@link WorkflowCommitOptions}.
6444
8583
  */
6445
- commit: () => Promise<unknown>;
8584
+ commit: (options?: { tag?: string }) => Promise<unknown>;
6446
8585
  }
6447
8586
 
6448
8587
  export declare type WorkflowVisibility = "sync" | "async" | "deferred";