@sanity/workflow-engine 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { analyzeCondition } from "@sanity/groq-condition-describe";
2
2
  import { AtomInsight } from "@sanity/groq-condition-describe";
3
+ import { atomReadsDataset } from "@sanity/groq-condition-describe";
3
4
  import { AtomRequirement } from "@sanity/groq-condition-describe";
4
5
  import { checklistLines as checklistLines_2 } from "@sanity/groq-condition-describe";
5
6
  import { ComparisonOp } from "@sanity/groq-condition-describe";
@@ -312,6 +313,13 @@ declare const ACTIVITY_STATUSES: readonly [
312
313
  "failed",
313
314
  ];
314
315
 
316
+ /** The activity slice of a stage rollup — total by construction, like
317
+ * {@link stageAutonomyOf}. */
318
+ export declare function activityAutonomyOf(
319
+ stageAutonomy: StageAutonomy,
320
+ activityName: string,
321
+ ): AutonomyAnswer;
322
+
315
323
  /** An activity's gates, each described — absent keys mirror undeclared gates. */
316
324
  export declare interface ActivityDescription {
317
325
  requirements?: Record<string, ConditionDescription>;
@@ -360,11 +368,19 @@ export declare interface ActivityEvaluation {
360
368
  */
361
369
  kind: ActivityKind;
362
370
  /**
363
- * Who, if anyone, the activity waits on — derived from the definition
371
+ * Who, if anyone, fires the activity's actions — derived from its shape
364
372
  * alone: `autonomous` (every action cascade-fired), `interactive` (only
365
373
  * fireAction-fired actions), `off-system` (`target` present), or `hybrid`.
366
374
  */
367
375
  classification: ExecutorClassification;
376
+ /**
377
+ * The causal refinement of {@link ActivityEvaluation.classification}:
378
+ * whether this activity completes without a caller, derived by
379
+ * {@link deriveWorkflowAutonomy}'s dataflow over the definition — a
380
+ * mechanically `autonomous` activity whose triggers only read
381
+ * caller-written state still reports the caller it waits on.
382
+ */
383
+ autonomy: AutonomyAnswer;
368
384
  /** Whether this activity is the current actor's responsibility right now. */
369
385
  pendingOnActor: boolean;
370
386
  /**
@@ -445,9 +461,9 @@ export declare type ActorKind = (typeof ACTOR_KINDS)[number];
445
461
 
446
462
  export { analyzeCondition };
447
463
 
448
- /** The definition surface applicability reads — structural, so authored,
464
+ /** The definition surface the start contexts read — structural, so authored,
449
465
  * stored, and deployed definition shapes all fit. `name` binds
450
- * `$definition` and names the definition in a broken-filter error. */
466
+ * `$definition` and names the definition in a broken-predicate error. */
451
467
  export declare interface ApplicabilitySource {
452
468
  name?: string | undefined;
453
469
  lifecycle?: WorkflowLifecycle | undefined;
@@ -456,6 +472,7 @@ export declare interface ApplicabilitySource {
456
472
  | {
457
473
  kind?: StartKind | undefined;
458
474
  filter?: string | undefined;
475
+ allowed?: string | undefined;
459
476
  }
460
477
  | undefined;
461
478
  }
@@ -470,7 +487,7 @@ export declare function applicableDefinitions<
470
487
  definitions: readonly T[];
471
488
  document: CandidateDocument;
472
489
  /** Caller-side filter bindings, shared by every definition's evaluation. */
473
- scope?: StartFilterScope;
490
+ scope?: StartScope;
474
491
  }): Promise<T[]>;
475
492
 
476
493
  /**
@@ -511,6 +528,8 @@ export declare type Assignee =
511
528
 
512
529
  export { AtomInsight };
513
530
 
531
+ export { atomReadsDataset };
532
+
514
533
  export { AtomRequirement };
515
534
 
516
535
  /**
@@ -607,6 +626,11 @@ export declare type AuthoringGuard = v.InferOutput<typeof AuthoringGuardSchema>;
607
626
 
608
627
  declare const AuthoringGuardSchema: v.StrictObjectSchema<
609
628
  {
629
+ /**
630
+ * Lake-id-segment grammar (`^[a-z0-9][a-z0-9-]*$`, deploy-enforced): the
631
+ * guard's lake `_id` derives from `(instanceId, name)` at stage entry.
632
+ * Unique per definition.
633
+ */
610
634
  name: v.SchemaWithPipe<
611
635
  readonly [
612
636
  v.StringSchema<undefined>,
@@ -768,8 +792,9 @@ declare const AuthoringGuardSchema: v.StrictObjectSchema<
768
792
  /**
769
793
  * Lake GROQ predicate — a distinct eval context: delta-mode GROQ
770
794
  * reading the `before()`/`after()` natives, `mutation`, `guard`, and
771
- * `identity()`. Bare ids/fields only. Omitted or empty means
772
- * UNCONDITIONAL DENY.
795
+ * `identity()`. Bare ids/fields only. Polarity: a result of strictly
796
+ * `true` ALLOWS the matched mutation; anything else (false, null, an
797
+ * evaluation error) DENIES. Omitted or empty means UNCONDITIONAL DENY.
773
798
  */
774
799
  predicate: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
775
800
  /**
@@ -1221,6 +1246,85 @@ export declare type AuthoringWorkflow = WorkflowFields<
1221
1246
  AuthoringStartBlock
1222
1247
  >;
1223
1248
 
1249
+ /** The narratable answer at one rollup level: the verdict plus what it waits
1250
+ * on. `waitsOn` may be non-empty on a `yes` verdict (an effect settle is a
1251
+ * wait the engine resolves itself) — consumers choose what to surface. */
1252
+ export declare interface AutonomyAnswer {
1253
+ completesWithoutCaller: AutonomyVerdict;
1254
+ waitsOn: AutonomyWait[];
1255
+ }
1256
+
1257
+ /** The workflow-level autonomy narrative: fully self-running, or the stages
1258
+ * where a run is held. */
1259
+ export declare function autonomySummary(
1260
+ autonomy: WorkflowAutonomy,
1261
+ ctx: DescribeContext,
1262
+ ): InsightPhrase;
1263
+
1264
+ /**
1265
+ * Whether a node resolves without a caller: `yes` — engine machinery alone
1266
+ * (the cascade plus the host's effect drain/ticker) gets it there; `no` —
1267
+ * someone outside the engine must act (a caller fires an action, an editor
1268
+ * writes a field or content); `conditional` — it may resolve either way
1269
+ * depending on values the analysis can't see (start-time seeds, mixed
1270
+ * alternative routes, unresolvable children, cyclic gates).
1271
+ *
1272
+ * `yes` still leans on the host: effects settle only if a drain runs, and
1273
+ * time only passes for a ticker. A cascade-fired action's `roles` pin is a
1274
+ * DEPENDENCY, not an assumption — the runtime leaves a pinned trigger armed
1275
+ * until a capable token cascades, and whether the deployment's tokens fulfil
1276
+ * the pin is configuration the analysis can't see, so a pinned trigger
1277
+ * reports `conditional` with the pin as its wait.
1278
+ */
1279
+ export declare type AutonomyVerdict = "yes" | "no" | "conditional";
1280
+
1281
+ /** One narratable dependency a non-`yes` verdict rests on. */
1282
+ export declare type AutonomyWait =
1283
+ | {
1284
+ kind: "caller-action";
1285
+ stage: string;
1286
+ activity: string;
1287
+ action: string;
1288
+ roles?: string[];
1289
+ }
1290
+ | {
1291
+ kind: "execute-pin";
1292
+ stage: string;
1293
+ activity: string;
1294
+ action: string;
1295
+ roles: string[];
1296
+ }
1297
+ | {
1298
+ kind: "editor-field";
1299
+ field: string;
1300
+ }
1301
+ | {
1302
+ kind: "editor-content";
1303
+ condition: Condition;
1304
+ }
1305
+ | {
1306
+ kind: "effect";
1307
+ effect: string;
1308
+ }
1309
+ | {
1310
+ kind: "start-input";
1311
+ field: string;
1312
+ }
1313
+ | {
1314
+ kind: "context";
1315
+ key?: string;
1316
+ }
1317
+ | {
1318
+ kind: "subworkflow";
1319
+ definition: string;
1320
+ resolved: boolean;
1321
+ }
1322
+ | {
1323
+ kind: "unwritten-field";
1324
+ field: string;
1325
+ handlerPossible: boolean;
1326
+ };
1327
+
1224
1328
  export declare interface AvailableAction {
1225
1329
  activity: string;
1226
1330
  activityStatus: ActivityStatus;
@@ -1648,11 +1752,14 @@ export declare interface ConditionVar {
1648
1752
  * the rest evaluate to `undefined` — and deploy rejects them at these
1649
1753
  * sites; a cascade-fired action's per-token gate is `roles`, never its
1650
1754
  * conditions.
1651
- * 3. **The start-filter context** — a definition's `start.filter` evaluates
1652
- * against a CANDIDATE (no instance exists yet): the candidate document is
1653
- * the root, {@link START_FILTER_VARS} are the only bound vars, and
1654
- * `*[...]` reads the WORKFLOW resource's dataset. None of the rendered
1655
- * condition vars exist there.
1755
+ * 3. **The start contexts** — a definition's `start.filter` and
1756
+ * `start.allowed` evaluate against a CANDIDATE (no instance exists yet):
1757
+ * `*[...]` reads the WORKFLOW resource's dataset and none of the rendered
1758
+ * condition vars exist. The two split on what a surface can know:
1759
+ * `filter` is browse-time-pure (candidate document as root,
1760
+ * {@link START_FILTER_VARS} — no `$fields`, which cannot exist before
1761
+ * inputs do), `allowed` is the start-time permission predicate
1762
+ * ({@link START_ALLOWED_VARS} — `$fields` bound, never a root).
1656
1763
  * 4. **Guard predicates** — NOT conditions. A lake mutation guard's
1657
1764
  * `predicate` is groq-js **delta-mode** GROQ over a document mutation:
1658
1765
  * `before()`/`after()`/`identity()` are dialect natives, and the wire
@@ -1666,7 +1773,7 @@ export declare interface ConditionVar {
1666
1773
  * instance and its snapshot.
1667
1774
  * - `'caller'` — rides the acting caller; without one the var is `undefined`
1668
1775
  * (conditions referencing it fail closed). Author predicates may not read
1669
- * these — they pre-evaluate once per instance, caller-free.
1776
+ * these — they pre-evaluate caller-free, per evaluation context.
1670
1777
  * - `'spawn'` — bound only at spawn sites (the per-row `$row`).
1671
1778
  */
1672
1779
  export declare type ConditionVarBinding = "always" | "caller" | "spawn";
@@ -1808,10 +1915,11 @@ export declare function contextMap(
1808
1915
 
1809
1916
  /**
1810
1917
  * A caller broke a public-API contract — a call the engine rejects
1811
- * before touching the lake: an invalid `tag`, a `workflow.query` GROQ that
1918
+ * without writing anything: an invalid `tag`, a `workflow.query` GROQ that
1812
1919
  * never binds `$tag`, a bare id where a GDR URI is required, an action,
1813
- * activity, or field the definition never declared. Fix the call site;
1814
- * retrying cannot succeed.
1920
+ * activity, or field the definition never declared, or a `startInstance`
1921
+ * `instanceId` reused for a different start (or a discarded one). Fix the
1922
+ * call site; retrying cannot succeed.
1815
1923
  */
1816
1924
  export declare class ContractViolationError extends WorkflowError<"contract-violation"> {
1817
1925
  constructor(message: string);
@@ -2229,11 +2337,13 @@ export declare interface DeployDefinitionsArgs<
2229
2337
  > {
2230
2338
  /**
2231
2339
  * Resource-alias bindings for this deploy (alias name → physical resource).
2232
- * Every `@<alias>:` reference in a definition is expanded to its bound
2233
- * physical resource at deploy, so the deployed definition holds only physical
2234
- * references. Deploy fails closed when a definition references an alias this
2235
- * map doesn't bind. Omit for single-resource workflows (definitions that
2236
- * reference no aliases).
2340
+ * A deploy-time abstraction ONLY: every `@<alias>:` reference in a
2341
+ * definition is expanded to its bound physical resource before the
2342
+ * deployed definition is fingerprinted and stored, and deploy fails closed
2343
+ * on an unbound alias. Aliases never exist past deploy — no stored
2344
+ * document carries one, the runtime never sees the map, and an
2345
+ * alias-shaped ref reaching a runtime boundary is rejected as malformed.
2346
+ * Omit for single-resource workflows (definitions referencing no aliases).
2237
2347
  */
2238
2348
  resourceAliases?: ResourceAliases;
2239
2349
  /** Authored definitions (the default — full compile-time checking) or
@@ -2308,18 +2418,37 @@ export declare interface DeployTarget {
2308
2418
  */
2309
2419
  export declare function deriveActivityKind(activity: Activity): ActivityKind;
2310
2420
 
2421
+ export declare interface DeriveAutonomyOptions {
2422
+ /** Child definitions for `spawn` recursion, by definition `name` — the
2423
+ * deploy batch or previously deployed set. An unresolvable child reports
2424
+ * `conditional` with an unresolved wait. */
2425
+ children?: ReadonlyMap<string, WorkflowDefinition>;
2426
+ }
2427
+
2311
2428
  /**
2312
- * Who, if anyone, the activity waits on — derived ahead-of-time from the
2313
- * definition alone: `off-system` when `target` is present; else `autonomous`
2314
- * (every action cascade-fired — no caller ever needed), `interactive` (only
2315
- * fireAction-fired actions), or `hybrid` (mixed). An actionless activity
2316
- * classifies `autonomous` vacuously — nothing waits on a caller (deploy's
2317
- * terminal-reachability invariant rejects it anyway).
2429
+ * Who, if anyone, fires the activity's actions — derived ahead-of-time from
2430
+ * the activity's shape alone: `off-system` when `target` is present; else
2431
+ * `autonomous` (every action cascade-fired — no caller fires anything),
2432
+ * `interactive` (only fireAction-fired actions), or `hybrid` (mixed). An
2433
+ * actionless activity classifies `autonomous` vacuously (deploy's
2434
+ * terminal-reachability invariant rejects it anyway). Shape-only: whether
2435
+ * the activity actually RESOLVES without a caller is the causal question
2436
+ * `deriveWorkflowAutonomy` answers.
2318
2437
  */
2319
2438
  export declare function deriveExecutorClassification(
2320
2439
  activity: Activity,
2321
2440
  ): ExecutorClassification;
2322
2441
 
2442
+ /**
2443
+ * Derive the causal autonomy of a stored definition. Pure and instance-free;
2444
+ * conditions must parse (they did at define/deploy time — an unparseable one
2445
+ * throws groq-js's syntax error rather than guessing).
2446
+ */
2447
+ export declare function deriveWorkflowAutonomy(
2448
+ definition: WorkflowDefinition,
2449
+ options?: DeriveAutonomyOptions,
2450
+ ): WorkflowAutonomy;
2451
+
2323
2452
  export declare type Describable =
2324
2453
  | ConditionInsight
2325
2454
  | FieldInsight
@@ -2337,6 +2466,12 @@ export declare function describeAtom(
2337
2466
  fallback: boolean;
2338
2467
  };
2339
2468
 
2469
+ /** One {@link AutonomyWait} as a phrase in the workflow vocabulary. */
2470
+ export declare function describeAutonomyWait(
2471
+ wait: AutonomyWait,
2472
+ ctx: DescribeContext,
2473
+ ): InsightPhrase;
2474
+
2340
2475
  /** Render one condition's insight as a checklist + frontier summary, in the
2341
2476
  * workflow vocabulary. */
2342
2477
  export declare function describeCondition(
@@ -2920,6 +3055,15 @@ export declare type EditMode = "set" | "append" | "unset";
2920
3055
 
2921
3056
  export declare type Effect = v.InferOutput<typeof EffectSchema>;
2922
3057
 
3058
+ /**
3059
+ * Every terminal state an effect run can record. `done` and `failed` are
3060
+ * reported through completion ({@link EffectCompletionStatus}); `cancelled`
3061
+ * is engine-stamped only — an abort cancelling the entry before dispatch.
3062
+ * A cancellation is not a failure: anything counting failures (dashboards,
3063
+ * retry tooling) must not count aborted-away effects among them.
3064
+ */
3065
+ declare const EFFECT_RUN_STATUSES: readonly ["done", "failed", "cancelled"];
3066
+
2923
3067
  /** The outcomes a completer may report through `completeEffect` — a run
2924
3068
  * either succeeded or failed. `cancelled` is not reportable: only an abort
2925
3069
  * stamps it, on entries that never dispatched. */
@@ -3095,14 +3239,7 @@ export declare function effectOutputsMap(
3095
3239
  instance: Pick<WorkflowInstance, "effectHistory">,
3096
3240
  ): Record<string, unknown>;
3097
3241
 
3098
- /**
3099
- * Every terminal state an effect run can record. `done` and `failed` are
3100
- * reported through completion ({@link EffectCompletionStatus}); `cancelled`
3101
- * is engine-stamped only — an abort cancelling the entry before dispatch.
3102
- * A cancellation is not a failure: anything counting failures (dashboards,
3103
- * retry tooling) must not count aborted-away effects among them.
3104
- */
3105
- export declare type EffectRunStatus = "done" | "failed" | "cancelled";
3242
+ export declare type EffectRunStatus = (typeof EFFECT_RUN_STATUSES)[number];
3106
3243
 
3107
3244
  declare const EffectSchema: v.StrictObjectSchema<
3108
3245
  {
@@ -3257,18 +3394,30 @@ export declare interface Engine {
3257
3394
  /** The startable half of {@link Engine.instancesForDocument}: the latest
3258
3395
  * deployed version of every definition that applies to the LOADED candidate
3259
3396
  * document — startable, a required subject entry accepts its `_type`, and
3260
- * `start.filter` passes. All matches, name ascending; advisory — a start
3261
- * picker's filter, never enforcement. */
3397
+ * `start.filter` (browse-time-pure `$fields` never binds) passes. All
3398
+ * matches, name ascending; advisory — a start picker's filter, never
3399
+ * enforcement. */
3262
3400
  definitionsForDocument: (
3263
3401
  args: DefinitionsForDocumentArgs,
3264
3402
  ) => Promise<DeployedDefinition[]>;
3403
+ /** Pre-flight `startInstance`'s gates for a definition + the
3404
+ * `initialFields` gathered so far: the `start.allowed` verdict with its
3405
+ * insight (disable the start affordance on a definitive false AND say
3406
+ * why) plus the still-missing required inputs. Bindability-aware — a
3407
+ * predicate reading a not-yet-supplied entry reports `'unevaluable'`
3408
+ * with the entries named in `unboundReads`, never a collapsed verdict.
3409
+ * Pure read; the enforcement moment is `startInstance` itself. */
3410
+ evaluateStart: (args: EvaluateStartArgs) => Promise<StartEvaluation>;
3265
3411
  /** GROQ query against the engine's workflow resource. `$tag`
3266
3412
  * is bound for tag-scoped filtering. */
3267
3413
  query: <T = unknown>(args: QueryArgs) => Promise<T>;
3268
3414
  /** Snapshot-aware GROQ — runs against the same in-memory view the
3269
- * engine's filters see for the supplied instance. The rendered scope's
3270
- * instance-derived vars ({@link FILTER_SCOPE_VARS}) are bound, ids in
3271
- * GDR URI form to match the snapshot's keying. */
3415
+ * engine's filters see for the supplied instance. The caller-free
3416
+ * rendered scope cascade gates evaluate in is bound the
3417
+ * instance-derived vars ({@link FILTER_SCOPE_VARS}) with the open
3418
+ * stage's overlay merged into `$fields`, `$assigned` at its caller-free
3419
+ * `false`, plus the author's pre-evaluated `$<predicate>` booleans —
3420
+ * ids in GDR URI form to match the snapshot's keying. */
3272
3421
  queryInScope: <T = unknown>(args: QueryInScopeArgs) => Promise<T>;
3273
3422
  /** List every pending effect on an instance. */
3274
3423
  listPendingEffects: (args: InstanceRefArgs) => Promise<PendingEffect[]>;
@@ -3347,8 +3496,9 @@ export declare interface EngineScopeArgs {
3347
3496
  * docs that live in a different Sanity resource than the workflow). Called
3348
3497
  * with a parsed GDR; return a client for that resource, or undefined to let
3349
3498
  * the engine route it — `client` for the workflow resource itself, a
3350
- * sibling derived from `client`'s credentials for anything else. Engine-scope
3351
- * configuration see `CreateEngineArgs`.
3499
+ * sibling derived from `client`'s credentials for anything else. A served
3500
+ * resource is also part of the declared surface for runtime-supplied refs.
3501
+ * Engine-scope configuration — see `CreateEngineArgs`.
3352
3502
  */
3353
3503
  resourceClients?: ResourceClientResolver;
3354
3504
  /**
@@ -3368,6 +3518,16 @@ export declare interface EngineScopeArgs {
3368
3518
  idempotencyTtlMs?: number;
3369
3519
  }
3370
3520
 
3521
+ /**
3522
+ * Pull the {@link GlobalDocumentReference} values out of `doc.ref` /
3523
+ * `doc.refs` (content) field entries. Content only — release field entries come
3524
+ * from {@link entryReleaseRefs}, so guard discovery (which reads this via
3525
+ * `collectEntryDocUris`) stays scoped to content docs.
3526
+ */
3527
+ export declare function entryDocRefs(
3528
+ entries: unknown,
3529
+ ): GlobalDocumentReference[];
3530
+
3371
3531
  /**
3372
3532
  * Pure error helpers — no client, no I/O. The one home for the engine's
3373
3533
  * "coerce an unknown caught value" + "rethrow with context" idioms, so the
@@ -3465,10 +3625,30 @@ export declare function evaluateMutationGuard(args: {
3465
3625
  context: MutationContext;
3466
3626
  }): Promise<boolean>;
3467
3627
 
3628
+ /** Args for `evaluateStart` — the pre-flight read of `startInstance`'s gates
3629
+ * for a definition + the `initialFields` gathered so far (possibly none). */
3630
+ export declare interface EvaluateStartArgs {
3631
+ /** The definition's `name` — which deployed workflow would be started. */
3632
+ definition: string;
3633
+ /** Optional explicit version. Defaults to the highest deployed version. */
3634
+ version?: number;
3635
+ /**
3636
+ * The candidate seed, in `StartInstanceArgs.initialFields` shape. Partial
3637
+ * mid-form input is expected and safe: the verdict is BINDABILITY-AWARE —
3638
+ * when the predicate reads a declared entry these inputs don't supply,
3639
+ * `outcome` reports `'unevaluable'` and {@link StartEvaluation.unboundReads}
3640
+ * names the entries, instead of the collapsed answer GROQ equality would
3641
+ * otherwise produce (`null == x` is false, not null — a definitive-looking
3642
+ * verdict one more input could overturn, in either direction).
3643
+ */
3644
+ initialFields?: InitialFieldValue[];
3645
+ }
3646
+
3468
3647
  /**
3469
- * Evaluate one `start.filter` in the start-filter context: `document` (may be
3470
- * absent — root reads then fail closed) as the GROQ root, the
3471
- * {@link StartFilterScope} bindings plus `$definition`, and — only when
3648
+ * Evaluate one `start.filter` in the start-filter context: `document` (may
3649
+ * be absent — a root read is then GROQ null, fail-closed or vacuous-pass by
3650
+ * shape) as the GROQ root, the {@link StartScope} bindings plus
3651
+ * `$definition`, and — only when
3472
3652
  * `analyzeCondition` says the filter reads the dataset — the scope's fetched
3473
3653
  * slice as `*`. Cheap pure evaluation otherwise: no I/O rides a filter that
3474
3654
  * never scans. GROQ null ("can't decide") is `false` — every consumer of
@@ -3482,7 +3662,7 @@ export declare function evaluateStartFilter(args: {
3482
3662
  filter: string;
3483
3663
  definition: Pick<ApplicabilitySource, "name">;
3484
3664
  document?: CandidateDocument | undefined;
3485
- scope?: StartFilterScope | undefined;
3665
+ scope?: StartScope | undefined;
3486
3666
  }): Promise<boolean>;
3487
3667
 
3488
3668
  /**
@@ -3532,8 +3712,8 @@ export declare interface ExecutionContext {
3532
3712
  }
3533
3713
 
3534
3714
  /**
3535
- * Executor classifications — who, if anyone, an activity waits on
3536
- * (derived; see `./activity-kind.ts`).
3715
+ * Executor classifications — who, if anyone, fires an activity's actions
3716
+ * (shape-derived; see `./activity-kind.ts`).
3537
3717
  */
3538
3718
  export declare const EXECUTOR_CLASSIFICATION_DISPLAY: {
3539
3719
  autonomous: {
@@ -3568,6 +3748,35 @@ export { explainCondition };
3568
3748
 
3569
3749
  export { ExplainConditionArgs };
3570
3750
 
3751
+ /**
3752
+ * Explain one `start.allowed` in the start-allowed context: no root (the
3753
+ * subject rides `$fields.<entry>.id` — deploy rejects a root read), the
3754
+ * {@link StartScope} bindings plus `$definition` and the caller's `$fields`
3755
+ * map, and the scope's fetched slice as `*` when the predicate reads the
3756
+ * dataset. Returns the full {@link ConditionInsight} — `outcome` is the
3757
+ * verdict (`'satisfied'` allows; `'unsatisfied'`/`'unevaluable'` refuse,
3758
+ * fail-closed) and the atom breakdown is what lets a surface disable its
3759
+ * start affordance AND say why. One evaluator serves both moments:
3760
+ * `startInstance` enforces the outcome, the `evaluateStart` verb pre-flights
3761
+ * it.
3762
+ *
3763
+ * A dataset-reading predicate with no slice provider THROWS instead of
3764
+ * failing closed: unlike a picker that merely hides a row, this verdict
3765
+ * refuses starts — evaluating over an empty `*` would let a `count()`-style
3766
+ * rule pass vacuously, and a silent "not allowed" would misreport a caller
3767
+ * bug as an author verdict. Parse/evaluation throws rethrow naming the
3768
+ * definition, like the filter's; a failed slice FETCH propagates as itself.
3769
+ */
3770
+ export declare function explainStartAllowed(args: {
3771
+ allowed: string;
3772
+ definition: Pick<ApplicabilitySource, "name">;
3773
+ /** The caller's input entries as a `$fields` map — the engine verbs build
3774
+ * it with `startFieldsParam` (field resolution's projection), so the
3775
+ * predicate can only see values the resolution would persist. */
3776
+ fields: Record<string, unknown>;
3777
+ scope?: StartScope | undefined;
3778
+ }): Promise<ConditionInsight>;
3779
+
3571
3780
  /**
3572
3781
  * Extract the bare document `_id` from a GDR URI — what
3573
3782
  * `*[_id == $docId]` in GROQ needs.
@@ -3987,6 +4196,35 @@ export declare function gdrUri(
3987
4196
  ): GdrUri;
3988
4197
 
3989
4198
  /**
4199
+ * The engine's reference value — deliberately NOT a lake reference, and
4200
+ * spelled differently on purpose (`id`/`type`, never `_ref`/`_id`):
4201
+ *
4202
+ * - **`id` is a URI, not a bare doc id**, because workflow references cross
4203
+ * resources (another dataset, a media library) and a bare `_id` is only
4204
+ * meaningful inside one dataset. Everything resource-aware — client
4205
+ * routing, spawn discovery, guard target resolution — reads the resource
4206
+ * out of this URI. It is stripped to a bare lake id at exactly one
4207
+ * boundary: `paramsForLake` (core/params.ts), when a query actually runs
4208
+ * against a lake.
4209
+ * - **`type` carries the target's schema type so consumers don't need a
4210
+ * fetch — as stamped by the ref's constructor, never verified against the
4211
+ * target.** Engine-built refs stamp it from real data (a projection row's
4212
+ * `_type`, the `releaseRef` constructor); a caller-supplied ref's `type`
4213
+ * is a trusted claim. Three advisory surfaces read it: the declared-subject-type
4214
+ * contract (an entry's `types: [...]` accepts/rejects incoming refs —
4215
+ * `refTypeIssues` at start `initialFields` and spawn `with`), start
4216
+ * applicability ("which definitions accept a doc of this `_type`" —
4217
+ * `acceptsDocumentType`), and guard `match.types` inference from resolved
4218
+ * targets (`resolveMatchTypes`). All must work when the target lives in a
4219
+ * resource the reader can't (or shouldn't) fetch from.
4220
+ *
4221
+ * In rendered GROQ this shape is what a reference-held value exposes
4222
+ * (`$fields.<entry>.id` / `.type`) — whereas `_id`/`_type` belong to
4223
+ * documents you hold (lake rows, `$row`, a dereferenced singular `doc.ref`).
4224
+ * The spelling is not a namespace guarantee: a content document can carry
4225
+ * its own `id`/`type` fields — what you're reading follows what the entry's
4226
+ * declared kind puts in your hand, never the key's spelling.
4227
+ *
3990
4228
  * `TType` preserves the `type` literal the constructors ({@link refDataset},
3991
4229
  * {@link gdrRef}, …) were called with, so a constructed ref satisfies
3992
4230
  * literal-typed targets (e.g. a `release.ref` value's `"system.release"`).
@@ -4243,6 +4481,11 @@ declare const GuardReadSchema: v.VariantSchema<
4243
4481
  /** Stored guards carry the printed string reads (the deploy resolver's input). */
4244
4482
  declare const GuardSchema: v.StrictObjectSchema<
4245
4483
  {
4484
+ /**
4485
+ * Lake-id-segment grammar (`^[a-z0-9][a-z0-9-]*$`, deploy-enforced): the
4486
+ * guard's lake `_id` derives from `(instanceId, name)` at stage entry.
4487
+ * Unique per definition.
4488
+ */
4246
4489
  name: v.SchemaWithPipe<
4247
4490
  readonly [
4248
4491
  v.StringSchema<undefined>,
@@ -4314,8 +4557,9 @@ declare const GuardSchema: v.StrictObjectSchema<
4314
4557
  /**
4315
4558
  * Lake GROQ predicate — a distinct eval context: delta-mode GROQ
4316
4559
  * reading the `before()`/`after()` natives, `mutation`, `guard`, and
4317
- * `identity()`. Bare ids/fields only. Omitted or empty means
4318
- * UNCONDITIONAL DENY.
4560
+ * `identity()`. Bare ids/fields only. Polarity: a result of strictly
4561
+ * `true` ALLOWS the matched mutation; anything else (false, null, an
4562
+ * evaluation error) DENIES. Omitted or empty means UNCONDITIONAL DENY.
4319
4563
  */
4320
4564
  predicate: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
4321
4565
  /**
@@ -4760,6 +5004,16 @@ export declare type InsightSite =
4760
5004
  activity?: string;
4761
5005
  };
4762
5006
 
5007
+ /**
5008
+ * Mint the Sanity document `_id` for a workflow instance — a fresh
5009
+ * {@link randomKey} suffix, so every instance (root or spawned child) gets a
5010
+ * unique doc id under its tag. Lives in the shell, not `core/`, because it
5011
+ * draws randomness; the deterministic {@link definitionDocId} is the pure-core
5012
+ * counterpart. Bare form — Sanity rejects `:` in document IDs, so this is
5013
+ * never a GDR URI.
5014
+ */
5015
+ export declare function instanceDocId(tag: string): string;
5016
+
4763
5017
  /**
4764
5018
  * The per-instance guard filter — the single definition of "this instance's
4765
5019
  * guards in one datasource". The engine's verdict load
@@ -4815,7 +5069,10 @@ export declare interface InstanceSession {
4815
5069
  /** Replace the held content with the current values the consumer observed
4816
5070
  * (last-write-wins, scoped). A self-doc updates the held instance only when
4817
5071
  * its `_updatedAt` is strictly newer — an older or timestamp-equal echo is
4818
- * ignored. Buffered if a commit is in flight. */
5072
+ * ignored. Buffered if a commit is in flight; a BUFFERED push that fails
5073
+ * validation surfaces on the NEXT `update` call (after that call's own
5074
+ * docs are processed), never from the commit itself — the commit's
5075
+ * outcome is never masked by a store echo's failure. */
4819
5076
  update(docs: LoadedDoc[]): void;
4820
5077
  /** Replace the held live guards (the consumer's guard stream,
4821
5078
  * last-write-wins). Guards are a separate stream from the watch-set:
@@ -4858,10 +5115,11 @@ export declare interface InstanceSession {
4858
5115
  * replace the target's staged rows (last write wins); appends accumulate.
4859
5116
  * Tolerant where commits are loud: a target that doesn't resolve in the
4860
5117
  * current stage (it moved under a mounted editor), a closed edit window
4861
- * (a skipped activity's field), or a value that doesn't
4862
- * fit the field's shape yet (a half-typed date) stages an INERT preview
4863
- * no echo, no throw; the commit at the semantic boundary is the loud
4864
- * surface. Never persisted — commit via {@link InstanceSession.editField}. */
5118
+ * (a skipped activity's field), a value that doesn't fit the field's
5119
+ * shape yet (a half-typed date), or a ref outside the declared resource
5120
+ * surface stages an INERT preview — no echo, no throw; the commit at the
5121
+ * semantic boundary is the loud surface. Never persisted — commit via
5122
+ * {@link InstanceSession.editField}. */
4865
5123
  previewField(args: {
4866
5124
  target: EditFieldTarget;
4867
5125
  mode?: EditMode;
@@ -4879,8 +5137,10 @@ export declare interface InstancesForDocumentArgs {
4879
5137
 
4880
5138
  /**
4881
5139
  * The instance-list GROQ for a {@link InstancesQueryFilter}, ordered by
4882
- * `startedAt` ascending. Adapters subscribe to it; {@link workflow.query}
4883
- * -style one-shot reads fetch it — both see the same rows.
5140
+ * `startedAt` ascending (descending + sliced under
5141
+ * {@link InstancesQueryFilter.limit}). Adapters subscribe to it;
5142
+ * {@link workflow.query}-style one-shot reads fetch it — both see the same
5143
+ * rows.
4884
5144
  */
4885
5145
  export declare function instancesQuery(args: {
4886
5146
  tag: string;
@@ -4924,6 +5184,14 @@ export declare interface InstancesQueryFilter {
4924
5184
  stage?: string;
4925
5185
  /** Include completed/aborted instances (default: in-flight only). */
4926
5186
  includeCompleted?: boolean;
5187
+ /**
5188
+ * Cap the read to the NEWEST `limit` instances — the query flips to
5189
+ * `startedAt desc` and slices, so a bounded consumer (a dashboard over an
5190
+ * unbounded dataset) reads the most recent rows instead of the oldest.
5191
+ * Unlimited reads keep the ascending order adapters index by. Must be a
5192
+ * positive integer.
5193
+ */
5194
+ limit?: number;
4927
5195
  }
4928
5196
 
4929
5197
  /**
@@ -4978,7 +5246,7 @@ export declare function isDefinitionApplicable(args: {
4978
5246
  definition: ApplicabilitySource;
4979
5247
  document: CandidateDocument;
4980
5248
  /** Caller-side filter bindings — omit for the bare candidate-doc context. */
4981
- scope?: StartFilterScope;
5249
+ scope?: StartScope;
4982
5250
  }): Promise<boolean>;
4983
5251
 
4984
5252
  /**
@@ -5098,6 +5366,19 @@ export declare function isTodoListEntry(
5098
5366
  * as null). The remaining columns stay loose (field validation owns them). */
5099
5367
  export declare function isTodoListItem(row: unknown): row is TodoListItem;
5100
5368
 
5369
+ /**
5370
+ * True when the instance document exists but its start never finished: the
5371
+ * `create` committed, yet priming (stage entry, activities, guard deploy)
5372
+ * did not. The shape is legal and resumable — `startInstance` with the same
5373
+ * `instanceId` completes the outstanding commits. Readers should present it
5374
+ * as an incomplete start, never as a normal run sitting at its initial
5375
+ * stage. Terminal instances are excluded: an aborted never-primed husk
5376
+ * reads as aborted, not as still-resumable.
5377
+ */
5378
+ export declare function isUnprimed(
5379
+ instance: Pick<WorkflowInstance, "stages" | "completedAt" | "abortedAt">,
5380
+ ): boolean;
5381
+
5101
5382
  /**
5102
5383
  * Deterministic id so deploy/retract are query-free and idempotent. Derived
5103
5384
  * from the guard's authored `name` — an author-controlled handle that stays
@@ -5396,6 +5677,8 @@ export declare interface MutationGuardDoc extends MutationGuardBody {
5396
5677
  _updatedAt?: string;
5397
5678
  }
5398
5679
 
5680
+ export declare const MutationGuardDocSchema: v.GenericSchema<MutationGuardDoc>;
5681
+
5399
5682
  export declare interface MutationGuardMatch {
5400
5683
  types?: string[];
5401
5684
  /** Bare document ids (resource-local; both published and `drafts.` forms). */
@@ -5404,6 +5687,14 @@ export declare interface MutationGuardMatch {
5404
5687
  actions: MutationGuardAction[];
5405
5688
  }
5406
5689
 
5690
+ /** Every wait narrated, de-duplicated by phrase: same-named actions on
5691
+ * sibling activities are distinct waits that tell one story — consumers
5692
+ * want one line per story, not one per site. */
5693
+ export declare function narrateAutonomyWaits(
5694
+ waits: readonly AutonomyWait[],
5695
+ ctx: DescribeContext,
5696
+ ): string[];
5697
+
5407
5698
  /** The default logger: emit nothing. The engine's twin of `@sanity/telemetry`'s `noopLogger`. */
5408
5699
  export declare const noopTelemetry: WorkflowTelemetryLogger;
5409
5700
 
@@ -5555,6 +5846,23 @@ export declare interface ParsedGdr {
5555
5846
  */
5556
5847
  export declare function parseGdr(uri: string): ParsedGdr;
5557
5848
 
5849
+ /**
5850
+ * Parse a fetched guard document, failing hard with a
5851
+ * {@link PersistedDocShapeError} naming the document and every offending
5852
+ * field. No model-version gate: the guard doc carries no stamp by design —
5853
+ * its `_type` cutover is its versioning event.
5854
+ */
5855
+ export declare function parseGuardDocument(doc: unknown): MutationGuardDoc;
5856
+
5857
+ /**
5858
+ * Parse a fetched `sanity.workflow.instance` document, failing hard with a
5859
+ * {@link PersistedDocShapeError} naming the document and every offending
5860
+ * field. The caller's model-version gate (`assertReadableModel`) comes
5861
+ * FIRST — a doc from beyond the reader floor is a governed
5862
+ * `ModelVersionAheadError`, not a shape error.
5863
+ */
5864
+ export declare function parseInstanceDocument(doc: unknown): WorkflowInstance;
5865
+
5558
5866
  /**
5559
5867
  * Parse a resource-shaped GDR (`<type>:<id>`, no document part) into the
5560
5868
  * {@link WorkflowResource} it names. The one grammar for resource addresses —
@@ -5643,6 +5951,26 @@ export declare interface PendingEffectClaim {
5643
5951
  leaseExpiresAt?: string;
5644
5952
  }
5645
5953
 
5954
+ /**
5955
+ * A persisted engine-owned document failed its shape parse — the stored
5956
+ * field tree does not match what this engine's data model declares for the
5957
+ * doc type. Distinct from {@link ModelVersionAheadError} (a declared,
5958
+ * governed fence: upgrade the engine); this is UNGOVERNED corruption — a
5959
+ * document no conforming engine wrote — and the read fails hard instead of
5960
+ * letting the malformed value corrupt behavior downstream. The issues name
5961
+ * every offending field path.
5962
+ */
5963
+ export declare class PersistedDocShapeError extends WorkflowError<"persisted-doc-shape"> {
5964
+ readonly documentId: string;
5965
+ readonly documentType: string;
5966
+ readonly issues: ValidationIssue[];
5967
+ constructor(args: {
5968
+ documentId: string;
5969
+ documentType: string;
5970
+ issues: ValidationIssue[];
5971
+ });
5972
+ }
5973
+
5646
5974
  /**
5647
5975
  * One row of the instance's idempotency ledger — a caller-supplied
5648
5976
  * `idempotencyKey` a state-changing verb already committed under. Recorded in
@@ -5711,6 +6039,14 @@ export declare interface QueryInScopeArgs extends InstanceRefArgs, QueryArgs {}
5711
6039
 
5712
6040
  export { quoted };
5713
6041
 
6042
+ /**
6043
+ * The one spelling of the instance read discipline — model gate
6044
+ * ({@link assertReadableModel}) first, shape parse
6045
+ * ({@link parseInstanceDocument}) second — shared by the point-read funnel
6046
+ * above and the list-read sites that fetch instance rows in bulk.
6047
+ */
6048
+ export declare function readInstanceDoc(doc: SanityDocument): WorkflowInstance;
6049
+
5714
6050
  /**
5715
6051
  * Whether a watched ref reads RAW — never perspective-scoped. The
5716
6052
  * instance, its ancestors, and its spawned children (all instance docs), and
@@ -5768,6 +6104,18 @@ export declare function refMediaLibrary<TType extends string = string>({
5768
6104
  type,
5769
6105
  }: ResourceRefArgs<TType>): GlobalDocumentReference<TType>;
5770
6106
 
6107
+ /**
6108
+ * Thrown when a field write carries a ref to a resource outside the
6109
+ * declared surface. The write does not commit; the engine has not mutated
6110
+ * state.
6111
+ */
6112
+ export declare class RefResourceUndeclaredError extends WorkflowError<"ref-resource-undeclared"> {
6113
+ readonly entryType: string;
6114
+ readonly entryName: string;
6115
+ readonly issues: string[];
6116
+ constructor(args: { entryName: string; entryType: string; issues: string[] });
6117
+ }
6118
+
5771
6119
  /**
5772
6120
  * The GDR `type`s in a `doc.ref` / `doc.refs` value that the entry's declared
5773
6121
  * accepted `types` reject — empty when the value conforms, the entry declares
@@ -5959,9 +6307,10 @@ export declare function resolveFieldEntry(
5959
6307
  export declare type ResourceAliases = Record<string, WorkflowResource>;
5960
6308
 
5961
6309
  /**
5962
- * Collapse a deployment's `resourceAliases` bindings into the engine's
5963
- * {@link ResourceAliases} map (handle name → physical resource) — the shape
5964
- * {@link deployDefinitions} consumes.
6310
+ * Collapse a deployment's `resourceAliases` bindings into the
6311
+ * {@link ResourceAliases} map (handle name → physical resource) that
6312
+ * `deployDefinitions` expands `@<handle>:` references against. Deploy-time
6313
+ * only — the map never reaches any other verb.
5965
6314
  */
5966
6315
  export declare function resourceAliasesToMap(
5967
6316
  resourceAliases: WorkflowDeployment["resourceAliases"],
@@ -5974,6 +6323,10 @@ export declare function resourceAliasesToMap(
5974
6323
  * engine route it — its own client for the workflow resource, a derived
5975
6324
  * sibling ({@link WorkflowClient.withConfig}) for anything else.
5976
6325
  *
6326
+ * Serving a resource also DECLARES it on the written-ref surface: the write
6327
+ * boundaries probe this resolver per ref, so narrowing a resolver rejects
6328
+ * refs to the resources it stops serving.
6329
+ *
5977
6330
  * Resolved clients ride the engine's API version like every other engine
5978
6331
  * client: the verb scope rebinds them onto `ENGINE_API_VERSION` when they
5979
6332
  * carry {@link WorkflowClient.withConfig} (one without it is used as
@@ -6008,6 +6361,13 @@ declare interface ResourceRefArgs<TType extends string> {
6008
6361
  type: TType;
6009
6362
  }
6010
6363
 
6364
+ export declare interface ResourceSurface {
6365
+ /** True when the parsed GDR targets a declared resource. */
6366
+ allows: (parsed: ParsedGdr) => boolean;
6367
+ /** Prose rendering of the declared surface, for rejection messages. */
6368
+ description: string;
6369
+ }
6370
+
6011
6371
  /**
6012
6372
  * Retract every guard for a stage being exited (lift predicate to allow), but
6013
6373
  * only once the instance has genuinely moved off that stage — or was aborted
@@ -6079,6 +6439,23 @@ declare const RoleAliasesSchema: v.RecordSchema<
6079
6439
  undefined
6080
6440
  >;
6081
6441
 
6442
+ /** Whether two workflow resources address the same place. */
6443
+ export declare function sameResource(
6444
+ a: WorkflowResource,
6445
+ b: WorkflowResource,
6446
+ ): boolean;
6447
+
6448
+ /**
6449
+ * The DECLARED field tree of a runtime schema — every entry key (optional
6450
+ * keys marked `key?`), every enum vocabulary, every variant arm — walked out
6451
+ * of valibot's runtime shape. The declared twin of `fieldTreeShape` (which
6452
+ * walks a CONCRETE document): the model-surface gate pins persisted doc
6453
+ * types by what their schema declares, so an optional field no fixture
6454
+ * reaches is still ledger-visible. Recursive grammars (a field shape nesting
6455
+ * field shapes) close with a `'(circular)'` marker.
6456
+ */
6457
+ export declare function schemaTreeShape(schema: v.GenericSchema): unknown;
6458
+
6082
6459
  export { ScopeAssignment };
6083
6460
 
6084
6461
  export { sentenceCase };
@@ -6123,6 +6500,21 @@ export declare type Stage = StageFields<
6123
6500
  Editable
6124
6501
  >;
6125
6502
 
6503
+ /** A stage's rollup: progress through any exit transition, plus each
6504
+ * activity's own completion answer. */
6505
+ export declare interface StageAutonomy extends AutonomyAnswer {
6506
+ stage: string;
6507
+ activities: Record<string, AutonomyAnswer>;
6508
+ }
6509
+
6510
+ /** The stage slice of a rollup. Total by construction — the rollup derives
6511
+ * from the same `definition.stages` consumers iterate, so a miss is an
6512
+ * engine bug worth failing loud on. */
6513
+ export declare function stageAutonomyOf(
6514
+ autonomy: WorkflowAutonomy,
6515
+ stageName: string,
6516
+ ): StageAutonomy;
6517
+
6126
6518
  export declare interface StageEntry {
6127
6519
  _key: string;
6128
6520
  name: StageName;
@@ -6138,6 +6530,9 @@ export declare interface StageEvaluation {
6138
6530
  stage: Stage;
6139
6531
  activities: ActivityEvaluation[];
6140
6532
  transitions: TransitionEvaluation[];
6533
+ /** The stage's causal-autonomy rollup — will it progress without a caller,
6534
+ * and what does it wait on. Definition-derived, identical for every actor. */
6535
+ autonomy: StageAutonomy;
6141
6536
  }
6142
6537
 
6143
6538
  /** Type-mirror of {@link stageFields}, parameterised over field/activity/transition/guard/editable. */
@@ -6170,15 +6565,33 @@ declare interface StageGuardArgs {
6170
6565
 
6171
6566
  export declare type StageName = string;
6172
6567
 
6568
+ /**
6569
+ * The vars a definition's `start.allowed` reads — the start-time permission
6570
+ * dialect: everything the filter context binds ({@link START_FILTER_VARS})
6571
+ * plus `$fields`, which start time always has. No candidate root ever binds
6572
+ * here (the subject rides `$fields.<entry>.id`; a root read is
6573
+ * deploy-rejected), so a false/GROQ-null verdict is genuinely fail-closed —
6574
+ * there is no unbound-binding ambiguity to caveat. Bound in one place:
6575
+ * `startContextParams` in the applicability evaluator.
6576
+ */
6577
+ export declare const START_ALLOWED_VARS: readonly {
6578
+ name: string;
6579
+ description: string;
6580
+ }[];
6581
+
6173
6582
  /**
6174
6583
  * The vars a definition's `start.filter` reads — the start-filter dialect,
6175
6584
  * not the rendered condition scope (no {@link ConditionVarBinding}: these
6176
6585
  * bind only while the filter is evaluated on the READ side — the
6177
6586
  * `definitionsForDocument` derivation and the Studio start control).
6178
- * `startInstance` never evaluates the filter. An absent binding evaluates to
6179
- * GROQ null, so a read of it fails closed: a picker with no `initialFields`
6180
- * in hand hides a `$fields`-reading definition rather than guessing. Bound in
6181
- * one place: `startFilterParams` in the applicability evaluator.
6587
+ * `startInstance` never evaluates the filter. BROWSE-TIME-PURE: every var is
6588
+ * knowable before any inputs exist, so `$fields` is deliberately absent a
6589
+ * `$fields` read in a filter is deploy-rejected with a pointer to
6590
+ * `start.allowed`, the context that binds it. An absent binding evaluates to
6591
+ * GROQ null; the VERDICT-level coercion (null result ⇒ not applicable) is
6592
+ * what fails closed — what a null read does inside the expression depends on
6593
+ * its shape. Bound in one place: `startContextParams` in the applicability
6594
+ * evaluator.
6182
6595
  */
6183
6596
  export declare const START_FILTER_VARS: readonly {
6184
6597
  name: string;
@@ -6203,44 +6616,66 @@ export declare type StartBlock = StartFields & {
6203
6616
  */
6204
6617
  export declare type StartContext = Record<string, unknown>;
6205
6618
 
6619
+ /**
6620
+ * What `evaluateStart` projects — the same gates `startInstance` enforces,
6621
+ * as renderable state. `allowed` and `missingRequired` are deliberately
6622
+ * orthogonal: a missing required input is a caller mistake (the verb throws
6623
+ * `RequiredFieldNotProvidedError` for it), never a `start.allowed` verdict.
6624
+ */
6625
+ export declare interface StartEvaluation {
6626
+ /** The `start.allowed` verdict — `outcome === 'satisfied'`. Vacuously true
6627
+ * when the definition declares no `allowed`, exactly like the verb. */
6628
+ allowed: boolean;
6629
+ /**
6630
+ * Three-valued, BINDABILITY-AWARE verdict: `'unsatisfied'` is a definitive
6631
+ * no for these inputs (disable and explain), `'unevaluable'` means the
6632
+ * predicate can't be decided yet — either a null operand reached an
6633
+ * ordered comparison, or the predicate reads an entry `initialFields`
6634
+ * doesn't supply ({@link StartEvaluation.unboundReads} non-empty). Keep
6635
+ * the affordance enabled on `'unevaluable'`; `startInstance` still
6636
+ * enforces the final verdict, where absence is final rather than
6637
+ * provisional.
6638
+ */
6639
+ outcome: ConditionOutcome;
6640
+ /** The `start.allowed` atom breakdown over the SUPPLIED inputs — what to
6641
+ * render next to a disabled start affordance. Present iff the definition
6642
+ * declares `allowed`. With `unboundReads` non-empty its own `outcome` may
6643
+ * be a collapsed provisional answer — the top-level `outcome` is the
6644
+ * trustworthy one. */
6645
+ insight?: ConditionInsight;
6646
+ /** The `$fields` entries the predicate reads that `initialFields` doesn't
6647
+ * supply — "fill these to decide". Non-empty forces `outcome:
6648
+ * 'unevaluable'`. Empty when the definition declares no `allowed`. */
6649
+ unboundReads: string[];
6650
+ /** Required input entries `initialFields` doesn't fill yet — the rows a
6651
+ * `startInstance` now would throw `RequiredFieldNotProvidedError` about. */
6652
+ missingRequired: {
6653
+ name: string;
6654
+ type: FieldEntry["type"];
6655
+ }[];
6656
+ }
6657
+
6206
6658
  /** Type-mirror of {@link startFields}: how standalone runs of this workflow
6207
6659
  * begin. Stored requires `kind` (desugar fills the `'interactive'` default);
6208
6660
  * authoring may omit it — so each variant declares it. */
6209
6661
  declare type StartFields = {
6210
6662
  filter?: string | undefined;
6663
+ allowed?: string | undefined;
6211
6664
  };
6212
6665
 
6213
6666
  /**
6214
- * The caller-side half of the start-filter context — everything the
6215
- * evaluating surface knows that the definition doesn't. Every member is
6216
- * optional because the surfaces genuinely differ (a picker holds no
6217
- * `initialFields`; a pure consumer may hold no clock): an absent binding
6218
- * evaluates to GROQ null, so a filter reading it FAILS CLOSED rather than
6219
- * guessing. The vars themselves are inventoried in `START_FILTER_VARS`.
6667
+ * Project caller-supplied `initialFields` into the `$fields` map the
6668
+ * start-allowed context binds: one key per declared `input`-sourced entry,
6669
+ * resolved through {@link suppliedFieldFor} the predicate can only ever see
6670
+ * a value the input resolution would persist, and an undeclared supplied name
6671
+ * never leaks in. Unsupplied (or null-supplied) entries stay unbound, so a
6672
+ * read of one is GROQ null. Document references bind as their GDR envelopes
6673
+ * (`$fields.<entry>.id` is the GDR URI).
6220
6674
  */
6221
- export declare interface StartFilterScope {
6222
- /** The engine's tag partition — binds `$tag`. */
6223
- tag?: string | undefined;
6224
- /** ISO clock reading — binds `$now`. */
6225
- now?: string | undefined;
6226
- /**
6227
- * The candidate initialFields by entry name — binds `$fields`. Document
6228
- * references bind as their GDR envelopes (`$fields.<entry>.id` is the GDR
6229
- * URI). A surface that holds the caller's inputs (the Studio start control)
6230
- * supplies them; a per-doc picker does not.
6231
- */
6232
- fields?: Record<string, unknown> | undefined;
6233
- /**
6234
- * The WORKFLOW resource's dataset, for filters that read it (`*[...]` or a
6235
- * deref) — invoked lazily, only when `analyzeCondition` says the filter
6236
- * needs it. A slice is fine as long as it covers what filters scan;
6237
- * `definitionsForDocument` supplies EVERY instance of the tag, completed
6238
- * included — `*` carries no hidden predicate, so authors qualify in-flight
6239
- * themselves (`!defined(completedAt)`). Absent ⇒ a dataset-reading filter
6240
- * fails closed (this surface cannot see the dataset, so it cannot decide).
6241
- */
6242
- fetchDataset?: (() => Promise<unknown[]>) | undefined;
6243
- }
6675
+ export declare function startFieldsParam(args: {
6676
+ entryDefs: readonly FieldEntry[];
6677
+ initialFields: readonly InitialFieldValue[];
6678
+ }): Record<string, unknown>;
6244
6679
 
6245
6680
  export declare interface StartInstanceArgs {
6246
6681
  /** The definition's `name` — which deployed workflow to instantiate. */
@@ -6275,7 +6710,14 @@ export declare interface StartInstanceArgs {
6275
6710
  * entry, so all forms read back the same under `$context.<name>`.
6276
6711
  */
6277
6712
  context?: StartContext;
6278
- /** Caller-supplied id; auto-generated otherwise. */
6713
+ /**
6714
+ * Caller-supplied id; auto-generated otherwise (mint one with
6715
+ * `instanceDocId`). This is start's idempotency key: a retry carrying the
6716
+ * same id resumes the earlier attempt's outstanding commits instead of
6717
+ * creating a duplicate — the per-instance request ledger can't cover the
6718
+ * create (it lives on the instance document), so id identity is the rail.
6719
+ * Inputs bind on the create only; a resume never re-reads them.
6720
+ */
6279
6721
  instanceId?: string;
6280
6722
  /**
6281
6723
  * URL path on the supplied client where the engine fetches the
@@ -6329,6 +6771,55 @@ export declare function startKindOf(definition: {
6329
6771
  | undefined;
6330
6772
  }): StartKind;
6331
6773
 
6774
+ /**
6775
+ * Thrown by `startInstance` when the definition's `start.allowed` predicate
6776
+ * is not satisfied for the supplied `initialFields` — evaluated false, or
6777
+ * GROQ null ("can't decide"; fail-closed, `insight.outcome` tells the two
6778
+ * apart). Carries the full {@link ConditionInsight} so a consumer renders
6779
+ * the same explanation the `evaluateStart` pre-flight would have shown.
6780
+ * There is no override arg — like `fireAction`, you don't bypass a verdict,
6781
+ * you change what produces it. Advisory under races like every engine-side
6782
+ * check.
6783
+ */
6784
+ export declare class StartNotAllowedError extends WorkflowError<"start-not-allowed"> {
6785
+ readonly definition: string;
6786
+ readonly insight: ConditionInsight;
6787
+ constructor(args: { definition: string; insight: ConditionInsight });
6788
+ }
6789
+
6790
+ /**
6791
+ * Thrown when a start fails AFTER its create committed but BEFORE priming:
6792
+ * the instance document exists, unprimed (see {@link isUnprimed}) — a
6793
+ * genuinely failed start, but a RESUMABLE one. The error names the id so
6794
+ * every surface's failure message can point at the retry rail: retrying
6795
+ * `startInstance` with this `instanceId` finishes the start instead of
6796
+ * creating a duplicate; aborting the instance discards it.
6797
+ */
6798
+ export declare class StartNotPrimedError extends WorkflowError<"start-not-primed"> {
6799
+ readonly instanceId: string;
6800
+ constructor(args: { instanceId: string; cause: unknown });
6801
+ }
6802
+
6803
+ /**
6804
+ * Thrown when a start's first cascade fails AFTER the instance was created
6805
+ * and primed: the run exists — stage entered, activities active, guards
6806
+ * deployed — it just hasn't auto-advanced to a stable stage yet. Callers
6807
+ * must not present this as a failed start: the workflow IS running, and a
6808
+ * `tick` (or retrying `startInstance` with the same `instanceId`) resumes
6809
+ * the settling.
6810
+ */
6811
+ export declare class StartNotSettledError extends WorkflowError<"start-not-settled"> {
6812
+ readonly instanceId: string;
6813
+ /** The primed instance re-read after the cascade failure — absent when
6814
+ * that best-effort read also failed. */
6815
+ readonly instance?: WorkflowInstance;
6816
+ constructor(args: {
6817
+ instanceId: string;
6818
+ instance?: WorkflowInstance;
6819
+ cause: unknown;
6820
+ });
6821
+ }
6822
+
6332
6823
  /**
6333
6824
  * Why a definition can't be started standalone, or `undefined` when it can.
6334
6825
  * Advisory — the engine itself does not refuse ({@link isStartableDefinition})
@@ -6339,6 +6830,38 @@ export declare function startRefusal(definition: {
6339
6830
  lifecycle?: WorkflowLifecycle | undefined;
6340
6831
  }): string | undefined;
6341
6832
 
6833
+ /**
6834
+ * The caller-side half of the start contexts — everything the evaluating
6835
+ * surface knows that the definition doesn't. Every member is optional
6836
+ * because the surfaces genuinely differ (a pure consumer may hold no clock):
6837
+ * an absent binding evaluates each read of it to GROQ null, and where that
6838
+ * null lands decides the verdict — a predicate that can't decide without
6839
+ * the binding fails closed, while a count-of-matches clause over values
6840
+ * every row stores passes vacuously (in GROQ null equals only null, so the
6841
+ * unbound read matches no stored value). The vars themselves are
6842
+ * inventoried in `START_FILTER_VARS` / `START_ALLOWED_VARS`; the caller's
6843
+ * `$fields` map is NOT scope — `start.filter` never binds it, and
6844
+ * `explainStartAllowed` takes it as its own argument.
6845
+ */
6846
+ export declare interface StartScope {
6847
+ /** The engine's tag partition — binds `$tag`. */
6848
+ tag?: string | undefined;
6849
+ /** ISO clock reading — binds `$now`. */
6850
+ now?: string | undefined;
6851
+ /**
6852
+ * The WORKFLOW resource's dataset, for predicates that read it (`*[...]` or
6853
+ * a deref) — invoked lazily, only when `analyzeCondition` says the
6854
+ * predicate needs it. A slice is fine as long as it covers what predicates
6855
+ * scan; the engine verbs supply EVERY instance of the tag, completed
6856
+ * included — `*` carries no hidden predicate, so authors qualify in-flight
6857
+ * themselves (`!defined(completedAt)`). Absent ⇒ a dataset-reading filter
6858
+ * fails closed (this surface cannot see the dataset, so it cannot decide),
6859
+ * while a dataset-reading `allowed` THROWS — see
6860
+ * {@link explainStartAllowed}.
6861
+ */
6862
+ fetchDataset?: (() => Promise<unknown[]>) | undefined;
6863
+ }
6864
+
6342
6865
  /**
6343
6866
  * Declared editability of a field — the generic edit seam's gate. Default
6344
6867
  * (absent) is NOT editable: a field is op-only engine working memory unless the
@@ -7181,6 +7704,29 @@ declare type TransitionFields = {
7181
7704
  */
7182
7705
  export declare function tryParseGdr(uri: string): ParsedGdr | undefined;
7183
7706
 
7707
+ /**
7708
+ * The `$fields` entries a `start.allowed` predicate reads that `fields` does
7709
+ * not bind — the BINDABILITY rule every pre-flight surface applies before
7710
+ * trusting a verdict over possibly-incomplete inputs. GROQ equality against
7711
+ * a missing operand collapses to a definitive-looking answer (`null == x` is
7712
+ * false, not null), so a verdict that read an unbound entry is PROVISIONAL in
7713
+ * both directions: supplying the entry could overturn a refusal AND an
7714
+ * approval. Non-empty ⇒ report "can't decide yet" instead of the collapsed
7715
+ * verdict (the `evaluateStart` verb does; the Studio start control does).
7716
+ *
7717
+ * The GATE never consults this: at `startInstance` the supplied inputs are
7718
+ * final, so evaluating over genuine absence — including rules that WANT it,
7719
+ * like `!defined($fields.rush)` — is the correct semantics there.
7720
+ *
7721
+ * `fields` is a produced `$fields` map (`startFieldsParam`, or a surface's
7722
+ * seed map) — producers bind only own keys with real values, so key presence
7723
+ * IS the supplied-ness rule.
7724
+ */
7725
+ export declare function unboundAllowedReads(
7726
+ allowed: string,
7727
+ fields: Record<string, unknown>,
7728
+ ): string[];
7729
+
7184
7730
  /**
7185
7731
  * The diagnose idiom shared by every consumer: each unsatisfied exit
7186
7732
  * transition with its insight summary. Consumers shape the result (the CLI
@@ -7221,27 +7767,13 @@ export declare function validateDefinition(
7221
7767
  definition: WorkflowDefinition,
7222
7768
  ): void;
7223
7769
 
7224
- /**
7225
- * Engine-scope tag — the environment partition primitive.
7226
- *
7227
- * Every engine operates against exactly one `tag` ("test", "prod", …).
7228
- * The tag partitions definitions and instances within a single workflow
7229
- * resource: `deploy(def, tag: "test")` and `deploy(def, tag: "prod")` are
7230
- * separate deployed workflows with independent lifecycles, and every read
7231
- * is scoped to one tag so test runs never surface in prod.
7232
- *
7233
- * The tag must be Sanity-ID-compatible since it's joined into IDs with
7234
- * `.` — ASCII lowercase + digits + dashes, no leading dash, no dots. It
7235
- * is also the ID prefix for every definition/instance the engine writes.
7236
- *
7237
- * There is no default. The tag selects which environment the engine reads
7238
- * and writes, and the engine enforces nothing — so the partition is the
7239
- * only thing keeping test runs out of prod. Every entry point
7240
- * (`createEngine`, the CLI, the MCP server) requires it explicitly and
7241
- * fails when it's absent rather than guessing an environment.
7242
- */
7243
7770
  export declare function validateTag(tag: string): void;
7244
7771
 
7772
+ declare interface ValidationIssue {
7773
+ path: ReadonlyArray<PropertyKey>;
7774
+ message: string;
7775
+ }
7776
+
7245
7777
  export declare type ValueExpr = ValueExprInternal;
7246
7778
 
7247
7779
  declare type ValueExprInternal =
@@ -7386,18 +7918,36 @@ export declare const workflow: {
7386
7918
  /**
7387
7919
  * Spawn a new workflow instance from a deployed definition.
7388
7920
  *
7389
- * Throws only on its own input contract a missing `required` input
7390
- * ({@link RequiredFieldNotProvidedError}) or a value that doesn't fit its
7391
- * declared kind. It does NOT evaluate the definition's `start.filter`: that
7392
- * is a read-side visibility rule (see `definitionsForDocument`), so a
7393
- * caller who supplies everything needed starts, full stop.
7921
+ * Two gates run before anything is written, in order: the required-input
7922
+ * check a missing `required` input is a CALLER mistake
7923
+ * ({@link RequiredFieldNotProvidedError}) then the definition's
7924
+ * `start.allowed` permission predicate, whose false/GROQ-null VERDICT
7925
+ * throws {@link StartNotAllowedError} (declaring the expression is the
7926
+ * opt-in; there is no override arg — pre-flight with `evaluateStart`).
7927
+ * Per-value SHAPE validation fires during field resolution, after the
7928
+ * verdict but still before any write. It does NOT evaluate `start.filter`:
7929
+ * that is a read-side visibility rule (see `definitionsForDocument`).
7394
7930
  *
7395
7931
  * Pins the snapshot at start-time, seeds the `context` bag, and enters
7396
7932
  * the initial stage — fields resolve and every in-scope activity is
7397
7933
  * born active. Then cascades until stable, so the initial stage's
7398
- * `when: 'true'` triggers have fired by the time this returns. Starting
7399
- * always changes state (`changed: true`) — `cascaded` reports how far
7400
- * the new instance auto-advanced.
7934
+ * `when: 'true'` triggers have fired by the time this returns.
7935
+ *
7936
+ * Start is three commits — create, prime, first cascade — and a supplied
7937
+ * `instanceId` is its idempotency key across them. When that id already
7938
+ * exists under this tag for the same start, the call RESUMES: input gates
7939
+ * and field resolution are skipped (those values were pinned at create)
7940
+ * and the outstanding commits run — the retry path for a start that
7941
+ * failed after its create landed (see `isUnprimed`). Reusing an id for a
7942
+ * DIFFERENT start (definition or explicit version mismatch) — or for an
7943
+ * unfinished start that was aborted (a discarded start) — throws
7944
+ * {@link ContractViolationError}. The mid-sequence failures are typed and
7945
+ * carry the retry id: a prime failure after the create committed throws
7946
+ * `StartNotPrimedError` (failed but resumable), and a cascade failure
7947
+ * after a successful prime throws `StartNotSettledError` — the run exists
7948
+ * by then and must not be reported as a failed start. `changed` is `true`
7949
+ * on a fresh start and rev-derived on a resume; `cascaded` reports how far
7950
+ * the instance auto-advanced.
7401
7951
  */
7402
7952
  startInstance: (
7403
7953
  rawArgs: Clocked<Telemetered<StartInstanceArgs & EngineScopeArgs>>,
@@ -7519,8 +8069,11 @@ export declare const workflow: {
7519
8069
  * Hydrates the instance's snapshot (instance + ancestors + every doc
7520
8070
  * declared by a `doc.ref` / `doc.refs` entry in scope), then
7521
8071
  * evaluates the supplied GROQ in groq-js against that dataset. The
7522
- * rendered scope's instance-derived vars ({@link FILTER_SCOPE_VARS}) are
7523
- * auto-bound ids in GDR URI form to match the snapshot's keying.
8072
+ * caller-free rendered scope cascade gates evaluate in is auto-bound —
8073
+ * the instance-derived vars ({@link FILTER_SCOPE_VARS}) with the open
8074
+ * stage's overlay merged into `$fields`, `$assigned` at its caller-free
8075
+ * `false`, plus the author's pre-evaluated `$<predicate>` booleans —
8076
+ * ids in GDR URI form to match the snapshot's keying.
7524
8077
  *
7525
8078
  * Use when an external consumer (a UI, a debug pane, a test) wants
7526
8079
  * to ask "what does the engine see for this workflow right now?"
@@ -7631,10 +8184,10 @@ export declare const workflow: {
7631
8184
  * visible to the engine's tag and filters it through the derivation
7632
8185
  * ({@link applicableDefinitions}): startable ∧ a required subject entry
7633
8186
  * accepts the doc's `_type` ∧ `start.filter` passes — evaluated in the
7634
- * start-filter context with `$tag`/`$definition`/`$now` bound and the
7635
- * tag's instance slice (completed included) backing dataset reads. A picker holds no
7636
- * `initialFields`, so `$fields` stays unbound here and a filter reading it
7637
- * fails closed (the definition is not surfaced).
8187
+ * browse-time-pure start-filter context with `$tag`/`$definition`/`$now`
8188
+ * bound and the tag's instance slice (completed included) backing dataset
8189
+ * reads. `start.allowed` never participates permission is a start-time
8190
+ * question; pre-flight it with {@link workflow.evaluateStart}.
7638
8191
  *
7639
8192
  * Takes the LOADED candidate document, not a ref — applicability evaluates
7640
8193
  * its content, under whatever perspective the caller read it with. Surfaces
@@ -7644,6 +8197,26 @@ export declare const workflow: {
7644
8197
  definitionsForDocument: (
7645
8198
  rawArgs: Clocked<DefinitionsForDocumentArgs & EngineScopeArgs>,
7646
8199
  ) => Promise<DeployedDefinition[]>;
8200
+ /**
8201
+ * Pre-flight the start gates for a definition + candidate `initialFields` —
8202
+ * the read `startInstance` enforces, as a {@link StartEvaluation} a surface
8203
+ * can render: `missingRequired` mirrors the input contract
8204
+ * ({@link RequiredFieldNotProvidedError}'s rows), and `allowed` /
8205
+ * `outcome` / `insight` carry the `start.allowed` verdict with its atom
8206
+ * breakdown — disable the start affordance on a definitive `false` and say
8207
+ * why. BINDABILITY-AWARE for partial mid-form inputs: when the predicate
8208
+ * reads an entry `initialFields` doesn't supply, `outcome` is
8209
+ * `'unevaluable'` and `unboundReads` names the entries ("fill these to
8210
+ * decide") instead of the collapsed answer GROQ equality would give —
8211
+ * this is the ONE deliberate divergence from the gate, where absence is
8212
+ * final, not provisional (a rule like `!defined($fields.rush)` genuinely
8213
+ * passes there when `rush` is absent). A definition with no
8214
+ * `start.allowed` is vacuously allowed, exactly like the verb. Pure read;
8215
+ * advisory under races — the enforcement moment is `startInstance` itself.
8216
+ */
8217
+ evaluateStart: (
8218
+ rawArgs: Clocked<EvaluateStartArgs & EngineScopeArgs>,
8219
+ ) => Promise<StartEvaluation>;
7647
8220
  /**
7648
8221
  * Permission helpers — Sanity ACL grants evaluated against documents
7649
8222
  * via GROQ. Used by `workflow.evaluate` to soft-gate actions when the
@@ -7705,6 +8278,12 @@ export declare interface WorkflowAdminOverrideData extends InstanceScopedEventDa
7705
8278
  changed: boolean;
7706
8279
  }
7707
8280
 
8281
+ /** The whole-definition rollup: every stage must progress, so the workflow
8282
+ * verdict conjoins the stage verdicts. */
8283
+ export declare interface WorkflowAutonomy extends AutonomyAnswer {
8284
+ stages: StageAutonomy[];
8285
+ }
8286
+
7708
8287
  export declare interface WorkflowClient {
7709
8288
  fetch: <T = unknown>(
7710
8289
  query: string,
@@ -8399,6 +8978,7 @@ export declare abstract class WorkflowError<
8399
8978
 
8400
8979
  export declare type WorkflowErrorKind =
8401
8980
  | "action-disabled"
8981
+ | "start-not-allowed"
8402
8982
  | "edit-field-denied"
8403
8983
  | "mutation-guard-denied"
8404
8984
  | "action-params-invalid"
@@ -8407,12 +8987,16 @@ export declare type WorkflowErrorKind =
8407
8987
  | "required-field-not-provided"
8408
8988
  | "workflow-state-diverged"
8409
8989
  | "partial-guard-deploy"
8990
+ | "start-not-primed"
8991
+ | "start-not-settled"
8410
8992
  | "concurrent-fire-action"
8411
8993
  | "concurrent-edit-field"
8412
8994
  | "concurrent-complete-effect"
8413
8995
  | "cascade-limit"
8414
8996
  | "field-value-shape"
8997
+ | "ref-resource-undeclared"
8415
8998
  | "model-version-ahead"
8999
+ | "persisted-doc-shape"
8416
9000
  | "instance-not-found"
8417
9001
  | "definition-not-found"
8418
9002
  | "definition-in-use"
@@ -8443,6 +9027,14 @@ export declare interface WorkflowEvaluation {
8443
9027
  * Empty when no condition reads a field.
8444
9028
  */
8445
9029
  fieldInsights: FieldInsight[];
9030
+ /**
9031
+ * The whole definition's causal-autonomy rollup: per activity, per stage,
9032
+ * and workflow-wide — will each level resolve without a caller, and what
9033
+ * does it wait on. Static (no instance state), stamped here so every
9034
+ * consumer reads one derivation. Spawn children are not resolved at
9035
+ * evaluation time, so their legs report `conditional`.
9036
+ */
9037
+ autonomy: WorkflowAutonomy;
8446
9038
  }
8447
9039
 
8448
9040
  export declare interface WorkflowFetchOptions {
@@ -8472,6 +9064,12 @@ export declare interface WorkflowFieldEditedData extends InstanceScopedEventData
8472
9064
 
8473
9065
  /** Type-mirror of {@link workflowFields}, parameterised over field/stage/start. */
8474
9066
  declare type WorkflowFields<TField, TStage, TStart> = {
9067
+ /**
9068
+ * Lake-id-segment grammar (`^[a-z0-9][a-z0-9-]*$`, deploy-enforced): the
9069
+ * name interpolates into every deployed document id
9070
+ * (`<tag>.<name>.v<version>`). Stable identity — instances pin to it,
9071
+ * subworkflows resolve by it.
9072
+ */
8475
9073
  name: string;
8476
9074
  title: string;
8477
9075
  description?: string | undefined;
@@ -8606,6 +9204,13 @@ export declare interface WorkflowInstance extends SanityDocument {
8606
9204
 
8607
9205
  export declare const WorkflowInstanceAborted: WorkflowTelemetryEvent<WorkflowAdminOverrideData>;
8608
9206
 
9207
+ /**
9208
+ * The persisted instance document, root to leaf. Reused verbatim by every
9209
+ * engine read boundary through {@link parseInstanceDocument}; the
9210
+ * model-surface gate derives its shape ledger from this schema.
9211
+ */
9212
+ export declare const WorkflowInstanceSchema: v.GenericSchema<WorkflowInstance>;
9213
+
8609
9214
  export declare const WorkflowInstanceStarted: WorkflowTelemetryEvent<WorkflowInstanceStartedData>;
8610
9215
 
8611
9216
  export declare interface WorkflowInstanceStartedData extends InstanceScopedEventData {
@@ -8800,10 +9405,11 @@ export declare interface WorkflowTransaction {
8800
9405
  /**
8801
9406
  * Queue a document delete. Deliberately a transaction-only capability —
8802
9407
  * the top-level client surface stays delete-free so no engine code path
8803
- * can casually remove documents; the sole consumer is `deleteDefinition`
8804
- * housekeeping (definition docs + orphaned guard docs). Both the real
8805
- * `@sanity/client` Transaction and the test fake's TransactionHandle
8806
- * carry this shape.
9408
+ * can casually remove documents; engine-side the sole consumer is
9409
+ * `deleteDefinition` housekeeping (definition docs + orphaned guard docs),
9410
+ * and outside the engine the CLI's `nuke` reset deletes raw engine-owned
9411
+ * docs through it. Both the real `@sanity/client` Transaction and the test
9412
+ * fake's TransactionHandle carry this shape.
8807
9413
  */
8808
9414
  delete: (id: string) => WorkflowTransaction;
8809
9415
  /**