@sanity/workflow-engine 0.20.0 → 0.21.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
@@ -508,15 +508,15 @@ export declare interface ActivityEvaluation {
508
508
  */
509
509
  scopedOut: boolean;
510
510
  /**
511
- * The activity's unmet {@link Activity.requirements}, by name — present iff at least
511
+ * The activity's unmet {@link Activity.requirements}, with authored display copy — present iff at least
512
512
  * one is unmet. The activity's own readiness summary: a consumer can explain why
513
513
  * the whole activity is gated without inspecting each action (every action also
514
514
  * carries the matching `requirements-unmet` `disabledReason`).
515
515
  */
516
- unmetRequirements?: string[];
516
+ unmetRequirements?: RequirementDescriptor[];
517
517
  /** Derived state per declared requirement, keyed by requirement name.
518
518
  * Present iff the activity declares requirements; `unmetRequirements`
519
- * is exactly the keys whose insight isn't satisfied. */
519
+ * contains descriptors for exactly the requirements whose insight isn't satisfied. */
520
520
  requirementInsights?: Record<string, ConditionInsight>;
521
521
  /** Derived state of the activity's `filter` existence gate. Present iff
522
522
  * declared. Advisory read: the engine's stage entry owns the gate itself. */
@@ -533,7 +533,7 @@ declare type ActivityFields<TField, TAction, TTarget, TGroup> = {
533
533
  group?: TGroup | undefined;
534
534
  target?: TTarget | undefined;
535
535
  filter?: string | undefined;
536
- requirements?: Record<string, string> | undefined;
536
+ requirements?: GroqRequirement[] | undefined;
537
537
  actions?: TAction[] | undefined;
538
538
  fields?: TField[] | undefined;
539
539
  };
@@ -546,20 +546,31 @@ export declare type ActivityStatus = (typeof ACTIVITY_STATUSES)[number];
546
546
 
547
547
  /**
548
548
  * Who is acting — advisory provenance, not an authenticated principal. The
549
- * engine always resolves it from the client's token (`/users/me`); there is
550
- * no way to pass or synthesize one. The stamp is still advisory: nothing
551
- * re-verifies it at read time, and only the lake's own token identity is
552
- * authenticated — hard enforcement lives there.
549
+ * engine always resolves it from the client's token; there is no way to pass
550
+ * or synthesize one. The stamp is still advisory: nothing re-verifies it at
551
+ * read time, and only the lake's own token identity is authenticated — hard
552
+ * enforcement lives there.
553
+ *
554
+ * `id` is the account-global user id (the global API host's `/users/me`) —
555
+ * the one identity namespace that is stable across projects and org-level
556
+ * resources. Robot tokens carry a single universal id, so for them `id`
557
+ * equals what every host returns. Instances written before ids were
558
+ * namespace-classified carry the workflow resource's local principal id in
559
+ * `id` instead; readers interpret those through the prefix classifier in
560
+ * `core/identity.ts` with the document's home resource as the implied
561
+ * scope.
553
562
  */
554
563
  export declare interface Actor {
555
564
  kind: ActorKind;
556
565
  id: string;
557
566
  /**
558
567
  * Free-form role names — typically a copy of Sanity's `user.roles[].name`
559
- * for the token behind the call. The engine treats these as opaque
560
- * strings, matched by literal membership: `Action.roles` and `assignees`
561
- * roles OR-match against this list. There is no actor-side wildcard — a
562
- * `"*"` here is just a literal string and matches nothing on its own.
568
+ * for the token behind the call, as the workflow resource's host reports
569
+ * them (project roles for dataset resources). The engine treats these as
570
+ * opaque strings, matched by literal membership: `Action.roles` and
571
+ * `assignees` roles OR-match against this list. There is no actor-side
572
+ * wildcard — a `"*"` here is just a literal string and matches nothing on
573
+ * its own.
563
574
  *
564
575
  * To let one role satisfy a gate it doesn't literally name — e.g. an
565
576
  * `administrator` satisfying a narrower gate, or one deployment's role
@@ -597,6 +608,9 @@ export declare type ActorResolution<User> =
597
608
 
598
609
  export { analyzeCondition };
599
610
 
611
+ /** Lake `identity()` sentinel for unauthenticated callers. */
612
+ export declare const ANONYMOUS_IDENTITY = "<anonymous>";
613
+
600
614
  /** The definition surface the start contexts read — structural, so authored,
601
615
  * stored, and deployed definition shapes all fit. `name` binds
602
616
  * `$definition` and names the definition in a broken-predicate error. */
@@ -608,7 +622,7 @@ export declare interface ApplicabilitySource {
608
622
  | {
609
623
  kind?: StartKind | undefined;
610
624
  filter?: string | undefined;
611
- allowed?: string | undefined;
625
+ requirements?: StartRequirement[] | undefined;
612
626
  }
613
627
  | undefined;
614
628
  }
@@ -1606,6 +1620,25 @@ declare type ClaimField = {
1606
1620
  group?: GroupMembership | undefined;
1607
1621
  };
1608
1622
 
1623
+ export declare interface ClassifiedPrincipal {
1624
+ namespace: PrincipalNamespace;
1625
+ /**
1626
+ * The account-global id when the string itself carries it: the id verbatim
1627
+ * for `global` and `robot` (a robot's single id IS its universal identity),
1628
+ * the embedded global id for `e-` project ids. Absent for plain `project`
1629
+ * ids (resolving those needs the project-users directory), sentinels, and
1630
+ * unknowns.
1631
+ */
1632
+ globalId?: string;
1633
+ }
1634
+
1635
+ /**
1636
+ * Classify a bare principal-id string by its namespace. Total — never
1637
+ * throws; ids that fit no known form come back `unknown` so the caller
1638
+ * decides how loud to be.
1639
+ */
1640
+ export declare function classifyPrincipalId(id: string): ClassifiedPrincipal;
1641
+
1609
1642
  /**
1610
1643
  * The `@sanity/client`-config fragment that addresses a {@link WorkflowResource}
1611
1644
  * — the one home for the dataset-vs-resource branch every client-building host
@@ -1634,6 +1667,13 @@ declare type ClientForGdr = (parsed: ParsedGdr) => WorkflowClient;
1634
1667
  /** Native project-user response returned by Sanity's project API. */
1635
1668
  export declare interface ClientProjectUser {
1636
1669
  readonly id: string;
1670
+ /**
1671
+ * The person's account-global user id — the canonical identity every
1672
+ * workflow value stores. This is the sanctioned bridge between a
1673
+ * project's local principal namespace and the global one; surfaces use
1674
+ * it to emit assignment values from project-member displays.
1675
+ */
1676
+ readonly sanityUserId?: string;
1637
1677
  readonly displayName?: string;
1638
1678
  readonly email?: string;
1639
1679
  readonly imageUrl?: string | null;
@@ -1966,14 +2006,15 @@ export declare interface ConditionVar {
1966
2006
  * the rest evaluate to `undefined` — and deploy rejects them at these
1967
2007
  * sites; a cascade-fired action's per-token gate is `roles`, never its
1968
2008
  * conditions.
1969
- * 3. **The start contexts** — a definition's `start.filter` and
1970
- * `start.allowed` evaluate against a CANDIDATE (no instance exists yet):
1971
- * `*[...]` reads the WORKFLOW resource's dataset and none of the rendered
2009
+ * 3. **The start contexts** — a definition's `start.filter` and start GROQ
2010
+ * requirements evaluate against a CANDIDATE (no instance exists yet):
2011
+ * `*[...]` reads the engine-owned `{definition, subject, completedAt}`
2012
+ * projection of the tag's instances and none of the rendered
1972
2013
  * condition vars exist. The two split on what a surface can know:
1973
2014
  * `filter` is browse-time-pure (candidate document as root,
1974
2015
  * {@link START_FILTER_VARS} — no `$fields`, which cannot exist before
1975
- * inputs do), `allowed` is the start-time permission predicate
1976
- * ({@link START_ALLOWED_VARS} — `$fields` bound, never a root).
2016
+ * inputs do), a `groq` requirement is the start-time readiness predicate
2017
+ * ({@link START_REQUIREMENT_VARS} — `$fields` bound, never a root).
1977
2018
  * 4. **Guard predicates** — NOT conditions. A lake mutation guard's
1978
2019
  * `predicate` is groq-js **delta-mode** GROQ over a document mutation:
1979
2020
  * `before()`/`after()`/`identity()` are dialect natives, and the wire
@@ -2298,6 +2339,24 @@ export declare const DATA_MODEL_CHANGES: readonly [
2298
2339
  applicability: "detectable";
2299
2340
  summary: "Pending-effect claims carry an exact-claim token gating mid-dispatch state reports.";
2300
2341
  }>,
2342
+ Readonly<{
2343
+ id: "classified-principal-ids";
2344
+ introducedInModel: 4;
2345
+ minReaderModel: 4;
2346
+ documentTypes: readonly ["instance"];
2347
+ compatibility: "reader-floor";
2348
+ applicability: "unconditional";
2349
+ summary: string;
2350
+ }>,
2351
+ Readonly<{
2352
+ id: "readiness-requirements";
2353
+ introducedInModel: 4;
2354
+ minReaderModel: 4;
2355
+ documentTypes: readonly ["definition"];
2356
+ compatibility: "reader-floor";
2357
+ applicability: "detectable";
2358
+ summary: "Start and activity readiness use named polymorphic requirement arrays.";
2359
+ }>,
2301
2360
  ];
2302
2361
 
2303
2362
  /**
@@ -2307,7 +2366,7 @@ export declare const DATA_MODEL_CHANGES: readonly [
2307
2366
  * carry a lower floor. Raising this maximum is a declared, DATAMODEL.md-logged
2308
2367
  * decision that requires readers-first fleet sequencing.
2309
2368
  */
2310
- export declare const DATA_MODEL_MIN_READER = 2;
2369
+ export declare const DATA_MODEL_MIN_READER = 4;
2311
2370
 
2312
2371
  /**
2313
2372
  * The engine's persisted data-model version — the provenance half of
@@ -2322,7 +2381,7 @@ export declare const DATA_MODEL_MIN_READER = 2;
2322
2381
  * job. Declare every bump in `DATAMODEL.md`; the model-surface snapshot test
2323
2382
  * keeps undeclared drift red.
2324
2383
  */
2325
- export declare const DATA_MODEL_VERSION = 3;
2384
+ export declare const DATA_MODEL_VERSION = 4;
2326
2385
 
2327
2386
  export declare interface DataModelChange {
2328
2387
  readonly id: string;
@@ -2511,10 +2570,6 @@ export declare interface DefinitionsForDocumentArgs {
2511
2570
  * perspective the caller loaded the document with.
2512
2571
  */
2513
2572
  document: CandidateDocument;
2514
- /** Resource-qualified identity of `document`. Required when an applicable
2515
- * definition's `start.filter` reads `$subjectHasInFlightInstance`; the
2516
- * loaded value's bare `_id` cannot distinguish resources. */
2517
- subject?: GdrUri;
2518
2573
  }
2519
2574
 
2520
2575
  /** A definition-level site address: every runtime {@link InsightSite}, plus
@@ -2535,9 +2590,8 @@ export declare type DefinitionSiteAddress =
2535
2590
 
2536
2591
  /**
2537
2592
  * GROQ listing EVERY deployed {@link WORKFLOW_DEFINITION_TYPE} visible to the
2538
- * caller's tag, grouped by name. `versionOrder` picks the in-group direction:
2539
- * `'desc'` puts each name's latest first (discovery keeps the head per name),
2540
- * `'asc'` walks history oldest-first (handler verification reads them all).
2593
+ * caller's tag, grouped by name. `versionOrder` picks the in-group direction;
2594
+ * handler verification uses oldest-first order while inspecting every version.
2541
2595
  *
2542
2596
  * Params: `$tag`.
2543
2597
  */
@@ -3041,13 +3095,13 @@ export declare type DisabledReason =
3041
3095
  /**
3042
3096
  * The activity's declared {@link Activity.requirements} aren't all satisfied —
3043
3097
  * the readiness axis. The activity is visible and the actor authorized, but
3044
- * its own preconditions don't hold yet. `unmetRequirements` names the
3045
- * failing keys so a consumer can disable the affirmative control and say
3098
+ * its own preconditions don't hold yet. `unmetRequirements` carries the
3099
+ * authored descriptors so a consumer can disable the affirmative control and say
3046
3100
  * which precondition is outstanding. Distinct from `filter-failed`
3047
3101
  * (visibility/authorization) and `mutation-guard-denied` (content-write).
3048
3102
  */
3049
3103
  kind: "requirements-unmet";
3050
- unmetRequirements: string[];
3104
+ unmetRequirements: RequirementDescriptor[];
3051
3105
  }
3052
3106
  | {
3053
3107
  /**
@@ -3132,7 +3186,8 @@ export declare interface DocumentActionDenialsArgs {
3132
3186
  };
3133
3187
  action: MutationGuardAction;
3134
3188
  guards: readonly MutationGuardDoc[];
3135
- /** Actor id, resolved as `identity()` in predicates. */
3189
+ /** The caller's principal id in the guarded resource's own namespace,
3190
+ * resolved as `identity()` in predicates. */
3136
3191
  identity?: string;
3137
3192
  }
3138
3193
 
@@ -3147,10 +3202,8 @@ declare type DocumentEnvelopeKey =
3147
3202
  | "minReaderModel";
3148
3203
 
3149
3204
  /**
3150
- * The document-prefilter GROQ arm — matches instances that MAY reference any
3151
- * of `documents`. A deliberate lake-side SUPERSET (it also matches
3152
- * exited-stage refs); narrow fetched rows to the exact watch-set with
3153
- * {@link instanceWatchesDocument}. Binds `$documents` + `$bareIds` into
3205
+ * The document GROQ arm — matches instances whose reactive watch-set
3206
+ * references any of `documents`. Binds `$documents` + `$bareIds` into
3154
3207
  * `params`. Exposed for list builders that compose their own conditions
3155
3208
  * (e.g. the CLI's cross-partition list); {@link instancesQuery} consumers get
3156
3209
  * it via the `document`/`documents` filter fields instead.
@@ -3731,6 +3784,9 @@ export declare interface Engine {
3731
3784
  setStage: (args: SetStageArgs) => Promise<OperationResult>;
3732
3785
  /** Admin override — hard-stop an in-flight instance where it stands. */
3733
3786
  abortInstance: (args: AbortInstanceArgs) => Promise<OperationResult>;
3787
+ /** Admin override — reset a failed/terminal activity in the current stage
3788
+ * back to `active` (re-run) or `skipped` (bypass), then cascade. */
3789
+ resetActivity: (args: ResetActivityArgs) => Promise<OperationResult>;
3734
3790
  /** Admin override — remove a deployed definition (instances are only ever aborted, never deleted). */
3735
3791
  deleteDefinition: (
3736
3792
  args: DeleteDefinitionArgs,
@@ -3790,12 +3846,13 @@ export declare interface Engine {
3790
3846
  args: DefinitionsForDocumentArgs,
3791
3847
  ) => Promise<DeployedDefinition[]>;
3792
3848
  /** Pre-flight `startInstance`'s gates for a definition + the
3793
- * `initialFields` gathered so far: structurally invalid rows, the
3794
- * `start.allowed` verdict with its insight, and still-missing required
3795
- * inputs. `allowed` is the overall startability signal; `outcome` describes
3796
- * only the condition. Bindability-aware — a
3797
- * predicate reading a not-yet-supplied entry reports `'unevaluable'`
3798
- * with the entries named in `unboundReads`, never a collapsed verdict.
3849
+ * `initialFields` gathered so far: structurally invalid rows, every
3850
+ * requirement's ordered descriptor/outcome/insight, and still-missing
3851
+ * required inputs. `allowed` is the overall startability signal; `outcome`
3852
+ * aggregates the requirements. Bindability-aware — a requirement reading
3853
+ * a not-yet-supplied entry (including `singleSubject`'s implicit subject
3854
+ * read) reports `'unevaluable'` with the entries named
3855
+ * in `unboundReads`, never a collapsed verdict.
3799
3856
  * Pure read; the enforcement moment is `startInstance` itself. */
3800
3857
  evaluateStart: (args: EvaluateStartArgs) => Promise<StartEvaluation>;
3801
3858
  /** GROQ query against the engine's workflow resource. `$tag`
@@ -3967,6 +4024,13 @@ export declare interface EvaluateFromSnapshotArgs {
3967
4024
  instance: WorkflowInstance;
3968
4025
  definition: WorkflowDefinition;
3969
4026
  actor: Actor;
4027
+ /**
4028
+ * The actor's principal id in the workflow resource's own identity
4029
+ * namespace — the comparand for everything the resource's lake evaluates
4030
+ * (`identity()` in guard predicates, the anchor ACL's grant filters).
4031
+ * Omitted when it equals {@link Actor.id} (org-level resources, robots).
4032
+ */
4033
+ localPrincipalId?: string;
3970
4034
  /**
3971
4035
  * Resolved grants for the actor. Omit to leave the rendered `$can`
3972
4036
  * undefined (conditions referencing it fail closed — the real lake
@@ -3996,14 +4060,16 @@ export declare interface EvaluateFromSnapshotArgs {
3996
4060
  */
3997
4061
  guards?: readonly MutationGuardDoc[];
3998
4062
  /**
3999
- * The actor's ACL grants per FOREIGN subject resource, keyed by
4000
- * resource-shaped GDR (`<type>:<id>`) feeds the subject-write forecast on
4001
- * effects-bearing actions ({@link SubjectPermissionDenial}). Omit, or omit a
4002
- * resource, to skip that resource's forecast (degrade open the subject's
4003
- * lake still enforces). {@link evaluateInstance} resolves this through each
4063
+ * Per-FOREIGN-subject-resource access — the actor's ACL grants PLUS the
4064
+ * actor's principal id in that resource's own namespace — keyed by
4065
+ * resource-shaped GDR (`<type>:<id>`); feeds the subject-write forecast on
4066
+ * effects-bearing actions ({@link SubjectPermissionDenial}). A resource
4067
+ * appears only when both halves resolved; omit it (or the whole map) to
4068
+ * skip that resource's forecast (degrade open — the subject's lake still
4069
+ * enforces). {@link evaluateInstance} resolves this through each
4004
4070
  * resource's own client via {@link subjectResourceGrants}.
4005
4071
  */
4006
- resourceGrants?: ReadonlyMap<string, Grant[]>;
4072
+ resourceGrants?: ReadonlyMap<string, SubjectResourceAccess>;
4007
4073
  }
4008
4074
 
4009
4075
  /**
@@ -4040,15 +4106,13 @@ export declare interface EvaluateStartArgs {
4040
4106
  * be absent — a root read is then GROQ null, fail-closed or vacuous-pass by
4041
4107
  * shape) as the GROQ root, the {@link StartScope} bindings plus
4042
4108
  * `$definition`, and — when `analyzeCondition` says the filter reads the
4043
- * dataset or the filter reads `$subjectHasInFlightInstance` the scope's
4044
- * fetched slice as `*`. Cheap pure evaluation otherwise: no I/O rides a
4109
+ * dataset the scope's fetched slice as `*`. Cheap pure evaluation otherwise: no I/O rides a
4045
4110
  * filter that needs neither. GROQ null ("can't decide") is `false` — every consumer of
4046
4111
  * this verdict fails closed; a parse/evaluation THROW is a malformed
4047
4112
  * predicate, not an unevaluable one — rethrown loud, naming the definition,
4048
4113
  * so every read surface that evaluates the filter reports the same context.
4049
- * A caller that omits the prospective subject required by the synthetic
4050
- * variable also throws as a scope-contract error. A failed slice FETCH
4051
- * propagates as itself: transport trouble, never definition blame.
4114
+ * A failed slice FETCH propagates as itself: transport trouble, never
4115
+ * definition blame.
4052
4116
  */
4053
4117
  export declare function evaluateStartFilter(args: {
4054
4118
  filter: string;
@@ -4165,7 +4229,7 @@ export { explainCondition };
4165
4229
  export { ExplainConditionArgs };
4166
4230
 
4167
4231
  /**
4168
- * Explain one `start.allowed` in the start-allowed context: no root (the
4232
+ * Explain one start GROQ requirement in the start-requirement context: no root (the
4169
4233
  * subject rides `$fields.<entry>.id` — deploy rejects a root read), the
4170
4234
  * {@link StartScope} bindings plus `$definition` and the caller's `$fields`
4171
4235
  * map, and the scope's fetched slice as `*` when the predicate reads the
@@ -4183,8 +4247,8 @@ export { ExplainConditionArgs };
4183
4247
  * bug as an author verdict. Parse/evaluation throws rethrow naming the
4184
4248
  * definition, like the filter's; a failed slice FETCH propagates as itself.
4185
4249
  */
4186
- export declare function explainStartAllowed(args: {
4187
- allowed: string;
4250
+ export declare function explainStartRequirement(args: {
4251
+ query: string;
4188
4252
  definition: Pick<ApplicabilitySource, "name" | "fields">;
4189
4253
  /** The caller's input entries as a `$fields` map — the engine verbs build
4190
4254
  * it with `startFieldsParam` (field resolution's projection), so the
@@ -4711,6 +4775,11 @@ declare function grantsPermissionOn(args: {
4711
4775
  userId?: string;
4712
4776
  }): Promise<boolean>;
4713
4777
 
4778
+ export declare type GroqRequirement = RequirementBase & {
4779
+ type: "groq";
4780
+ query: string;
4781
+ };
4782
+
4714
4783
  /** Type-mirror of {@link GroupSchema} — one declared group. */
4715
4784
  export declare type Group = {
4716
4785
  name: string;
@@ -5096,6 +5165,11 @@ export { guillemets };
5096
5165
  */
5097
5166
  export declare function hashDefinitionContent(def: WorkflowDefinition): string;
5098
5167
 
5168
+ /** Whether the definition declares a `singleSubject` start requirement. */
5169
+ export declare function hasSingleSubjectRequirement(
5170
+ definition: Pick<ApplicabilitySource, "start">,
5171
+ ): boolean;
5172
+
5099
5173
  /**
5100
5174
  * History-entry `_type` discriminators. Order roughly matches the
5101
5175
  * order a typical run produces them in a single fire-action commit.
@@ -5683,17 +5757,16 @@ export declare function instancesQuery(args: {
5683
5757
  export declare interface InstancesQueryFilter {
5684
5758
  /**
5685
5759
  * Only instances that may reference this document (resource-qualified GDR
5686
- * URI). This is a lake-side PREFILTER a deliberate superset that also
5687
- * matches exited-stage refs; narrow the fetched rows to the exact reactive
5688
- * watch-set with {@link instanceWatchesDocument}.
5760
+ * URI). The lake-side predicate matches the reactive watch-set's
5761
+ * workflow, open-stage, activity, ancestor, live-child, and own-id references.
5689
5762
  */
5690
5763
  document?: GdrUri;
5691
5764
  /**
5692
5765
  * The multi-document form of {@link InstancesQueryFilter.document}: one
5693
- * prefilter matching instances that may reference ANY of the given docs,
5766
+ * predicate matching instances that reference ANY of the given docs,
5694
5767
  * for consumers discovering instances across many open documents at once.
5695
- * Merged with `document` when both are set; per-document narrowing is the
5696
- * caller's ({@link instanceWatchesDocument} again). A DEFINED-but-EMPTY
5768
+ * Merged with `document` when both are set. Callers may defensively recheck
5769
+ * with {@link instanceWatchesDocument}. A DEFINED-but-EMPTY
5697
5770
  * array matches nothing (the GROQ-natural reading of membership in an
5698
5771
  * empty set) — omit the field for the unconstrained every-in-flight read.
5699
5772
  */
@@ -5952,12 +6025,35 @@ export declare function lakeGuardId(args: {
5952
6025
  }): string;
5953
6026
 
5954
6027
  /**
5955
- * The latest deployed version of each definition name the reduction every
5956
- * "what would a start load?" consumer applies over a
5957
- * {@link definitionsListGroq} read (GROQ has no group-by, so it happens
5958
- * client-side). Order-independent: the highest version per name wins under
5959
- * either `versionOrder`; names keep first-appearance order, so a
5960
- * name-ascending read stays name-ascending.
6028
+ * The id a lake-facing check binds for an actor: the resource-local
6029
+ * principal id when the namespaces diverge, otherwise the actor's own
6030
+ * (account-global) id. Every advisory that must agree with the lake's
6031
+ * `identity()` guard previews and pre-flights, the anchor ACL's `$can`
6032
+ * grant filters resolves through this one rule; binding `actor.id`
6033
+ * directly at a lake edge reintroduces the divergent-identity mismatch.
6034
+ */
6035
+ export declare function lakePrincipalId(args: {
6036
+ actor: {
6037
+ id: string;
6038
+ };
6039
+ localPrincipalId?: string | undefined;
6040
+ }): string;
6041
+
6042
+ /**
6043
+ * GROQ listing only the latest deployed version of each definition name.
6044
+ * The correlated inner query keeps the reduction in the lake, so discovery
6045
+ * reads do not transfer every historical definition body.
6046
+ *
6047
+ * Params: `$tag`.
6048
+ */
6049
+ export declare function latestDefinitionsGroq(): string;
6050
+
6051
+ /**
6052
+ * The latest deployed version of each definition name from any supplied row
6053
+ * set. Useful at boundaries that may already hold multiple versions; callers
6054
+ * starting from the lake can use {@link latestDefinitionsGroq}. The highest
6055
+ * version wins regardless of input order, while names keep first-appearance
6056
+ * order.
5961
6057
  */
5962
6058
  export declare function latestDeployedDefinitions<
5963
6059
  T extends {
@@ -6155,9 +6251,10 @@ export declare interface MutationContext {
6155
6251
  | null;
6156
6252
  action: MutationGuardAction;
6157
6253
  /**
6158
- * Caller-asserted actor id, resolved as `identity()` in the predicate.
6159
- * Advisory the engine takes the caller's word for who is acting; only
6160
- * the lake's own token identity is authenticated.
6254
+ * The caller's principal id in the guarded resource's own namespace
6255
+ * (what that lake's `identity()` returns), resolved as `identity()` in
6256
+ * the predicate. Advisory — the engine takes the caller's word for who
6257
+ * is acting; only the lake's own token identity is authenticated.
6161
6258
  */
6162
6259
  identity?: string;
6163
6260
  }
@@ -6555,6 +6652,24 @@ export declare type PersonActor = Omit<Actor, "kind"> & {
6555
6652
  readonly kind: "person";
6556
6653
  };
6557
6654
 
6655
+ /**
6656
+ * The namespace a bare principal-id string belongs to.
6657
+ *
6658
+ * - `global` — account-global (comparable across every org-level context)
6659
+ * - `project` — scoped to ONE project; which project is not in the string,
6660
+ * it comes from context (for stored engine values: the anchor resource
6661
+ * the document lives in)
6662
+ * - `robot` — a token principal with one universal id (never needs mapping)
6663
+ * - `sentinel` — `<anonymous>` / `<system>`, never a person
6664
+ * - `unknown` — unclassifiable; treat as unresolvable and surface it
6665
+ */
6666
+ export declare type PrincipalNamespace =
6667
+ | "global"
6668
+ | "project"
6669
+ | "robot"
6670
+ | "sentinel"
6671
+ | "unknown";
6672
+
6558
6673
  /**
6559
6674
  * One row of the instance's idempotency ledger — a caller-supplied
6560
6675
  * `idempotencyKey` a state-changing verb already committed under. Recorded in
@@ -6603,6 +6718,11 @@ export declare type ProgressTarget =
6603
6718
  field: string;
6604
6719
  };
6605
6720
 
6721
+ /** Project a readable instance into the only row shape start predicates may scan. */
6722
+ export declare function projectStartSliceRow(
6723
+ instance: WorkflowInstance,
6724
+ ): StartSliceRow;
6725
+
6606
6726
  /**
6607
6727
  * Project a store-resolved doc onto its watch ref's identity, the way the
6608
6728
  * lake's perspective reads do: the published-form id becomes `_id` (the form
@@ -6659,8 +6779,8 @@ export declare const READER_MODEL_ROLLOUT_URL =
6659
6779
  export declare class ReaderModelAcknowledgementError extends WorkflowError<"reader-model-acknowledgement"> {
6660
6780
  readonly code = "WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
6661
6781
  readonly expectedMinReaderModel: unknown;
6662
- readonly engineMinReaderModel = 2;
6663
- readonly engineModelVersion = 3;
6782
+ readonly engineMinReaderModel = 4;
6783
+ readonly engineModelVersion = 4;
6664
6784
  readonly documentationUrl =
6665
6785
  "https://github.com/sanity-io/workflows/blob/main/docs/reader-model-rollout.md";
6666
6786
  constructor(expectedMinReaderModel: unknown, context?: string);
@@ -6826,8 +6946,8 @@ export declare function remediationsFor(
6826
6946
  /**
6827
6947
  * A verb that would unstick a {@link StuckCause} — the *what to do about it*
6828
6948
  * half of a diagnosis. Surface-neutral identifiers; a consumer maps each to
6829
- * its own command or button. `retry-effect` and `reset-activity` are not yet
6830
- * callable engine operations — see {@link SuggestedRemediation.available}.
6949
+ * its own command or button. `retry-effect` is not yet a callable engine
6950
+ * operation — see {@link SuggestedRemediation.available}.
6831
6951
  */
6832
6952
  export declare type RemediationVerb =
6833
6953
  | "retry-effect"
@@ -6870,8 +6990,65 @@ export declare function requiredReaderModel(
6870
6990
  document: unknown,
6871
6991
  ): number;
6872
6992
 
6993
+ declare type RequirementBase = {
6994
+ name: string;
6995
+ title?: string | undefined;
6996
+ description?: string | undefined;
6997
+ };
6998
+
6999
+ /** Authored identity and display copy for one readiness requirement. */
7000
+ export declare interface RequirementDescriptor {
7001
+ name: string;
7002
+ title?: string | undefined;
7003
+ description?: string | undefined;
7004
+ }
7005
+
7006
+ /**
7007
+ * Names an author's predicate may not use — engine-owned and author names
7008
+ * share one namespace, so a predicate redefining a binding would silently
7009
+ * shadow engine behaviour. Rejected at deploy; derived from the condition
7010
+ * inventory.
7011
+ */
6873
7012
  export declare const RESERVED_CONDITION_VARS: readonly string[];
6874
7013
 
7014
+ /**
7015
+ * What to reset a stuck activity INTO — the two non-`failed` outcomes that
7016
+ * unstick a stage gated on it. `active` re-runs it (back in progress, so a
7017
+ * caller drives it to completion again); `skipped` bypasses it (terminal but
7018
+ * resolved, so `$allActivitiesDone` can satisfy and a gated exit transition
7019
+ * fire). `done` is deliberately absent: a reset is recovery, not a silent
7020
+ * declaration that the work succeeded.
7021
+ */
7022
+ declare const RESET_ACTIVITY_TARGETS: readonly ["active", "skipped"];
7023
+
7024
+ export declare interface ResetActivityArgs extends DedupableOperationArgs {
7025
+ /** Name of the activity to reset, within the instance's current stage. */
7026
+ activity: string;
7027
+ /**
7028
+ * What to reset it into. `active` (the default) re-runs the activity — back
7029
+ * in progress, for a caller to drive to completion again; `skipped` bypasses
7030
+ * it — terminal but resolved, so a `$allActivitiesDone`-gated exit transition
7031
+ * can fire. `done` is intentionally not offered: a reset is recovery, not a
7032
+ * silent success.
7033
+ */
7034
+ to?: ResetActivityTarget;
7035
+ }
7036
+
7037
+ export declare type ResetActivityResult =
7038
+ | {
7039
+ fired: false;
7040
+ }
7041
+ | {
7042
+ fired: true;
7043
+ stage: StageName;
7044
+ activity: ActivityName;
7045
+ from: ActivityStatus;
7046
+ to: ResetActivityTarget;
7047
+ };
7048
+
7049
+ export declare type ResetActivityTarget =
7050
+ (typeof RESET_ACTIVITY_TARGETS)[number];
7051
+
6875
7052
  /**
6876
7053
  * Resolve the engine's `WorkflowAccess` for a client — both halves
6877
7054
  * fetched from its token in parallel and cached. Throws if the client
@@ -7181,6 +7358,34 @@ export declare interface SetStageArgs extends DedupableOperationArgs {
7181
7358
  */
7182
7359
  export declare const silentLogger: EngineLogger;
7183
7360
 
7361
+ export declare type SingleSubjectRequirement = RequirementBase & {
7362
+ type: "singleSubject";
7363
+ };
7364
+
7365
+ /**
7366
+ * Evaluate a declared `singleSubject` requirement — resolves `true` when it
7367
+ * REFUSES the start: an in-flight instance (no `completedAt`) of THIS
7368
+ * definition already holds the prospective subject. The subject resolves from
7369
+ * the `$fields` map's subject entry (a GDR envelope, `.id` the identity),
7370
+ * falling back to `scope.subject`. No subject in hand means nothing to compare — the requirement
7371
+ * passes; a pre-flight surface reports the unbound subject entry as
7372
+ * provisional through {@link unboundRequirementReads}. The rule is
7373
+ * count-shaped, so an absent `scope.fetchDataset` THROWS like a
7374
+ * dataset-reading GROQ requirement — an empty `*` would pass vacuously. One
7375
+ * evaluator serves every moment: the `startInstance` gate enforces it
7376
+ * (`StartNotAllowedError`), the `evaluateStart` verb pre-flights it, and start
7377
+ * surfaces evaluate it directly for their controls — every site gated by
7378
+ * {@link hasSingleSubjectRequirement}, so only a rule this engine knows arrives
7379
+ * here (an absent or unsupported rule is the caller's contract violation).
7380
+ * Advisory under races like every engine-side check.
7381
+ */
7382
+ export declare function singleSubjectRequirementRefused(args: {
7383
+ definition: Pick<ApplicabilitySource, "name" | "fields" | "start">;
7384
+ /** A produced `$fields` map (`startFieldsParam`, or a surface's seed map). */
7385
+ fields?: Record<string, unknown> | undefined;
7386
+ scope?: StartScope | undefined;
7387
+ }): Promise<boolean>;
7388
+
7184
7389
  /** One site whose verdict a proposed assignment flips. */
7185
7390
  export declare interface SiteConsequence {
7186
7391
  site: InsightSite;
@@ -7308,21 +7513,6 @@ export declare class StaleEffectClaimError extends WorkflowError<"stale-effect-c
7308
7513
  });
7309
7514
  }
7310
7515
 
7311
- /**
7312
- * The vars a definition's `start.allowed` reads — the start-time permission
7313
- * dialect: everything the filter context binds ({@link START_FILTER_VARS})
7314
- * plus `$fields`, which start time always has. No candidate root ever binds
7315
- * here (the subject rides `$fields.<entry>.id`; a root read is
7316
- * deploy-rejected). Pre-flights can omit not-yet-collected input bindings and
7317
- * report those reads as provisional. Bound in one place: `startContextParams`
7318
- * in the applicability evaluator.
7319
- */
7320
- export declare const START_ALLOWED_VARS: readonly {
7321
- name: string;
7322
- label: string;
7323
- description: string;
7324
- }[];
7325
-
7326
7516
  /**
7327
7517
  * The vars a definition's `start.filter` reads — the start-filter dialect,
7328
7518
  * not the rendered condition scope (no {@link ConditionVarBinding}: these
@@ -7330,8 +7520,7 @@ export declare const START_ALLOWED_VARS: readonly {
7330
7520
  * `definitionsForDocument` derivation and the Studio start control).
7331
7521
  * `startInstance` never evaluates the filter. BROWSE-TIME-PURE: `$fields` is
7332
7522
  * deliberately absent — a `$fields` read is deploy-rejected with a pointer
7333
- * to `start.allowed`. The synthetic subject variable requires a prospective
7334
- * subject; evaluation throws when the caller omits that required scope.
7523
+ * to start GROQ requirements.
7335
7524
  * Bound in one place: `startContextParams` in the applicability evaluator.
7336
7525
  */
7337
7526
  export declare const START_FILTER_VARS: readonly {
@@ -7342,6 +7531,21 @@ export declare const START_FILTER_VARS: readonly {
7342
7531
 
7343
7532
  declare const START_KINDS: readonly ["interactive", "autonomous"];
7344
7533
 
7534
+ /**
7535
+ * The vars a definition's start GROQ requirements read — the start-time readiness
7536
+ * dialect: everything the filter context binds ({@link START_FILTER_VARS})
7537
+ * plus `$fields`, which start time always has. No candidate root ever binds
7538
+ * here (the subject rides `$fields.<entry>.id`; a root read is
7539
+ * deploy-rejected). Pre-flights can omit not-yet-collected input bindings and
7540
+ * report those reads as provisional. Bound in one place: `startContextParams`
7541
+ * in the applicability evaluator.
7542
+ */
7543
+ export declare const START_REQUIREMENT_VARS: readonly {
7544
+ name: string;
7545
+ label: string;
7546
+ description: string;
7547
+ }[];
7548
+
7345
7549
  export declare type StartBlock = StartFields & {
7346
7550
  kind: StartKind;
7347
7551
  };
@@ -7362,34 +7566,34 @@ export declare type StartContext = Record<string, unknown>;
7362
7566
  * What `evaluateStart` projects — the same gates `startInstance` enforces,
7363
7567
  * as renderable state. `allowed` and `missingRequired` are deliberately
7364
7568
  * orthogonal: a missing required input is a caller mistake (the verb throws
7365
- * `RequiredFieldNotProvidedError` for it), never a `start.allowed` verdict.
7569
+ * `RequiredFieldNotProvidedError` for it), never a readiness verdict.
7366
7570
  */
7367
7571
  export declare interface StartEvaluation {
7368
7572
  /** Overall preflight startability: every supplied initial-field row is
7369
- * valid and the `start.allowed` outcome is satisfied. `outcome` describes
7370
- * only the condition; structurally invalid rows can therefore produce
7573
+ * valid and every declared start requirement is satisfied. `outcome` describes only the
7574
+ * predicates; structurally invalid rows can therefore produce
7371
7575
  * `allowed: false` with `outcome: 'satisfied'`. */
7372
7576
  allowed: boolean;
7373
7577
  /**
7374
- * Three-valued, BINDABILITY-AWARE verdict: `'unsatisfied'` is a definitive
7375
- * no for these inputs (disable and explain), `'unevaluable'` means the
7376
- * predicate can't be decided yet — either a null operand reached an
7377
- * ordered comparison, or the predicate reads an entry `initialFields`
7378
- * doesn't supply ({@link StartEvaluation.unboundReads} non-empty). Keep
7379
- * the affordance enabled on `'unevaluable'`; `startInstance` still
7380
- * enforces the final verdict, where absence is final rather than
7381
- * provisional.
7578
+ * Three-valued, BINDABILITY-AWARE verdict over the start predicates:
7579
+ * `'unsatisfied'` is a definitive no for these inputs (disable and
7580
+ * explain), `'unevaluable'` means the verdict can't be decided yet —
7581
+ * either a null operand reached an ordered comparison, or a predicate
7582
+ * reads an entry `initialFields` doesn't supply
7583
+ * ({@link StartEvaluation.unboundReads} non-empty). One exception outranks
7584
+ * the provisional rule: any definitive requirement refusal keeps the
7585
+ * aggregate `'unsatisfied'` even while other reads are unbound. Keep the
7586
+ * affordance enabled on `'unevaluable'`; `startInstance` still enforces
7587
+ * the final verdict, where absence is final rather than provisional.
7382
7588
  */
7383
7589
  outcome: ConditionOutcome;
7384
- /** The `start.allowed` atom breakdown over the SUPPLIED inputs what to
7385
- * render next to a disabled start affordance. Present iff the definition
7386
- * declares `allowed`. With `unboundReads` non-empty its own `outcome` may
7387
- * be a collapsed provisional answer the top-level `outcome` is the
7388
- * trustworthy one. */
7389
- insight?: ConditionInsight;
7390
- /** The `$fields` entries the predicate reads that `initialFields` doesn't
7391
- * supply — "fill these to decide". Non-empty forces `outcome:
7392
- * 'unevaluable'`. Empty when the definition declares no `allowed`. */
7590
+ /** One result per declared readiness node, in author order. */
7591
+ requirements: StartRequirementEvaluation[];
7592
+ /** The `$fields` entries the predicates read that `initialFields` doesn't
7593
+ * supply "fill these to decide", including a `singleSubject` node's
7594
+ * implicit subject read. Non-empty makes undecided requirements
7595
+ * `'unevaluable'`; a separate definitive refusal still keeps the aggregate
7596
+ * outcome `'unsatisfied'`. Empty when the definition declares no requirements. */
7393
7597
  unboundReads: string[];
7394
7598
  /** Required input entries `initialFields` doesn't fill yet — the rows a
7395
7599
  * `startInstance` now would throw `RequiredFieldNotProvidedError` about. */
@@ -7408,12 +7612,12 @@ export declare interface StartEvaluation {
7408
7612
  * authoring may omit it — so each variant declares it. */
7409
7613
  declare type StartFields = {
7410
7614
  filter?: string | undefined;
7411
- allowed?: string | undefined;
7615
+ requirements?: StartRequirement[] | undefined;
7412
7616
  };
7413
7617
 
7414
7618
  /**
7415
7619
  * Project caller-supplied `initialFields` into the `$fields` map the
7416
- * start-allowed context binds: one key per declared `input`-sourced entry,
7620
+ * start-requirement context binds: one key per declared `input`-sourced entry,
7417
7621
  * resolved through {@link suppliedFieldFor} — the predicate can only ever see
7418
7622
  * a value the input resolution would persist, and an undeclared supplied name
7419
7623
  * never leaks in. Unsupplied (or null-supplied) entries stay unbound, so a
@@ -7521,19 +7725,21 @@ export declare function startKindOf(definition: {
7521
7725
  }): StartKind;
7522
7726
 
7523
7727
  /**
7524
- * Thrown by `startInstance` when the definition's `start.allowed` predicate
7525
- * is not satisfied for the supplied `initialFields` evaluated false, or
7526
- * GROQ null ("can't decide"; fail-closed, `insight.outcome` tells the two
7527
- * apart). Carries the full {@link ConditionInsight} so a consumer renders
7528
- * the same explanation the `evaluateStart` pre-flight would have shown.
7728
+ * Thrown by `startInstance` when one or more declared start requirements are
7729
+ * not satisfied for the supplied `initialFields`. Carries every unmet node's
7730
+ * authored descriptor in declaration order; `evaluateStart` provides the
7731
+ * corresponding per-node outcomes and GROQ insights.
7529
7732
  * There is no override arg — like `fireAction`, you don't bypass a verdict,
7530
7733
  * you change what produces it. Advisory under races like every engine-side
7531
7734
  * check.
7532
7735
  */
7533
7736
  export declare class StartNotAllowedError extends WorkflowError<"start-not-allowed"> {
7534
7737
  readonly definition: string;
7535
- readonly insight: ConditionInsight;
7536
- constructor(args: { definition: string; insight: ConditionInsight });
7738
+ readonly unmetRequirements: RequirementDescriptor[];
7739
+ constructor(args: {
7740
+ definition: string;
7741
+ unmetRequirements: RequirementDescriptor[];
7742
+ });
7537
7743
  }
7538
7744
 
7539
7745
  /**
@@ -7579,43 +7785,57 @@ export declare function startRefusal(definition: {
7579
7785
  lifecycle?: WorkflowLifecycle | undefined;
7580
7786
  }): string | undefined;
7581
7787
 
7788
+ export declare type StartRequirement =
7789
+ | GroqRequirement
7790
+ | SingleSubjectRequirement;
7791
+
7792
+ export declare interface StartRequirementEvaluation extends RequirementDescriptor {
7793
+ /** Whether this requirement is satisfied, unsatisfied, or not yet decidable. */
7794
+ outcome: ConditionOutcome;
7795
+ /** GROQ explanation; absent for non-GROQ requirement kinds. */
7796
+ insight?: ConditionInsight | undefined;
7797
+ }
7798
+
7582
7799
  /**
7583
7800
  * The caller-side half of the start contexts — everything the evaluating
7584
7801
  * surface knows that the definition doesn't. Every member is optional
7585
7802
  * because the surfaces genuinely differ (a pure consumer may hold no clock):
7586
- * except for the subject identity required by `$subjectHasInFlightInstance`,
7587
- * an absent binding evaluates each read of it to GROQ null, and where that
7803
+ * An absent binding evaluates each read of it to GROQ null, and where that
7588
7804
  * null lands decides the verdict — a predicate that can't decide without
7589
7805
  * the binding fails closed, while a count-of-matches clause over values
7590
7806
  * every row stores passes vacuously (in GROQ null equals only null, so the
7591
7807
  * unbound read matches no stored value). The vars themselves are
7592
- * inventoried in `START_FILTER_VARS` / `START_ALLOWED_VARS`; the caller's
7808
+ * inventoried in `START_FILTER_VARS` / `START_REQUIREMENT_VARS`; the caller's
7593
7809
  * `$fields` map is NOT scope — `start.filter` never binds it, and
7594
- * `explainStartAllowed` takes it as its own argument.
7810
+ * `explainStartRequirement` takes it as its own argument.
7595
7811
  */
7596
7812
  export declare interface StartScope {
7597
7813
  /** The engine's tag partition — binds `$tag`. */
7598
7814
  tag?: string | undefined;
7599
7815
  /** ISO clock reading — binds `$now`. */
7600
7816
  now?: string | undefined;
7601
- /** Resource-qualified identity of the prospective subject. Required when
7602
- * `start.filter` reads `$subjectHasInFlightInstance`; unlike a loaded
7817
+ /** Resource-qualified identity of the prospective subject. Unlike a loaded
7603
7818
  * document's bare `_id`, this stays collision-free across resources. */
7604
7819
  subject?: GdrUri | undefined;
7605
7820
  /**
7606
- * The WORKFLOW resource's dataset, for predicates that read it (`*[...]` or
7607
- * a deref) or `$subjectHasInFlightInstance` — invoked lazily only when the
7608
- * predicate needs it. A slice is fine as long as it covers what predicates
7609
- * scan; the engine verbs supply EVERY instance of the tag, completed
7610
- * included — `*` carries no hidden predicate, so authors qualify in-flight
7821
+ * The engine-owned start slice, for predicates that read `*[...]` or for a
7822
+ * `singleSubject` requirement — invoked lazily only when evaluation needs it.
7823
+ * Each row exposes exactly `{definition, subject, completedAt}`; completed
7824
+ * rows are included, so authors qualify in-flight
7611
7825
  * themselves (`!defined(completedAt)`). Absent ⇒ a dataset-reading filter
7612
7826
  * fails closed (this surface cannot see the dataset, so it cannot decide),
7613
- * while a dataset-reading `allowed` THROWS — see
7614
- * {@link explainStartAllowed}.
7827
+ * while a dataset-reading requirement THROWS — see
7828
+ * {@link explainStartRequirement}.
7615
7829
  */
7616
7830
  fetchDataset?: (() => Promise<unknown[]>) | undefined;
7617
7831
  }
7618
7832
 
7833
+ export declare interface StartSliceRow {
7834
+ definition: string;
7835
+ subject: GdrUri | null;
7836
+ completedAt: string | null;
7837
+ }
7838
+
7619
7839
  /**
7620
7840
  * Declared editability of a field — the generic edit seam's gate. Default
7621
7841
  * (absent) is NOT editable: a field is op-only engine working memory unless the
@@ -8040,6 +8260,17 @@ export declare interface SubjectPermissionDenial {
8040
8260
  permission: DocumentValuePermission;
8041
8261
  }
8042
8262
 
8263
+ /** One foreign resource's forecast inputs: the actor's grants there, and the
8264
+ * actor's principal id in THAT resource's own identity namespace. */
8265
+ declare interface SubjectResourceAccess {
8266
+ grants: Grant[];
8267
+ /** What that resource's lake `identity()` returns for the acting token —
8268
+ * resolved through the resource's own routed client, so a dataset
8269
+ * resource contributes the actor's per-project user id and an org-level
8270
+ * resource the account-global id. */
8271
+ actorId: string;
8272
+ }
8273
+
8043
8274
  /**
8044
8275
  * A document the reactive layer should subscribe to, with its GDR
8045
8276
  * exploded so consumers never re-parse: the resource-addressing parts
@@ -8286,6 +8517,9 @@ export declare interface SweepStaleClaimsResult {
8286
8517
  released: PendingEffect[];
8287
8518
  }
8288
8519
 
8520
+ /** Lake `identity()` sentinel for system-initiated actions. */
8521
+ export declare const SYSTEM_IDENTITY = "<system>";
8522
+
8289
8523
  /**
8290
8524
  * The engine's read-partition invariant as a GROQ predicate: a document is
8291
8525
  * visible when its `tag` equals the caller's `$tag` param. The single
@@ -8456,7 +8690,7 @@ declare type TransitionFields = {
8456
8690
  export declare function tryParseGdr(uri: string): ParsedGdr | undefined;
8457
8691
 
8458
8692
  /**
8459
- * The `$fields` entries a `start.allowed` predicate reads that `fields` does
8693
+ * The `$fields` entries a start GROQ requirement reads that `fields` does
8460
8694
  * not bind — the BINDABILITY rule every pre-flight surface applies before
8461
8695
  * trusting a verdict over possibly-incomplete inputs. GROQ equality against
8462
8696
  * a missing operand collapses to a definitive-looking answer (`null == x` is
@@ -8473,8 +8707,8 @@ export declare function tryParseGdr(uri: string): ParsedGdr | undefined;
8473
8707
  * seed map) — producers bind only own keys with real values, so key presence
8474
8708
  * IS the supplied-ness rule.
8475
8709
  */
8476
- export declare function unboundAllowedReads(
8477
- allowed: string,
8710
+ export declare function unboundRequirementReads(
8711
+ query: string,
8478
8712
  fields: Record<string, unknown>,
8479
8713
  ): string[];
8480
8714
 
@@ -8670,12 +8904,12 @@ export declare const workflow: {
8670
8904
  /**
8671
8905
  * Spawn a new workflow instance from a deployed definition.
8672
8906
  *
8673
- * Two gates run before anything is written, in order: the required-input
8674
- * check a missing `required` input is a CALLER mistake
8675
- * ({@link RequiredFieldNotProvidedError}) then the definition's
8676
- * `start.allowed` permission predicate, whose false/GROQ-null VERDICT
8677
- * throws {@link StartNotAllowedError} (declaring the expression is the
8678
- * opt-in; there is no override arg — pre-flight with `evaluateStart`).
8907
+ * The gates run before anything is written, in order: supplied rows must be
8908
+ * structurally consumable, required inputs must be present
8909
+ * ({@link RequiredFieldNotProvidedError}), then every declared start
8910
+ * requirement is evaluated in author order. All unmet `groq` and
8911
+ * `singleSubject` nodes are reported by one {@link StartNotAllowedError}
8912
+ * (there is no override arg — pre-flight with `evaluateStart`).
8679
8913
  * Per-value SHAPE validation fires during field resolution, after the
8680
8914
  * verdict but still before any write. It does NOT evaluate `start.filter`:
8681
8915
  * that is a read-side visibility rule (see `definitionsForDocument`).
@@ -8807,6 +9041,18 @@ export declare const workflow: {
8807
9041
  abortInstance: (
8808
9042
  rawArgs: Clocked<Telemetered<AbortInstanceArgs & EngineScopeArgs>>,
8809
9043
  ) => Promise<OperationResult>;
9044
+ /**
9045
+ * Admin override — reset a failed (or otherwise terminal) activity in the
9046
+ * instance's current stage: `to: 'active'` re-runs it, `to: 'skipped'`
9047
+ * (the bypass) resolves it so a `$allActivitiesDone`-gated exit can fire.
9048
+ * Defaults to `active`. Cascades after the reset, so an unblocked
9049
+ * transition fires in the same call. ACL gating should be enforced
9050
+ * upstream. `changed: false` means the reset was a no-op (instance
9051
+ * terminal, or the activity already at the target status).
9052
+ */
9053
+ resetActivity: (
9054
+ rawArgs: Clocked<Telemetered<ResetActivityArgs & EngineScopeArgs>>,
9055
+ ) => Promise<OperationResult>;
8810
9056
  /**
8811
9057
  * Fetch a workflow instance by id, scoped to the engine's tag.
8812
9058
  * Throws when the instance doesn't exist or isn't visible to this
@@ -8921,17 +9167,16 @@ export declare const workflow: {
8921
9167
  * For a non-reactive, content-change-driven runtime (a Sanity Function, an
8922
9168
  * Inngest/durable worker, any server) that holds no instances in memory: a
8923
9169
  * document changed; which instances should it `tick`? The watch-set covers
8924
- * the instance itself, its ancestors, and the docs named by
9170
+ * the instance itself, its ancestors, live spawned children, and the docs named by
8925
9171
  * `doc.ref` / `subject` / `doc.refs` / `release.ref` field entries on the
8926
9172
  * workflow scope
8927
9173
  * **and the current stage** — so a hand-rolled GROQ over `fields[]` gets it
8928
- * subtly wrong (misses stage-scope refs, `release.ref`, ancestors).
9174
+ * subtly wrong (misses stage-scope refs, `release.ref`, ancestors, and children).
8929
9175
  *
8930
- * A coarse GROQ prefilter narrows candidates server-side (in-flight,
8931
- * tag-scoped, mentioning the doc anywhere in state); the authoritative match
8932
- * is {@link instanceWatchesDocument}, derived from `collectWatchRefs`, so the
8933
- * reverse stays in lockstep with the forward set and honours the
8934
- * open-stage-only rule the prefilter deliberately over-approximates.
9176
+ * The GROQ filter narrows candidates server-side (in-flight, tag-scoped,
9177
+ * matching workflow/open-stage refs); {@link instanceWatchesDocument},
9178
+ * derived from `collectWatchRefs`, rechecks the result so the reverse stays
9179
+ * in lockstep with the forward set.
8935
9180
  *
8936
9181
  * Single-resource: instances always live in the engine's own resource, so
8937
9182
  * this reads one client (unlike {@link guardsForInstance}, whose guard docs
@@ -8955,15 +9200,13 @@ export declare const workflow: {
8955
9200
  * ({@link applicableDefinitions}): startable ∧ the `subject`-kind entry
8956
9201
  * accepts the doc's `_type` ∧ `start.filter` passes — evaluated in the
8957
9202
  * browse-time-pure start-filter context with `$tag`/`$definition`/`$now`
8958
- * bound and the tag's instance slice (completed included) backing dataset
8959
- * reads. `start.allowed` never participatespermission is a start-time
9203
+ * bound and the tag's projected start slice (completed included) backing dataset
9204
+ * reads. Start requirements never participatereadiness is a start-time
8960
9205
  * question; pre-flight it with {@link workflow.evaluateStart}.
8961
9206
  *
8962
9207
  * Takes the LOADED candidate document, not a ref — applicability evaluates
8963
- * its content, under whatever perspective the caller read it with. A
8964
- * resource-qualified `subject` accompanies it when a filter reads
8965
- * `$subjectHasInFlightInstance`; a bare document id cannot identify a
8966
- * cross-resource subject. Surfaces ALL matches (name ascending), no engine
9208
+ * its content under whatever perspective the caller read it with. Surfaces
9209
+ * ALL matches (name ascending), no engine
8967
9210
  * ranking — presenting a picker or auto-picking is consumer policy.
8968
9211
  * Advisory like every engine-side check.
8969
9212
  */
@@ -8974,18 +9217,20 @@ export declare const workflow: {
8974
9217
  * Pre-flight the start gates for a definition + candidate `initialFields` —
8975
9218
  * the read `startInstance` enforces, as a {@link StartEvaluation} a surface
8976
9219
  * can render: `missingRequired` mirrors the input contract
8977
- * ({@link RequiredFieldNotProvidedError}'s rows), and `allowed` /
8978
- * `outcome` / `insight` carry the `start.allowed` verdict with its atom
8979
- * breakdown disable the start affordance on a definitive `false` and say
8980
- * why. BINDABILITY-AWARE for partial mid-form inputs: when the predicate
8981
- * reads an entry `initialFields` doesn't supply, `outcome` is
9220
+ * ({@link RequiredFieldNotProvidedError}'s rows), while `requirements`
9221
+ * preserves every declared node's authored descriptor, outcome, and GROQ
9222
+ * insight when applicable. `allowed` / `outcome` aggregate those ordered
9223
+ * results. BINDABILITY-AWARE for partial mid-form inputs: when a predicate
9224
+ * reads an entry `initialFields` doesn't supply including a
9225
+ * `singleSubject` node's implicit subject read — `outcome` is
8982
9226
  * `'unevaluable'` and `unboundReads` names the entries ("fill these to
8983
9227
  * decide") instead of the collapsed answer GROQ equality would give —
8984
9228
  * this is the ONE deliberate divergence from the gate, where absence is
8985
9229
  * final, not provisional (a rule like `!defined($fields.rush)` genuinely
8986
- * passes there when `rush` is absent). A definition with no
8987
- * `start.allowed` is vacuously allowed, exactly like the verb. Pure read;
8988
- * advisory under races — the enforcement moment is `startInstance` itself.
9230
+ * passes there when `rush` is absent). A definition declaring no start
9231
+ * requirements is vacuously allowed, exactly like
9232
+ * the verb. Pure read; advisory under races — the enforcement moment is
9233
+ * `startInstance` itself.
8989
9234
  */
8990
9235
  evaluateStart: (
8991
9236
  rawArgs: Clocked<EvaluateStartArgs & EngineScopeArgs>,
@@ -9032,6 +9277,16 @@ declare const WORKFLOW_LIFECYCLES: readonly ["standalone", "child"];
9032
9277
  */
9033
9278
  export declare interface WorkflowAccess {
9034
9279
  actor: Actor;
9280
+ /**
9281
+ * The actor's principal id in the workflow resource's OWN identity
9282
+ * namespace — what that resource's lake `identity()` returns for the same
9283
+ * token (the per-project user id for dataset resources; equal to
9284
+ * {@link Actor.id} for org-level resources and robots). In-memory only,
9285
+ * never persisted: it exists for the lake-facing edge — binding
9286
+ * `identity()` in guard previews and grant-filter evaluation so the
9287
+ * advisory verdicts agree with what the lake itself would decide.
9288
+ */
9289
+ localPrincipalId?: string;
9035
9290
  grants?: Grant[];
9036
9291
  }
9037
9292
 
@@ -9045,6 +9300,8 @@ export declare interface WorkflowActionFiredData extends InstanceScopedEventData
9045
9300
  cascaded: number;
9046
9301
  }
9047
9302
 
9303
+ export declare const WorkflowActivityReset: WorkflowTelemetryEvent<WorkflowAdminOverrideData>;
9304
+
9048
9305
  /** Outcome flag for the admin-override events — the attempt emits either way. */
9049
9306
  export declare interface WorkflowAdminOverrideData extends InstanceScopedEventData {
9050
9307
  /** `false` = the override was a no-op (already at the target / already terminal). */
@@ -9187,6 +9444,13 @@ export declare interface WorkflowClientConfig {
9187
9444
  dataset?: string;
9188
9445
  apiVersion?: string;
9189
9446
  requestTagPrefix?: string;
9447
+ /**
9448
+ * Host selection, mirroring `@sanity/client`: `false` derives a sibling
9449
+ * addressing the global API host instead of the project host. The engine
9450
+ * uses this for exactly one read — resolving the acting token's
9451
+ * account-global identity from the global `/users/me`.
9452
+ */
9453
+ useProjectHostname?: boolean;
9190
9454
  }
9191
9455
 
9192
9456
  export declare interface WorkflowCommitOptions {
@@ -9236,7 +9500,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9236
9500
  ]
9237
9501
  >;
9238
9502
  readonly expectedMinReaderModel: v.OptionalSchema<
9239
- v.CustomSchema<2, undefined>,
9503
+ v.CustomSchema<4, undefined>,
9240
9504
  undefined
9241
9505
  >;
9242
9506
  readonly tag: v.SchemaWithPipe<
@@ -9523,7 +9787,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9523
9787
  v.MinLengthAction<
9524
9788
  {
9525
9789
  name: string;
9526
- expectedMinReaderModel?: 2 | undefined;
9790
+ expectedMinReaderModel?: 4 | undefined;
9527
9791
  tag: string;
9528
9792
  workflowResource:
9529
9793
  | {
@@ -9584,7 +9848,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9584
9848
  v.CheckAction<
9585
9849
  {
9586
9850
  name: string;
9587
- expectedMinReaderModel?: 2 | undefined;
9851
+ expectedMinReaderModel?: 4 | undefined;
9588
9852
  tag: string;
9589
9853
  workflowResource:
9590
9854
  | {
@@ -9643,7 +9907,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9643
9907
  issue: v.CheckIssue<
9644
9908
  {
9645
9909
  name: string;
9646
- expectedMinReaderModel?: 2 | undefined;
9910
+ expectedMinReaderModel?: 4 | undefined;
9647
9911
  tag: string;
9648
9912
  workflowResource:
9649
9913
  | {
@@ -9704,7 +9968,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9704
9968
  v.CheckAction<
9705
9969
  {
9706
9970
  name: string;
9707
- expectedMinReaderModel?: 2 | undefined;
9971
+ expectedMinReaderModel?: 4 | undefined;
9708
9972
  tag: string;
9709
9973
  workflowResource:
9710
9974
  | {
@@ -9763,7 +10027,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9763
10027
  issue: v.CheckIssue<
9764
10028
  {
9765
10029
  name: string;
9766
- expectedMinReaderModel?: 2 | undefined;
10030
+ expectedMinReaderModel?: 4 | undefined;
9767
10031
  tag: string;
9768
10032
  workflowResource:
9769
10033
  | {
@@ -10113,7 +10377,7 @@ export declare interface WorkflowInstance extends SanityDocument {
10113
10377
  modelVersion?: number;
10114
10378
  /**
10115
10379
  * Reader floor — the oldest engine data model that can safely interpret
10116
- * this document. Derived from features actually present, bounded by
10380
+ * this document. No lower than the writer's unconditional
10117
10381
  * {@link DATA_MODEL_MIN_READER}, and written alongside
10118
10382
  * {@link WorkflowInstance.modelVersion}. Full persists never lower it.
10119
10383
  */