@sanity/workflow-engine 0.17.0 → 0.18.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
@@ -40,17 +40,15 @@ export declare function abortReason(
40
40
  ): string | undefined;
41
41
 
42
42
  /**
43
- * The structured half of applicability: does a required subject entry
44
- * ({@link isSubjectEntry} — `doc.ref` / `doc.refs`, workflow scope) accept
45
- * `documentType`? Name-blind and EXISTENTIAL: ANY required ref entry counts
46
- * a multi-input definition (say, contract + counterparty) surfaces from
47
- * either document's picker, and the start dialog collects the remaining
48
- * required entries (their fail-hard validation backstops). An entry without
49
- * `types` accepts any type; a definition with NO required ref entry takes no
50
- * subject and never matches. Cheap and indexable — no GROQ evaluation — so a
51
- * consumer can pre-filter before loading document content. (`required` is
52
- * pinned to caller-filled `input` entries by a deploy invariant, so it alone
53
- * identifies the handoff.)
43
+ * The structured half of applicability: does the definition's subject entry
44
+ * ({@link isSubjectEntry} — the `subject` kind, workflow scope, at most one
45
+ * by deploy invariant) accept `documentType`? The kind is the discriminator,
46
+ * so a definition surfaces from its SUBJECT's document picker and nowhere
47
+ * else; the start dialog collects the remaining required entries (their
48
+ * fail-hard validation backstops). A subject without `types` accepts any
49
+ * type; a definition with NO subject entry takes no subject and never
50
+ * matches. Cheap and indexable — no GROQ evaluation — so a consumer can
51
+ * pre-filter before loading document content.
54
52
  */
55
53
  export declare function acceptsDocumentType(
56
54
  definition: Pick<ApplicabilitySource, "fields">,
@@ -83,6 +81,11 @@ export declare type Action = ActionFields<Op, string[]> & {
83
81
  roles?: string[] | undefined;
84
82
  };
85
83
 
84
+ export declare const ACTION_SEMANTICS: readonly [
85
+ "decision.accept",
86
+ "decision.decline",
87
+ ];
88
+
86
89
  /**
87
90
  * The engine's per-reason detail fragment — the same wording
88
91
  * {@link ActionDisabledError} embeds in its message. Exported so a dev-facing
@@ -124,6 +127,8 @@ export declare type ActionDisabledReason = Exclude<
124
127
 
125
128
  export declare interface ActionEvaluation {
126
129
  action: Action;
130
+ /** The action's advisory workflow meaning, unchanged from its definition. */
131
+ semantics?: ActionSemantic[] | undefined;
127
132
  allowed: boolean;
128
133
  /**
129
134
  * The action is cascade-fired (`when`): the engine fires it on truth, it
@@ -147,6 +152,7 @@ export declare interface ActionEvaluation {
147
152
  * group-membership grammars. */
148
153
  declare type ActionFields<TOp, TGroup> = {
149
154
  name: string;
155
+ semantics?: ActionSemantic[] | undefined;
150
156
  title?: string | undefined;
151
157
  description?: string | undefined;
152
158
  group?: TGroup | undefined;
@@ -168,36 +174,131 @@ export declare type ActionParam = v.InferOutput<typeof ActionParamSchema>;
168
174
  * effects: missing required params → ActionParamsInvalidError, action
169
175
  * does not commit. Resolved values feed `ValueExpr.param` lookups.
170
176
  */
171
- declare const ActionParamSchema: v.StrictObjectSchema<
172
- {
173
- readonly type: v.PicklistSchema<
174
- readonly [
175
- "string",
176
- "number",
177
- "boolean",
178
- "url",
179
- "dateTime",
180
- "actor",
181
- "doc.ref",
182
- "doc.refs",
183
- "json",
184
- ],
185
- `Invalid option: expected one of ${string}`
186
- >;
187
- readonly name: v.SchemaWithPipe<
188
- readonly [
189
- v.StringSchema<undefined>,
190
- v.MinLengthAction<string, 1, "must be a non-empty string">,
191
- ]
192
- >;
193
- readonly title: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
194
- readonly description: v.OptionalSchema<
195
- v.StringSchema<undefined>,
177
+ declare const ActionParamSchema: v.SchemaWithPipe<
178
+ readonly [
179
+ v.StrictObjectSchema<
180
+ {
181
+ readonly type: v.PicklistSchema<
182
+ readonly [
183
+ "string",
184
+ "number",
185
+ "boolean",
186
+ "url",
187
+ "dateTime",
188
+ "actor",
189
+ "doc.ref",
190
+ "doc.refs",
191
+ "json",
192
+ ],
193
+ `Invalid option: expected one of ${string}`
194
+ >;
195
+ readonly name: v.SchemaWithPipe<
196
+ readonly [
197
+ v.StringSchema<undefined>,
198
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
199
+ ]
200
+ >;
201
+ readonly title: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
202
+ readonly description: v.OptionalSchema<
203
+ v.StringSchema<undefined>,
204
+ undefined
205
+ >;
206
+ readonly required: v.OptionalSchema<
207
+ v.BooleanSchema<undefined>,
208
+ undefined
209
+ >;
210
+ readonly options: v.OptionalSchema<
211
+ v.GenericSchema<ChoiceOptions>,
212
+ undefined
213
+ >;
214
+ readonly validation: v.OptionalSchema<
215
+ v.GenericSchema<ScalarValidation>,
216
+ undefined
217
+ >;
218
+ },
196
219
  undefined
197
- >;
198
- readonly required: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
199
- },
200
- undefined
220
+ >,
221
+ v.CheckAction<
222
+ {
223
+ type:
224
+ | "string"
225
+ | "number"
226
+ | "boolean"
227
+ | "doc.ref"
228
+ | "doc.refs"
229
+ | "url"
230
+ | "actor"
231
+ | "dateTime"
232
+ | "json";
233
+ name: string;
234
+ title?: string | undefined;
235
+ description?: string | undefined;
236
+ required?: boolean | undefined;
237
+ options?: ChoiceOptions | undefined;
238
+ validation?: ScalarValidation | undefined;
239
+ },
240
+ (
241
+ issue: v.CheckIssue<{
242
+ type:
243
+ | "string"
244
+ | "number"
245
+ | "boolean"
246
+ | "doc.ref"
247
+ | "doc.refs"
248
+ | "url"
249
+ | "actor"
250
+ | "dateTime"
251
+ | "json";
252
+ name: string;
253
+ title?: string | undefined;
254
+ description?: string | undefined;
255
+ required?: boolean | undefined;
256
+ options?: ChoiceOptions | undefined;
257
+ validation?: ScalarValidation | undefined;
258
+ }>,
259
+ ) => string
260
+ >,
261
+ v.CheckAction<
262
+ {
263
+ type:
264
+ | "string"
265
+ | "number"
266
+ | "boolean"
267
+ | "doc.ref"
268
+ | "doc.refs"
269
+ | "url"
270
+ | "actor"
271
+ | "dateTime"
272
+ | "json";
273
+ name: string;
274
+ title?: string | undefined;
275
+ description?: string | undefined;
276
+ required?: boolean | undefined;
277
+ options?: ChoiceOptions | undefined;
278
+ validation?: ScalarValidation | undefined;
279
+ },
280
+ (
281
+ issue: v.CheckIssue<{
282
+ type:
283
+ | "string"
284
+ | "number"
285
+ | "boolean"
286
+ | "doc.ref"
287
+ | "doc.refs"
288
+ | "url"
289
+ | "actor"
290
+ | "dateTime"
291
+ | "json";
292
+ name: string;
293
+ title?: string | undefined;
294
+ description?: string | undefined;
295
+ required?: boolean | undefined;
296
+ options?: ChoiceOptions | undefined;
297
+ validation?: ScalarValidation | undefined;
298
+ }>,
299
+ ) => string
300
+ >,
301
+ ]
201
302
  >;
202
303
 
203
304
  /**
@@ -242,6 +343,8 @@ export declare function actionRendering(action: {
242
343
  | undefined;
243
344
  }): "absent" | "automation" | "button";
244
345
 
346
+ export declare type ActionSemantic = (typeof ACTION_SEMANTICS)[number];
347
+
245
348
  /** The fireable-action verdict for one action on an activity — its `allowed`
246
349
  * state, structured `disabledReason`, and declared params, tagged with the
247
350
  * owning activity. The per-action atom both projections share:
@@ -459,6 +562,26 @@ export declare const ACTOR_KINDS: readonly ["person", "agent", "system"];
459
562
 
460
563
  export declare type ActorKind = (typeof ACTOR_KINDS)[number];
461
564
 
565
+ export declare type ActorResolution<User> =
566
+ | {
567
+ readonly status: "resolved";
568
+ readonly actor: PersonActor;
569
+ readonly user: User;
570
+ }
571
+ | {
572
+ readonly status: "missing";
573
+ readonly actor: PersonActor;
574
+ }
575
+ | {
576
+ readonly status: "inaccessible";
577
+ readonly actor: PersonActor;
578
+ readonly cause?: unknown;
579
+ }
580
+ | {
581
+ readonly status: "not-person";
582
+ readonly actor: Actor;
583
+ };
584
+
462
585
  export { analyzeCondition };
463
586
 
464
587
  /** The definition surface the start contexts read — structural, so authored,
@@ -508,6 +631,11 @@ export declare function assertReadableModel<
508
631
  },
509
632
  >(doc: T): T;
510
633
 
634
+ export declare function assertReaderModelAcknowledgement(
635
+ expectedMinReaderModel: unknown,
636
+ context?: string,
637
+ ): asserts expectedMinReaderModel is typeof DATA_MODEL_MIN_READER;
638
+
511
639
  /**
512
640
  * One member of an `assignees`-kind entry's value — and the value of the
513
641
  * singular `assignee` kind. The WHO-FOR spec the inbox reverse-query and the
@@ -1355,10 +1483,10 @@ export declare interface AvailableActionsResult {
1355
1483
  /**
1356
1484
  * Type each caller-supplied name→value against the workflow's declared field
1357
1485
  * entries — the engine takes typed {@link InitialFieldValue}s, and a value's
1358
- * type IS its declared entry's kind. Only `input`-sourced entries read
1359
- * caller values (the engine silently ignores the rest), so anything else
1360
- * fails here, naming the fields that ARE settable; value validation stays
1361
- * in the engine.
1486
+ * type IS its declared entry's kind. Only `input`-sourced entries read caller
1487
+ * values, so anything else fails here, naming the fields that ARE settable.
1488
+ * The engine repeats this structural validation at its contract boundary;
1489
+ * value validation also stays there.
1362
1490
  */
1363
1491
  export declare function buildInitialFields({
1364
1492
  declared,
@@ -1422,6 +1550,17 @@ export declare interface ChildrenArgs extends InstanceRefArgs {
1422
1550
  activity?: string;
1423
1551
  }
1424
1552
 
1553
+ export declare interface ChoiceOption {
1554
+ title: string;
1555
+ value: ChoiceValue;
1556
+ }
1557
+
1558
+ export declare interface ChoiceOptions {
1559
+ list: ChoiceOption[];
1560
+ }
1561
+
1562
+ export declare type ChoiceValue = string | number;
1563
+
1425
1564
  /**
1426
1565
  * The action half of the mirrored claim pair. `field` references an
1427
1566
  * author-declared actor-valued entry (the pair's other half), resolved
@@ -1482,6 +1621,21 @@ export declare function clientConfigFromResource(res: WorkflowResource):
1482
1621
  * resolves to a client, or the router throws. */
1483
1622
  declare type ClientForGdr = (parsed: ParsedGdr) => WorkflowClient;
1484
1623
 
1624
+ /** Native project-user response returned by Sanity's project API. */
1625
+ export declare interface ClientProjectUser {
1626
+ readonly id: string;
1627
+ readonly displayName?: string;
1628
+ readonly email?: string;
1629
+ readonly imageUrl?: string | null;
1630
+ readonly [key: string]: unknown;
1631
+ }
1632
+
1633
+ /** Project-user directory for CLI, MCP, and server runtimes using the engine client. */
1634
+ export declare function clientProjectUserDirectory(
1635
+ client: WorkflowClient,
1636
+ projectId: string,
1637
+ ): ProjectUserDirectory<ClientProjectUser>;
1638
+
1485
1639
  /**
1486
1640
  * The engine's single source of "now".
1487
1641
  *
@@ -1949,7 +2103,9 @@ export declare class ContractViolationError extends WorkflowError<"contract-viol
1949
2103
  * `completeEffect` — the runtime still decides when to drain and
1950
2104
  * reports outcomes via `completeEffect`.
1951
2105
  */
1952
- export declare function createEngine(args: CreateEngineArgs): Engine;
2106
+ export declare function createEngine<Client extends WorkflowClient>(
2107
+ args: CreateEngineArgs<Client>,
2108
+ ): Engine;
1953
2109
 
1954
2110
  /**
1955
2111
  * The {@link EngineScopeArgs} scope pinned at construction, plus the
@@ -1959,8 +2115,11 @@ export declare function createEngine(args: CreateEngineArgs): Engine;
1959
2115
  * partition is the only thing keeping reads and writes off the wrong
1960
2116
  * environment.
1961
2117
  */
1962
- export declare interface CreateEngineArgs extends EngineScopeArgs {
1963
- effectHandlers?: Record<string, EffectHandler>;
2118
+ export declare interface CreateEngineArgs<
2119
+ Client extends WorkflowClient = WorkflowClient,
2120
+ > extends EngineScopeArgs {
2121
+ client: Client;
2122
+ effectHandlers?: Record<string, EffectHandler<Client>>;
1964
2123
  missingHandler?: MissingHandlerPolicy;
1965
2124
  loggerFactory?: LoggerFactory;
1966
2125
  /**
@@ -2012,19 +2171,70 @@ export declare function createTelemetryIntake(args: {
2012
2171
  }): TelemetryIntake;
2013
2172
 
2014
2173
  /**
2015
- * The reader floor the current model imposes — the compatibility half of
2016
- * {@link MODEL_STAMP}, stamped as `minReaderModel`: the oldest engine model
2017
- * that can safely interpret a document written at {@link DATA_MODEL_VERSION}.
2018
- * An additive change leaves it untouched (older engines read newer docs
2019
- * fine mixed fleets of Functions, Studios, and CLIs are the steady state);
2020
- * ONLY a breaking shape change raises it, and doing so is a declared,
2021
- * DATAMODEL.md-logged decision to fence out older readers.
2174
+ * The append-only, machine-readable counterpart of the model log in
2175
+ * `DATAMODEL.md`. It records compatibility decisions; the prose log retains
2176
+ * the reasoning, absent-value semantics, and old-writer round-trip proof.
2177
+ */
2178
+ export declare const DATA_MODEL_CHANGES: readonly [
2179
+ Readonly<{
2180
+ id: "governed-model-stamps";
2181
+ introducedInModel: 1;
2182
+ minReaderModel: 0;
2183
+ documentTypes: readonly ["definition", "instance"];
2184
+ compatibility: "additive";
2185
+ applicability: "unconditional";
2186
+ summary: "Definition and instance documents carry model provenance and reader-floor stamps.";
2187
+ }>,
2188
+ Readonly<{
2189
+ id: "subject-field-kind";
2190
+ introducedInModel: 2;
2191
+ minReaderModel: 0;
2192
+ documentTypes: readonly ["definition", "instance"];
2193
+ compatibility: "additive";
2194
+ applicability: "detectable";
2195
+ summary: "A workflow-level subject field identifies the document a workflow is about.";
2196
+ }>,
2197
+ Readonly<{
2198
+ id: "typed-scalar-choice-lists";
2199
+ introducedInModel: 2;
2200
+ minReaderModel: 2;
2201
+ documentTypes: readonly ["definition", "instance"];
2202
+ compatibility: "reader-floor";
2203
+ applicability: "detectable";
2204
+ summary: "Scalar fields may constrain writes to a persisted typed choice list.";
2205
+ }>,
2206
+ Readonly<{
2207
+ id: "action-semantics";
2208
+ introducedInModel: 2;
2209
+ minReaderModel: 0;
2210
+ documentTypes: readonly ["definition"];
2211
+ compatibility: "additive";
2212
+ applicability: "detectable";
2213
+ summary: "Ordinary actions may carry a closed bag of advisory workflow semantics.";
2214
+ }>,
2215
+ Readonly<{
2216
+ id: "inclusive-scalar-bounds";
2217
+ introducedInModel: 2;
2218
+ minReaderModel: 2;
2219
+ documentTypes: readonly ["definition", "instance"];
2220
+ compatibility: "reader-floor";
2221
+ applicability: "detectable";
2222
+ summary: "String, text, and number values may carry persisted inclusive bounds.";
2223
+ }>,
2224
+ ];
2225
+
2226
+ /**
2227
+ * The maximum reader floor this writer can emit. Individual documents derive
2228
+ * their `minReaderModel` from the compatibility-bearing features actually
2229
+ * present; a document written at {@link DATA_MODEL_VERSION} may therefore
2230
+ * carry a lower floor. Raising this maximum is a declared, DATAMODEL.md-logged
2231
+ * decision that requires readers-first fleet sequencing.
2022
2232
  */
2023
- export declare const DATA_MODEL_MIN_READER = 0;
2233
+ export declare const DATA_MODEL_MIN_READER = 2;
2024
2234
 
2025
2235
  /**
2026
2236
  * The engine's persisted data-model version — the provenance half of
2027
- * {@link MODEL_STAMP}, stamped as `modelVersion` on every engine-owned
2237
+ * the model stamp, written as `modelVersion` on every engine-owned
2028
2238
  * document at its construction and persist choke-points. Conforms-to
2029
2239
  * semantics: the stamp means "this document conforms to model N now" —
2030
2240
  * instances are re-stamped on every full persist, so mixed-version fleets
@@ -2035,7 +2245,19 @@ export declare const DATA_MODEL_MIN_READER = 0;
2035
2245
  * job. Declare every bump in `DATAMODEL.md`; the model-surface snapshot test
2036
2246
  * keeps undeclared drift red.
2037
2247
  */
2038
- export declare const DATA_MODEL_VERSION = 1;
2248
+ export declare const DATA_MODEL_VERSION = 2;
2249
+
2250
+ export declare interface DataModelChange {
2251
+ readonly id: string;
2252
+ readonly introducedInModel: number;
2253
+ readonly minReaderModel: number;
2254
+ readonly documentTypes: readonly DataModelDocumentType[];
2255
+ readonly compatibility: "additive" | "reader-floor";
2256
+ readonly applicability: "unconditional" | "detectable";
2257
+ readonly summary: string;
2258
+ }
2259
+
2260
+ export declare type DataModelDocumentType = "definition" | "instance";
2039
2261
 
2040
2262
  /**
2041
2263
  * Split a dataset resource id (`<projectId>.<dataset>`) into its parts — the
@@ -2211,6 +2433,10 @@ export declare interface DefinitionsForDocumentArgs {
2211
2433
  * perspective the caller loaded the document with.
2212
2434
  */
2213
2435
  document: CandidateDocument;
2436
+ /** Resource-qualified identity of `document`. Required when an applicable
2437
+ * definition's `start.filter` reads `$subjectHasInFlightInstance`; the
2438
+ * loaded value's bare `_id` cannot distinguish resources. */
2439
+ subject?: GdrUri;
2214
2440
  }
2215
2441
 
2216
2442
  /** A definition-level site address: every runtime {@link InsightSite}, plus
@@ -2335,6 +2561,8 @@ export declare interface DeployDefinitionResult {
2335
2561
  export declare interface DeployDefinitionsArgs<
2336
2562
  T extends WorkflowDefinitionInput<T> = WorkflowDefinition,
2337
2563
  > {
2564
+ /** Reviewed literal acknowledging the installed writer's maximum reader-floor capability. */
2565
+ expectedMinReaderModel: typeof DATA_MODEL_MIN_READER;
2338
2566
  /**
2339
2567
  * Resource-alias bindings for this deploy (alias name → physical resource).
2340
2568
  * A deploy-time abstraction ONLY: every `@<alias>:` reference in a
@@ -2392,7 +2620,7 @@ export declare type DeployedDefinition = WorkflowDefinition & {
2392
2620
  *
2393
2621
  * An aborted instance also no-ops: it parks on its stage forever, so a stale
2394
2622
  * deploy landing after abort's retract would re-lock the subjects with no
2395
- * remaining path to lift them. The gate is `abortedAt`, not `completedAt` —
2623
+ * remaining path to delete them. The gate is `abortedAt`, not `completedAt` —
2396
2624
  * a normal move *into* a terminal stage stamps `completedAt` in the same
2397
2625
  * persist and must still deploy that stage's guards.
2398
2626
  */
@@ -2404,6 +2632,7 @@ export declare function deployStageGuards(args: StageGuardArgs): Promise<void>;
2404
2632
  * definitions themselves — carrying `resourceAliases` here is what keeps a diff
2405
2633
  * fingerprinting the same physical content `deployDefinitions` would. */
2406
2634
  export declare interface DeployTarget {
2635
+ expectedMinReaderModel: typeof DATA_MODEL_MIN_READER;
2407
2636
  tag: string;
2408
2637
  workflowResource: WorkflowResource;
2409
2638
  resourceAliases?: ResourceAliases;
@@ -2746,7 +2975,7 @@ export declare type DisabledReason =
2746
2975
  /**
2747
2976
  * The actor's grants on an involved subject's resource don't allow the
2748
2977
  * content write the action's effects would perform there. Forecast
2749
- * per-resource: each subject named by a `doc.ref`/`doc.refs`/`release.ref`
2978
+ * per-resource: each subject named by a `doc.ref`/`subject`/`doc.refs`/`release.ref`
2750
2979
  * field entry outside the workflow's own resource is checked against
2751
2980
  * THAT resource's ACL. Advisory like every engine verdict — the subject's
2752
2981
  * lake still enforces; and it degrades open: a resource whose grants
@@ -2941,6 +3170,7 @@ export declare interface EditableFieldEvaluation {
2941
3170
  name: string;
2942
3171
  type: FieldKind;
2943
3172
  title?: string;
3173
+ validation?: ScalarValidation;
2944
3174
  /** Current resolved value; `undefined` until the field is first resolved. */
2945
3175
  value: unknown;
2946
3176
  /** Whether THIS actor may edit the field right now (window + predicate + guard). */
@@ -3072,6 +3302,20 @@ export declare type EffectCompletionStatus = Exclude<
3072
3302
  "cancelled"
3073
3303
  >;
3074
3304
 
3305
+ export declare type EffectHandler<
3306
+ Client extends WorkflowClient = WorkflowClient,
3307
+ > = {
3308
+ /** Bivariant so a concretely typed handler registry remains readable from
3309
+ * the non-generic Engine surface; only the typed drain invokes handlers. */
3310
+ bivarianceHack(
3311
+ params: Record<string, unknown>,
3312
+ ctx: EffectHandlerContext<Client>,
3313
+ ): Promise<{
3314
+ outputs?: Record<string, unknown>;
3315
+ ops?: FieldOp[];
3316
+ } | void>;
3317
+ }["bivarianceHack"];
3318
+
3075
3319
  /**
3076
3320
  * External effect handler — invoked at drain time with the resolved
3077
3321
  * `params` and a context. Returning `outputs` records them on the run's
@@ -3082,8 +3326,10 @@ export declare type EffectCompletionStatus = Exclude<
3082
3326
  * applier as an action's field ops (so a created doc's ref enters `$fields`, or
3083
3327
  * a screened outcome lands in a field the activity/stage gate reads). Effects
3084
3328
  * report results as field state, never by flipping an activity status, so `ops`
3085
- * excludes `status.set`. Throwing marks the effect as failed (and returns no
3086
- * `ops`).
3329
+ * excludes `status.set`. Unlike definition-authored field ops, completion ops
3330
+ * have no authoring location from which to infer a target scope: every returned
3331
+ * op must explicitly set `target.scope` to `'workflow'` or `'stage'`. Throwing
3332
+ * marks the effect as failed (and returns no `ops`).
3087
3333
  *
3088
3334
  * **Delivery is at-least-once — a handler MAY run more than once for the
3089
3335
  * same effect.** Two overlaps produce a double-run: the dispatching process
@@ -3101,38 +3347,38 @@ export declare type EffectCompletionStatus = Exclude<
3101
3347
  * already completed; derive external-system identifiers from `ctx.effectKey`
3102
3348
  * so the receiving system can dedupe the overlap the ledger can't see.
3103
3349
  */
3104
- export declare type EffectHandler = (
3105
- params: Record<string, unknown>,
3106
- ctx: {
3107
- /** The engine's own client — bound to the workflow resource the
3108
- * instance lives in. Use it for same-resource subjects; for a
3109
- * cross-resource subject, route through {@link clientFor} instead. */
3110
- client: WorkflowClient;
3111
- /**
3112
- * Resolve the client bound to a subject doc's own resource. A handler
3113
- * patches the SUBJECT (which may live in a different Sanity resource
3114
- * than the instance split-dataset GDR deploys); the drainer's
3115
- * `completeEffect` writes the INSTANCE through {@link client}. One
3116
- * client can't address both, so a handler that patches a foreign
3117
- * subject must route its write here.
3118
- *
3119
- * Pass the subject's GDR the URI a binding like `$fields.subject._id`
3120
- * resolves to (the hydrated doc's `_id`), or a full
3121
- * {@link GlobalDocumentReference}. Returns the `resourceClients` client
3122
- * for that resource when one is mapped, {@link client} for the workflow
3123
- * resource itself, and a sibling derived from {@link client}'s
3124
- * credentials otherwise. Throws if `ref` isn't a GDR a bare id can't
3125
- * be routed, so failing loud beats silently patching the wrong dataset.
3126
- */
3127
- clientFor: (ref: GdrUri | GlobalDocumentReference) => WorkflowClient;
3128
- instanceId: string;
3129
- effectKey: string;
3130
- log: (message: string, extra?: Record<string, unknown>) => void;
3131
- },
3132
- ) => Promise<{
3133
- outputs?: Record<string, unknown>;
3134
- ops?: FieldOp[];
3135
- } | void>;
3350
+ declare type EffectHandlerContext<Client extends WorkflowClient> = {
3351
+ /**
3352
+ * The exact concrete client supplied to `createEngine`, bound to the
3353
+ * workflow resource rather than an
3354
+ * engine wrapper. Its class identity, namespaces, configuration, and
3355
+ * credentials are preserved. Handler-owned requests are not automatically
3356
+ * tagged by the engine.
3357
+ */
3358
+ client: Client;
3359
+ /**
3360
+ * Resolve the client bound to a subject doc's own resource. A handler
3361
+ * patches the SUBJECT (which may live in a different Sanity resource
3362
+ * than the instance split-dataset GDR deploys); the drainer's
3363
+ * `completeEffect` writes the INSTANCE through {@link client}. One
3364
+ * client can't address both, so a handler that patches a foreign
3365
+ * subject must route its write here.
3366
+ *
3367
+ * Pass the subject's GDR — the URI a binding like `$fields.subject._id`
3368
+ * resolves to (the hydrated doc's `_id`), or a full
3369
+ * {@link GlobalDocumentReference}. Returns the `resourceClients` client
3370
+ * for that resource when one is mapped, {@link client} for the workflow
3371
+ * resource itself, and a sibling derived from {@link client}'s
3372
+ * credentials otherwise. Mapped clients are returned unchanged; an
3373
+ * unmapped foreign resource necessarily returns a configured sibling.
3374
+ * Throws if `ref` isn't a GDR — a bare id can't
3375
+ * be routed, so failing loud beats silently patching the wrong dataset.
3376
+ */
3377
+ clientFor: (ref: GdrUri | GlobalDocumentReference) => WorkflowClient;
3378
+ instanceId: string;
3379
+ effectKey: string;
3380
+ log: (message: string, extra?: Record<string, unknown>) => void;
3381
+ };
3136
3382
 
3137
3383
  export declare interface EffectHistoryEntry {
3138
3384
  _key: string;
@@ -3320,6 +3566,10 @@ export declare interface Engine {
3320
3566
  /** The resolved telemetry logger ({@link noopTelemetry} unless injected) —
3321
3567
  * exposed so adapters built on the engine log through the same seam. */
3322
3568
  readonly telemetry: WorkflowTelemetryLogger;
3569
+ /** Resolve durable actor provenance through this engine's project client. */
3570
+ resolveActor: (
3571
+ args: ResolveClientActorArgs,
3572
+ ) => Promise<ActorResolution<ClientProjectUser>>;
3323
3573
  deployDefinitions: <T extends WorkflowDefinitionInput<T>>(
3324
3574
  args: DeployDefinitionsArgs<T>,
3325
3575
  ) => Promise<DeployDefinitionsResult>;
@@ -3351,7 +3601,7 @@ export declare interface Engine {
3351
3601
  getInstance: (args: InstanceRefArgs) => Promise<WorkflowInstance>;
3352
3602
  /** The reactive {@link WatchSet} for an instance — every document whose
3353
3603
  * change should re-evaluate it (the instance, its ancestors, and the docs
3354
- * named by `doc.ref`/`doc.refs`/`release.ref` field entries on the workflow scope
3604
+ * named by `doc.ref`/`subject`/`doc.refs`/`release.ref` field entries on the workflow scope
3355
3605
  * + current stage) as exploded {@link SubscriptionDocument}s, plus the
3356
3606
  * instance's read perspective. Fetches the instance, then derives. A
3357
3607
  * reactive adapter that already holds the live instance calls the pure
@@ -3366,8 +3616,9 @@ export declare interface Engine {
3366
3616
  * its own; the consumer drives it. */
3367
3617
  session: (args: SessionArgs) => InstanceSession;
3368
3618
  /** Every lake mutation guard this instance registered, unioned across the
3369
- * instance's own resource and the resource of each `doc.ref`/`doc.refs`
3370
- * GDR it holds in state. For coherency refresh and housekeeping. */
3619
+ * instance's own resource and the resource of each
3620
+ * `doc.ref`/`subject`/`doc.refs` GDR it holds in state. For coherency
3621
+ * refresh and housekeeping. */
3371
3622
  guardsForInstance: (args: InstanceRefArgs) => Promise<MutationGuardDoc[]>;
3372
3623
  /** Every lake mutation guard a workflow deployed (any version), across the
3373
3624
  * datasources its guards statically name — the workflow resource plus any
@@ -3386,14 +3637,14 @@ export declare interface Engine {
3386
3637
  * in-flight instance whose watch-set includes `document` (a resource-qualified
3387
3638
  * GDR URI). For a non-reactive, content-change-driven runtime deciding which
3388
3639
  * instances a changed doc should `tick`. Matches the same ref set the forward
3389
- * watch-set uses (self, ancestors, current-stage `doc.ref`/`doc.refs`/`release.ref`)
3640
+ * watch-set uses (self, ancestors, current-stage `doc.ref`/`subject`/`doc.refs`/`release.ref`)
3390
3641
  * via the shared `collectWatchRefs`. Sorted by `startedAt` asc. */
3391
3642
  instancesForDocument: (
3392
3643
  args: InstancesForDocumentArgs,
3393
3644
  ) => Promise<WorkflowInstance[]>;
3394
3645
  /** The startable half of {@link Engine.instancesForDocument}: the latest
3395
3646
  * deployed version of every definition that applies to the LOADED candidate
3396
- * document — startable, a required subject entry accepts its `_type`, and
3647
+ * document — startable, the `subject`-kind entry accepts its `_type`, and
3397
3648
  * `start.filter` (browse-time-pure — `$fields` never binds) passes. All
3398
3649
  * matches, name ascending; advisory — a start picker's filter, never
3399
3650
  * enforcement. */
@@ -3401,9 +3652,10 @@ export declare interface Engine {
3401
3652
  args: DefinitionsForDocumentArgs,
3402
3653
  ) => Promise<DeployedDefinition[]>;
3403
3654
  /** Pre-flight `startInstance`'s gates for a definition + the
3404
- * `initialFields` gathered so far: the `start.allowed` verdict with its
3405
- * insight (disable the start affordance on a definitive false AND say
3406
- * why) plus the still-missing required inputs. Bindability-aware — a
3655
+ * `initialFields` gathered so far: structurally invalid rows, the
3656
+ * `start.allowed` verdict with its insight, and still-missing required
3657
+ * inputs. `allowed` is the overall startability signal; `outcome` describes
3658
+ * only the condition. Bindability-aware — a
3407
3659
  * predicate reading a not-yet-supplied entry reports `'unevaluable'`
3408
3660
  * with the entries named in `unboundReads`, never a collapsed verdict.
3409
3661
  * Pure read; the enforcement moment is `startInstance` itself. */
@@ -3452,8 +3704,9 @@ export declare interface Engine {
3452
3704
  * housekeeping exports — derives its working client onto this version via
3453
3705
  * {@link WorkflowClient.withConfig}, and `resourceClients`-resolved clients
3454
3706
  * are rebound the same way. A caller's configured `apiVersion` therefore
3455
- * never reaches engine traffic (the caller's own client instance is
3456
- * untouched). The one exception is a client that lacks `withConfig` and so
3707
+ * never reaches engine-owned traffic (the caller's own client instance is
3708
+ * untouched). Effect handlers are host traffic and deliberately receive the
3709
+ * caller's configuration unchanged. The other exception is a client that lacks `withConfig` and so
3457
3710
  * cannot be rebound — it must be built to serve this version; see
3458
3711
  * {@link WorkflowClient.withConfig}.
3459
3712
  */
@@ -3520,9 +3773,9 @@ export declare interface EngineScopeArgs {
3520
3773
 
3521
3774
  /**
3522
3775
  * Pull the {@link GlobalDocumentReference} values out of `doc.ref` /
3523
- * `doc.refs` (content) field entries. Content only — release field entries come
3524
- * from {@link entryReleaseRefs}, so guard discovery (which reads this via
3525
- * `collectEntryDocUris`) stays scoped to content docs.
3776
+ * `subject` / `doc.refs` (content) field entries. Content only — release
3777
+ * field entries come from {@link entryReleaseRefs}, so guard discovery (which
3778
+ * reads this via `collectEntryDocUris`) stays scoped to content docs.
3526
3779
  */
3527
3780
  export declare function entryDocRefs(
3528
3781
  entries: unknown,
@@ -3648,14 +3901,15 @@ export declare interface EvaluateStartArgs {
3648
3901
  * Evaluate one `start.filter` in the start-filter context: `document` (may
3649
3902
  * be absent — a root read is then GROQ null, fail-closed or vacuous-pass by
3650
3903
  * shape) as the GROQ root, the {@link StartScope} bindings plus
3651
- * `$definition`, and — only when
3652
- * `analyzeCondition` says the filter reads the dataset — the scope's fetched
3653
- * slice as `*`. Cheap pure evaluation otherwise: no I/O rides a filter that
3654
- * never scans. GROQ null ("can't decide") is `false` — every consumer of
3904
+ * `$definition`, and — when `analyzeCondition` says the filter reads the
3905
+ * dataset or the filter reads `$subjectHasInFlightInstance` — the scope's
3906
+ * fetched slice as `*`. Cheap pure evaluation otherwise: no I/O rides a
3907
+ * filter that needs neither. GROQ null ("can't decide") is `false` — every consumer of
3655
3908
  * this verdict fails closed; a parse/evaluation THROW is a malformed
3656
- * predicate, not an unevaluable one — rethrown loud, naming the definition
3657
- * (only writable by bypassing deploy validation), so every read surface that
3658
- * evaluates the filter reports the same context. A failed slice FETCH
3909
+ * predicate, not an unevaluable one — rethrown loud, naming the definition,
3910
+ * so every read surface that evaluates the filter reports the same context.
3911
+ * A caller that omits the prospective subject required by the synthetic
3912
+ * variable also throws as a scope-contract error. A failed slice FETCH
3659
3913
  * propagates as itself: transport trouble, never definition blame.
3660
3914
  */
3661
3915
  export declare function evaluateStartFilter(args: {
@@ -3744,6 +3998,30 @@ export declare const EXECUTOR_CLASSIFICATIONS: readonly [
3744
3998
  export declare type ExecutorClassification =
3745
3999
  (typeof EXECUTOR_CLASSIFICATIONS)[number];
3746
4000
 
4001
+ /**
4002
+ * Expand every `@<alias>:` reference in a definition to the physical GDR prefix
4003
+ * its alias binds to, returning a definition that carries only physical
4004
+ * references. Fails closed — naming the definition + alias — when a referenced
4005
+ * alias isn't bound: the check that stops a portable definition from deploying
4006
+ * against the wrong (or no) resource.
4007
+ *
4008
+ * Runs at deploy (inside {@link planDefinitionDeploy}), BEFORE the content
4009
+ * fingerprint, so a deployed definition never carries a logical alias. The
4010
+ * stored references are physical, and a rebind (same source, a different alias
4011
+ * map) surfaces as changed content — a new version, never a silent shift in what
4012
+ * the workflow reads. A no-op when the definition references no aliases.
4013
+ *
4014
+ * Rewrites string VALUES only (via the shared {@link mapJsonStrings} deep-walk),
4015
+ * skipping the prose fields in {@link PROSE_KEYS}. A reference that stands alone
4016
+ * (the whole value is `@<alias>:<id>`, e.g. a literal `doc.ref`) must expand to a
4017
+ * well-formed GDR — a malformed one (`@content:a:b`, an empty id) is rejected
4018
+ * here rather than failing when an instance later reads it.
4019
+ */
4020
+ export declare function expandResourceAliases(
4021
+ definition: WorkflowDefinition,
4022
+ resourceAliases: ResourceAliases | undefined,
4023
+ ): WorkflowDefinition;
4024
+
3747
4025
  export { explainCondition };
3748
4026
 
3749
4027
  export { ExplainConditionArgs };
@@ -3769,7 +4047,7 @@ export { ExplainConditionArgs };
3769
4047
  */
3770
4048
  export declare function explainStartAllowed(args: {
3771
4049
  allowed: string;
3772
- definition: Pick<ApplicabilitySource, "name">;
4050
+ definition: Pick<ApplicabilitySource, "name" | "fields">;
3773
4051
  /** The caller's input entries as a `$fields` map — the engine verbs build
3774
4052
  * it with `startFieldsParam` (field resolution's projection), so the
3775
4053
  * predicate can only see values the resolution would persist. */
@@ -3819,6 +4097,10 @@ export declare const FIELD_KIND_DISPLAY: {
3819
4097
  title: string;
3820
4098
  description: string;
3821
4099
  };
4100
+ subject: {
4101
+ title: string;
4102
+ description: string;
4103
+ };
3822
4104
  "release.ref": {
3823
4105
  title: string;
3824
4106
  description: string;
@@ -3887,6 +4169,7 @@ declare const FIELD_SCOPES: readonly ["workflow", "stage", "activity"];
3887
4169
  declare const FIELD_VALUE_KINDS: readonly [
3888
4170
  "doc.ref",
3889
4171
  "doc.refs",
4172
+ "subject",
3890
4173
  "release.ref",
3891
4174
  "string",
3892
4175
  "text",
@@ -3930,6 +4213,8 @@ declare type FieldEntryFields<TEditable, TGroup> = FieldBase<
3930
4213
  TGroup
3931
4214
  > & {
3932
4215
  type: FieldValueKind;
4216
+ options?: ChoiceOptions | undefined;
4217
+ validation?: ScalarValidation | undefined;
3933
4218
  types?: string[] | undefined;
3934
4219
  fields?: FieldShape[] | undefined;
3935
4220
  of?: FieldShape[] | undefined;
@@ -3985,6 +4270,8 @@ export declare interface FieldShape {
3985
4270
  name: string;
3986
4271
  title?: string | undefined;
3987
4272
  description?: string | undefined;
4273
+ options?: ChoiceOptions | undefined;
4274
+ validation?: ScalarValidation | undefined;
3988
4275
  fields?: FieldShape[] | undefined;
3989
4276
  of?: FieldShape[] | undefined;
3990
4277
  }
@@ -4018,6 +4305,10 @@ declare type FieldValueKind = (typeof FIELD_VALUE_KINDS)[number];
4018
4305
  export declare interface FieldValueMap {
4019
4306
  "doc.ref": GlobalDocumentReference | null;
4020
4307
  "doc.refs": GlobalDocumentReference[];
4308
+ /** THE document the workflow is about — same value as
4309
+ * {@link FieldValueMap."doc.ref"}, distinct kind so the runtime and UI
4310
+ * identify the subject deterministically (workflow scope, at most one). */
4311
+ subject: GlobalDocumentReference | null;
4021
4312
  "release.ref": ReleaseRef | null;
4022
4313
  string: string | null;
4023
4314
  /** Multiline string — same value as {@link FieldValueMap.string}, distinct kind for rendering. */
@@ -4354,9 +4645,6 @@ export declare type Guard = v.InferOutput<typeof GuardSchema>;
4354
4645
  */
4355
4646
  export declare const GUARD_DOC_TYPE = "temp.system.guard";
4356
4647
 
4357
- /** Retracted predicate — unconditionally allows, lifting the guard. */
4358
- export declare const GUARD_LIFTED_PREDICATE = "true";
4359
-
4360
4648
  export declare const GUARD_OWNER = "robot:workflow-engine";
4361
4649
 
4362
4650
  /**
@@ -4955,6 +5243,67 @@ export declare interface HydratedSnapshot {
4955
5243
  */
4956
5244
  export declare function inFlightFilter(): string;
4957
5245
 
5246
+ /** Why one caller-supplied initial-field row cannot be consumed. */
5247
+ export declare type InitialFieldIssue = {
5248
+ index: number;
5249
+ name: string;
5250
+ type: FieldKind;
5251
+ } & (
5252
+ | {
5253
+ reason: "duplicate";
5254
+ duplicateIndexes: number[];
5255
+ }
5256
+ | {
5257
+ reason: "undeclared";
5258
+ }
5259
+ | {
5260
+ reason: "wrong-kind";
5261
+ expectedTypes: FieldKind[];
5262
+ }
5263
+ | {
5264
+ reason: "not-input";
5265
+ source:
5266
+ | "working-memory"
5267
+ | Exclude<NonNullable<FieldEntry["initialValue"]>["type"], "input">;
5268
+ }
5269
+ | ({
5270
+ reason: "invalid-scope";
5271
+ stage: string;
5272
+ declaredType: FieldKind;
5273
+ } & (
5274
+ | {
5275
+ scope: "stage";
5276
+ }
5277
+ | {
5278
+ scope: "activity";
5279
+ activity: string;
5280
+ }
5281
+ ))
5282
+ );
5283
+
5284
+ /**
5285
+ * Explain every structural reason caller-supplied initial-field rows cannot
5286
+ * be consumed. Value-shape validation remains in the resolver because it
5287
+ * needs the matched declaration's complete schema.
5288
+ */
5289
+ export declare function initialFieldIssues(
5290
+ args: ScopedInitialFieldDeclarations & {
5291
+ initialFields: readonly InitialFieldValue[];
5292
+ },
5293
+ ): InitialFieldIssue[];
5294
+
5295
+ /**
5296
+ * Thrown before start writes when one or more caller-supplied `initialFields`
5297
+ * rows cannot be consumed by workflow-scope, input-sourced declarations.
5298
+ * `issues` is stable machine-readable remediation context; the message renders
5299
+ * the same complete set for logs and command-line callers.
5300
+ */
5301
+ export declare class InitialFieldsInvalidError extends WorkflowError<"initial-fields-invalid"> {
5302
+ readonly definition?: string;
5303
+ readonly issues: InitialFieldIssue[];
5304
+ constructor(args: { definition?: string; issues: InitialFieldIssue[] });
5305
+ }
5306
+
4958
5307
  /**
4959
5308
  * Initial value for an `input`-sourced entry — what a caller supplies at
4960
5309
  * `startInstance` (or `setStage`). Same discriminator shape as
@@ -5020,8 +5369,8 @@ export declare function instanceDocId(tag: string): string;
5020
5369
  * ({@link verdictGuardsForInstance}) fetches it once against the engine
5021
5370
  * datasource; the reactive adapters feed the same query/params to their
5022
5371
  * stores as a live subscription; {@link guardsForInstance} unions it across
5023
- * datasources for housekeeping. Matches lifted guards too (predicate
5024
- * `"true"`) the consumer decides how to render a lifted guard.
5372
+ * datasources for housekeeping. Ordinary stage retraction deletes guards, so
5373
+ * results represent active persisted guard documents.
5025
5374
  */
5026
5375
  export declare function instanceGuardQuery(instanceId: string): CompiledQuery;
5027
5376
 
@@ -5265,13 +5614,14 @@ export declare function isFilterScopedOut(entry: {
5265
5614
  export declare function isGdr(value: unknown): value is GlobalDocumentReference;
5266
5615
 
5267
5616
  /**
5268
- * Whether a guard has been lifted (retracted to the unconditional-allow
5269
- * predicate). A lift patches the predicate and keeps the doc, so lifted
5270
- * guards still appear in guard queries and streams — this is the
5271
- * discriminator a consumer filters or renders by.
5617
+ * Whether an entry is caller-filled at start/spawn the only source that
5618
+ * reads `initialFields`. Every other source (query/literal/fieldRead, or
5619
+ * working memory) resolves itself at materialisation, so a `$fields.<name>`
5620
+ * read of it can never bind on any read surface that evaluates a
5621
+ * `start.filter`. The deploy read-check keys on this.
5272
5622
  */
5273
- export declare function isGuardLifted(
5274
- guard: Pick<MutationGuardDoc, "predicate">,
5623
+ export declare function isInputSourced(
5624
+ entry: Pick<FieldEntry, "initialValue">,
5275
5625
  ): boolean;
5276
5626
 
5277
5627
  /**
@@ -5295,6 +5645,32 @@ export declare function isNotesEntry(
5295
5645
  }
5296
5646
  >;
5297
5647
 
5648
+ /** Whether a project-user API failure explicitly means the user is absent. */
5649
+ export declare function isProjectUserNotFoundError(error: unknown): boolean;
5650
+
5651
+ /** Entry-level {@link isSingleDocRefKind}: narrows a resolved entry to the
5652
+ * single-GDR arms (`doc.ref` / `subject`) — the value is one GDR (or null)
5653
+ * and the entry may carry the accepted-target `types`. */
5654
+ export declare function isSingleDocRefEntry(
5655
+ entry: ResolvedFieldEntry,
5656
+ ): entry is Extract<
5657
+ ResolvedFieldEntry,
5658
+ {
5659
+ _type: "doc.ref" | "subject";
5660
+ }
5661
+ >;
5662
+
5663
+ /**
5664
+ * Kinds whose value is a single content-document GDR — `doc.ref` and its
5665
+ * elevated alias `subject`. The one predicate behind every "this entry is one
5666
+ * document reference" branch (deref reads, query/spawn ref coercion, input
5667
+ * shape checks, watch-set collection), so the alias can't drift out of a
5668
+ * site.
5669
+ */
5670
+ export declare function isSingleDocRefKind(
5671
+ kind: string,
5672
+ ): kind is "doc.ref" | "subject";
5673
+
5298
5674
  /**
5299
5675
  * Whether a human may start this definition standalone (the default). A
5300
5676
  * `lifecycle: 'child'` definition is spawn-only — instantiated by a parent via
@@ -5308,15 +5684,16 @@ export declare function isStartableDefinition(definition: {
5308
5684
  }): boolean;
5309
5685
 
5310
5686
  /**
5311
- * The SUBJECT-ENTRY RULE: a required `doc.ref` / `doc.refs` input entry is
5312
- * what makes a definition "about" a subject document. One predicate shared by
5313
- * applicability's type matching (`acceptsDocumentType`), the autonomous-start
5314
- * deploy invariant, and the root-read deploy check (a root-reading
5315
- * `start.filter` needs a subject entry to bind a candidate root) so
5316
- * "counts as a subject" can't drift between them.
5687
+ * The SUBJECT-ENTRY RULE: the `subject`-kind entry is what makes a definition
5688
+ * "about" a subject document the kind is the discriminator, never a
5689
+ * heuristic over required refs. One predicate shared by applicability's type
5690
+ * matching (`acceptsDocumentType`), the autonomous-start deploy invariant,
5691
+ * and the root-read deploy check (a root-reading `start.filter` needs a
5692
+ * subject entry to bind a candidate root) — so "counts as a subject" can't
5693
+ * drift between them.
5317
5694
  */
5318
5695
  export declare function isSubjectEntry(
5319
- entry: Pick<FieldEntry, "required" | "type">,
5696
+ entry: Pick<FieldEntry, "type">,
5320
5697
  ): boolean;
5321
5698
 
5322
5699
  /**
@@ -5445,6 +5822,15 @@ export declare interface LoadedDoc {
5445
5822
 
5446
5823
  export declare type LoggerFactory = (name: string) => EngineLogger;
5447
5824
 
5825
+ /** A by-name reference to another workflow definition (an
5826
+ * `action.spawn.definition`). No `version` (or `'latest'`) means "the
5827
+ * highest deployed version at spawn time"; a numeric version pins one
5828
+ * deployed version permanently. */
5829
+ export declare interface LogicalRef {
5830
+ name: string;
5831
+ version?: number | "latest";
5832
+ }
5833
+
5448
5834
  export declare type ManualTarget = v.InferOutput<
5449
5835
  typeof StoredManualTargetSchema
5450
5836
  >;
@@ -5471,7 +5857,7 @@ export { MAX_COUNTERFACTUAL_INDEX };
5471
5857
 
5472
5858
  /**
5473
5859
  * The oldest engine model that can safely read a persisted engine document.
5474
- * The engine always writes the {@link MODEL_STAMP} pair, so a `modelVersion`
5860
+ * The engine always writes the stamp pair, so a `modelVersion`
5475
5861
  * with no `minReaderModel` is malformed foreign data — read conservatively:
5476
5862
  * the stamp itself is the floor. A doc with neither is model 0 (floor 0).
5477
5863
  */
@@ -5818,12 +6204,7 @@ export declare function parseDefinitionInput(
5818
6204
  caller: string,
5819
6205
  ): WorkflowDefinition;
5820
6206
 
5821
- /**
5822
- * The ONE way to read an instance's frozen {@link WorkflowInstance.definitionSnapshot}
5823
- * back into a {@link WorkflowDefinition} — wraps the parse so a corrupt
5824
- * snapshot fails with the instance id attached instead of a bare
5825
- * `SyntaxError`.
5826
- */
6207
+ /** Parse the frozen definition from a complete persisted instance. */
5827
6208
  export declare function parseDefinitionSnapshot(
5828
6209
  instance: WorkflowInstance,
5829
6210
  ): WorkflowDefinition;
@@ -5840,6 +6221,11 @@ export declare interface ParsedGdr {
5840
6221
  documentId: string;
5841
6222
  }
5842
6223
 
6224
+ declare type ParsedWorkflowConfig = v.InferOutput<typeof WorkflowConfigSchema>;
6225
+
6226
+ declare type ParsedWorkflowDeployment =
6227
+ ParsedWorkflowConfig["deployments"][number];
6228
+
5843
6229
  /**
5844
6230
  * Parse a GDR URI into its scheme + addressing parts. Throws on
5845
6231
  * unknown scheme or malformed shape.
@@ -5971,6 +6357,10 @@ export declare class PersistedDocShapeError extends WorkflowError<"persisted-doc
5971
6357
  });
5972
6358
  }
5973
6359
 
6360
+ export declare type PersonActor = Omit<Actor, "kind"> & {
6361
+ readonly kind: "person";
6362
+ };
6363
+
5974
6364
  /**
5975
6365
  * One row of the instance's idempotency ledger — a caller-supplied
5976
6366
  * `idempotencyKey` a state-changing verb already committed under. Recorded in
@@ -6030,6 +6420,26 @@ export declare function projectToWatchRef(args: {
6030
6420
  perspective: WorkflowPerspective | undefined;
6031
6421
  }): SanityDocument;
6032
6422
 
6423
+ /** Host integration for current project-user data. */
6424
+ export declare interface ProjectUserDirectory<User> {
6425
+ /** Return `inaccessible` for lookup failures; directory methods must not reject. */
6426
+ readonly findById: (id: string) => Promise<ProjectUserLookup<User>>;
6427
+ readonly findByEmail?: (email: string) => Promise<ProjectUserLookup<User>>;
6428
+ }
6429
+
6430
+ export declare type ProjectUserLookup<User> =
6431
+ | {
6432
+ readonly status: "resolved";
6433
+ readonly user: User;
6434
+ }
6435
+ | {
6436
+ readonly status: "missing";
6437
+ }
6438
+ | {
6439
+ readonly status: "inaccessible";
6440
+ readonly cause?: unknown;
6441
+ };
6442
+
6033
6443
  export declare interface QueryArgs {
6034
6444
  groq: string;
6035
6445
  params?: Record<string, unknown>;
@@ -6039,6 +6449,20 @@ export declare interface QueryInScopeArgs extends InstanceRefArgs, QueryArgs {}
6039
6449
 
6040
6450
  export { quoted };
6041
6451
 
6452
+ export declare const READER_MODEL_ROLLOUT_URL =
6453
+ "https://github.com/sanity-io/workflows/blob/main/docs/reader-model-rollout.md";
6454
+
6455
+ /** A producer has not explicitly acknowledged this engine's writer capability. */
6456
+ export declare class ReaderModelAcknowledgementError extends WorkflowError<"reader-model-acknowledgement"> {
6457
+ readonly code = "WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
6458
+ readonly expectedMinReaderModel: unknown;
6459
+ readonly engineMinReaderModel = 2;
6460
+ readonly engineModelVersion = 2;
6461
+ readonly documentationUrl =
6462
+ "https://github.com/sanity-io/workflows/blob/main/docs/reader-model-rollout.md";
6463
+ constructor(expectedMinReaderModel: unknown, context?: string);
6464
+ }
6465
+
6042
6466
  /**
6043
6467
  * The one spelling of the instance read discipline — model gate
6044
6468
  * ({@link assertReadableModel}) first, shape parse
@@ -6097,6 +6521,15 @@ export declare function refDataset<TType extends string = string>({
6097
6521
  type: TType;
6098
6522
  }): GlobalDocumentReference<TType>;
6099
6523
 
6524
+ /**
6525
+ * Kinds whose entries carry the accepted-target `types` facet — the
6526
+ * content-document reference kinds. `release.ref` is excluded: it points at a
6527
+ * release system doc whose type is fixed.
6528
+ */
6529
+ export declare function refKindAcceptsTypes(
6530
+ kind: string,
6531
+ ): kind is "doc.ref" | "doc.refs" | "subject";
6532
+
6100
6533
  /** Make a GDR pointer to a Media-Library-resource doc. */
6101
6534
  export declare function refMediaLibrary<TType extends string = string>({
6102
6535
  resourceId,
@@ -6117,15 +6550,24 @@ export declare class RefResourceUndeclaredError extends WorkflowError<"ref-resou
6117
6550
  }
6118
6551
 
6119
6552
  /**
6120
- * The GDR `type`s in a `doc.ref` / `doc.refs` value that the entry's declared
6121
- * accepted `types` rejectempty when the value conforms, the entry declares
6122
- * no `types`, or the kind isn't a ref. A GDR's `type` names the target
6123
- * document's schema type, so this is the declared-subject-type contract.
6124
- * Skips anything that isn't GDR-shaped the shape schemas scream about
6125
- * those. Boundaries that need their own error framing (the spawn `with`
6126
- * projection gates its remediation hint on the generic `"document"` type
6127
- * being among the rejects) read this; everything else goes through
6128
- * {@link refTypeIssues} / {@link checkFieldValue}.
6553
+ * Every spawn reference a definition carries, as logical
6554
+ * {@link LogicalRef} entriesthe definition's outgoing edges in the
6555
+ * spawn-dependency graph. The one walk deploy ordering, delete's
6556
+ * referrer check, and external dependency tooling all share, so what
6557
+ * counts as a reference can never drift between them.
6558
+ */
6559
+ export declare function refsOf(def: WorkflowDefinition): LogicalRef[];
6560
+
6561
+ /**
6562
+ * The GDR `type`s in a reference-kind ({@link refKindAcceptsTypes}) value
6563
+ * that the entry's declared accepted `types` reject — empty when the value
6564
+ * conforms, the entry declares no `types`, or the kind isn't a ref. A GDR's
6565
+ * `type` names the target document's schema type, so this is the
6566
+ * declared-target-type contract. Skips anything that isn't GDR-shaped — the
6567
+ * shape schemas scream about those. Boundaries that need their own error
6568
+ * framing (the spawn `with` projection gates its remediation hint on the
6569
+ * generic `"document"` type being among the rejects) read this; everything
6570
+ * else goes through {@link refTypeIssues} / {@link checkFieldValue}.
6129
6571
  */
6130
6572
  export declare function rejectedRefTypes(args: {
6131
6573
  entryType: string;
@@ -6213,12 +6655,18 @@ export declare class RequiredFieldNotProvidedError extends WorkflowError<"requir
6213
6655
  });
6214
6656
  }
6215
6657
 
6216
- /**
6217
- * Names an author's predicate may not use — engine-owned and author names
6218
- * share one namespace, so a predicate redefining a binding would silently
6219
- * shadow engine behaviour. Rejected at deploy; derived from
6220
- * {@link CONDITION_VARS}.
6221
- */
6658
+ /** The known persisted-model requirements applicable to one canonical stored tree. */
6659
+ export declare function requiredModelFeatures(
6660
+ documentType: DataModelDocumentType,
6661
+ document: unknown,
6662
+ ): readonly DataModelChange[];
6663
+
6664
+ /** The oldest engine model that can safely interpret one canonical stored tree. */
6665
+ export declare function requiredReaderModel(
6666
+ documentType: DataModelDocumentType,
6667
+ document: unknown,
6668
+ ): number;
6669
+
6222
6670
  export declare const RESERVED_CONDITION_VARS: readonly string[];
6223
6671
 
6224
6672
  /**
@@ -6242,6 +6690,23 @@ export declare interface ResolveAccessArgs {
6242
6690
  grantsFromPath?: string;
6243
6691
  }
6244
6692
 
6693
+ /** Resolves person provenance without treating agent or system ids as user ids. */
6694
+ export declare function resolveActor<User>(
6695
+ directory: ProjectUserDirectory<User>,
6696
+ actor: Actor,
6697
+ ): Promise<ActorResolution<User>>;
6698
+
6699
+ /** Resolve an actor through a plain engine client, without a UI adapter. */
6700
+ export declare function resolveClientActor(
6701
+ client: WorkflowClient,
6702
+ args: ResolveClientActorArgs,
6703
+ ): Promise<ActorResolution<ClientProjectUser>>;
6704
+
6705
+ export declare interface ResolveClientActorArgs {
6706
+ readonly actor: Actor;
6707
+ readonly projectId: string;
6708
+ }
6709
+
6245
6710
  /**
6246
6711
  * A resolved field entry as the engine persists it on an instance.
6247
6712
  * Discriminated by `_type` (bare — unique within this union); the `value`
@@ -6249,9 +6714,9 @@ export declare interface ResolveAccessArgs {
6249
6714
  * `initialValue` is `{type:'query'}`, of any kind) additionally carries
6250
6715
  * `resolvedAt` — the lake-read time — so `resolvedAt` is provenance-driven,
6251
6716
  * not kind-specific. `object` / `array` entries carry their declared
6252
- * sub-field shape (`fields` / `of`) — and `doc.ref` / `doc.refs` entries
6253
- * their declared accepted `types` — so the instance is self-describing for
6254
- * op-time validation and rendering.
6717
+ * sub-field shape (`fields` / `of`) — and the reference kinds
6718
+ * ({@link refKindAcceptsTypes}) their declared accepted `types` — so the
6719
+ * instance is self-describing for op-time validation and rendering.
6255
6720
  */
6256
6721
  export declare type ResolvedFieldEntry = {
6257
6722
  [K in FieldKind]: {
@@ -6263,6 +6728,8 @@ export declare type ResolvedFieldEntry = {
6263
6728
  value: FieldValueMap[K];
6264
6729
  /** Lake-read time, present only on `query`-sourced entries. */
6265
6730
  resolvedAt?: string;
6731
+ options?: ChoiceOptions;
6732
+ validation?: ScalarValidation;
6266
6733
  } & (K extends "object"
6267
6734
  ? {
6268
6735
  fields: FieldShape[];
@@ -6273,7 +6740,7 @@ export declare type ResolvedFieldEntry = {
6273
6740
  of: FieldShape[];
6274
6741
  }
6275
6742
  : Record<never, never>) &
6276
- (K extends "doc.ref" | "doc.refs"
6743
+ (K extends "doc.ref" | "doc.refs" | "subject"
6277
6744
  ? {
6278
6745
  types?: string[];
6279
6746
  }
@@ -6327,10 +6794,9 @@ export declare function resourceAliasesToMap(
6327
6794
  * boundaries probe this resolver per ref, so narrowing a resolver rejects
6328
6795
  * refs to the resources it stops serving.
6329
6796
  *
6330
- * Resolved clients ride the engine's API version like every other engine
6331
- * client: the verb scope rebinds them onto `ENGINE_API_VERSION` when they
6332
- * carry {@link WorkflowClient.withConfig} (one without it is used as
6333
- * returned and must be built to serve that version).
6797
+ * Engine-owned verb scopes rebind resolved clients onto
6798
+ * `ENGINE_API_VERSION`. Effect handlers receive resolver clients unchanged
6799
+ * because handler traffic belongs to the host.
6334
6800
  */
6335
6801
  export declare type ResourceClientResolver = (
6336
6802
  parsed: ParsedGdr,
@@ -6369,10 +6835,10 @@ export declare interface ResourceSurface {
6369
6835
  }
6370
6836
 
6371
6837
  /**
6372
- * Retract every guard for a stage being exited (lift predicate to allow), but
6838
+ * Retract every guard for a stage being exited by deleting it, but
6373
6839
  * only once the instance has genuinely moved off that stage — or was aborted
6374
6840
  * on it: an aborted instance keeps its `currentStage` (no stage move), so the
6375
- * `abortedAt` stamp is what licenses lifting the stage it still occupies. The
6841
+ * `abortedAt` stamp is what licenses retracting the stage it still occupies. The
6376
6842
  * gate is `abortedAt`, not `completedAt` — normal completion parks the
6377
6843
  * instance on a structurally terminal stage whose guards must stay live.
6378
6844
  * Skip if it is still live on the stage (a concurrent loop-back re-entered
@@ -6445,6 +6911,19 @@ export declare function sameResource(
6445
6911
  b: WorkflowResource,
6446
6912
  ): boolean;
6447
6913
 
6914
+ /** Inclusive scalar bounds. String/text bounds measure character length;
6915
+ * number bounds measure the numeric value. */
6916
+ export declare interface ScalarValidation {
6917
+ min?: number | undefined;
6918
+ max?: number | undefined;
6919
+ }
6920
+
6921
+ export declare function scalarValidationIssues(args: {
6922
+ entryType: string;
6923
+ validation: ScalarValidation | undefined;
6924
+ value: unknown;
6925
+ }): string[] | undefined;
6926
+
6448
6927
  /**
6449
6928
  * The DECLARED field tree of a runtime schema — every entry key (optional
6450
6929
  * keys marked `key?`), every enum vocabulary, every variant arm — walked out
@@ -6458,6 +6937,20 @@ export declare function schemaTreeShape(schema: v.GenericSchema): unknown;
6458
6937
 
6459
6938
  export { ScopeAssignment };
6460
6939
 
6940
+ export declare interface ScopedInitialFieldDeclarations {
6941
+ workflowFields: readonly FieldEntry[];
6942
+ stages?: readonly {
6943
+ name: string;
6944
+ fields?: readonly FieldEntry[] | undefined;
6945
+ activities?:
6946
+ | readonly {
6947
+ name: string;
6948
+ fields?: readonly FieldEntry[] | undefined;
6949
+ }[]
6950
+ | undefined;
6951
+ }[];
6952
+ }
6953
+
6461
6954
  export { sentenceCase };
6462
6955
 
6463
6956
  export declare interface SessionArgs {
@@ -6492,6 +6985,27 @@ export declare interface SiteConsequence {
6492
6985
  after: ConditionOutcome;
6493
6986
  }
6494
6987
 
6988
+ /** One statically invalid parent-to-child spawn contract found at deploy. */
6989
+ export declare type SpawnContractIssue =
6990
+ | {
6991
+ reason: "unresolved-definition";
6992
+ from: string;
6993
+ ref: string;
6994
+ }
6995
+ | {
6996
+ reason: "unconsumable-field";
6997
+ from: string;
6998
+ child: string;
6999
+ field: string;
7000
+ detail: string;
7001
+ };
7002
+
7003
+ /** Deploy refused one or more invalid static `spawn` contracts. */
7004
+ export declare class SpawnContractsInvalidError extends WorkflowError<"spawn-contracts-invalid"> {
7005
+ readonly issues: SpawnContractIssue[];
7006
+ constructor(args: { issues: SpawnContractIssue[]; message: string });
7007
+ }
7008
+
6495
7009
  export declare type Stage = StageFields<
6496
7010
  FieldEntry,
6497
7011
  Activity,
@@ -6570,12 +7084,13 @@ export declare type StageName = string;
6570
7084
  * dialect: everything the filter context binds ({@link START_FILTER_VARS})
6571
7085
  * plus `$fields`, which start time always has. No candidate root ever binds
6572
7086
  * here (the subject rides `$fields.<entry>.id`; a root read is
6573
- * deploy-rejected), so a false/GROQ-null verdict is genuinely fail-closed —
6574
- * there is no unbound-binding ambiguity to caveat. Bound in one place:
6575
- * `startContextParams` in the applicability evaluator.
7087
+ * deploy-rejected). Pre-flights can omit not-yet-collected input bindings and
7088
+ * report those reads as provisional. Bound in one place: `startContextParams`
7089
+ * in the applicability evaluator.
6576
7090
  */
6577
7091
  export declare const START_ALLOWED_VARS: readonly {
6578
7092
  name: string;
7093
+ label: string;
6579
7094
  description: string;
6580
7095
  }[];
6581
7096
 
@@ -6584,17 +7099,15 @@ export declare const START_ALLOWED_VARS: readonly {
6584
7099
  * not the rendered condition scope (no {@link ConditionVarBinding}: these
6585
7100
  * bind only while the filter is evaluated on the READ side — the
6586
7101
  * `definitionsForDocument` derivation and the Studio start control).
6587
- * `startInstance` never evaluates the filter. BROWSE-TIME-PURE: every var is
6588
- * knowable before any inputs exist, so `$fields` is deliberately absent a
6589
- * `$fields` read in a filter is deploy-rejected with a pointer to
6590
- * `start.allowed`, the context that binds it. An absent binding evaluates to
6591
- * GROQ null; the VERDICT-level coercion (null result not applicable) is
6592
- * what fails closed — what a null read does inside the expression depends on
6593
- * its shape. Bound in one place: `startContextParams` in the applicability
6594
- * evaluator.
7102
+ * `startInstance` never evaluates the filter. BROWSE-TIME-PURE: `$fields` is
7103
+ * deliberately absent a `$fields` read is deploy-rejected with a pointer
7104
+ * to `start.allowed`. The synthetic subject variable requires a prospective
7105
+ * subject; evaluation throws when the caller omits that required scope.
7106
+ * Bound in one place: `startContextParams` in the applicability evaluator.
6595
7107
  */
6596
7108
  export declare const START_FILTER_VARS: readonly {
6597
7109
  name: string;
7110
+ label: string;
6598
7111
  description: string;
6599
7112
  }[];
6600
7113
 
@@ -6623,8 +7136,10 @@ export declare type StartContext = Record<string, unknown>;
6623
7136
  * `RequiredFieldNotProvidedError` for it), never a `start.allowed` verdict.
6624
7137
  */
6625
7138
  export declare interface StartEvaluation {
6626
- /** The `start.allowed` verdict `outcome === 'satisfied'`. Vacuously true
6627
- * when the definition declares no `allowed`, exactly like the verb. */
7139
+ /** Overall preflight startability: every supplied initial-field row is
7140
+ * valid and the `start.allowed` outcome is satisfied. `outcome` describes
7141
+ * only the condition; structurally invalid rows can therefore produce
7142
+ * `allowed: false` with `outcome: 'satisfied'`. */
6628
7143
  allowed: boolean;
6629
7144
  /**
6630
7145
  * Three-valued, BINDABILITY-AWARE verdict: `'unsatisfied'` is a definitive
@@ -6653,6 +7168,10 @@ export declare interface StartEvaluation {
6653
7168
  name: string;
6654
7169
  type: FieldEntry["type"];
6655
7170
  }[];
7171
+ /** Supplied rows `startInstance` would reject before writing. Empty means
7172
+ * every row targets a workflow-scope, input-sourced declaration exactly
7173
+ * once. Render these as corrections, not an authorization verdict. */
7174
+ invalidInitialFields: InitialFieldIssue[];
6656
7175
  }
6657
7176
 
6658
7177
  /** Type-mirror of {@link startFields}: how standalone runs of this workflow
@@ -6691,10 +7210,11 @@ export declare interface StartInstanceArgs {
6691
7210
  * `initialValue` resolution for query/working-memory entries).
6692
7211
  *
6693
7212
  * To start an instance "about" a specific document, declare a
6694
- * `{ type: "doc.ref", name: "subject", initialValue: { type: "input" } }`
7213
+ * `{ type: "subject", name: "subject", initialValue: { type: "input" } }`
6695
7214
  * entry on the workflow and pass
6696
- * `{ type: "doc.ref", name: "subject", value: { id, type } }` here.
6697
- * Conditions then read it as `$fields.subject`.
7215
+ * `{ type: "subject", name: "subject", value: { id, type } }` here.
7216
+ * Conditions then read it as `$fields.subject`, and document pickers key
7217
+ * on the `subject` kind.
6698
7218
  */
6699
7219
  initialFields?: InitialFieldValue[];
6700
7220
  ancestors?: GlobalDocumentReference[];
@@ -6834,6 +7354,7 @@ export declare function startRefusal(definition: {
6834
7354
  * The caller-side half of the start contexts — everything the evaluating
6835
7355
  * surface knows that the definition doesn't. Every member is optional
6836
7356
  * because the surfaces genuinely differ (a pure consumer may hold no clock):
7357
+ * except for the subject identity required by `$subjectHasInFlightInstance`,
6837
7358
  * an absent binding evaluates each read of it to GROQ null, and where that
6838
7359
  * null lands decides the verdict — a predicate that can't decide without
6839
7360
  * the binding fails closed, while a count-of-matches clause over values
@@ -6848,9 +7369,13 @@ export declare interface StartScope {
6848
7369
  tag?: string | undefined;
6849
7370
  /** ISO clock reading — binds `$now`. */
6850
7371
  now?: string | undefined;
7372
+ /** Resource-qualified identity of the prospective subject. Required when
7373
+ * `start.filter` reads `$subjectHasInFlightInstance`; unlike a loaded
7374
+ * document's bare `_id`, this stays collision-free across resources. */
7375
+ subject?: GdrUri | undefined;
6851
7376
  /**
6852
7377
  * The WORKFLOW resource's dataset, for predicates that read it (`*[...]` or
6853
- * a deref) — invoked lazily, only when `analyzeCondition` says the
7378
+ * a deref) or `$subjectHasInFlightInstance` — invoked lazily only when the
6854
7379
  * predicate needs it. A slice is fine as long as it covers what predicates
6855
7380
  * scan; the engine verbs supply EVERY instance of the tag, completed
6856
7381
  * included — `*` carries no hidden predicate, so authors qualify in-flight
@@ -7843,7 +8368,7 @@ export declare const wallClock: Clock;
7843
8368
  export declare interface WatchSet {
7844
8369
  /**
7845
8370
  * Docs to subscribe to — instance + ancestors + the docs named by
7846
- * `doc.ref`/`doc.refs`/`release.ref` field entries — deduped by
8371
+ * `doc.ref`/`subject`/`doc.refs`/`release.ref` field entries — deduped by
7847
8372
  * `globalDocumentId`. Refs that aren't resource-qualified GDR URIs are
7848
8373
  * skipped (without a resource there's nothing to subscribe against).
7849
8374
  */
@@ -8067,7 +8592,7 @@ export declare const workflow: {
8067
8592
  * filters see for a given instance.
8068
8593
  *
8069
8594
  * Hydrates the instance's snapshot (instance + ancestors + every doc
8070
- * declared by a `doc.ref` / `doc.refs` entry in scope), then
8595
+ * declared by a `doc.ref` / `subject` / `doc.refs` entry in scope), then
8071
8596
  * evaluates the supplied GROQ in groq-js against that dataset. The
8072
8597
  * caller-free rendered scope cascade gates evaluate in is auto-bound —
8073
8598
  * the instance-derived vars ({@link FILTER_SCOPE_VARS}) with the open
@@ -8153,7 +8678,8 @@ export declare const workflow: {
8153
8678
  * Inngest/durable worker, any server) that holds no instances in memory: a
8154
8679
  * document changed; which instances should it `tick`? The watch-set covers
8155
8680
  * the instance itself, its ancestors, and the docs named by
8156
- * `doc.ref` / `doc.refs` / `release.ref` field entries on the workflow scope
8681
+ * `doc.ref` / `subject` / `doc.refs` / `release.ref` field entries on the
8682
+ * workflow scope
8157
8683
  * **and the current stage** — so a hand-rolled GROQ over `fields[]` gets it
8158
8684
  * subtly wrong (misses stage-scope refs, `release.ref`, ancestors).
8159
8685
  *
@@ -8182,7 +8708,7 @@ export declare const workflow: {
8182
8708
  * deployed definition that APPLIES to `document` — what a start picker for
8183
8709
  * it should offer. Loads the latest deployed version of each definition
8184
8710
  * visible to the engine's tag and filters it through the derivation
8185
- * ({@link applicableDefinitions}): startable ∧ a required subject entry
8711
+ * ({@link applicableDefinitions}): startable ∧ the `subject`-kind entry
8186
8712
  * accepts the doc's `_type` ∧ `start.filter` passes — evaluated in the
8187
8713
  * browse-time-pure start-filter context with `$tag`/`$definition`/`$now`
8188
8714
  * bound and the tag's instance slice (completed included) backing dataset
@@ -8190,9 +8716,12 @@ export declare const workflow: {
8190
8716
  * question; pre-flight it with {@link workflow.evaluateStart}.
8191
8717
  *
8192
8718
  * Takes the LOADED candidate document, not a ref — applicability evaluates
8193
- * its content, under whatever perspective the caller read it with. Surfaces
8194
- * ALL matches (name ascending), no engine ranking — presenting a picker or
8195
- * auto-picking is consumer policy. Advisory like every engine-side check.
8719
+ * its content, under whatever perspective the caller read it with. A
8720
+ * resource-qualified `subject` accompanies it when a filter reads
8721
+ * `$subjectHasInFlightInstance`; a bare document id cannot identify a
8722
+ * cross-resource subject. Surfaces ALL matches (name ascending), no engine
8723
+ * ranking — presenting a picker or auto-picking is consumer policy.
8724
+ * Advisory like every engine-side check.
8196
8725
  */
8197
8726
  definitionsForDocument: (
8198
8727
  rawArgs: Clocked<DefinitionsForDocumentArgs & EngineScopeArgs>,
@@ -8433,7 +8962,12 @@ export declare interface WorkflowCommitOptions {
8433
8962
  tag?: string;
8434
8963
  }
8435
8964
 
8436
- export declare type WorkflowConfig = v.InferOutput<typeof WorkflowConfigSchema>;
8965
+ export declare type WorkflowConfig = Omit<
8966
+ ParsedWorkflowConfig,
8967
+ "deployments"
8968
+ > & {
8969
+ deployments: WorkflowDeployment[];
8970
+ };
8437
8971
 
8438
8972
  declare const WorkflowConfigSchema: v.ObjectSchema<
8439
8973
  {
@@ -8448,6 +8982,10 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
8448
8982
  v.NonEmptyAction<string, "must not be empty">,
8449
8983
  ]
8450
8984
  >;
8985
+ readonly expectedMinReaderModel: v.OptionalSchema<
8986
+ v.CustomSchema<2, undefined>,
8987
+ undefined
8988
+ >;
8451
8989
  readonly tag: v.SchemaWithPipe<
8452
8990
  readonly [
8453
8991
  v.StringSchema<undefined>,
@@ -8709,6 +9247,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
8709
9247
  v.MinLengthAction<
8710
9248
  {
8711
9249
  name: string;
9250
+ expectedMinReaderModel?: 2 | undefined;
8712
9251
  tag: string;
8713
9252
  workflowResource:
8714
9253
  | {
@@ -8769,6 +9308,7 @@ declare const WorkflowConfigSchema: v.ObjectSchema<
8769
9308
  v.CheckAction<
8770
9309
  {
8771
9310
  name: string;
9311
+ expectedMinReaderModel?: 2 | undefined;
8772
9312
  tag: string;
8773
9313
  workflowResource:
8774
9314
  | {
@@ -8930,7 +9470,12 @@ declare const WorkflowDefinitionSchema: v.GenericSchema<
8930
9470
  WorkflowFields<FieldEntry, Stage, StartBlock>
8931
9471
  >;
8932
9472
 
8933
- export declare type WorkflowDeployment = WorkflowConfig["deployments"][number];
9473
+ export declare type WorkflowDeployment = Omit<
9474
+ ParsedWorkflowDeployment,
9475
+ "expectedMinReaderModel"
9476
+ > & {
9477
+ expectedMinReaderModel: typeof DATA_MODEL_MIN_READER;
9478
+ };
8934
9479
 
8935
9480
  export declare const WorkflowEffectCompleted: WorkflowTelemetryEvent<WorkflowEffectCompletedData>;
8936
9481
 
@@ -8985,6 +9530,7 @@ export declare type WorkflowErrorKind =
8985
9530
  | "effect-ops-invalid"
8986
9531
  | "effect-outputs-invalid"
8987
9532
  | "required-field-not-provided"
9533
+ | "initial-fields-invalid"
8988
9534
  | "workflow-state-diverged"
8989
9535
  | "partial-guard-deploy"
8990
9536
  | "start-not-primed"
@@ -8996,10 +9542,12 @@ export declare type WorkflowErrorKind =
8996
9542
  | "field-value-shape"
8997
9543
  | "ref-resource-undeclared"
8998
9544
  | "model-version-ahead"
9545
+ | "reader-model-acknowledgement"
8999
9546
  | "persisted-doc-shape"
9000
9547
  | "instance-not-found"
9001
9548
  | "definition-not-found"
9002
9549
  | "definition-in-use"
9550
+ | "spawn-contracts-invalid"
9003
9551
  | "effect-not-found"
9004
9552
  | "missing-effect-handler"
9005
9553
  | "contract-violation";
@@ -9095,9 +9643,9 @@ export declare interface WorkflowInstance extends SanityDocument {
9095
9643
  modelVersion?: number;
9096
9644
  /**
9097
9645
  * Reader floor — the oldest engine data model that can safely interpret
9098
- * this document (see {@link DATA_MODEL_MIN_READER}). Written alongside
9099
- * {@link WorkflowInstance.modelVersion}; additive model changes leave it,
9100
- * only breaking ones raise it.
9646
+ * this document. Derived from features actually present, bounded by
9647
+ * {@link DATA_MODEL_MIN_READER}, and written alongside
9648
+ * {@link WorkflowInstance.modelVersion}. Full persists never lower it.
9101
9649
  */
9102
9650
  minReaderModel?: number;
9103
9651
  /**
@@ -9405,11 +9953,11 @@ export declare interface WorkflowTransaction {
9405
9953
  /**
9406
9954
  * Queue a document delete. Deliberately a transaction-only capability —
9407
9955
  * the top-level client surface stays delete-free so no engine code path
9408
- * can casually remove documents; engine-side the sole consumer is
9409
- * `deleteDefinition` housekeeping (definition docs + orphaned guard docs),
9410
- * and outside the engine the CLI's `nuke` reset deletes raw engine-owned
9411
- * docs through it. Both the real `@sanity/client` Transaction and the test
9412
- * fake's TransactionHandle carry this shape.
9956
+ * can casually remove documents. Engine-side consumers are guard retraction
9957
+ * and `deleteDefinition` housekeeping; outside the engine the CLI's `nuke`
9958
+ * reset deletes raw engine-owned docs through it. Both the real
9959
+ * `@sanity/client` Transaction and the test fake's TransactionHandle carry
9960
+ * this shape.
9413
9961
  */
9414
9962
  delete: (id: string) => WorkflowTransaction;
9415
9963
  /**