@sanity/workflow-engine 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -136,13 +136,6 @@ export declare type Assignee =
136
136
  role: string;
137
137
  };
138
138
 
139
- /** The assignees recorded for a task in a stage — the runtime half of the
140
- * "who is this waiting on" answer. Empty when the task is unassigned. */
141
- export declare function assigneesOf(
142
- stage: StageEntry | undefined,
143
- taskName: string,
144
- ): Assignee[];
145
-
146
139
  /**
147
140
  * Persisted document and effect-queue `_type` discriminators.
148
141
  */
@@ -771,6 +764,33 @@ declare const AuthoringStageSchema: v.StrictObjectSchema<
771
764
  >,
772
765
  undefined
773
766
  >;
767
+ /**
768
+ * Readiness gates, by name — conditions over the rendered scope that must
769
+ * hold for the task to be *executable*, orthogonal to `filter`
770
+ * (visibility). An unmet requirement keeps the task visible but disables
771
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
772
+ * all must hold (any-of lives inside one condition). Advisory like every
773
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
774
+ * and guards (content-write locks).
775
+ */
776
+ requirements: v.OptionalSchema<
777
+ v.RecordSchema<
778
+ v.SchemaWithPipe<
779
+ readonly [
780
+ v.StringSchema<undefined>,
781
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
782
+ ]
783
+ >,
784
+ v.SchemaWithPipe<
785
+ readonly [
786
+ v.StringSchema<undefined>,
787
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
788
+ ]
789
+ >,
790
+ undefined
791
+ >,
792
+ undefined
793
+ >;
774
794
  /**
775
795
  * Auto-completion condition — evaluated at activation and on every
776
796
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -2591,6 +2611,33 @@ declare const AuthoringTaskSchema: v.StrictObjectSchema<
2591
2611
  >,
2592
2612
  undefined
2593
2613
  >;
2614
+ /**
2615
+ * Readiness gates, by name — conditions over the rendered scope that must
2616
+ * hold for the task to be *executable*, orthogonal to `filter`
2617
+ * (visibility). An unmet requirement keeps the task visible but disables
2618
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
2619
+ * all must hold (any-of lives inside one condition). Advisory like every
2620
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
2621
+ * and guards (content-write locks).
2622
+ */
2623
+ requirements: v.OptionalSchema<
2624
+ v.RecordSchema<
2625
+ v.SchemaWithPipe<
2626
+ readonly [
2627
+ v.StringSchema<undefined>,
2628
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
2629
+ ]
2630
+ >,
2631
+ v.SchemaWithPipe<
2632
+ readonly [
2633
+ v.StringSchema<undefined>,
2634
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
2635
+ ]
2636
+ >,
2637
+ undefined
2638
+ >,
2639
+ undefined
2640
+ >;
2594
2641
  /**
2595
2642
  * Auto-completion condition — evaluated at activation and on every
2596
2643
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -4157,6 +4204,41 @@ declare const AuthoringWorkflowSchema: v.StrictObjectSchema<
4157
4204
  >,
4158
4205
  undefined
4159
4206
  >;
4207
+ /**
4208
+ * Readiness gates, by name — conditions over the rendered scope that must
4209
+ * hold for the task to be *executable*, orthogonal to `filter`
4210
+ * (visibility). An unmet requirement keeps the task visible but disables
4211
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
4212
+ * all must hold (any-of lives inside one condition). Advisory like every
4213
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
4214
+ * and guards (content-write locks).
4215
+ */
4216
+ requirements: v.OptionalSchema<
4217
+ v.RecordSchema<
4218
+ v.SchemaWithPipe<
4219
+ readonly [
4220
+ v.StringSchema<undefined>,
4221
+ v.MinLengthAction<
4222
+ string,
4223
+ 1,
4224
+ "must be a non-empty string"
4225
+ >,
4226
+ ]
4227
+ >,
4228
+ v.SchemaWithPipe<
4229
+ readonly [
4230
+ v.StringSchema<undefined>,
4231
+ v.MinLengthAction<
4232
+ string,
4233
+ 1,
4234
+ "must be a non-empty string"
4235
+ >,
4236
+ ]
4237
+ >,
4238
+ undefined
4239
+ >,
4240
+ undefined
4241
+ >;
4160
4242
  /**
4161
4243
  * Auto-completion condition — evaluated at activation and on every
4162
4244
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -6021,6 +6103,11 @@ declare const AuthoringWorkflowSchema: v.StrictObjectSchema<
6021
6103
  description?: string | undefined;
6022
6104
  activation?: "auto" | "manual" | undefined;
6023
6105
  filter?: string | undefined;
6106
+ requirements?:
6107
+ | {
6108
+ [x: string]: string;
6109
+ }
6110
+ | undefined;
6024
6111
  completeWhen?: string | undefined;
6025
6112
  failWhen?: string | undefined;
6026
6113
  ops?:
@@ -6547,10 +6634,10 @@ export declare interface AvailableActionsResult {
6547
6634
  declare type Bound<
6548
6635
  Args extends {
6549
6636
  client: WorkflowClient;
6550
- tags: string[];
6637
+ tag: string;
6551
6638
  workflowResource: WorkflowResource;
6552
6639
  },
6553
- > = Omit<Args, "client" | "tags" | "workflowResource">;
6640
+ > = Omit<Args, "client" | "tag" | "workflowResource">;
6554
6641
 
6555
6642
  /**
6556
6643
  * Build a snapshot from a set of loaded docs. Pure transform.
@@ -6570,8 +6657,6 @@ export declare function buildSnapshot(args: {
6570
6657
  docs: LoadedDoc[];
6571
6658
  }): HydratedSnapshot;
6572
6659
 
6573
- export declare function canonicalTag(tags: string[]): string;
6574
-
6575
6660
  /**
6576
6661
  * Thrown when auto-transitions on an instance fail to stabilise within
6577
6662
  * {@link CascadeLimitError.limit} passes — the signature of a runaway
@@ -6713,10 +6798,11 @@ export declare function contentReleaseName(args: {
6713
6798
  }): string | undefined;
6714
6799
 
6715
6800
  /**
6716
- * Construct an engine bound to a workflow resource + tags. The returned
6801
+ * Construct an engine bound to a workflow resource + tag. The returned
6717
6802
  * object exposes the same verbs as the `workflow.*` namespace, minus
6718
- * the boilerplate config arguments (client / tags / workflowResource)
6719
- * which are pinned at construction.
6803
+ * the boilerplate config arguments (client / tag / workflowResource)
6804
+ * which are pinned at construction. The `tag` is required — see
6805
+ * {@link validateTag} for the accepted shape.
6720
6806
  *
6721
6807
  * Effect handlers + missingHandler are stored for future drain wiring
6722
6808
  * (Step 10). They have no effect on `fireAction` / `tick` /
@@ -6728,7 +6814,14 @@ export declare function createEngine(args: CreateEngineArgs): Engine;
6728
6814
  export declare interface CreateEngineArgs {
6729
6815
  client: WorkflowClient;
6730
6816
  workflowResource: WorkflowResource;
6731
- tags: string[];
6817
+ /**
6818
+ * Engine-scope environment partition (e.g. `"test"`, `"prod"`). Every
6819
+ * definition/instance the engine writes is stamped with this tag and
6820
+ * every read is scoped to it. Required and never defaulted — the engine
6821
+ * enforces nothing, so the partition is the only thing keeping reads and
6822
+ * writes off the wrong environment.
6823
+ */
6824
+ tag: string;
6732
6825
  /**
6733
6826
  * Optional routing for cross-resource reads. When the engine needs
6734
6827
  * a doc whose GDR points at a resource other than its own, it calls
@@ -6763,6 +6856,16 @@ export declare function datasetResourceParts(id: string): {
6763
6856
  dataset: string;
6764
6857
  };
6765
6858
 
6859
+ /**
6860
+ * The perspective a content read falls back to when the instance has no
6861
+ * explicit release perspective: `drafts` overlay published (Sanity
6862
+ * draft-precedence), so an unpublished or in-flight subject is what the engine
6863
+ * — and the reactive adapters — evaluate, never a published-only read that
6864
+ * silently misses the draft. The single home for the draft-precedence default;
6865
+ * every content read uses `instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE`.
6866
+ */
6867
+ export declare const DEFAULT_CONTENT_PERSPECTIVE = "drafts";
6868
+
6766
6869
  /**
6767
6870
  * Default LoggerFactory — writes through the global `console`. Exposed
6768
6871
  * so callers outside `createEngine` (drive scripts, the
@@ -6774,8 +6877,8 @@ export declare const defaultLoggerFactory: LoggerFactory;
6774
6877
 
6775
6878
  export declare interface DeleteDefinitionArgs {
6776
6879
  client: WorkflowClient;
6777
- /** Engine-scope tags — required. See `validateTags` + `tags.ts`. */
6778
- tags: string[];
6880
+ /** Engine-scope environment partition — required. See {@link validateTag} + `tags.ts`. */
6881
+ tag: string;
6779
6882
  /** The Sanity resource the engine's own data lives in. */
6780
6883
  workflowResource: WorkflowResource;
6781
6884
  /** Cross-resource routing — see `StartInstanceArgs.resourceClients`. */
@@ -6837,8 +6940,8 @@ export declare interface DeployDefinitionResult {
6837
6940
 
6838
6941
  export declare interface DeployDefinitionsArgs {
6839
6942
  client: WorkflowClient;
6840
- /** Engine-scope tags — required. See `validateTags` + `tags.ts`. */
6841
- tags: string[];
6943
+ /** Engine-scope environment partition — required. See {@link validateTag} + `tags.ts`. */
6944
+ tag: string;
6842
6945
  /**
6843
6946
  * The Sanity resource the engine's own data lives in. Used to mint
6844
6947
  * GDR URIs for every doc the engine writes (definitions, instances,
@@ -6868,11 +6971,11 @@ export declare interface DeployDefinitionsResult {
6868
6971
  */
6869
6972
  export declare function deployStageGuards(args: StageGuardArgs): Promise<void>;
6870
6973
 
6871
- /** Where a definition deploys: the engine tags it partitions under, and the
6974
+ /** Where a definition deploys: the engine tag it partitions under, and the
6872
6975
  * workflow resource it belongs to. Structurally what `deployDefinitions`
6873
6976
  * receives, minus the definitions themselves. */
6874
6977
  export declare interface DeployTarget {
6875
- tags: string[];
6978
+ tag: string;
6876
6979
  workflowResource: WorkflowResource;
6877
6980
  }
6878
6981
 
@@ -6886,7 +6989,6 @@ export declare interface DiagnoseInput {
6886
6989
  instance: Pick<
6887
6990
  WorkflowInstance,
6888
6991
  | "currentStage"
6889
- | "stages"
6890
6992
  | "completedAt"
6891
6993
  | "abortedAt"
6892
6994
  | "effectHistory"
@@ -6895,6 +6997,7 @@ export declare interface DiagnoseInput {
6895
6997
  >;
6896
6998
  tasks: TaskEvaluation[];
6897
6999
  transitions: TransitionEvaluation[];
7000
+ assignees: Record<string, Assignee[]>;
6898
7001
  }
6899
7002
 
6900
7003
  /** Narrow a full {@link WorkflowEvaluation} to the {@link DiagnoseInput} the
@@ -6906,17 +7009,20 @@ export declare function diagnoseInputFromEvaluation(
6906
7009
  /**
6907
7010
  * Classify an instance. Terminal states win first; then the genuine stuck
6908
7011
  * causes (nothing advances on its own or via a normal action); then `waiting`
6909
- * (an action is available — healthy); else `progressing` (a transition is
6910
- * already satisfied and will cascade).
7012
+ * (an action is available — healthy); then `blocked` (an active task held by
7013
+ * an unmet requirement healthy, not yet actionable); else `progressing` (a
7014
+ * transition is already satisfied and will cascade).
6911
7015
  */
6912
7016
  export declare function diagnoseInstance(input: DiagnoseInput): Diagnosis;
6913
7017
 
6914
- /** The `workflow.diagnose` result — the verdict plus the evaluation it was
7018
+ /** The `workflow.diagnose` result — the verdict, the structured remediations
7019
+ * that would unstick it (empty unless `stuck`), and the evaluation it was
6915
7020
  * derived from, so a consumer can render the supporting evidence (current
6916
7021
  * stage tasks + transitions) without a second projection. */
6917
7022
  export declare interface DiagnoseResult {
6918
7023
  evaluation: WorkflowEvaluation;
6919
7024
  diagnosis: Diagnosis;
7025
+ remediations: SuggestedRemediation[];
6920
7026
  }
6921
7027
 
6922
7028
  export declare type Diagnosis =
@@ -6929,6 +7035,12 @@ export declare type Diagnosis =
6929
7035
  assignees: Assignee[];
6930
7036
  actions: string[];
6931
7037
  }
7038
+ | {
7039
+ state: "blocked";
7040
+ task: string;
7041
+ requirements: string[];
7042
+ assignees: Assignee[];
7043
+ }
6932
7044
  | {
6933
7045
  state: "completed";
6934
7046
  at: string;
@@ -6998,6 +7110,18 @@ export declare type DisabledReason =
6998
7110
  | {
6999
7111
  kind: "instance-completed";
7000
7112
  completedAt: string;
7113
+ }
7114
+ | {
7115
+ /**
7116
+ * The task's declared {@link Task.requirements} aren't all satisfied —
7117
+ * the readiness axis. The task is visible and the actor authorized, but
7118
+ * its own preconditions don't hold yet. `unmetRequirements` names the
7119
+ * failing keys so a consumer can disable the affirmative control and say
7120
+ * which precondition is outstanding. Distinct from `filter-failed`
7121
+ * (visibility/authorization) and `mutation-guard-denied` (content-write).
7122
+ */
7123
+ kind: "requirements-unmet";
7124
+ unmetRequirements: string[];
7001
7125
  };
7002
7126
 
7003
7127
  export declare interface DispatchResult {
@@ -7239,7 +7363,7 @@ export declare interface Engine {
7239
7363
  * (e.g. test bench, drain workers) that need them; the verbs already
7240
7364
  * thread them through internally. */
7241
7365
  readonly client: WorkflowClient;
7242
- readonly tags: readonly string[];
7366
+ readonly tag: string;
7243
7367
  readonly workflowResource: WorkflowResource;
7244
7368
  readonly effectHandlers: Readonly<Record<string, EffectHandler>>;
7245
7369
  readonly missingHandler: MissingHandlerPolicy;
@@ -7316,8 +7440,8 @@ export declare interface Engine {
7316
7440
  instanceId: string;
7317
7441
  task?: string;
7318
7442
  }) => Promise<WorkflowInstance[]>;
7319
- /** GROQ query against the engine's workflow resource. `$engineTags`
7320
- * is bound for tag-aware filtering. */
7443
+ /** GROQ query against the engine's workflow resource. `$tag`
7444
+ * is bound for tag-scoped filtering. */
7321
7445
  query: <T = unknown>(args: {
7322
7446
  groq: string;
7323
7447
  params?: Record<string, unknown>;
@@ -7351,7 +7475,7 @@ export declare interface Engine {
7351
7475
  access?: WorkflowAccessOverride;
7352
7476
  }) => Promise<DrainEffectsResult>;
7353
7477
  /**
7354
- * Inspect every deployed definition in the engine's tags and apply
7478
+ * Inspect every deployed definition in the engine's tag and apply
7355
7479
  * the configured missingHandler policy at `phase: "deploy"` for any
7356
7480
  * effect name without a registered handler. Catches "definition
7357
7481
  * shipped, handler removed" misconfigurations at startup instead of
@@ -7371,8 +7495,8 @@ export declare interface EngineLogger {
7371
7495
 
7372
7496
  export declare interface EvaluateArgs {
7373
7497
  client: WorkflowClient;
7374
- /** Engine-scope tags — required. */
7375
- tags: string[];
7498
+ /** Engine-scope environment partition — required. */
7499
+ tag: string;
7376
7500
  /** Engine workflow resource — required. */
7377
7501
  workflowResource: WorkflowResource;
7378
7502
  instanceId: string;
@@ -8366,16 +8490,10 @@ export declare interface OpAppliedSummary {
8366
8490
  resolved?: Record<string, unknown>;
8367
8491
  }
8368
8492
 
8369
- /** The instance's current, not-yet-exited {@link StageEntry}, if it has one —
8370
- * the engine's canonical {@link findOpenStageEntry}, over the diagnosis input. */
8371
- export declare function openStage(
8372
- instance: Pick<WorkflowInstance, "currentStage" | "stages">,
8373
- ): StageEntry | undefined;
8374
-
8375
8493
  export declare interface OperationArgs {
8376
8494
  client: WorkflowClient;
8377
- /** Engine-scope tags — required. */
8378
- tags: string[];
8495
+ /** Engine-scope environment partition — required. */
8496
+ tag: string;
8379
8497
  /** The Sanity resource the engine's own data lives in. */
8380
8498
  workflowResource: WorkflowResource;
8381
8499
  /** Cross-resource routing — see `StartInstanceArgs.resourceClients`. */
@@ -8528,6 +8646,30 @@ export declare interface ReleaseRef extends GlobalDocumentReference {
8528
8646
  releaseName: string;
8529
8647
  }
8530
8648
 
8649
+ /**
8650
+ * The remediations that would unstick a diagnosis — the *what to do about it*
8651
+ * half. Empty unless the instance is `stuck`: a `waiting` instance advances on
8652
+ * its own next action (see `availableActions`), and terminal or `progressing`
8653
+ * ones need nothing. Each verb is flagged
8654
+ * {@link SuggestedRemediation.available} from {@link RUNNABLE_VERBS}.
8655
+ */
8656
+ export declare function remediationsFor(
8657
+ diagnosis: Diagnosis,
8658
+ ): SuggestedRemediation[];
8659
+
8660
+ /**
8661
+ * A verb that would unstick a {@link StuckCause} — the *what to do about it*
8662
+ * half of a diagnosis. Surface-neutral identifiers; a consumer maps each to
8663
+ * its own command or button. `retry-effect`, `drain-effects` and `reset-task`
8664
+ * are not yet callable engine operations — see {@link SuggestedRemediation.available}.
8665
+ */
8666
+ export declare type RemediationVerb =
8667
+ | "retry-effect"
8668
+ | "drain-effects"
8669
+ | "reset-task"
8670
+ | "set-stage"
8671
+ | "abort";
8672
+
8531
8673
  /**
8532
8674
  * Resolve the engine's `WorkflowAccess` for a client. Override wins
8533
8675
  * outright; otherwise both halves are fetched in parallel and
@@ -8714,8 +8856,8 @@ export declare type StageName = string;
8714
8856
 
8715
8857
  export declare interface StartInstanceArgs {
8716
8858
  client: WorkflowClient;
8717
- /** Engine-scope tags — required. */
8718
- tags: string[];
8859
+ /** Engine-scope environment partition — required. */
8860
+ tag: string;
8719
8861
  /** The Sanity resource the engine's own data lives in. */
8720
8862
  workflowResource: WorkflowResource;
8721
8863
  /**
@@ -9359,6 +9501,33 @@ declare const StoredStageSchema: v.StrictObjectSchema<
9359
9501
  >,
9360
9502
  undefined
9361
9503
  >;
9504
+ /**
9505
+ * Readiness gates, by name — conditions over the rendered scope that must
9506
+ * hold for the task to be *executable*, orthogonal to `filter`
9507
+ * (visibility). An unmet requirement keeps the task visible but disables
9508
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
9509
+ * all must hold (any-of lives inside one condition). Advisory like every
9510
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
9511
+ * and guards (content-write locks).
9512
+ */
9513
+ requirements: v.OptionalSchema<
9514
+ v.RecordSchema<
9515
+ v.SchemaWithPipe<
9516
+ readonly [
9517
+ v.StringSchema<undefined>,
9518
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
9519
+ ]
9520
+ >,
9521
+ v.SchemaWithPipe<
9522
+ readonly [
9523
+ v.StringSchema<undefined>,
9524
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
9525
+ ]
9526
+ >,
9527
+ undefined
9528
+ >,
9529
+ undefined
9530
+ >;
9362
9531
  /**
9363
9532
  * Auto-completion condition — evaluated at activation and on every
9364
9533
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -10603,6 +10772,33 @@ declare const StoredTaskSchema: v.StrictObjectSchema<
10603
10772
  >,
10604
10773
  undefined
10605
10774
  >;
10775
+ /**
10776
+ * Readiness gates, by name — conditions over the rendered scope that must
10777
+ * hold for the task to be *executable*, orthogonal to `filter`
10778
+ * (visibility). An unmet requirement keeps the task visible but disables
10779
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
10780
+ * all must hold (any-of lives inside one condition). Advisory like every
10781
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
10782
+ * and guards (content-write locks).
10783
+ */
10784
+ requirements: v.OptionalSchema<
10785
+ v.RecordSchema<
10786
+ v.SchemaWithPipe<
10787
+ readonly [
10788
+ v.StringSchema<undefined>,
10789
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
10790
+ ]
10791
+ >,
10792
+ v.SchemaWithPipe<
10793
+ readonly [
10794
+ v.StringSchema<undefined>,
10795
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
10796
+ ]
10797
+ >,
10798
+ undefined
10799
+ >,
10800
+ undefined
10801
+ >;
10606
10802
  /**
10607
10803
  * Auto-completion condition — evaluated at activation and on every
10608
10804
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -11652,6 +11848,17 @@ export declare type StuckCause =
11652
11848
  }
11653
11849
  | {
11654
11850
  kind: "no-transition-fires";
11851
+ }
11852
+ /**
11853
+ * Every task resolved, but an exit transition's filter is *unevaluable*
11854
+ * (GROQ `null` — a referenced operand is missing/unreadable), so selection
11855
+ * halts. Unlike {@link StuckCause} `no-transition-fires` this is recoverable:
11856
+ * the cascade re-fires once the operand resolves (e.g. the subject is
11857
+ * published or the field is filled). Carries the undecidable transitions.
11858
+ */
11859
+ | {
11860
+ kind: "transition-unevaluable";
11861
+ transitions: string[];
11655
11862
  };
11656
11863
 
11657
11864
  /**
@@ -11767,15 +11974,24 @@ declare const SubworkflowsSchema: v.StrictObjectSchema<
11767
11974
  undefined
11768
11975
  >;
11769
11976
 
11977
+ export declare interface SuggestedRemediation {
11978
+ verb: RemediationVerb;
11979
+ /** True iff a consumer can run this as an engine operation today. `false`
11980
+ * marks a remediation we can name but not yet execute — a planned verb, or
11981
+ * an operational step (re-running the effect drainer). */
11982
+ available: boolean;
11983
+ /** Surface-neutral one-line rationale — no color or terminal symbols; a
11984
+ * consumer decorates it for its own surface. */
11985
+ rationale: string;
11986
+ }
11987
+
11770
11988
  /**
11771
11989
  * The engine's read-partition invariant as a GROQ predicate: a document is
11772
- * visible when its `tags[]` intersect the caller's tag set.
11773
- *
11774
- * @param param - GROQ parameter holding the caller's tags. Defaults to
11775
- * `engineTags`, matching the `workflow.query` guard so any query built with
11776
- * it stays tag-scoped.
11990
+ * visible when its `tag` equals the caller's `$tag` param. The single
11991
+ * definition of "tag-scoped" shared by the engine's internal lookups, the
11992
+ * `workflow.query` guard, and the CLI/MCP read helpers.
11777
11993
  */
11778
- export declare function tagScopeFilter(param?: string): string;
11994
+ export declare function tagScopeFilter(): string;
11779
11995
 
11780
11996
  export declare type Task = v.InferOutput<typeof StoredTaskSchema>;
11781
11997
 
@@ -11807,6 +12023,13 @@ export declare interface TaskEntry {
11807
12023
  filterEvaluation?: {
11808
12024
  at: string;
11809
12025
  truthy: boolean;
12026
+ /**
12027
+ * The filter evaluated to GROQ `null` — a referenced operand was missing
12028
+ * or incomparable, so it couldn't be decided. The engine fails closed
12029
+ * (keeps the task in scope) rather than reading this as a deliberate
12030
+ * `false`; `truthy` is `false` here but the task is NOT skipped.
12031
+ */
12032
+ unevaluable?: boolean;
11810
12033
  detail?: string;
11811
12034
  };
11812
12035
  /**
@@ -11825,6 +12048,13 @@ export declare interface TaskEvaluation {
11825
12048
  status: TaskStatus;
11826
12049
  /** Whether this task is the current actor's responsibility right now. */
11827
12050
  pendingOnActor: boolean;
12051
+ /**
12052
+ * The task's unmet {@link Task.requirements}, by name — present iff at least
12053
+ * one is unmet. The task's own readiness summary: a consumer can explain why
12054
+ * the whole task is gated without inspecting each action (every action also
12055
+ * carries the matching `requirements-unmet` `disabledReason`).
12056
+ */
12057
+ unmetRequirements?: string[];
11828
12058
  actions: ActionEvaluation[];
11829
12059
  }
11830
12060
 
@@ -11841,8 +12071,17 @@ export declare type Transition = v.InferOutput<typeof StoredTransitionSchema>;
11841
12071
 
11842
12072
  export declare interface TransitionEvaluation {
11843
12073
  transition: Transition;
11844
- /** Result of evaluating the transition's filter at read time. */
12074
+ /** Whether the transition's filter is definitively satisfied at read time. */
11845
12075
  filterSatisfied: boolean;
12076
+ /**
12077
+ * The filter evaluated to GROQ `null` — a referenced operand was missing or
12078
+ * incomparable, so it couldn't be decided. `filterSatisfied` is `false`, but
12079
+ * this is a *recoverable hold*, not a deliberate `false`: the engine halts
12080
+ * selection (it won't fall through to a later transition) and the cascade
12081
+ * re-fires once the operand resolves. Lets a diagnosis tell an undecidable
12082
+ * hold apart from a genuine routing dead-end.
12083
+ */
12084
+ unevaluable: boolean;
11846
12085
  }
11847
12086
 
11848
12087
  export declare type TransitionOp = v.InferOutput<
@@ -11865,18 +12104,25 @@ export declare function validateDefinition(
11865
12104
  ): void;
11866
12105
 
11867
12106
  /**
11868
- * Engine-scope tags — the multi-tenant isolation primitive.
12107
+ * Engine-scope tag — the environment partition primitive.
11869
12108
  *
11870
- * Every engine takes a required `tags: string[]` set at construction.
11871
- * Tags must be Sanity-ID-compatible since they're joined into IDs with
11872
- * `.` ASCII lowercase + digits + dashes, no leading dash, no dots.
12109
+ * Every engine operates against exactly one `tag` ("test", "prod", …).
12110
+ * The tag partitions definitions and instances within a single workflow
12111
+ * resource: `deploy(def, tag: "test")` and `deploy(def, tag: "prod")` are
12112
+ * separate deployed workflows with independent lifecycles, and every read
12113
+ * is scoped to one tag so test runs never surface in prod.
11873
12114
  *
11874
- * The first tag in the array is the *canonical* tag, used as the ID
11875
- * prefix when the engine writes new definitions or instances. The full
11876
- * tag set goes onto every written doc and is the basis for read
11877
- * filtering (tag intersection between engine and doc).
12115
+ * The tag must be Sanity-ID-compatible since it's joined into IDs with
12116
+ * `.` ASCII lowercase + digits + dashes, no leading dash, no dots. It
12117
+ * is also the ID prefix for every definition/instance the engine writes.
12118
+ *
12119
+ * There is no default. The tag selects which environment the engine reads
12120
+ * and writes, and the engine enforces nothing — so the partition is the
12121
+ * only thing keeping test runs out of prod. Every entry point
12122
+ * (`createEngine`, the CLI, the MCP server) requires it explicitly and
12123
+ * fails when it's absent rather than guessing an environment.
11878
12124
  */
11879
- export declare function validateTags(tags: string[]): void;
12125
+ export declare function validateTag(tag: string): void;
11880
12126
 
11881
12127
  /**
11882
12128
  * The guards allowed to influence this instance's verdicts, read from the
@@ -12032,44 +12278,43 @@ export declare const workflow: {
12032
12278
  */
12033
12279
  abortInstance: (args: Clocked<AbortInstanceArgs>) => Promise<DispatchResult>;
12034
12280
  /**
12035
- * Fetch a workflow instance by id, scoped to the engine's tags.
12281
+ * Fetch a workflow instance by id, scoped to the engine's tag.
12036
12282
  * Throws when the instance doesn't exist or isn't visible to this
12037
12283
  * engine.
12038
12284
  */
12039
12285
  getInstance: (args: {
12040
12286
  client: WorkflowClient;
12041
- tags: string[];
12287
+ tag: string;
12042
12288
  workflowResource: WorkflowResource;
12043
12289
  instanceId: string;
12044
12290
  }) => Promise<WorkflowInstance>;
12045
12291
  guardsForInstance: (args: {
12046
12292
  client: WorkflowClient;
12047
- tags: string[];
12293
+ tag: string;
12048
12294
  workflowResource: WorkflowResource;
12049
12295
  instanceId: string;
12050
12296
  resourceClients?: ResourceClientResolver;
12051
12297
  }) => Promise<MutationGuardDoc[]>;
12052
12298
  guardsForDefinition: (args: {
12053
12299
  client: WorkflowClient;
12054
- tags: string[];
12300
+ tag: string;
12055
12301
  workflowResource: WorkflowResource;
12056
12302
  /** The definition's `name`. */
12057
12303
  definition: string;
12058
12304
  resourceClients?: ResourceClientResolver;
12059
12305
  }) => Promise<MutationGuardDoc[]>;
12060
12306
  /**
12061
- * Run a caller-supplied GROQ query with the engine's tags bound as
12062
- * `$engineTags`. This does NOT rewrite the query — arbitrary GROQ
12307
+ * Run a caller-supplied GROQ query with the engine's tag bound as
12308
+ * `$tag`. This does NOT rewrite the query — arbitrary GROQ
12063
12309
  * can't be safely tag-scoped after the fact — so the CALLER MUST
12064
- * filter on `$engineTags` (e.g.
12065
- * `count(tags[@ in $engineTags]) > 0`). To guard against accidental
12066
- * cross-tenant reads, a query that never references `$engineTags` is
12310
+ * filter on `$tag` (e.g. `tag == $tag`). To guard against accidental
12311
+ * cross-partition reads, a query that never references `$tag` is
12067
12312
  * rejected before it reaches the lake. Caller is responsible for type
12068
12313
  * narrowing the result.
12069
12314
  */
12070
12315
  query: <T = unknown>(args: {
12071
12316
  client: WorkflowClient;
12072
- tags: string[];
12317
+ tag: string;
12073
12318
  workflowResource: WorkflowResource;
12074
12319
  groq: string;
12075
12320
  params?: Record<string, unknown>;
@@ -12093,7 +12338,7 @@ export declare const workflow: {
12093
12338
  queryInScope: <T = unknown>(
12094
12339
  args: Clocked<{
12095
12340
  client: WorkflowClient;
12096
- tags: string[];
12341
+ tag: string;
12097
12342
  workflowResource: WorkflowResource;
12098
12343
  resourceClients?: ResourceClientResolver;
12099
12344
  instanceId: string;
@@ -12107,7 +12352,7 @@ export declare const workflow: {
12107
12352
  */
12108
12353
  listPendingEffects: (args: {
12109
12354
  client: WorkflowClient;
12110
- tags: string[];
12355
+ tag: string;
12111
12356
  workflowResource: WorkflowResource;
12112
12357
  instanceId: string;
12113
12358
  }) => Promise<PendingEffect[]>;
@@ -12118,7 +12363,7 @@ export declare const workflow: {
12118
12363
  */
12119
12364
  findPendingEffects: (args: {
12120
12365
  client: WorkflowClient;
12121
- tags: string[];
12366
+ tag: string;
12122
12367
  workflowResource: WorkflowResource;
12123
12368
  instanceId: string;
12124
12369
  claimed?: boolean;
@@ -12160,14 +12405,14 @@ export declare const workflow: {
12160
12405
  * record. (The per-task `spawnedInstances` field on the active stage
12161
12406
  * only exists while that stage is current; history survives.) Strips
12162
12407
  * the GDR URI on each `instanceRef` to a bare `_id`, fetches the
12163
- * instances, drops any that aren't visible to this engine's tags, and
12408
+ * instances, drops any that aren't visible to this engine's tag, and
12164
12409
  * returns them sorted by `startedAt` ascending.
12165
12410
  *
12166
12411
  * Pass `task` to restrict to a single spawning task on the parent.
12167
12412
  */
12168
12413
  children: (args: {
12169
12414
  client: WorkflowClient;
12170
- tags: string[];
12415
+ tag: string;
12171
12416
  workflowResource: WorkflowResource;
12172
12417
  instanceId: string;
12173
12418
  task?: string;
@@ -12259,6 +12504,7 @@ export declare interface WorkflowClient {
12259
12504
  },
12260
12505
  >(
12261
12506
  doc: T,
12507
+ options?: WorkflowCommitOptions,
12262
12508
  ) => Promise<T>;
12263
12509
  /**
12264
12510
  * Build a multi-document transaction. All operations succeed or fail
@@ -12311,6 +12557,21 @@ export declare interface WorkflowClient {
12311
12557
  }) => Promise<T>;
12312
12558
  }
12313
12559
 
12560
+ export declare interface WorkflowCommitOptions {
12561
+ /**
12562
+ * When the mutation becomes visible to subsequent queries. The engine
12563
+ * reads its own writes back through GROQ (`fetch`) — a freshly-spawned
12564
+ * child listed by id, a just-deployed definition — so single-document
12565
+ * writes (`create`, `patch().commit()`) commit `'sync'`: the change is
12566
+ * query-visible before the call resolves and the next read can't miss it.
12567
+ * `@sanity/client` already defaults to `'sync'`, but the engine states it
12568
+ * rather than depend on a transport default a consumer's client config
12569
+ * could change underneath it. (Transactions ride that same default — see
12570
+ * {@link WorkflowTransaction.commit}.)
12571
+ */
12572
+ visibility?: WorkflowVisibility;
12573
+ }
12574
+
12314
12575
  export declare type WorkflowDefinition = v.InferOutput<
12315
12576
  typeof WorkflowDefinitionSchema
12316
12577
  >;
@@ -12453,6 +12714,41 @@ declare const WorkflowDefinitionSchema: v.StrictObjectSchema<
12453
12714
  >,
12454
12715
  undefined
12455
12716
  >;
12717
+ /**
12718
+ * Readiness gates, by name — conditions over the rendered scope that must
12719
+ * hold for the task to be *executable*, orthogonal to `filter`
12720
+ * (visibility). An unmet requirement keeps the task visible but disables
12721
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
12722
+ * all must hold (any-of lives inside one condition). Advisory like every
12723
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
12724
+ * and guards (content-write locks).
12725
+ */
12726
+ requirements: v.OptionalSchema<
12727
+ v.RecordSchema<
12728
+ v.SchemaWithPipe<
12729
+ readonly [
12730
+ v.StringSchema<undefined>,
12731
+ v.MinLengthAction<
12732
+ string,
12733
+ 1,
12734
+ "must be a non-empty string"
12735
+ >,
12736
+ ]
12737
+ >,
12738
+ v.SchemaWithPipe<
12739
+ readonly [
12740
+ v.StringSchema<undefined>,
12741
+ v.MinLengthAction<
12742
+ string,
12743
+ 1,
12744
+ "must be a non-empty string"
12745
+ >,
12746
+ ]
12747
+ >,
12748
+ undefined
12749
+ >,
12750
+ undefined
12751
+ >;
12456
12752
  /**
12457
12753
  * Auto-completion condition — evaluated at activation and on every
12458
12754
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -13737,6 +14033,11 @@ declare const WorkflowDefinitionSchema: v.StrictObjectSchema<
13737
14033
  description?: string | undefined;
13738
14034
  activation?: "auto" | "manual" | undefined;
13739
14035
  filter?: string | undefined;
14036
+ requirements?:
14037
+ | {
14038
+ [x: string]: string;
14039
+ }
14040
+ | undefined;
13740
14041
  completeWhen?: string | undefined;
13741
14042
  failWhen?: string | undefined;
13742
14043
  ops?:
@@ -14120,11 +14421,11 @@ export declare interface WorkflowFetchOptions {
14120
14421
  export declare interface WorkflowInstance extends SanityDocument {
14121
14422
  _type: typeof WORKFLOW_INSTANCE_TYPE;
14122
14423
  /**
14123
- * Engine-scope tags stamped on the instance at create time. Engines
14124
- * with disjoint tag sets cannot read this instance; engines sharing
14125
- * any tag with this array can.
14424
+ * Engine-scope environment partition stamped on the instance at create
14425
+ * time. Reads are scoped to a single tag, so an engine only sees
14426
+ * instances whose `tag` equals its own.
14126
14427
  */
14127
- tags: string[];
14428
+ tag: string;
14128
14429
  /**
14129
14430
  * The Sanity resource this instance lives in. Stored on the doc so
14130
14431
  * any internal operation can mint GDRs for ancestors / spawned
@@ -14215,7 +14516,7 @@ export declare interface WorkflowPatch {
14215
14516
  setIfMissing: (props: Record<string, unknown>) => WorkflowPatch;
14216
14517
  unset: (paths: string[]) => WorkflowPatch;
14217
14518
  ifRevisionId: (rev: string) => WorkflowPatch;
14218
- commit: () => Promise<SanityDocument>;
14519
+ commit: (options?: WorkflowCommitOptions) => Promise<SanityDocument>;
14219
14520
  }
14220
14521
 
14221
14522
  /**
@@ -14316,7 +14617,20 @@ export declare interface WorkflowTransaction {
14316
14617
  * carry this shape.
14317
14618
  */
14318
14619
  delete: (id: string) => WorkflowTransaction;
14620
+ /**
14621
+ * Commit the batch. No options — a transaction rides the client's *default*
14622
+ * `visibility`: `'sync'` in `@sanity/client` (so a post-spawn `listInstances`
14623
+ * GROQ sees freshly-created children), immediate in the in-memory test fake.
14624
+ * This is a standing engine assumption — a consumer that configured an
14625
+ * `async` default would make spawn read-backs racy. Single-document writes
14626
+ * state `'sync'` explicitly via {@link WorkflowCommitOptions}; transactions
14627
+ * can't (yet) — the test fake's commit options don't carry `visibility`, and
14628
+ * per repo convention that gap is closed upstream in the fake, not worked
14629
+ * around here.
14630
+ */
14319
14631
  commit: () => Promise<unknown>;
14320
14632
  }
14321
14633
 
14634
+ export declare type WorkflowVisibility = "sync" | "async" | "deferred";
14635
+
14322
14636
  export {};