@sanity/workflow-engine 0.20.0 → 0.22.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
  */
@@ -2639,8 +2693,13 @@ export declare interface DeployDefinitionResult {
2639
2693
  export declare interface DeployDefinitionsArgs<
2640
2694
  T extends WorkflowDefinitionInput<T> = WorkflowDefinition,
2641
2695
  > {
2642
- /** Reviewed literal acknowledging the installed writer's maximum reader-floor capability. */
2643
- expectedMinReaderModel: typeof DATA_MODEL_MIN_READER;
2696
+ /**
2697
+ * Reviewed numeric literal acknowledging the installed writer's maximum reader-floor capability.
2698
+ * The deploy gate checks the exact installed value and links the readers-first rollout; do not
2699
+ * import {@link DATA_MODEL_MIN_READER}, because that would auto-advance this acknowledgement on
2700
+ * upgrade.
2701
+ */
2702
+ expectedMinReaderModel: number;
2644
2703
  /**
2645
2704
  * Resource-alias bindings for this deploy (alias name → physical resource).
2646
2705
  * A deploy-time abstraction ONLY: every `@<alias>:` reference in a
@@ -2710,7 +2769,8 @@ export declare function deployStageGuards(args: StageGuardArgs): Promise<void>;
2710
2769
  * definitions themselves — carrying `resourceAliases` here is what keeps a diff
2711
2770
  * fingerprinting the same physical content `deployDefinitions` would. */
2712
2771
  export declare interface DeployTarget {
2713
- expectedMinReaderModel: typeof DATA_MODEL_MIN_READER;
2772
+ /** Reviewed numeric literal, checked against the exact installed floor before client access. */
2773
+ expectedMinReaderModel: number;
2714
2774
  tag: string;
2715
2775
  workflowResource: WorkflowResource;
2716
2776
  resourceAliases?: ResourceAliases;
@@ -3041,13 +3101,13 @@ export declare type DisabledReason =
3041
3101
  /**
3042
3102
  * The activity's declared {@link Activity.requirements} aren't all satisfied —
3043
3103
  * 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
3104
+ * its own preconditions don't hold yet. `unmetRequirements` carries the
3105
+ * authored descriptors so a consumer can disable the affirmative control and say
3046
3106
  * which precondition is outstanding. Distinct from `filter-failed`
3047
3107
  * (visibility/authorization) and `mutation-guard-denied` (content-write).
3048
3108
  */
3049
3109
  kind: "requirements-unmet";
3050
- unmetRequirements: string[];
3110
+ unmetRequirements: RequirementDescriptor[];
3051
3111
  }
3052
3112
  | {
3053
3113
  /**
@@ -3132,7 +3192,8 @@ export declare interface DocumentActionDenialsArgs {
3132
3192
  };
3133
3193
  action: MutationGuardAction;
3134
3194
  guards: readonly MutationGuardDoc[];
3135
- /** Actor id, resolved as `identity()` in predicates. */
3195
+ /** The caller's principal id in the guarded resource's own namespace,
3196
+ * resolved as `identity()` in predicates. */
3136
3197
  identity?: string;
3137
3198
  }
3138
3199
 
@@ -3147,10 +3208,8 @@ declare type DocumentEnvelopeKey =
3147
3208
  | "minReaderModel";
3148
3209
 
3149
3210
  /**
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
3211
+ * The document GROQ arm — matches instances whose reactive watch-set
3212
+ * references any of `documents`. Binds `$documents` + `$bareIds` into
3154
3213
  * `params`. Exposed for list builders that compose their own conditions
3155
3214
  * (e.g. the CLI's cross-partition list); {@link instancesQuery} consumers get
3156
3215
  * it via the `document`/`documents` filter fields instead.
@@ -3731,6 +3790,9 @@ export declare interface Engine {
3731
3790
  setStage: (args: SetStageArgs) => Promise<OperationResult>;
3732
3791
  /** Admin override — hard-stop an in-flight instance where it stands. */
3733
3792
  abortInstance: (args: AbortInstanceArgs) => Promise<OperationResult>;
3793
+ /** Admin override — reset a failed/terminal activity in the current stage
3794
+ * back to `active` (re-run) or `skipped` (bypass), then cascade. */
3795
+ resetActivity: (args: ResetActivityArgs) => Promise<OperationResult>;
3734
3796
  /** Admin override — remove a deployed definition (instances are only ever aborted, never deleted). */
3735
3797
  deleteDefinition: (
3736
3798
  args: DeleteDefinitionArgs,
@@ -3790,12 +3852,13 @@ export declare interface Engine {
3790
3852
  args: DefinitionsForDocumentArgs,
3791
3853
  ) => Promise<DeployedDefinition[]>;
3792
3854
  /** 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.
3855
+ * `initialFields` gathered so far: structurally invalid rows, every
3856
+ * requirement's ordered descriptor/outcome/insight, and still-missing
3857
+ * required inputs. `allowed` is the overall startability signal; `outcome`
3858
+ * aggregates the requirements. Bindability-aware — a requirement reading
3859
+ * a not-yet-supplied entry (including `singleSubject`'s implicit subject
3860
+ * read) reports `'unevaluable'` with the entries named
3861
+ * in `unboundReads`, never a collapsed verdict.
3799
3862
  * Pure read; the enforcement moment is `startInstance` itself. */
3800
3863
  evaluateStart: (args: EvaluateStartArgs) => Promise<StartEvaluation>;
3801
3864
  /** GROQ query against the engine's workflow resource. `$tag`
@@ -3967,6 +4030,13 @@ export declare interface EvaluateFromSnapshotArgs {
3967
4030
  instance: WorkflowInstance;
3968
4031
  definition: WorkflowDefinition;
3969
4032
  actor: Actor;
4033
+ /**
4034
+ * The actor's principal id in the workflow resource's own identity
4035
+ * namespace — the comparand for everything the resource's lake evaluates
4036
+ * (`identity()` in guard predicates, the anchor ACL's grant filters).
4037
+ * Omitted when it equals {@link Actor.id} (org-level resources, robots).
4038
+ */
4039
+ localPrincipalId?: string;
3970
4040
  /**
3971
4041
  * Resolved grants for the actor. Omit to leave the rendered `$can`
3972
4042
  * undefined (conditions referencing it fail closed — the real lake
@@ -3996,14 +4066,16 @@ export declare interface EvaluateFromSnapshotArgs {
3996
4066
  */
3997
4067
  guards?: readonly MutationGuardDoc[];
3998
4068
  /**
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
4069
+ * Per-FOREIGN-subject-resource access — the actor's ACL grants PLUS the
4070
+ * actor's principal id in that resource's own namespace — keyed by
4071
+ * resource-shaped GDR (`<type>:<id>`); feeds the subject-write forecast on
4072
+ * effects-bearing actions ({@link SubjectPermissionDenial}). A resource
4073
+ * appears only when both halves resolved; omit it (or the whole map) to
4074
+ * skip that resource's forecast (degrade open — the subject's lake still
4075
+ * enforces). {@link evaluateInstance} resolves this through each
4004
4076
  * resource's own client via {@link subjectResourceGrants}.
4005
4077
  */
4006
- resourceGrants?: ReadonlyMap<string, Grant[]>;
4078
+ resourceGrants?: ReadonlyMap<string, SubjectResourceAccess>;
4007
4079
  }
4008
4080
 
4009
4081
  /**
@@ -4040,15 +4112,13 @@ export declare interface EvaluateStartArgs {
4040
4112
  * be absent — a root read is then GROQ null, fail-closed or vacuous-pass by
4041
4113
  * shape) as the GROQ root, the {@link StartScope} bindings plus
4042
4114
  * `$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
4115
+ * dataset the scope's fetched slice as `*`. Cheap pure evaluation otherwise: no I/O rides a
4045
4116
  * filter that needs neither. GROQ null ("can't decide") is `false` — every consumer of
4046
4117
  * this verdict fails closed; a parse/evaluation THROW is a malformed
4047
4118
  * predicate, not an unevaluable one — rethrown loud, naming the definition,
4048
4119
  * 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.
4120
+ * A failed slice FETCH propagates as itself: transport trouble, never
4121
+ * definition blame.
4052
4122
  */
4053
4123
  export declare function evaluateStartFilter(args: {
4054
4124
  filter: string;
@@ -4165,7 +4235,7 @@ export { explainCondition };
4165
4235
  export { ExplainConditionArgs };
4166
4236
 
4167
4237
  /**
4168
- * Explain one `start.allowed` in the start-allowed context: no root (the
4238
+ * Explain one start GROQ requirement in the start-requirement context: no root (the
4169
4239
  * subject rides `$fields.<entry>.id` — deploy rejects a root read), the
4170
4240
  * {@link StartScope} bindings plus `$definition` and the caller's `$fields`
4171
4241
  * map, and the scope's fetched slice as `*` when the predicate reads the
@@ -4183,8 +4253,8 @@ export { ExplainConditionArgs };
4183
4253
  * bug as an author verdict. Parse/evaluation throws rethrow naming the
4184
4254
  * definition, like the filter's; a failed slice FETCH propagates as itself.
4185
4255
  */
4186
- export declare function explainStartAllowed(args: {
4187
- allowed: string;
4256
+ export declare function explainStartRequirement(args: {
4257
+ query: string;
4188
4258
  definition: Pick<ApplicabilitySource, "name" | "fields">;
4189
4259
  /** The caller's input entries as a `$fields` map — the engine verbs build
4190
4260
  * it with `startFieldsParam` (field resolution's projection), so the
@@ -4711,6 +4781,11 @@ declare function grantsPermissionOn(args: {
4711
4781
  userId?: string;
4712
4782
  }): Promise<boolean>;
4713
4783
 
4784
+ export declare type GroqRequirement = RequirementBase & {
4785
+ type: "groq";
4786
+ query: string;
4787
+ };
4788
+
4714
4789
  /** Type-mirror of {@link GroupSchema} — one declared group. */
4715
4790
  export declare type Group = {
4716
4791
  name: string;
@@ -5096,6 +5171,11 @@ export { guillemets };
5096
5171
  */
5097
5172
  export declare function hashDefinitionContent(def: WorkflowDefinition): string;
5098
5173
 
5174
+ /** Whether the definition declares a `singleSubject` start requirement. */
5175
+ export declare function hasSingleSubjectRequirement(
5176
+ definition: Pick<ApplicabilitySource, "start">,
5177
+ ): boolean;
5178
+
5099
5179
  /**
5100
5180
  * History-entry `_type` discriminators. Order roughly matches the
5101
5181
  * order a typical run produces them in a single fire-action commit.
@@ -5683,17 +5763,16 @@ export declare function instancesQuery(args: {
5683
5763
  export declare interface InstancesQueryFilter {
5684
5764
  /**
5685
5765
  * 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}.
5766
+ * URI). The lake-side predicate matches the reactive watch-set's
5767
+ * workflow, open-stage, activity, ancestor, live-child, and own-id references.
5689
5768
  */
5690
5769
  document?: GdrUri;
5691
5770
  /**
5692
5771
  * The multi-document form of {@link InstancesQueryFilter.document}: one
5693
- * prefilter matching instances that may reference ANY of the given docs,
5772
+ * predicate matching instances that reference ANY of the given docs,
5694
5773
  * 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
5774
+ * Merged with `document` when both are set. Callers may defensively recheck
5775
+ * with {@link instanceWatchesDocument}. A DEFINED-but-EMPTY
5697
5776
  * array matches nothing (the GROQ-natural reading of membership in an
5698
5777
  * empty set) — omit the field for the unconstrained every-in-flight read.
5699
5778
  */
@@ -5952,12 +6031,35 @@ export declare function lakeGuardId(args: {
5952
6031
  }): string;
5953
6032
 
5954
6033
  /**
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.
6034
+ * The id a lake-facing check binds for an actor: the resource-local
6035
+ * principal id when the namespaces diverge, otherwise the actor's own
6036
+ * (account-global) id. Every advisory that must agree with the lake's
6037
+ * `identity()` guard previews and pre-flights, the anchor ACL's `$can`
6038
+ * grant filters resolves through this one rule; binding `actor.id`
6039
+ * directly at a lake edge reintroduces the divergent-identity mismatch.
6040
+ */
6041
+ export declare function lakePrincipalId(args: {
6042
+ actor: {
6043
+ id: string;
6044
+ };
6045
+ localPrincipalId?: string | undefined;
6046
+ }): string;
6047
+
6048
+ /**
6049
+ * GROQ listing only the latest deployed version of each definition name.
6050
+ * The correlated inner query keeps the reduction in the lake, so discovery
6051
+ * reads do not transfer every historical definition body.
6052
+ *
6053
+ * Params: `$tag`.
6054
+ */
6055
+ export declare function latestDefinitionsGroq(): string;
6056
+
6057
+ /**
6058
+ * The latest deployed version of each definition name from any supplied row
6059
+ * set. Useful at boundaries that may already hold multiple versions; callers
6060
+ * starting from the lake can use {@link latestDefinitionsGroq}. The highest
6061
+ * version wins regardless of input order, while names keep first-appearance
6062
+ * order.
5961
6063
  */
5962
6064
  export declare function latestDeployedDefinitions<
5963
6065
  T extends {
@@ -6155,9 +6257,10 @@ export declare interface MutationContext {
6155
6257
  | null;
6156
6258
  action: MutationGuardAction;
6157
6259
  /**
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.
6260
+ * The caller's principal id in the guarded resource's own namespace
6261
+ * (what that lake's `identity()` returns), resolved as `identity()` in
6262
+ * the predicate. Advisory — the engine takes the caller's word for who
6263
+ * is acting; only the lake's own token identity is authenticated.
6161
6264
  */
6162
6265
  identity?: string;
6163
6266
  }
@@ -6555,6 +6658,24 @@ export declare type PersonActor = Omit<Actor, "kind"> & {
6555
6658
  readonly kind: "person";
6556
6659
  };
6557
6660
 
6661
+ /**
6662
+ * The namespace a bare principal-id string belongs to.
6663
+ *
6664
+ * - `global` — account-global (comparable across every org-level context)
6665
+ * - `project` — scoped to ONE project; which project is not in the string,
6666
+ * it comes from context (for stored engine values: the anchor resource
6667
+ * the document lives in)
6668
+ * - `robot` — a token principal with one universal id (never needs mapping)
6669
+ * - `sentinel` — `<anonymous>` / `<system>`, never a person
6670
+ * - `unknown` — unclassifiable; treat as unresolvable and surface it
6671
+ */
6672
+ export declare type PrincipalNamespace =
6673
+ | "global"
6674
+ | "project"
6675
+ | "robot"
6676
+ | "sentinel"
6677
+ | "unknown";
6678
+
6558
6679
  /**
6559
6680
  * One row of the instance's idempotency ledger — a caller-supplied
6560
6681
  * `idempotencyKey` a state-changing verb already committed under. Recorded in
@@ -6603,6 +6724,11 @@ export declare type ProgressTarget =
6603
6724
  field: string;
6604
6725
  };
6605
6726
 
6727
+ /** Project a readable instance into the only row shape start predicates may scan. */
6728
+ export declare function projectStartSliceRow(
6729
+ instance: WorkflowInstance,
6730
+ ): StartSliceRow;
6731
+
6606
6732
  /**
6607
6733
  * Project a store-resolved doc onto its watch ref's identity, the way the
6608
6734
  * lake's perspective reads do: the published-form id becomes `_id` (the form
@@ -6653,16 +6779,16 @@ export declare interface QueryInScopeArgs extends InstanceRefArgs, QueryArgs {}
6653
6779
  export { quoted };
6654
6780
 
6655
6781
  export declare const READER_MODEL_ROLLOUT_URL =
6656
- "https://github.com/sanity-io/workflows/blob/main/docs/reader-model-rollout.md";
6782
+ "https://www.sanity.io/docs/editorial-workflows/prerelease";
6657
6783
 
6658
6784
  /** A producer has not explicitly acknowledged this engine's writer capability. */
6659
6785
  export declare class ReaderModelAcknowledgementError extends WorkflowError<"reader-model-acknowledgement"> {
6660
6786
  readonly code = "WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
6661
6787
  readonly expectedMinReaderModel: unknown;
6662
- readonly engineMinReaderModel = 2;
6663
- readonly engineModelVersion = 3;
6788
+ readonly engineMinReaderModel = 4;
6789
+ readonly engineModelVersion = 4;
6664
6790
  readonly documentationUrl =
6665
- "https://github.com/sanity-io/workflows/blob/main/docs/reader-model-rollout.md";
6791
+ "https://www.sanity.io/docs/editorial-workflows/prerelease";
6666
6792
  constructor(expectedMinReaderModel: unknown, context?: string);
6667
6793
  }
6668
6794
 
@@ -6826,8 +6952,8 @@ export declare function remediationsFor(
6826
6952
  /**
6827
6953
  * A verb that would unstick a {@link StuckCause} — the *what to do about it*
6828
6954
  * 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}.
6955
+ * its own command or button. `retry-effect` is not yet a callable engine
6956
+ * operation — see {@link SuggestedRemediation.available}.
6831
6957
  */
6832
6958
  export declare type RemediationVerb =
6833
6959
  | "retry-effect"
@@ -6870,8 +6996,65 @@ export declare function requiredReaderModel(
6870
6996
  document: unknown,
6871
6997
  ): number;
6872
6998
 
6999
+ declare type RequirementBase = {
7000
+ name: string;
7001
+ title?: string | undefined;
7002
+ description?: string | undefined;
7003
+ };
7004
+
7005
+ /** Authored identity and display copy for one readiness requirement. */
7006
+ export declare interface RequirementDescriptor {
7007
+ name: string;
7008
+ title?: string | undefined;
7009
+ description?: string | undefined;
7010
+ }
7011
+
7012
+ /**
7013
+ * Names an author's predicate may not use — engine-owned and author names
7014
+ * share one namespace, so a predicate redefining a binding would silently
7015
+ * shadow engine behaviour. Rejected at deploy; derived from the condition
7016
+ * inventory.
7017
+ */
6873
7018
  export declare const RESERVED_CONDITION_VARS: readonly string[];
6874
7019
 
7020
+ /**
7021
+ * What to reset a stuck activity INTO — the two non-`failed` outcomes that
7022
+ * unstick a stage gated on it. `active` re-runs it (back in progress, so a
7023
+ * caller drives it to completion again); `skipped` bypasses it (terminal but
7024
+ * resolved, so `$allActivitiesDone` can satisfy and a gated exit transition
7025
+ * fire). `done` is deliberately absent: a reset is recovery, not a silent
7026
+ * declaration that the work succeeded.
7027
+ */
7028
+ declare const RESET_ACTIVITY_TARGETS: readonly ["active", "skipped"];
7029
+
7030
+ export declare interface ResetActivityArgs extends DedupableOperationArgs {
7031
+ /** Name of the activity to reset, within the instance's current stage. */
7032
+ activity: string;
7033
+ /**
7034
+ * What to reset it into. `active` (the default) re-runs the activity — back
7035
+ * in progress, for a caller to drive to completion again; `skipped` bypasses
7036
+ * it — terminal but resolved, so a `$allActivitiesDone`-gated exit transition
7037
+ * can fire. `done` is intentionally not offered: a reset is recovery, not a
7038
+ * silent success.
7039
+ */
7040
+ to?: ResetActivityTarget;
7041
+ }
7042
+
7043
+ export declare type ResetActivityResult =
7044
+ | {
7045
+ fired: false;
7046
+ }
7047
+ | {
7048
+ fired: true;
7049
+ stage: StageName;
7050
+ activity: ActivityName;
7051
+ from: ActivityStatus;
7052
+ to: ResetActivityTarget;
7053
+ };
7054
+
7055
+ export declare type ResetActivityTarget =
7056
+ (typeof RESET_ACTIVITY_TARGETS)[number];
7057
+
6875
7058
  /**
6876
7059
  * Resolve the engine's `WorkflowAccess` for a client — both halves
6877
7060
  * fetched from its token in parallel and cached. Throws if the client
@@ -7181,6 +7364,34 @@ export declare interface SetStageArgs extends DedupableOperationArgs {
7181
7364
  */
7182
7365
  export declare const silentLogger: EngineLogger;
7183
7366
 
7367
+ export declare type SingleSubjectRequirement = RequirementBase & {
7368
+ type: "singleSubject";
7369
+ };
7370
+
7371
+ /**
7372
+ * Evaluate a declared `singleSubject` requirement — resolves `true` when it
7373
+ * REFUSES the start: an in-flight instance (no `completedAt`) of THIS
7374
+ * definition already holds the prospective subject. The subject resolves from
7375
+ * the `$fields` map's subject entry (a GDR envelope, `.id` the identity),
7376
+ * falling back to `scope.subject`. No subject in hand means nothing to compare — the requirement
7377
+ * passes; a pre-flight surface reports the unbound subject entry as
7378
+ * provisional through {@link unboundRequirementReads}. The rule is
7379
+ * count-shaped, so an absent `scope.fetchDataset` THROWS like a
7380
+ * dataset-reading GROQ requirement — an empty `*` would pass vacuously. One
7381
+ * evaluator serves every moment: the `startInstance` gate enforces it
7382
+ * (`StartNotAllowedError`), the `evaluateStart` verb pre-flights it, and start
7383
+ * surfaces evaluate it directly for their controls — every site gated by
7384
+ * {@link hasSingleSubjectRequirement}, so only a rule this engine knows arrives
7385
+ * here (an absent or unsupported rule is the caller's contract violation).
7386
+ * Advisory under races like every engine-side check.
7387
+ */
7388
+ export declare function singleSubjectRequirementRefused(args: {
7389
+ definition: Pick<ApplicabilitySource, "name" | "fields" | "start">;
7390
+ /** A produced `$fields` map (`startFieldsParam`, or a surface's seed map). */
7391
+ fields?: Record<string, unknown> | undefined;
7392
+ scope?: StartScope | undefined;
7393
+ }): Promise<boolean>;
7394
+
7184
7395
  /** One site whose verdict a proposed assignment flips. */
7185
7396
  export declare interface SiteConsequence {
7186
7397
  site: InsightSite;
@@ -7308,21 +7519,6 @@ export declare class StaleEffectClaimError extends WorkflowError<"stale-effect-c
7308
7519
  });
7309
7520
  }
7310
7521
 
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
7522
  /**
7327
7523
  * The vars a definition's `start.filter` reads — the start-filter dialect,
7328
7524
  * not the rendered condition scope (no {@link ConditionVarBinding}: these
@@ -7330,8 +7526,7 @@ export declare const START_ALLOWED_VARS: readonly {
7330
7526
  * `definitionsForDocument` derivation and the Studio start control).
7331
7527
  * `startInstance` never evaluates the filter. BROWSE-TIME-PURE: `$fields` is
7332
7528
  * 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.
7529
+ * to start GROQ requirements.
7335
7530
  * Bound in one place: `startContextParams` in the applicability evaluator.
7336
7531
  */
7337
7532
  export declare const START_FILTER_VARS: readonly {
@@ -7342,6 +7537,21 @@ export declare const START_FILTER_VARS: readonly {
7342
7537
 
7343
7538
  declare const START_KINDS: readonly ["interactive", "autonomous"];
7344
7539
 
7540
+ /**
7541
+ * The vars a definition's start GROQ requirements read — the start-time readiness
7542
+ * dialect: everything the filter context binds ({@link START_FILTER_VARS})
7543
+ * plus `$fields`, which start time always has. No candidate root ever binds
7544
+ * here (the subject rides `$fields.<entry>.id`; a root read is
7545
+ * deploy-rejected). Pre-flights can omit not-yet-collected input bindings and
7546
+ * report those reads as provisional. Bound in one place: `startContextParams`
7547
+ * in the applicability evaluator.
7548
+ */
7549
+ export declare const START_REQUIREMENT_VARS: readonly {
7550
+ name: string;
7551
+ label: string;
7552
+ description: string;
7553
+ }[];
7554
+
7345
7555
  export declare type StartBlock = StartFields & {
7346
7556
  kind: StartKind;
7347
7557
  };
@@ -7362,34 +7572,34 @@ export declare type StartContext = Record<string, unknown>;
7362
7572
  * What `evaluateStart` projects — the same gates `startInstance` enforces,
7363
7573
  * as renderable state. `allowed` and `missingRequired` are deliberately
7364
7574
  * orthogonal: a missing required input is a caller mistake (the verb throws
7365
- * `RequiredFieldNotProvidedError` for it), never a `start.allowed` verdict.
7575
+ * `RequiredFieldNotProvidedError` for it), never a readiness verdict.
7366
7576
  */
7367
7577
  export declare interface StartEvaluation {
7368
7578
  /** 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
7579
+ * valid and every declared start requirement is satisfied. `outcome` describes only the
7580
+ * predicates; structurally invalid rows can therefore produce
7371
7581
  * `allowed: false` with `outcome: 'satisfied'`. */
7372
7582
  allowed: boolean;
7373
7583
  /**
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.
7584
+ * Three-valued, BINDABILITY-AWARE verdict over the start predicates:
7585
+ * `'unsatisfied'` is a definitive no for these inputs (disable and
7586
+ * explain), `'unevaluable'` means the verdict can't be decided yet —
7587
+ * either a null operand reached an ordered comparison, or a predicate
7588
+ * reads an entry `initialFields` doesn't supply
7589
+ * ({@link StartEvaluation.unboundReads} non-empty). One exception outranks
7590
+ * the provisional rule: any definitive requirement refusal keeps the
7591
+ * aggregate `'unsatisfied'` even while other reads are unbound. Keep the
7592
+ * affordance enabled on `'unevaluable'`; `startInstance` still enforces
7593
+ * the final verdict, where absence is final rather than provisional.
7382
7594
  */
7383
7595
  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`. */
7596
+ /** One result per declared readiness node, in author order. */
7597
+ requirements: StartRequirementEvaluation[];
7598
+ /** The `$fields` entries the predicates read that `initialFields` doesn't
7599
+ * supply "fill these to decide", including a `singleSubject` node's
7600
+ * implicit subject read. Non-empty makes undecided requirements
7601
+ * `'unevaluable'`; a separate definitive refusal still keeps the aggregate
7602
+ * outcome `'unsatisfied'`. Empty when the definition declares no requirements. */
7393
7603
  unboundReads: string[];
7394
7604
  /** Required input entries `initialFields` doesn't fill yet — the rows a
7395
7605
  * `startInstance` now would throw `RequiredFieldNotProvidedError` about. */
@@ -7408,12 +7618,12 @@ export declare interface StartEvaluation {
7408
7618
  * authoring may omit it — so each variant declares it. */
7409
7619
  declare type StartFields = {
7410
7620
  filter?: string | undefined;
7411
- allowed?: string | undefined;
7621
+ requirements?: StartRequirement[] | undefined;
7412
7622
  };
7413
7623
 
7414
7624
  /**
7415
7625
  * Project caller-supplied `initialFields` into the `$fields` map the
7416
- * start-allowed context binds: one key per declared `input`-sourced entry,
7626
+ * start-requirement context binds: one key per declared `input`-sourced entry,
7417
7627
  * resolved through {@link suppliedFieldFor} — the predicate can only ever see
7418
7628
  * a value the input resolution would persist, and an undeclared supplied name
7419
7629
  * never leaks in. Unsupplied (or null-supplied) entries stay unbound, so a
@@ -7521,19 +7731,21 @@ export declare function startKindOf(definition: {
7521
7731
  }): StartKind;
7522
7732
 
7523
7733
  /**
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.
7734
+ * Thrown by `startInstance` when one or more declared start requirements are
7735
+ * not satisfied for the supplied `initialFields`. Carries every unmet node's
7736
+ * authored descriptor in declaration order; `evaluateStart` provides the
7737
+ * corresponding per-node outcomes and GROQ insights.
7529
7738
  * There is no override arg — like `fireAction`, you don't bypass a verdict,
7530
7739
  * you change what produces it. Advisory under races like every engine-side
7531
7740
  * check.
7532
7741
  */
7533
7742
  export declare class StartNotAllowedError extends WorkflowError<"start-not-allowed"> {
7534
7743
  readonly definition: string;
7535
- readonly insight: ConditionInsight;
7536
- constructor(args: { definition: string; insight: ConditionInsight });
7744
+ readonly unmetRequirements: RequirementDescriptor[];
7745
+ constructor(args: {
7746
+ definition: string;
7747
+ unmetRequirements: RequirementDescriptor[];
7748
+ });
7537
7749
  }
7538
7750
 
7539
7751
  /**
@@ -7579,43 +7791,57 @@ export declare function startRefusal(definition: {
7579
7791
  lifecycle?: WorkflowLifecycle | undefined;
7580
7792
  }): string | undefined;
7581
7793
 
7794
+ export declare type StartRequirement =
7795
+ | GroqRequirement
7796
+ | SingleSubjectRequirement;
7797
+
7798
+ export declare interface StartRequirementEvaluation extends RequirementDescriptor {
7799
+ /** Whether this requirement is satisfied, unsatisfied, or not yet decidable. */
7800
+ outcome: ConditionOutcome;
7801
+ /** GROQ explanation; absent for non-GROQ requirement kinds. */
7802
+ insight?: ConditionInsight | undefined;
7803
+ }
7804
+
7582
7805
  /**
7583
7806
  * The caller-side half of the start contexts — everything the evaluating
7584
7807
  * surface knows that the definition doesn't. Every member is optional
7585
7808
  * 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
7809
+ * An absent binding evaluates each read of it to GROQ null, and where that
7588
7810
  * null lands decides the verdict — a predicate that can't decide without
7589
7811
  * the binding fails closed, while a count-of-matches clause over values
7590
7812
  * every row stores passes vacuously (in GROQ null equals only null, so the
7591
7813
  * unbound read matches no stored value). The vars themselves are
7592
- * inventoried in `START_FILTER_VARS` / `START_ALLOWED_VARS`; the caller's
7814
+ * inventoried in `START_FILTER_VARS` / `START_REQUIREMENT_VARS`; the caller's
7593
7815
  * `$fields` map is NOT scope — `start.filter` never binds it, and
7594
- * `explainStartAllowed` takes it as its own argument.
7816
+ * `explainStartRequirement` takes it as its own argument.
7595
7817
  */
7596
7818
  export declare interface StartScope {
7597
7819
  /** The engine's tag partition — binds `$tag`. */
7598
7820
  tag?: string | undefined;
7599
7821
  /** ISO clock reading — binds `$now`. */
7600
7822
  now?: string | undefined;
7601
- /** Resource-qualified identity of the prospective subject. Required when
7602
- * `start.filter` reads `$subjectHasInFlightInstance`; unlike a loaded
7823
+ /** Resource-qualified identity of the prospective subject. Unlike a loaded
7603
7824
  * document's bare `_id`, this stays collision-free across resources. */
7604
7825
  subject?: GdrUri | undefined;
7605
7826
  /**
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
7827
+ * The engine-owned start slice, for predicates that read `*[...]` or for a
7828
+ * `singleSubject` requirement — invoked lazily only when evaluation needs it.
7829
+ * Each row exposes exactly `{definition, subject, completedAt}`; completed
7830
+ * rows are included, so authors qualify in-flight
7611
7831
  * themselves (`!defined(completedAt)`). Absent ⇒ a dataset-reading filter
7612
7832
  * fails closed (this surface cannot see the dataset, so it cannot decide),
7613
- * while a dataset-reading `allowed` THROWS — see
7614
- * {@link explainStartAllowed}.
7833
+ * while a dataset-reading requirement THROWS — see
7834
+ * {@link explainStartRequirement}.
7615
7835
  */
7616
7836
  fetchDataset?: (() => Promise<unknown[]>) | undefined;
7617
7837
  }
7618
7838
 
7839
+ export declare interface StartSliceRow {
7840
+ definition: string;
7841
+ subject: GdrUri | null;
7842
+ completedAt: string | null;
7843
+ }
7844
+
7619
7845
  /**
7620
7846
  * Declared editability of a field — the generic edit seam's gate. Default
7621
7847
  * (absent) is NOT editable: a field is op-only engine working memory unless the
@@ -8040,6 +8266,17 @@ export declare interface SubjectPermissionDenial {
8040
8266
  permission: DocumentValuePermission;
8041
8267
  }
8042
8268
 
8269
+ /** One foreign resource's forecast inputs: the actor's grants there, and the
8270
+ * actor's principal id in THAT resource's own identity namespace. */
8271
+ declare interface SubjectResourceAccess {
8272
+ grants: Grant[];
8273
+ /** What that resource's lake `identity()` returns for the acting token —
8274
+ * resolved through the resource's own routed client, so a dataset
8275
+ * resource contributes the actor's per-project user id and an org-level
8276
+ * resource the account-global id. */
8277
+ actorId: string;
8278
+ }
8279
+
8043
8280
  /**
8044
8281
  * A document the reactive layer should subscribe to, with its GDR
8045
8282
  * exploded so consumers never re-parse: the resource-addressing parts
@@ -8286,6 +8523,9 @@ export declare interface SweepStaleClaimsResult {
8286
8523
  released: PendingEffect[];
8287
8524
  }
8288
8525
 
8526
+ /** Lake `identity()` sentinel for system-initiated actions. */
8527
+ export declare const SYSTEM_IDENTITY = "<system>";
8528
+
8289
8529
  /**
8290
8530
  * The engine's read-partition invariant as a GROQ predicate: a document is
8291
8531
  * visible when its `tag` equals the caller's `$tag` param. The single
@@ -8456,7 +8696,7 @@ declare type TransitionFields = {
8456
8696
  export declare function tryParseGdr(uri: string): ParsedGdr | undefined;
8457
8697
 
8458
8698
  /**
8459
- * The `$fields` entries a `start.allowed` predicate reads that `fields` does
8699
+ * The `$fields` entries a start GROQ requirement reads that `fields` does
8460
8700
  * not bind — the BINDABILITY rule every pre-flight surface applies before
8461
8701
  * trusting a verdict over possibly-incomplete inputs. GROQ equality against
8462
8702
  * a missing operand collapses to a definitive-looking answer (`null == x` is
@@ -8473,8 +8713,8 @@ export declare function tryParseGdr(uri: string): ParsedGdr | undefined;
8473
8713
  * seed map) — producers bind only own keys with real values, so key presence
8474
8714
  * IS the supplied-ness rule.
8475
8715
  */
8476
- export declare function unboundAllowedReads(
8477
- allowed: string,
8716
+ export declare function unboundRequirementReads(
8717
+ query: string,
8478
8718
  fields: Record<string, unknown>,
8479
8719
  ): string[];
8480
8720
 
@@ -8670,12 +8910,12 @@ export declare const workflow: {
8670
8910
  /**
8671
8911
  * Spawn a new workflow instance from a deployed definition.
8672
8912
  *
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`).
8913
+ * The gates run before anything is written, in order: supplied rows must be
8914
+ * structurally consumable, required inputs must be present
8915
+ * ({@link RequiredFieldNotProvidedError}), then every declared start
8916
+ * requirement is evaluated in author order. All unmet `groq` and
8917
+ * `singleSubject` nodes are reported by one {@link StartNotAllowedError}
8918
+ * (there is no override arg — pre-flight with `evaluateStart`).
8679
8919
  * Per-value SHAPE validation fires during field resolution, after the
8680
8920
  * verdict but still before any write. It does NOT evaluate `start.filter`:
8681
8921
  * that is a read-side visibility rule (see `definitionsForDocument`).
@@ -8807,6 +9047,18 @@ export declare const workflow: {
8807
9047
  abortInstance: (
8808
9048
  rawArgs: Clocked<Telemetered<AbortInstanceArgs & EngineScopeArgs>>,
8809
9049
  ) => Promise<OperationResult>;
9050
+ /**
9051
+ * Admin override — reset a failed (or otherwise terminal) activity in the
9052
+ * instance's current stage: `to: 'active'` re-runs it, `to: 'skipped'`
9053
+ * (the bypass) resolves it so a `$allActivitiesDone`-gated exit can fire.
9054
+ * Defaults to `active`. Cascades after the reset, so an unblocked
9055
+ * transition fires in the same call. ACL gating should be enforced
9056
+ * upstream. `changed: false` means the reset was a no-op (instance
9057
+ * terminal, or the activity already at the target status).
9058
+ */
9059
+ resetActivity: (
9060
+ rawArgs: Clocked<Telemetered<ResetActivityArgs & EngineScopeArgs>>,
9061
+ ) => Promise<OperationResult>;
8810
9062
  /**
8811
9063
  * Fetch a workflow instance by id, scoped to the engine's tag.
8812
9064
  * Throws when the instance doesn't exist or isn't visible to this
@@ -8921,17 +9173,16 @@ export declare const workflow: {
8921
9173
  * For a non-reactive, content-change-driven runtime (a Sanity Function, an
8922
9174
  * Inngest/durable worker, any server) that holds no instances in memory: a
8923
9175
  * document changed; which instances should it `tick`? The watch-set covers
8924
- * the instance itself, its ancestors, and the docs named by
9176
+ * the instance itself, its ancestors, live spawned children, and the docs named by
8925
9177
  * `doc.ref` / `subject` / `doc.refs` / `release.ref` field entries on the
8926
9178
  * workflow scope
8927
9179
  * **and the current stage** — so a hand-rolled GROQ over `fields[]` gets it
8928
- * subtly wrong (misses stage-scope refs, `release.ref`, ancestors).
9180
+ * subtly wrong (misses stage-scope refs, `release.ref`, ancestors, and children).
8929
9181
  *
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.
9182
+ * The GROQ filter narrows candidates server-side (in-flight, tag-scoped,
9183
+ * matching workflow/open-stage refs); {@link instanceWatchesDocument},
9184
+ * derived from `collectWatchRefs`, rechecks the result so the reverse stays
9185
+ * in lockstep with the forward set.
8935
9186
  *
8936
9187
  * Single-resource: instances always live in the engine's own resource, so
8937
9188
  * this reads one client (unlike {@link guardsForInstance}, whose guard docs
@@ -8955,15 +9206,13 @@ export declare const workflow: {
8955
9206
  * ({@link applicableDefinitions}): startable ∧ the `subject`-kind entry
8956
9207
  * accepts the doc's `_type` ∧ `start.filter` passes — evaluated in the
8957
9208
  * 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
9209
+ * bound and the tag's projected start slice (completed included) backing dataset
9210
+ * reads. Start requirements never participatereadiness is a start-time
8960
9211
  * question; pre-flight it with {@link workflow.evaluateStart}.
8961
9212
  *
8962
9213
  * 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
9214
+ * its content under whatever perspective the caller read it with. Surfaces
9215
+ * ALL matches (name ascending), no engine
8967
9216
  * ranking — presenting a picker or auto-picking is consumer policy.
8968
9217
  * Advisory like every engine-side check.
8969
9218
  */
@@ -8974,18 +9223,20 @@ export declare const workflow: {
8974
9223
  * Pre-flight the start gates for a definition + candidate `initialFields` —
8975
9224
  * the read `startInstance` enforces, as a {@link StartEvaluation} a surface
8976
9225
  * 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
9226
+ * ({@link RequiredFieldNotProvidedError}'s rows), while `requirements`
9227
+ * preserves every declared node's authored descriptor, outcome, and GROQ
9228
+ * insight when applicable. `allowed` / `outcome` aggregate those ordered
9229
+ * results. BINDABILITY-AWARE for partial mid-form inputs: when a predicate
9230
+ * reads an entry `initialFields` doesn't supply including a
9231
+ * `singleSubject` node's implicit subject read — `outcome` is
8982
9232
  * `'unevaluable'` and `unboundReads` names the entries ("fill these to
8983
9233
  * decide") instead of the collapsed answer GROQ equality would give —
8984
9234
  * this is the ONE deliberate divergence from the gate, where absence is
8985
9235
  * 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.
9236
+ * passes there when `rush` is absent). A definition declaring no start
9237
+ * requirements is vacuously allowed, exactly like
9238
+ * the verb. Pure read; advisory under races — the enforcement moment is
9239
+ * `startInstance` itself.
8989
9240
  */
8990
9241
  evaluateStart: (
8991
9242
  rawArgs: Clocked<EvaluateStartArgs & EngineScopeArgs>,
@@ -9032,6 +9283,16 @@ declare const WORKFLOW_LIFECYCLES: readonly ["standalone", "child"];
9032
9283
  */
9033
9284
  export declare interface WorkflowAccess {
9034
9285
  actor: Actor;
9286
+ /**
9287
+ * The actor's principal id in the workflow resource's OWN identity
9288
+ * namespace — what that resource's lake `identity()` returns for the same
9289
+ * token (the per-project user id for dataset resources; equal to
9290
+ * {@link Actor.id} for org-level resources and robots). In-memory only,
9291
+ * never persisted: it exists for the lake-facing edge — binding
9292
+ * `identity()` in guard previews and grant-filter evaluation so the
9293
+ * advisory verdicts agree with what the lake itself would decide.
9294
+ */
9295
+ localPrincipalId?: string;
9035
9296
  grants?: Grant[];
9036
9297
  }
9037
9298
 
@@ -9045,6 +9306,8 @@ export declare interface WorkflowActionFiredData extends InstanceScopedEventData
9045
9306
  cascaded: number;
9046
9307
  }
9047
9308
 
9309
+ export declare const WorkflowActivityReset: WorkflowTelemetryEvent<WorkflowAdminOverrideData>;
9310
+
9048
9311
  /** Outcome flag for the admin-override events — the attempt emits either way. */
9049
9312
  export declare interface WorkflowAdminOverrideData extends InstanceScopedEventData {
9050
9313
  /** `false` = the override was a no-op (already at the target / already terminal). */
@@ -9187,6 +9450,13 @@ export declare interface WorkflowClientConfig {
9187
9450
  dataset?: string;
9188
9451
  apiVersion?: string;
9189
9452
  requestTagPrefix?: string;
9453
+ /**
9454
+ * Host selection, mirroring `@sanity/client`: `false` derives a sibling
9455
+ * addressing the global API host instead of the project host. The engine
9456
+ * uses this for exactly one read — resolving the acting token's
9457
+ * account-global identity from the global `/users/me`.
9458
+ */
9459
+ useProjectHostname?: boolean;
9190
9460
  }
9191
9461
 
9192
9462
  export declare interface WorkflowCommitOptions {
@@ -9236,7 +9506,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9236
9506
  ]
9237
9507
  >;
9238
9508
  readonly expectedMinReaderModel: v.OptionalSchema<
9239
- v.CustomSchema<2, undefined>,
9509
+ v.CustomSchema<number, undefined>,
9240
9510
  undefined
9241
9511
  >;
9242
9512
  readonly tag: v.SchemaWithPipe<
@@ -9523,7 +9793,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9523
9793
  v.MinLengthAction<
9524
9794
  {
9525
9795
  name: string;
9526
- expectedMinReaderModel?: 2 | undefined;
9796
+ expectedMinReaderModel?: number | undefined;
9527
9797
  tag: string;
9528
9798
  workflowResource:
9529
9799
  | {
@@ -9584,7 +9854,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9584
9854
  v.CheckAction<
9585
9855
  {
9586
9856
  name: string;
9587
- expectedMinReaderModel?: 2 | undefined;
9857
+ expectedMinReaderModel?: number | undefined;
9588
9858
  tag: string;
9589
9859
  workflowResource:
9590
9860
  | {
@@ -9643,7 +9913,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9643
9913
  issue: v.CheckIssue<
9644
9914
  {
9645
9915
  name: string;
9646
- expectedMinReaderModel?: 2 | undefined;
9916
+ expectedMinReaderModel?: number | undefined;
9647
9917
  tag: string;
9648
9918
  workflowResource:
9649
9919
  | {
@@ -9704,7 +9974,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9704
9974
  v.CheckAction<
9705
9975
  {
9706
9976
  name: string;
9707
- expectedMinReaderModel?: 2 | undefined;
9977
+ expectedMinReaderModel?: number | undefined;
9708
9978
  tag: string;
9709
9979
  workflowResource:
9710
9980
  | {
@@ -9763,7 +10033,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
9763
10033
  issue: v.CheckIssue<
9764
10034
  {
9765
10035
  name: string;
9766
- expectedMinReaderModel?: 2 | undefined;
10036
+ expectedMinReaderModel?: number | undefined;
9767
10037
  tag: string;
9768
10038
  workflowResource:
9769
10039
  | {
@@ -9930,7 +10200,11 @@ export declare type WorkflowDeployment = Omit<
9930
10200
  ParsedWorkflowDeployment,
9931
10201
  "expectedMinReaderModel"
9932
10202
  > & {
9933
- expectedMinReaderModel: typeof DATA_MODEL_MIN_READER;
10203
+ /**
10204
+ * Reviewed numeric literal. Runtime validation owns the exact installed-floor check so a stale
10205
+ * acknowledgement reaches the readers-first rollout guidance instead of becoming a type error.
10206
+ */
10207
+ expectedMinReaderModel: number;
9934
10208
  };
9935
10209
 
9936
10210
  export declare const WorkflowEffectCompleted: WorkflowTelemetryEvent<WorkflowEffectCompletedData>;
@@ -10113,7 +10387,7 @@ export declare interface WorkflowInstance extends SanityDocument {
10113
10387
  modelVersion?: number;
10114
10388
  /**
10115
10389
  * Reader floor — the oldest engine data model that can safely interpret
10116
- * this document. Derived from features actually present, bounded by
10390
+ * this document. No lower than the writer's unconditional
10117
10391
  * {@link DATA_MODEL_MIN_READER}, and written alongside
10118
10392
  * {@link WorkflowInstance.modelVersion}. Full persists never lower it.
10119
10393
  */