@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.cjs CHANGED
@@ -55,28 +55,6 @@ function findCurrentActivityEntry(host, activityName) {
55
55
  return findOpenStageEntry(host)?.activities.find(a => a.name === activityName);
56
56
  }
57
57
 
58
- const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
59
-
60
- function terminalState(instance) {
61
- return instance.abortedAt !== void 0 ? "aborted" : instance.completedAt !== void 0 ? "completed" : "in-flight";
62
- }
63
-
64
- function isUnprimed(instance) {
65
- return instance.stages.length === 0 && terminalState(instance) === "in-flight";
66
- }
67
-
68
- function parseDefinitionSnapshot(instance) {
69
- try {
70
- return JSON.parse(instance.definitionSnapshot);
71
- } catch (err) {
72
- invariants.rethrowWithContext(err, `Failed to parse definitionSnapshot on instance "${instance._id}"`);
73
- }
74
- }
75
-
76
- function parentRef(instance) {
77
- return instance.ancestors.at(-1);
78
- }
79
-
80
58
  function effectiveEditable(baseline, override) {
81
59
  if (baseline === void 0) return;
82
60
  if (override === void 0) return baseline;
@@ -116,6 +94,9 @@ function resolveFieldSite(site, stage) {
116
94
  ...site.entry.title !== void 0 ? {
117
95
  title: site.entry.title
118
96
  } : {},
97
+ ...site.entry.validation !== void 0 ? {
98
+ validation: site.entry.validation
99
+ } : {},
119
100
  ref: {
120
101
  scope: site.scope,
121
102
  field: site.entry.name
@@ -150,7 +131,7 @@ function resolveEditTarget(args) {
150
131
  }
151
132
 
152
133
  function fieldWindowOpen(instance, site) {
153
- const terminal = terminalState(instance);
134
+ const terminal = invariants.terminalState(instance);
154
135
  if (terminal !== "in-flight") return {
155
136
  open: !1,
156
137
  detail: `instance ${terminal}`
@@ -203,14 +184,8 @@ function editDisabledReason(args) {
203
184
  kind: "not-editable"
204
185
  };
205
186
  if (!window.open) {
206
- const terminal = terminalState(instance);
207
- return terminal === "aborted" && instance.abortedAt !== void 0 ? {
208
- kind: "instance-aborted",
209
- abortedAt: instance.abortedAt
210
- } : terminal === "completed" && instance.completedAt !== void 0 ? {
211
- kind: "instance-completed",
212
- completedAt: instance.completedAt
213
- } : {
187
+ const terminalReason = instanceTerminalReason(instance);
188
+ return terminalReason !== void 0 ? terminalReason : {
214
189
  kind: "edit-window-closed",
215
190
  detail: window.detail ?? "closed"
216
191
  };
@@ -222,6 +197,18 @@ function editDisabledReason(args) {
222
197
  };
223
198
  }
224
199
 
200
+ function instanceTerminalReason(instance) {
201
+ const terminal = invariants.terminalState(instance);
202
+ if (terminal === "aborted" && instance.abortedAt !== void 0) return {
203
+ kind: "instance-aborted",
204
+ abortedAt: instance.abortedAt
205
+ };
206
+ if (terminal === "completed" && instance.completedAt !== void 0) return {
207
+ kind: "instance-completed",
208
+ completedAt: instance.completedAt
209
+ };
210
+ }
211
+
225
212
  function defineWorkflowEvent(options) {
226
213
  return {
227
214
  type: "log",
@@ -332,7 +319,7 @@ function instanceStartedData(args) {
332
319
  function instanceStartedDataFor(args) {
333
320
  return instanceStartedData({
334
321
  definition: {
335
- ...parseDefinitionSnapshot(args.instance),
322
+ ...invariants.parseDefinitionSnapshot(args.instance),
336
323
  ...args.instance.pinnedContentHash !== void 0 ? {
337
324
  contentHash: args.instance.pinnedContentHash
338
325
  } : {}
@@ -360,7 +347,7 @@ function stageTransitionedData(args) {
360
347
  }
361
348
 
362
349
  function openStage$1(instance) {
363
- const definition = parseDefinitionSnapshot(instance);
350
+ const definition = invariants.parseDefinitionSnapshot(instance);
364
351
  return {
365
352
  definition: definition,
366
353
  stage: definition.stages.find(s => s.name === instance.currentStage)
@@ -861,13 +848,13 @@ const checklistLines = groqConditionDescribe.checklistLines;
861
848
  function toGroqDescribeContext(ctx) {
862
849
  return {
863
850
  renderRead: read => renderWorkflowRead(read, ctx),
864
- renderRequirement: workflowRequirement,
851
+ renderRequirement: requirement => workflowRequirement(requirement, ctx),
865
852
  atomPatterns: [ rolesGateText, claimGateText ],
866
853
  nowParam: "now"
867
854
  };
868
855
  }
869
856
 
870
- const VAR_LABELS = new Map(invariants.CONDITION_VARS.map(entry => [ entry.name, entry.label ]));
857
+ const VAR_LABELS = new Map([ ...invariants.START_ALLOWED_VARS, ...invariants.CONDITION_VARS ].map(entry => [ entry.name, entry.label ]));
871
858
 
872
859
  function renderWorkflowRead(read, ctx) {
873
860
  if (read.variable === "fields" && typeof read.path[0] == "string") {
@@ -907,7 +894,7 @@ function renderWorkflowRead(read, ctx) {
907
894
  });
908
895
  }
909
896
 
910
- function workflowRequirement(requirement) {
897
+ function workflowRequirement(requirement, ctx) {
911
898
  const {target: target} = requirement, clause = clauseVarRequirement(requirement);
912
899
  if (clause !== void 0) return clause;
913
900
  if (target.variable === "assigned" && target.path.length === 0) {
@@ -920,7 +907,7 @@ function workflowRequirement(requirement) {
920
907
  text: "you must not be assigned to this activity"
921
908
  });
922
909
  }
923
- if (target.variable === "effectStatus" && typeof target.path[0] == "string") return effectStatusPhrase(requirement, target.path[0]);
910
+ return effectStatusPhrase(requirement, ctx);
924
911
  }
925
912
 
926
913
  function clauseVarRequirement(requirement) {
@@ -940,28 +927,40 @@ function clauseVarRequirement(requirement) {
940
927
  params: {},
941
928
  text: "no activity in this stage may have failed"
942
929
  });
930
+ if (target.variable === invariants.SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR) return kind === "truthy" ? groqConditionDescribe.phrase("requirement.subject-has-in-flight-instance", {
931
+ params: {},
932
+ text: "the subject must already have an in-flight workflow"
933
+ }) : groqConditionDescribe.phrase("requirement.subject-has-no-in-flight-instance", {
934
+ params: {},
935
+ text: "the subject must not already have an in-flight workflow"
936
+ });
943
937
  }
944
938
  }
945
939
 
946
- function effectStatusPhrase(requirement, effect) {
947
- const name = `the ${groqConditionDescribe.quoted(effect)} automation`;
940
+ function effectStatusPhrase(requirement, ctx) {
941
+ const {target: target} = requirement;
942
+ if (target.variable !== "effectStatus" || typeof target.path[0] != "string") return;
943
+ const effect = target.path[0], title = effectTitleOf(effect, ctx), name = `the ${groqConditionDescribe.quoted(title)} automation`;
948
944
  if (requirement.kind === "equals" && requirement.value === "done") return groqConditionDescribe.phrase("requirement.effect-done", {
949
945
  params: {
950
- effect: effect
946
+ effect: effect,
947
+ title: title
951
948
  },
952
- text: `${name} must have completed`
949
+ text: `${name} must have succeeded`
953
950
  });
954
951
  if (requirement.kind === "equals" && requirement.value === "failed") return groqConditionDescribe.phrase("requirement.effect-failed", {
955
952
  params: {
956
- effect: effect
953
+ effect: effect,
954
+ title: title
957
955
  },
958
956
  text: `${name} must have failed`
959
957
  });
960
958
  if (requirement.kind === "defined") return groqConditionDescribe.phrase("requirement.effect-settled", {
961
959
  params: {
962
- effect: effect
960
+ effect: effect,
961
+ title: title
963
962
  },
964
- text: `${name} must have settled`
963
+ text: `${name} must have finished`
965
964
  });
966
965
  }
967
966
 
@@ -1249,12 +1248,16 @@ const WAIT_TEXTS = {
1249
1248
  },
1250
1249
  text: `waits on editor content matching ${groqConditionDescribe.quoted(wait.condition)}`
1251
1250
  }),
1252
- effect: wait => groqConditionDescribe.phrase("autonomy-wait.effect", {
1253
- params: {
1254
- effect: wait.effect
1255
- },
1256
- text: `waits on the ${groqConditionDescribe.quoted(wait.effect)} automation`
1257
- }),
1251
+ effect: (wait, ctx) => {
1252
+ const title = effectTitleOf(wait.effect, ctx);
1253
+ return groqConditionDescribe.phrase("autonomy-wait.effect", {
1254
+ params: {
1255
+ effect: wait.effect,
1256
+ title: title
1257
+ },
1258
+ text: `waits on the ${groqConditionDescribe.quoted(title)} automation`
1259
+ });
1260
+ },
1258
1261
  "start-input": (wait, ctx) => {
1259
1262
  const title = fieldTitleOf({
1260
1263
  path: [ wait.field ]
@@ -1367,6 +1370,10 @@ function actionTitle(site, ctx) {
1367
1370
  return ctx.definition.stages.flatMap(stage => stage.activities ?? []).filter(activity => activity.name === site.activity).flatMap(activity => activity.actions ?? []).find(candidate => candidate.name === site.action)?.title ?? groqConditionDescribe.humanize(site.action);
1368
1371
  }
1369
1372
 
1373
+ function effectTitleOf(name, ctx) {
1374
+ return findEffect(ctx.definition, name)?.title ?? name;
1375
+ }
1376
+
1370
1377
  function conditionSitesOf(definition) {
1371
1378
  const sites = [];
1372
1379
  for (const [predicate, condition] of Object.entries(definition.predicates ?? {})) sites.push({
@@ -1494,25 +1501,17 @@ async function applicableDefinitions(args) {
1494
1501
  }
1495
1502
 
1496
1503
  async function evaluateStartFilter(args) {
1497
- const {filter: filter, definition: definition, document: document, scope: scope} = args;
1498
- let parsed;
1499
- try {
1500
- parsed = {
1501
- tree: groqJs.parse(filter),
1502
- needsDataset: groqConditionDescribe.analyzeCondition(filter).readsDataset
1503
- };
1504
- } catch (err) {
1505
- rethrowNamingDefinition({
1506
- err: err,
1507
- definition: definition,
1508
- key: "filter"
1509
- });
1510
- }
1511
- let dataset = [];
1512
- if (parsed.needsDataset) {
1513
- if (scope?.fetchDataset === void 0) return !1;
1514
- dataset = await scope.fetchDataset();
1515
- }
1504
+ const {filter: filter, definition: definition, document: document, scope: scope} = args, parsed = parseStartFilter({
1505
+ filter: filter,
1506
+ definition: definition
1507
+ });
1508
+ assertFilterSubject({
1509
+ readsSubject: parsed.readsSubject,
1510
+ definition: definition,
1511
+ scope: scope
1512
+ });
1513
+ const dataset = await filterDataset(parsed.needsDataset, scope);
1514
+ if (dataset === void 0) return !1;
1516
1515
  try {
1517
1516
  const result = await (await groqJs.evaluate(parsed.tree, {
1518
1517
  ...document !== void 0 ? {
@@ -1521,7 +1520,8 @@ async function evaluateStartFilter(args) {
1521
1520
  dataset: dataset,
1522
1521
  params: startContextParams({
1523
1522
  definition: definition,
1524
- scope: scope
1523
+ scope: scope,
1524
+ dataset: dataset
1525
1525
  })
1526
1526
  })).get();
1527
1527
  return invariants.isUnevaluable(result) ? !1 : !!result;
@@ -1534,11 +1534,38 @@ async function evaluateStartFilter(args) {
1534
1534
  }
1535
1535
  }
1536
1536
 
1537
+ function parseStartFilter(args) {
1538
+ const {filter: filter, definition: definition} = args;
1539
+ try {
1540
+ const readsSubject = readsSubjectHasInFlightInstance(filter);
1541
+ return {
1542
+ tree: groqJs.parse(filter),
1543
+ needsDataset: groqConditionDescribe.analyzeCondition(filter).readsDataset || readsSubject,
1544
+ readsSubject: readsSubject
1545
+ };
1546
+ } catch (err) {
1547
+ rethrowNamingDefinition({
1548
+ err: err,
1549
+ definition: definition,
1550
+ key: "filter"
1551
+ });
1552
+ }
1553
+ }
1554
+
1555
+ function assertFilterSubject(args) {
1556
+ if (!(!args.readsSubject || args.scope?.subject !== void 0)) throw new invariants.ContractViolationError(`start.filter on definition "${args.definition.name ?? "<unnamed>"}" reads $${invariants.SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR}, but the caller supplied no resource-qualified \`scope.subject\` — a loaded document alone cannot identify its project and dataset.`);
1557
+ }
1558
+
1559
+ async function filterDataset(needsDataset, scope) {
1560
+ if (!needsDataset) return [];
1561
+ if (scope?.fetchDataset !== void 0) return scope.fetchDataset();
1562
+ }
1563
+
1537
1564
  async function explainStartAllowed(args) {
1538
1565
  const {allowed: allowed, definition: definition, fields: fields, scope: scope} = args;
1539
1566
  let needsDataset;
1540
1567
  try {
1541
- needsDataset = groqConditionDescribe.analyzeCondition(allowed).readsDataset;
1568
+ needsDataset = groqConditionDescribe.analyzeCondition(allowed).readsDataset || readsSubjectHasInFlightInstance(allowed);
1542
1569
  } catch (err) {
1543
1570
  rethrowNamingDefinition({
1544
1571
  err: err,
@@ -1558,7 +1585,8 @@ async function explainStartAllowed(args) {
1558
1585
  params: startContextParams({
1559
1586
  definition: definition,
1560
1587
  scope: scope,
1561
- fields: fields
1588
+ fields: fields,
1589
+ dataset: dataset
1562
1590
  })
1563
1591
  });
1564
1592
  } catch (err) {
@@ -1574,11 +1602,23 @@ function unboundAllowedReads(allowed, fields) {
1574
1602
  return [ ...invariants.conditionFieldReadNames(allowed) ].filter(name => !Object.hasOwn(fields, name));
1575
1603
  }
1576
1604
 
1605
+ function unboundAllowedReadsWithSubject(args) {
1606
+ const {allowed: allowed, fields: fields, definition: definition} = args, subjectField = (definition.fields ?? []).find(invariants.isSubjectEntry)?.name, reads = unboundAllowedReads(allowed, fields);
1607
+ return subjectField !== void 0 && readsSubjectHasInFlightInstance(allowed) && !Object.hasOwn(fields, subjectField) && !reads.includes(subjectField) && reads.push(subjectField),
1608
+ reads;
1609
+ }
1610
+
1577
1611
  function rethrowNamingDefinition({err: err, definition: definition, key: key}) {
1578
- invariants.rethrowWithContext(err, `start.${key} on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (deploy validation rejects this was the definition written around it?)`);
1612
+ invariants.rethrowWithContext(err, `start.${key} on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (check the predicate and its evaluation inputs)`);
1613
+ }
1614
+
1615
+ function startContextParams({definition: definition, scope: scope, fields: fields, dataset: dataset}) {
1616
+ const subject = fields === void 0 ? scope?.subject : startSubject(definition, fields) ?? scope?.subject, params = baseStartParams(definition, scope);
1617
+ return fields !== void 0 && (params.fields = fields), subject !== void 0 && (params[invariants.SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR] = subjectHasInFlightInstance(dataset, subject)),
1618
+ params;
1579
1619
  }
1580
1620
 
1581
- function startContextParams({definition: definition, scope: scope, fields: fields}) {
1621
+ function baseStartParams(definition, scope) {
1582
1622
  return {
1583
1623
  ...definition.name !== void 0 ? {
1584
1624
  definition: definition.name
@@ -1588,13 +1628,33 @@ function startContextParams({definition: definition, scope: scope, fields: field
1588
1628
  } : {},
1589
1629
  ...scope?.now !== void 0 ? {
1590
1630
  now: scope.now
1591
- } : {},
1592
- ...fields !== void 0 ? {
1593
- fields: fields
1594
1631
  } : {}
1595
1632
  };
1596
1633
  }
1597
1634
 
1635
+ function readsSubjectHasInFlightInstance(groq) {
1636
+ return invariants.conditionParameterNames(groq).has(invariants.SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR);
1637
+ }
1638
+
1639
+ function startSubject(definition, fields) {
1640
+ const subjectEntry = (definition.fields ?? []).find(invariants.isSubjectEntry);
1641
+ if (subjectEntry === void 0) return;
1642
+ const value = fields[subjectEntry.name];
1643
+ return invariants.isGdr(value) ? value.id : void 0;
1644
+ }
1645
+
1646
+ function subjectHasInFlightInstance(dataset, subject) {
1647
+ return dataset.some(row => {
1648
+ if (typeof row != "object" || row === null) return !1;
1649
+ const instance = row;
1650
+ return instance._type !== invariants.WORKFLOW_INSTANCE_TYPE || instance.completedAt !== void 0 || !Array.isArray(instance.fields) ? !1 : instance.fields.some(entry => {
1651
+ if (typeof entry != "object" || entry === null) return !1;
1652
+ const field = entry;
1653
+ return field._type === "subject" && invariants.isGdr(field.value) && field.value.id === subject;
1654
+ });
1655
+ });
1656
+ }
1657
+
1598
1658
  async function evaluateDeclaredStartFilter(args) {
1599
1659
  const {definition: definition, document: document, scope: scope} = args, filter = definition.start?.filter;
1600
1660
  return filter === void 0 ? !0 : evaluateStartFilter({
@@ -1730,9 +1790,9 @@ function overlayInstanceInSnapshot(snapshot, instance) {
1730
1790
 
1731
1791
  function resolveFieldRead(args) {
1732
1792
  const {kind: kind, value: value, path: path, snapshot: snapshot, targetKind: targetKind} = args;
1733
- if (kind === "doc.ref") {
1793
+ if (invariants.isSingleDocRefKind(kind)) {
1734
1794
  if (!invariants.isGdr(value)) return null;
1735
- if (path === void 0 && targetKind === "doc.ref") return value;
1795
+ if (path === void 0 && targetKind !== void 0 && invariants.isSingleDocRefKind(targetKind)) return value;
1736
1796
  const base = derefBase(value, snapshot);
1737
1797
  return path === void 0 ? base : walkDocPath(base, path);
1738
1798
  }
@@ -1751,7 +1811,7 @@ function buildParams(args) {
1751
1811
  return {
1752
1812
  self: invariants.selfGdr(instance),
1753
1813
  fields: renderedFields(instance.fields ?? [], snapshot),
1754
- parent: parentRef(instance)?.id ?? null,
1814
+ parent: invariants.parentRef(instance)?.id ?? null,
1755
1815
  ancestors: instance.ancestors.map(a => a.id),
1756
1816
  stage: instance.currentStage,
1757
1817
  now: now,
@@ -1905,7 +1965,7 @@ function instanceDocId(tag) {
1905
1965
 
1906
1966
  function mapFieldRefValues(args) {
1907
1967
  const {entryType: entryType, value: value, fields: fields, of: of, mapRef: mapRef} = args;
1908
- return value == null ? value : entryType === "doc.ref" || entryType === "release.ref" ? mapRef(value) : entryType === "doc.refs" ? mapItems(value, mapRef) : entryType === "object" ? mapRowRefValues({
1968
+ return value == null ? value : invariants.isSingleDocRefKind(entryType) || entryType === "release.ref" ? mapRef(value) : entryType === "doc.refs" ? mapItems(value, mapRef) : entryType === "object" ? mapRowRefValues({
1909
1969
  row: value,
1910
1970
  shapes: fields ?? [],
1911
1971
  mapRef: mapRef
@@ -2112,6 +2172,27 @@ class RequiredFieldNotProvidedError extends invariants.WorkflowError {
2112
2172
  }
2113
2173
  }
2114
2174
 
2175
+ class InitialFieldsInvalidError extends invariants.WorkflowError {
2176
+ definition;
2177
+ issues;
2178
+ constructor(args) {
2179
+ const where = args.definition === void 0 ? "" : ` for workflow "${args.definition}"`, lines = args.issues.map(formatInitialFieldIssue).join(`\n`);
2180
+ super("initial-fields-invalid", `Initial fields${where} contain rows the engine cannot consume:\n${lines}\nRemove or correct every listed row before retrying.`),
2181
+ this.name = "InitialFieldsInvalidError", args.definition !== void 0 && (this.definition = args.definition),
2182
+ this.issues = args.issues;
2183
+ }
2184
+ }
2185
+
2186
+ function formatInitialFieldIssue(issue) {
2187
+ const row = ` - row ${issue.index}: ${issue.name} (${issue.type})`;
2188
+ if (issue.reason === "duplicate") return `${row} duplicates rows ${issue.duplicateIndexes.join(", ")}`;
2189
+ if (issue.reason === "undeclared") return `${row} is not declared by the workflow`;
2190
+ if (issue.reason === "wrong-kind") return `${row} has the wrong kind; expected ${issue.expectedTypes.join(" or ")}`;
2191
+ if (issue.reason === "not-input") return `${row} is ${issue.source}-sourced, not caller-input-sourced`;
2192
+ const scope = issue.scope === "activity" ? `activity "${issue.activity}" in stage "${issue.stage}"` : `stage "${issue.stage}"`;
2193
+ return `${row} targets ${scope}, not workflow scope (declared as ${issue.declaredType})`;
2194
+ }
2195
+
2115
2196
  class WorkflowStateDivergedError extends invariants.WorkflowError {
2116
2197
  instanceId;
2117
2198
  guardError;
@@ -2235,10 +2316,28 @@ function validateActionParams({action: action, activityName: activityName, calle
2235
2316
  });
2236
2317
  continue;
2237
2318
  }
2238
- present && (checkParamType(value, decl) || issues.push({
2319
+ if (!present) continue;
2320
+ if (!checkParamType(value, decl)) {
2321
+ issues.push({
2322
+ param: decl.name,
2323
+ reason: `expected type=${decl.type}, got ${typeof value}`
2324
+ });
2325
+ continue;
2326
+ }
2327
+ const choiceIssues = invariants.choiceValueIssues(decl.options, value);
2328
+ choiceIssues !== void 0 && issues.push({
2239
2329
  param: decl.name,
2240
- reason: `expected type=${decl.type}, got ${typeof value}`
2241
- }));
2330
+ reason: choiceIssues.join("; ")
2331
+ });
2332
+ const validationIssues = invariants.scalarValidationIssues({
2333
+ entryType: decl.type,
2334
+ validation: decl.validation,
2335
+ value: value
2336
+ });
2337
+ validationIssues !== void 0 && issues.push({
2338
+ param: decl.name,
2339
+ reason: validationIssues.join("; ")
2340
+ });
2242
2341
  }
2243
2342
  if (issues.length > 0) throw new ActionParamsInvalidError({
2244
2343
  action: action.name,
@@ -2348,7 +2447,10 @@ function validateEffectOps(ops, effectName) {
2348
2447
  const result = v__namespace.safeParse(v__namespace.array(invariants.StoredFieldOpSchema), ops);
2349
2448
  if (!result.success) throw new EffectOpsInvalidError({
2350
2449
  effect: effectName,
2351
- issues: invariants.formatIssues(result.issues)
2450
+ issues: invariants.formatIssues(result.issues, issue => {
2451
+ const path = issue.path;
2452
+ return issue.input === void 0 && path?.at(-2)?.key === "target" && path.at(-1)?.key === "scope" ? "Effect completion field ops require target.scope: 'workflow' | 'stage'." : issue.message;
2453
+ })
2352
2454
  });
2353
2455
  const activityScoped = result.output.find(op => op.target.scope === "activity");
2354
2456
  if (activityScoped !== void 0) throw new EffectOpsInvalidError({
@@ -2429,9 +2531,16 @@ function entryShape(entry) {
2429
2531
  fields: entry.fields
2430
2532
  } : entry._type === "array" ? {
2431
2533
  of: entry.of
2432
- } : entry._type === "doc.ref" || entry._type === "doc.refs" ? entry.types !== void 0 ? {
2534
+ } : invariants.isSingleDocRefEntry(entry) || entry._type === "doc.refs" ? entry.types !== void 0 ? {
2433
2535
  types: entry.types
2434
- } : {} : {};
2536
+ } : {} : {
2537
+ ...entry.options !== void 0 ? {
2538
+ options: entry.options
2539
+ } : {},
2540
+ ...entry.validation !== void 0 ? {
2541
+ validation: entry.validation
2542
+ } : {}
2543
+ };
2435
2544
  }
2436
2545
 
2437
2546
  function entrySlot(entry) {
@@ -2670,10 +2779,12 @@ function resolveOpValue(args) {
2670
2779
  }
2671
2780
 
2672
2781
  function readEntryFromMutation(ctx, src) {
2673
- const scopes = src.scope !== void 0 ? [ src.scope ] : [ "stage", "workflow" ], stageEntry = findOpenStageEntry(ctx.mutation);
2782
+ const scopes = src.scope !== void 0 ? [ src.scope ] : [ "activity", "stage", "workflow" ], stageEntry = findOpenStageEntry(ctx.mutation);
2674
2783
  for (const scope of scopes) {
2675
- const entry = (scope === "workflow" ? ctx.mutation.fields : stageEntry?.fields)?.find(s => s.name === src.field);
2676
- if (entry !== void 0 && entry.value !== void 0) return entry;
2784
+ let host;
2785
+ scope === "workflow" ? host = ctx.mutation.fields : scope === "stage" ? host = stageEntry?.fields : host = findCurrentActivityEntry(ctx.mutation, ctx.activityName)?.fields;
2786
+ const entry = host?.find(s => s.name === src.field);
2787
+ if (entry !== void 0) return entry;
2677
2788
  }
2678
2789
  }
2679
2790
 
@@ -3114,53 +3225,6 @@ async function assertInstanceWriteAllowed(args) {
3114
3225
  });
3115
3226
  }
3116
3227
 
3117
- const DATA_MODEL_VERSION = 1, DATA_MODEL_MIN_READER = 0, MODEL_STAMP = {
3118
- modelVersion: DATA_MODEL_VERSION,
3119
- minReaderModel: DATA_MODEL_MIN_READER
3120
- };
3121
-
3122
- function fieldTreeShape(value) {
3123
- if (Array.isArray(value)) return value.map(fieldTreeShape);
3124
- if (value === null) return "null";
3125
- if (typeof value == "object") {
3126
- const record = value;
3127
- return Object.fromEntries(Object.keys(record).toSorted().map(key => [ key, fieldTreeShape(record[key]) ]));
3128
- }
3129
- return typeof value;
3130
- }
3131
-
3132
- function modelVersionOf(doc) {
3133
- const stamp = doc.modelVersion;
3134
- return typeof stamp == "number" ? stamp : 0;
3135
- }
3136
-
3137
- function minReaderModelOf(doc) {
3138
- const floor = doc.minReaderModel;
3139
- return typeof floor == "number" ? floor : modelVersionOf(doc);
3140
- }
3141
-
3142
- class ModelVersionAheadError extends invariants.WorkflowError {
3143
- documentId;
3144
- documentModelVersion;
3145
- requiredReaderModel;
3146
- engineModelVersion;
3147
- constructor(args) {
3148
- super("model-version-ahead", `Document "${args.documentId}" was written by engine data model ${args.documentModelVersion} and requires a reader at model ${args.requiredReaderModel} or newer; this engine reads up to model ${DATA_MODEL_VERSION}. Upgrade @sanity/workflow-engine to a version that understands it.`),
3149
- this.name = "ModelVersionAheadError", this.documentId = args.documentId, this.documentModelVersion = args.documentModelVersion,
3150
- this.requiredReaderModel = args.requiredReaderModel, this.engineModelVersion = DATA_MODEL_VERSION;
3151
- }
3152
- }
3153
-
3154
- function assertReadableModel(doc) {
3155
- const requiredReaderModel = minReaderModelOf(doc);
3156
- if (requiredReaderModel > DATA_MODEL_VERSION) throw new ModelVersionAheadError({
3157
- documentId: doc._id,
3158
- documentModelVersion: modelVersionOf(doc),
3159
- requiredReaderModel: requiredReaderModel
3160
- });
3161
- return doc;
3162
- }
3163
-
3164
3228
  async function renderConditionScope(source, opts) {
3165
3229
  const {instance: instance, definition: definition, snapshot: snapshot, now: now} = source, base = buildParams({
3166
3230
  instance: instance,
@@ -3302,7 +3366,7 @@ function noTransitionFiresCause(input) {
3302
3366
  }
3303
3367
 
3304
3368
  function diagnoseInstance(input) {
3305
- const {instance: instance} = input, terminal = terminalState(instance);
3369
+ const {instance: instance} = input, terminal = invariants.terminalState(instance);
3306
3370
  if (terminal === "aborted" && instance.abortedAt !== void 0) {
3307
3371
  const reason = abortReason(instance);
3308
3372
  return {
@@ -3465,7 +3529,7 @@ function liveSubworkflowRefs(instance) {
3465
3529
  }
3466
3530
 
3467
3531
  function readsRaw(ref) {
3468
- return ref.type === WORKFLOW_INSTANCE_TYPE || ref.type === "system.release";
3532
+ return ref.type === invariants.WORKFLOW_INSTANCE_TYPE || ref.type === "system.release";
3469
3533
  }
3470
3534
 
3471
3535
  const DEFAULT_CONTENT_PERSPECTIVE = "drafts";
@@ -3534,7 +3598,7 @@ function instanceWatchesDocument(instance, document) {
3534
3598
  }
3535
3599
 
3536
3600
  function entryDocRefs(entries) {
3537
- return fieldEntries(entries).flatMap(entry => entry._type === "doc.ref" ? invariants.isGdr(entry.value) ? [ entry.value ] : [] : entry._type === "doc.refs" ? Array.isArray(entry.value) ? entry.value.filter(invariants.isGdr) : [] : []);
3601
+ return fieldEntries(entries).flatMap(entry => typeof entry._type == "string" && invariants.isSingleDocRefKind(entry._type) ? invariants.isGdr(entry.value) ? [ entry.value ] : [] : entry._type === "doc.refs" ? Array.isArray(entry.value) ? entry.value.filter(invariants.isGdr) : [] : []);
3538
3602
  }
3539
3603
 
3540
3604
  function entryReleaseRefs(entries) {
@@ -3542,7 +3606,7 @@ function entryReleaseRefs(entries) {
3542
3606
  }
3543
3607
 
3544
3608
  function entrySubjectRefs(entries) {
3545
- return fieldEntries(entries).flatMap(entry => (entry._type === "doc.ref" || entry._type === "release.ref") && invariants.isGdr(entry.value) ? [ entry.value ] : []);
3609
+ return fieldEntries(entries).flatMap(entry => (typeof entry._type == "string" && invariants.isSingleDocRefKind(entry._type) || entry._type === "release.ref") && invariants.isGdr(entry.value) ? [ entry.value ] : []);
3546
3610
  }
3547
3611
 
3548
3612
  function fieldEntries(entries) {
@@ -3565,7 +3629,13 @@ function perspectiveFromReleaseValue(value) {
3565
3629
 
3566
3630
  async function resolveDeclaredFields(args) {
3567
3631
  const {entryDefs: entryDefs, initialFields: initialFields, ctx: ctx, randomKey: randomKey2} = args;
3568
- if (entryDefs === void 0 || entryDefs.length === 0) return [];
3632
+ if (assertInitialFieldsConsumable({
3633
+ workflowFields: entryDefs ?? [],
3634
+ initialFields: initialFields,
3635
+ ...ctx.definitionName !== void 0 ? {
3636
+ definitionName: ctx.definitionName
3637
+ } : {}
3638
+ }), entryDefs === void 0 || entryDefs.length === 0) return [];
3569
3639
  assertRequiredInputProvided({
3570
3640
  entryDefs: entryDefs,
3571
3641
  initialFields: initialFields,
@@ -3587,6 +3657,83 @@ async function resolveDeclaredFields(args) {
3587
3657
  return out;
3588
3658
  }
3589
3659
 
3660
+ function initialFieldIssues(args) {
3661
+ const duplicateIndexes = /* @__PURE__ */ new Map;
3662
+ return args.initialFields.forEach((field, index) => {
3663
+ const key = `${field.name}\0${field.type}`, indexes = duplicateIndexes.get(key) ?? [];
3664
+ indexes.push(index), duplicateIndexes.set(key, indexes);
3665
+ }), args.initialFields.flatMap((field, index) => {
3666
+ const issues = [], indexes = duplicateIndexes.get(`${field.name}\0${field.type}`) ?? [];
3667
+ indexes.length > 1 && issues.push({
3668
+ index: index,
3669
+ name: field.name,
3670
+ type: field.type,
3671
+ reason: "duplicate",
3672
+ duplicateIndexes: indexes.filter(candidate => candidate !== index)
3673
+ });
3674
+ const workflowMatches = args.workflowFields.filter(entry => entry.name === field.name);
3675
+ if (workflowMatches.length > 0) {
3676
+ const exact = workflowMatches.find(entry => entry.type === field.type);
3677
+ return exact === void 0 ? issues.push({
3678
+ index: index,
3679
+ name: field.name,
3680
+ type: field.type,
3681
+ reason: "wrong-kind",
3682
+ expectedTypes: [ ...new Set(workflowMatches.map(entry => entry.type)) ]
3683
+ }) : exact.initialValue?.type !== "input" && issues.push({
3684
+ index: index,
3685
+ name: field.name,
3686
+ type: field.type,
3687
+ reason: "not-input",
3688
+ source: exact.initialValue?.type ?? "working-memory"
3689
+ }), issues;
3690
+ }
3691
+ const scoped = findNonWorkflowDeclaration(args.stages ?? [], field.name);
3692
+ return scoped !== void 0 ? issues.push({
3693
+ index: index,
3694
+ name: field.name,
3695
+ type: field.type,
3696
+ reason: "invalid-scope",
3697
+ ...scoped
3698
+ }) : issues.push({
3699
+ index: index,
3700
+ name: field.name,
3701
+ type: field.type,
3702
+ reason: "undeclared"
3703
+ }), issues;
3704
+ });
3705
+ }
3706
+
3707
+ function findNonWorkflowDeclaration(stages, name) {
3708
+ for (const stage of stages) {
3709
+ const stageField = stage.fields?.find(entry => entry.name === name);
3710
+ if (stageField !== void 0) return {
3711
+ scope: "stage",
3712
+ stage: stage.name,
3713
+ declaredType: stageField.type
3714
+ };
3715
+ for (const activity of stage.activities ?? []) {
3716
+ const activityField = activity.fields?.find(entry => entry.name === name);
3717
+ if (activityField !== void 0) return {
3718
+ scope: "activity",
3719
+ stage: stage.name,
3720
+ activity: activity.name,
3721
+ declaredType: activityField.type
3722
+ };
3723
+ }
3724
+ }
3725
+ }
3726
+
3727
+ function assertInitialFieldsConsumable(args) {
3728
+ const issues = initialFieldIssues(args);
3729
+ if (issues.length !== 0) throw new InitialFieldsInvalidError({
3730
+ ...args.definitionName !== void 0 ? {
3731
+ definition: args.definitionName
3732
+ } : {},
3733
+ issues: issues
3734
+ });
3735
+ }
3736
+
3590
3737
  function missingRequiredInputs(args) {
3591
3738
  return args.entryDefs.filter(entry => entry.required === !0 && invariants.isInputSourced(entry)).filter(entry => {
3592
3739
  const match = suppliedFieldFor(entry, args.initialFields);
@@ -3726,6 +3873,12 @@ function buildResolvedEntry({entry: entry, value: value, _key: _key, now: now})
3726
3873
  ...entry.description !== void 0 ? {
3727
3874
  description: entry.description
3728
3875
  } : {},
3876
+ ...entry.options !== void 0 ? {
3877
+ options: entry.options
3878
+ } : {},
3879
+ ...entry.validation !== void 0 ? {
3880
+ validation: entry.validation
3881
+ } : {},
3729
3882
  value: value,
3730
3883
  ...entry.initialValue?.type === "query" ? {
3731
3884
  resolvedAt: now
@@ -3755,7 +3908,9 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3755
3908
  value: value,
3756
3909
  types: entry.types,
3757
3910
  fields: entry.fields,
3758
- of: entry.of
3911
+ of: entry.of,
3912
+ options: entry.options,
3913
+ validation: entry.validation
3759
3914
  });
3760
3915
  return issues !== void 0 ? (ctx.recordDiscard?.({
3761
3916
  field: entry.name,
@@ -3778,7 +3933,9 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3778
3933
  value: value,
3779
3934
  types: entry.types,
3780
3935
  fields: entry.fields,
3781
- of: entry.of
3936
+ of: entry.of,
3937
+ options: entry.options,
3938
+ validation: entry.validation
3782
3939
  }), entry.initialValue?.type === "input" && ctx.inputProvenance !== "definition" && assertRefsWithinSurface({
3783
3940
  entryType: entry.type,
3784
3941
  entryName: entry.name,
@@ -3801,8 +3958,8 @@ function fieldMapFromResolved(entries) {
3801
3958
  }
3802
3959
 
3803
3960
  function assertInputValueShape(entry, value) {
3804
- if (entry.type === "doc.ref") {
3805
- assertGdrShape(value, `field entry "${entry.name}" (doc.ref)`);
3961
+ if (invariants.isSingleDocRefKind(entry.type)) {
3962
+ assertGdrShape(value, `field entry "${entry.name}" (${entry.type})`);
3806
3963
  return;
3807
3964
  }
3808
3965
  if (entry.type === "release.ref") {
@@ -3825,7 +3982,7 @@ function assertGdrShape(value, context) {
3825
3982
  }
3826
3983
 
3827
3984
  function normalizeQueryResult({entryType: entryType, raw: raw, workflowResource: workflowResource}) {
3828
- return raw == null ? raw : entryType === "doc.ref" ? coerceToGdr(raw, workflowResource) : entryType === "doc.refs" ? Array.isArray(raw) ? raw.map(item => coerceToGdr(item, workflowResource)).filter(v2 => v2 !== null) : [] : raw;
3985
+ return raw == null ? raw : invariants.isSingleDocRefKind(entryType) ? coerceToGdr(raw, workflowResource) : entryType === "doc.refs" ? Array.isArray(raw) ? raw.map(item => coerceToGdr(item, workflowResource)).filter(v2 => v2 !== null) : [] : raw;
3829
3986
  }
3830
3987
 
3831
3988
  function coerceGdrShape(raw, workflowResource) {
@@ -3856,11 +4013,21 @@ function coerceToGdr(raw, workflowResource) {
3856
4013
  } : null;
3857
4014
  }
3858
4015
 
3859
- const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ], NonEmpty = invariants.NonEmptyString, UnknownRecord = v__namespace.record(v__namespace.string(), v__namespace.unknown()), PersistedFieldShapeSchema = v__namespace.lazy(() => v__namespace.looseObject(invariants.tolerantEntries()({
4016
+ const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ], NonEmpty = invariants.NonEmptyString, UnknownRecord = v__namespace.record(v__namespace.string(), v__namespace.unknown()), PersistedChoiceOptionsSchema = v__namespace.looseObject({
4017
+ list: v__namespace.array(v__namespace.looseObject({
4018
+ title: v__namespace.string(),
4019
+ value: v__namespace.union([ v__namespace.string(), v__namespace.number() ])
4020
+ }))
4021
+ }), PersistedScalarValidationSchema = v__namespace.looseObject({
4022
+ min: v__namespace.optional(v__namespace.number()),
4023
+ max: v__namespace.optional(v__namespace.number())
4024
+ }), PersistedFieldShapeSchema = v__namespace.lazy(() => v__namespace.looseObject(invariants.tolerantEntries()({
3860
4025
  type: v__namespace.picklist(invariants.FIELD_VALUE_KINDS),
3861
4026
  name: NonEmpty,
3862
4027
  title: v__namespace.optional(v__namespace.string()),
3863
4028
  description: v__namespace.optional(v__namespace.string()),
4029
+ options: v__namespace.optional(PersistedChoiceOptionsSchema),
4030
+ validation: v__namespace.optional(PersistedScalarValidationSchema),
3864
4031
  fields: v__namespace.optional(v__namespace.array(PersistedFieldShapeSchema)),
3865
4032
  of: v__namespace.optional(v__namespace.array(PersistedFieldShapeSchema))
3866
4033
  }))), ExecutionContextSchema = invariants.tolerantObject()({
@@ -3890,6 +4057,8 @@ function fieldArm(kind, value) {
3890
4057
  name: NonEmpty,
3891
4058
  title: v__namespace.exactOptional(v__namespace.string()),
3892
4059
  description: v__namespace.exactOptional(v__namespace.string()),
4060
+ options: v__namespace.exactOptional(PersistedChoiceOptionsSchema),
4061
+ validation: v__namespace.exactOptional(PersistedScalarValidationSchema),
3893
4062
  value: value,
3894
4063
  resolvedAt: v__namespace.exactOptional(invariants.IsoTimestamp)
3895
4064
  };
@@ -3901,6 +4070,9 @@ const OptionalRefTypes = v__namespace.exactOptional(v__namespace.array(v__namesp
3901
4070
  })), v__namespace.looseObject(invariants.tolerantEntries()({
3902
4071
  ...fieldArm("doc.refs", invariants.fieldValueSchemas["doc.refs"]),
3903
4072
  types: OptionalRefTypes
4073
+ })), v__namespace.looseObject(invariants.tolerantEntries()({
4074
+ ...fieldArm("subject", invariants.fieldValueSchemas.subject),
4075
+ types: OptionalRefTypes
3904
4076
  })), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("release.ref", invariants.fieldValueSchemas["release.ref"]))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("string", invariants.fieldValueSchemas.string))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("text", invariants.fieldValueSchemas.text))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("number", invariants.fieldValueSchemas.number))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("boolean", invariants.fieldValueSchemas.boolean))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("date", invariants.fieldValueSchemas.date))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("datetime", invariants.fieldValueSchemas.datetime))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("url", invariants.fieldValueSchemas.url))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("actor", invariants.fieldValueSchemas.actor))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("assignee", invariants.fieldValueSchemas.assignee))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("assignees", invariants.fieldValueSchemas.assignees))), v__namespace.looseObject(invariants.tolerantEntries()({
3905
4077
  ...fieldArm("object", v__namespace.union([ v__namespace.null(), UnknownRecord ])),
3906
4078
  fields: v__namespace.array(PersistedFieldShapeSchema)
@@ -4114,7 +4286,7 @@ const TransitionVia = v__namespace.exactOptional(v__namespace.picklist([ "transi
4114
4286
  _type: v__namespace.pipe(v__namespace.string(), v__namespace.check(t => !knownHistoryTypes.has(t), "must not shadow a known history variant"))
4115
4287
  }), HistoryEntrySchema = v__namespace.union([ v__namespace.variant("_type", HISTORY_ARMS), UnknownHistoryEntry ]), WorkflowInstanceSchema = v__namespace.looseObject(invariants.tolerantEntries()({
4116
4288
  _id: NonEmpty,
4117
- _type: v__namespace.literal(WORKFLOW_INSTANCE_TYPE),
4289
+ _type: v__namespace.literal(invariants.WORKFLOW_INSTANCE_TYPE),
4118
4290
  _rev: v__namespace.string(),
4119
4291
  _createdAt: invariants.IsoTimestamp,
4120
4292
  _updatedAt: invariants.IsoTimestamp,
@@ -4147,7 +4319,7 @@ function parseInstanceDocument(doc) {
4147
4319
  return invariants.parsePersistedDoc({
4148
4320
  schema: WorkflowInstanceSchema,
4149
4321
  doc: doc,
4150
- docType: WORKFLOW_INSTANCE_TYPE
4322
+ docType: invariants.WORKFLOW_INSTANCE_TYPE
4151
4323
  });
4152
4324
  }
4153
4325
 
@@ -4157,7 +4329,7 @@ async function getInstanceDocument(client, instanceId) {
4157
4329
  }
4158
4330
 
4159
4331
  function readInstanceDoc(doc) {
4160
- return parseInstanceDocument(assertReadableModel(doc));
4332
+ return parseInstanceDocument(invariants.assertReadableModel(doc));
4161
4333
  }
4162
4334
 
4163
4335
  async function reload({client: client, instanceId: instanceId, tag: tag}) {
@@ -4226,14 +4398,12 @@ function buildInstanceBase(args) {
4226
4398
  ...actor !== void 0 ? {
4227
4399
  actor: actor
4228
4400
  } : {}
4229
- }, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries;
4230
- return {
4401
+ }, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries, body = {
4231
4402
  _id: id,
4232
- _type: WORKFLOW_INSTANCE_TYPE,
4403
+ _type: invariants.WORKFLOW_INSTANCE_TYPE,
4233
4404
  _rev: "",
4234
4405
  _createdAt: now,
4235
4406
  _updatedAt: now,
4236
- ...MODEL_STAMP,
4237
4407
  tag: args.tag,
4238
4408
  workflowResource: args.workflowResource,
4239
4409
  definition: args.definitionName,
@@ -4257,6 +4427,13 @@ function buildInstanceBase(args) {
4257
4427
  startedAt: now,
4258
4428
  lastChangedAt: now
4259
4429
  };
4430
+ return {
4431
+ ...body,
4432
+ ...invariants.modelStampFor({
4433
+ documentType: "instance",
4434
+ document: body
4435
+ })
4436
+ };
4260
4437
  }
4261
4438
 
4262
4439
  async function hydrateSnapshot(args) {
@@ -4313,7 +4490,7 @@ async function loadByGdr({defaultClient: defaultClient, clientForGdr: clientForG
4313
4490
  async function readDoc({client: client, id: id, perspective: perspective}) {
4314
4491
  if (perspective === "raw") {
4315
4492
  const doc2 = await client.getDocument(id) ?? null;
4316
- return doc2 ? doc2._type === WORKFLOW_INSTANCE_TYPE ? readInstanceDoc(doc2) : assertReadableModel(doc2) : null;
4493
+ return doc2 ? doc2._type === invariants.WORKFLOW_INSTANCE_TYPE ? readInstanceDoc(doc2) : invariants.assertReadableModel(doc2) : null;
4317
4494
  }
4318
4495
  const {query: query, params: params} = contentDocQuery(id);
4319
4496
  return await client.fetch(query, params, {
@@ -4370,7 +4547,7 @@ async function loadContext({client: client, instanceId: instanceId, options: opt
4370
4547
  if (!instance) throw new invariants.InstanceNotFoundError({
4371
4548
  instanceId: instanceId
4372
4549
  });
4373
- const definition = parseDefinitionSnapshot(instance), {clientForGdr: clientForGdr, refSurface: refSurface} = options, clock = options.clock ?? wallClock, snapshot = await hydrateSnapshot({
4550
+ const definition = invariants.parseDefinitionSnapshot(instance), {clientForGdr: clientForGdr, refSurface: refSurface} = options, clock = options.clock ?? wallClock, snapshot = await hydrateSnapshot({
4374
4551
  client: client,
4375
4552
  clientForGdr: clientForGdr,
4376
4553
  instance: instance,
@@ -4937,11 +5114,7 @@ function dedupById(guards) {
4937
5114
  return [ ...new Map(guards.map(g => [ g._id, g ])).values() ].sort((a, b) => a._id.localeCompare(b._id));
4938
5115
  }
4939
5116
 
4940
- const GUARD_OWNER = "robot:workflow-engine", GUARD_LIFTED_PREDICATE = "true";
4941
-
4942
- function isGuardLifted(guard) {
4943
- return guard.predicate === GUARD_LIFTED_PREDICATE;
4944
- }
5117
+ const GUARD_OWNER = "robot:workflow-engine";
4945
5118
 
4946
5119
  function resolveGuardRoute(guard, ctx) {
4947
5120
  const targets = resolveIdRefTargets(guard.match.idRefs, ctx);
@@ -5069,15 +5242,30 @@ async function deployStageGuards(args) {
5069
5242
  }
5070
5243
 
5071
5244
  async function retractStageGuards(args) {
5245
+ const observed = [];
5246
+ for (const {client: client, guardId: guardId} of resolvedStageGuardRoutes(args)) {
5247
+ const guard = await client.getDocument(guardId, {
5248
+ tag: REQUEST_TAG.guardRetract
5249
+ });
5250
+ if (guard) {
5251
+ if (guard._rev === void 0) throw new Error(`Cannot retract guard ${guardId}: persisted document has no revision`);
5252
+ observed.push({
5253
+ client: client,
5254
+ guardId: guardId,
5255
+ predicate: guard.predicate,
5256
+ revision: guard._rev
5257
+ });
5258
+ }
5259
+ }
5072
5260
  const live = await committedInstance(args);
5073
- if (live !== void 0 && !(live.currentStage === args.stageName && live.abortedAt === void 0)) for (const {client: client, guardId: guardId} of resolvedStageGuardRoutes(args)) await client.getDocument(guardId, {
5074
- tag: REQUEST_TAG.guardRetract
5075
- }) && await client.patch(guardId).set({
5076
- predicate: GUARD_LIFTED_PREDICATE
5077
- }).commit({
5078
- ...SYNC_COMMIT,
5079
- tag: REQUEST_TAG.guardRetract
5080
- });
5261
+ if (live !== void 0 && !(live.currentStage === args.stageName && live.abortedAt === void 0)) for (const {client: client, guardId: guardId, predicate: predicate, revision: revision} of observed) {
5262
+ const revisionCheck = client.patch(guardId).set({
5263
+ predicate: predicate
5264
+ }).ifRevisionId(revision);
5265
+ await client.transaction().patch(revisionCheck).delete(guardId).commit({
5266
+ tag: REQUEST_TAG.guardRetract
5267
+ });
5268
+ }
5081
5269
  }
5082
5270
 
5083
5271
  async function deleteOrphanedDefinitionGuards(args) {
@@ -5276,7 +5464,7 @@ async function persistThenDeploy({ctx: ctx, mutation: mutation, deploy: deploy})
5276
5464
  committedRev: committed._rev,
5277
5465
  restore: instanceStateFields({
5278
5466
  ...ctx.instance,
5279
- minReaderModel: minReaderModelOf(ctx.instance)
5467
+ minReaderModel: invariants.minReaderModelOf(ctx.instance)
5280
5468
  }),
5281
5469
  unset: ctx.instance.completedAt === void 0 ? [ "completedAt" ] : [],
5282
5470
  reversible: !spawned,
@@ -5607,7 +5795,7 @@ async function resolveContextHandoff({ctx: ctx, sub: sub, parentScope: parentSco
5607
5795
  }
5608
5796
 
5609
5797
  function canonicaliseSpawnValue({kind: kind, value: value, cx: cx}) {
5610
- return kind === "doc.ref" ? coerceSpawnGdr(value, cx) : kind === "doc.refs" && Array.isArray(value) ? value.map(v2 => coerceSpawnGdr(v2, cx)).filter(v2 => v2 !== null) : value;
5798
+ return invariants.isSingleDocRefKind(kind) ? coerceSpawnGdr(value, cx) : kind === "doc.refs" && Array.isArray(value) ? value.map(v2 => coerceSpawnGdr(v2, cx)).filter(v2 => v2 !== null) : value;
5611
5799
  }
5612
5800
 
5613
5801
  function assertBareIdRootsCorrectly(id, cx) {
@@ -5642,16 +5830,16 @@ async function resolveDefinitionRef({client: client, ref: ref, tag: tag}) {
5642
5830
  };
5643
5831
  wantsExplicit && (params.version = ref.version);
5644
5832
  const result = await client.fetch(definitionLookupGroq(wantsExplicit), params);
5645
- return result ? assertReadableModel(result) : null;
5833
+ return result ? invariants.assertReadableModel(result) : null;
5646
5834
  }
5647
5835
 
5648
5836
  async function prepareChildInstance(args) {
5649
5837
  const {client: client, parent: parent, definition: definition, initialFields: initialFields, context: context, actor: actor, now: now, refSurface: refSurface} = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId = instanceDocId(childTag), childRef = {
5650
5838
  id: invariants.gdrFromResource(workflowResource, childDocId),
5651
- type: WORKFLOW_INSTANCE_TYPE
5839
+ type: invariants.WORKFLOW_INSTANCE_TYPE
5652
5840
  }, ancestors = [ ...parent.ancestors, {
5653
5841
  id: invariants.selfGdr(parent),
5654
- type: WORKFLOW_INSTANCE_TYPE
5842
+ type: invariants.WORKFLOW_INSTANCE_TYPE
5655
5843
  } ], inheritedPerspective = parent.perspective, fieldDiscards = [], childFields = await resolveDeclaredFields({
5656
5844
  entryDefs: definition.fields,
5657
5845
  initialFields: initialFields,
@@ -6140,7 +6328,7 @@ async function evaluateInstance(args) {
6140
6328
  client: client,
6141
6329
  instanceId: instanceId,
6142
6330
  tag: tag
6143
- }), definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
6331
+ }), definition = invariants.parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
6144
6332
  client: client,
6145
6333
  workflowResource: workflowResource,
6146
6334
  resourceClients: resourceClients
@@ -6334,6 +6522,9 @@ async function evaluateEditableFields(args) {
6334
6522
  ...site.title !== void 0 ? {
6335
6523
  title: site.title
6336
6524
  } : {},
6525
+ ...site.validation !== void 0 ? {
6526
+ validation: site.validation
6527
+ } : {},
6337
6528
  value: value,
6338
6529
  editable: reason === void 0,
6339
6530
  ...reason !== void 0 ? {
@@ -6559,7 +6750,7 @@ function triggeredActionVerdict({action: action, when: when, insights: insights}
6559
6750
  },
6560
6751
  ...insights
6561
6752
  }) : {
6562
- action: action,
6753
+ ...actionEvaluationIdentity(action),
6563
6754
  allowed: !1,
6564
6755
  triggered: !0,
6565
6756
  disabledReason: {
@@ -6605,12 +6796,21 @@ function fireableActionVerdict({args: args, insights: insights}) {
6605
6796
  },
6606
6797
  ...insights
6607
6798
  }) : {
6608
- action: action,
6799
+ ...actionEvaluationIdentity(action),
6609
6800
  allowed: !0,
6610
6801
  ...insights
6611
6802
  };
6612
6803
  }
6613
6804
 
6805
+ function actionEvaluationIdentity(action) {
6806
+ return {
6807
+ action: action,
6808
+ ...action.semantics !== void 0 ? {
6809
+ semantics: action.semantics
6810
+ } : {}
6811
+ };
6812
+ }
6813
+
6614
6814
  async function instanceGuardReason({instance: instance, actor: actor, guards: guards}) {
6615
6815
  if (guards === void 0 || guards.length === 0) return;
6616
6816
  const denied = await instanceWriteDenials({
@@ -6625,15 +6825,8 @@ async function instanceGuardReason({instance: instance, actor: actor, guards: gu
6625
6825
  }
6626
6826
 
6627
6827
  function lifecycleReason({instance: instance, status: status, stageHasExits: stageHasExits}) {
6628
- const terminal = terminalState(instance);
6629
- if (terminal === "aborted" && instance.abortedAt !== void 0) return {
6630
- kind: "instance-aborted",
6631
- abortedAt: instance.abortedAt
6632
- };
6633
- if (terminal === "completed" && instance.completedAt !== void 0) return {
6634
- kind: "instance-completed",
6635
- completedAt: instance.completedAt
6636
- };
6828
+ const terminalReason = instanceTerminalReason(instance);
6829
+ if (terminalReason !== void 0) return terminalReason;
6637
6830
  if (!stageHasExits) return {
6638
6831
  kind: "stage-terminal",
6639
6832
  stage: instance.currentStage
@@ -6646,7 +6839,7 @@ function lifecycleReason({instance: instance, status: status, stageHasExits: sta
6646
6839
 
6647
6840
  function disabled(args) {
6648
6841
  return {
6649
- action: args.action,
6842
+ ...actionEvaluationIdentity(args.action),
6650
6843
  allowed: !1,
6651
6844
  disabledReason: args.reason,
6652
6845
  ...args.insight !== void 0 ? {
@@ -7022,7 +7215,7 @@ async function fireTriggeredAction({ctx: ctx, mutation: mutation, activity: acti
7022
7215
  async function primeInitialStage({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
7023
7216
  const instance = await getInstanceDocument(client, instanceId);
7024
7217
  if (!instance || instance.stages.length > 0) return;
7025
- const definition = parseDefinitionSnapshot(instance), stage = definition.stages.find(s => s.name === instance.currentStage);
7218
+ const definition = invariants.parseDefinitionSnapshot(instance), stage = definition.stages.find(s => s.name === instance.currentStage);
7026
7219
  if (stage === void 0) return;
7027
7220
  const ctx = await buildEngineContext({
7028
7221
  client: client,
@@ -7269,7 +7462,7 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
7269
7462
  async function stampDrainedRows({client: client, instance: instance, condemned: condemned, clock: clock, executionContext: executionContext}) {
7270
7463
  const now = (clock ?? wallClock)(), stamp = resolveExecutionContext(executionContext), ids = condemned.map(row => invariants.toBareId(row.ref.id)), children = await client.fetch("*[_id in $ids]{_id, currentStage, completedAt, abortedAt, modelVersion, minReaderModel}", {
7271
7464
  ids: ids
7272
- }), byId = new Map(children.map(c => [ assertReadableModel(c)._id, c ])), condemnedKeys = new Set(condemned.map(row => row._key)), history = [ ...instance.history ], subworkflows = (instance.subworkflows ?? []).map(row => {
7465
+ }), byId = new Map(children.map(c => [ invariants.assertReadableModel(c)._id, c ])), condemnedKeys = new Set(condemned.map(row => row._key)), history = [ ...instance.history ], subworkflows = (instance.subworkflows ?? []).map(row => {
7273
7466
  if (row.resolved !== void 0 || !condemnedKeys.has(row._key)) return row;
7274
7467
  const child = byId.get(invariants.toBareId(row.ref.id)), resolved = child !== void 0 ? terminalResolution(child) : {
7275
7468
  at: now,
@@ -7316,7 +7509,7 @@ function subworkflowResolvedEntry({row: row, at: at, status: status}) {
7316
7509
  async function propagateToAncestors({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
7317
7510
  const loaded = await loadPropagationPair(client, instanceId);
7318
7511
  if (loaded === void 0) return;
7319
- const {child: child, parent: parent} = loaded, definition = parseDefinitionSnapshot(parent), ctx = await buildEngineContext({
7512
+ const {child: child, parent: parent} = loaded, definition = invariants.parseDefinitionSnapshot(parent), ctx = await buildEngineContext({
7320
7513
  client: client,
7321
7514
  clientForGdr: clientForGdr,
7322
7515
  refSurface: refSurface,
@@ -7373,10 +7566,10 @@ async function propagateToAncestors({client: client, instanceId: instanceId, act
7373
7566
  async function loadPropagationPair(client, instanceId) {
7374
7567
  const child = await getInstanceDocument(client, instanceId);
7375
7568
  if (!child) return;
7376
- const parentGdr = parentRef(child);
7569
+ const parentGdr = invariants.parentRef(child);
7377
7570
  if (parentGdr === void 0) return;
7378
7571
  const parentDoc = await client.getDocument(invariants.toBareId(parentGdr.id));
7379
- if (!(!parentDoc || parentDoc._type !== WORKFLOW_INSTANCE_TYPE)) return {
7572
+ if (!(!parentDoc || parentDoc._type !== invariants.WORKFLOW_INSTANCE_TYPE)) return {
7380
7573
  child: child,
7381
7574
  parent: readInstanceDoc(parentDoc)
7382
7575
  };
@@ -7400,7 +7593,7 @@ async function recordOrphanedPropagation({ctx: ctx, mutation: mutation, child: c
7400
7593
  at: ctx.now,
7401
7594
  instanceRef: {
7402
7595
  id: childUri,
7403
- type: WORKFLOW_INSTANCE_TYPE
7596
+ type: invariants.WORKFLOW_INSTANCE_TYPE
7404
7597
  },
7405
7598
  detail: `Instance "${child._id}" names this instance as its parent but no subworkflow-registry row matches it, so its state cannot drive any gate here.`
7406
7599
  }), await persist(ctx, mutation));
@@ -7408,7 +7601,7 @@ async function recordOrphanedPropagation({ctx: ctx, mutation: mutation, child: c
7408
7601
 
7409
7602
  function startMutation(instance) {
7410
7603
  return {
7411
- minReaderModel: minReaderModelOf(instance),
7604
+ minReaderModel: invariants.minReaderModelOf(instance),
7412
7605
  currentStage: instance.currentStage,
7413
7606
  fields: (instance.fields ?? []).map(s => ({
7414
7607
  ...s
@@ -7457,9 +7650,7 @@ function startMutation(instance) {
7457
7650
  }
7458
7651
 
7459
7652
  function instanceStateFields(src) {
7460
- const fields = {
7461
- modelVersion: DATA_MODEL_VERSION,
7462
- minReaderModel: Math.max(src.minReaderModel, DATA_MODEL_MIN_READER),
7653
+ const state = {
7463
7654
  currentStage: src.currentStage,
7464
7655
  fields: src.fields,
7465
7656
  stages: src.stages,
@@ -7468,10 +7659,22 @@ function instanceStateFields(src) {
7468
7659
  effectHistory: src.effectHistory,
7469
7660
  context: src.context,
7470
7661
  history: src.history,
7471
- processedRequests: src.processedRequests ?? []
7662
+ processedRequests: src.processedRequests ?? [],
7663
+ ...src.completedAt !== void 0 ? {
7664
+ completedAt: src.completedAt
7665
+ } : {},
7666
+ ...src.abortedAt !== void 0 ? {
7667
+ abortedAt: src.abortedAt
7668
+ } : {}
7669
+ };
7670
+ return {
7671
+ ...invariants.modelStampFor({
7672
+ documentType: "instance",
7673
+ document: state,
7674
+ storedMinReaderModel: src.minReaderModel
7675
+ }),
7676
+ ...state
7472
7677
  };
7473
- return src.completedAt !== void 0 && (fields.completedAt = src.completedAt), src.abortedAt !== void 0 && (fields.abortedAt = src.abortedAt),
7474
- fields;
7475
7678
  }
7476
7679
 
7477
7680
  function liveViewContext(ctx, mutation) {
@@ -7683,16 +7886,7 @@ function validateEffectOutputs(args) {
7683
7886
  issues.push(`"${key}" is not a declared output`);
7684
7887
  continue;
7685
7888
  }
7686
- const shapeIssues = invariants.checkFieldValue({
7687
- entryType: shape.type,
7688
- value: value,
7689
- ...shape.fields !== void 0 ? {
7690
- fields: shape.fields
7691
- } : {},
7692
- ...shape.of !== void 0 ? {
7693
- of: shape.of
7694
- } : {}
7695
- });
7889
+ const shapeIssues = effectOutputIssues(shape, value);
7696
7890
  shapeIssues !== void 0 && issues.push(...shapeIssues.map(i => `"${key}": ${i}`));
7697
7891
  }
7698
7892
  if (issues.length > 0) throw new EffectOutputsInvalidError({
@@ -7701,6 +7895,25 @@ function validateEffectOutputs(args) {
7701
7895
  });
7702
7896
  }
7703
7897
 
7898
+ function effectOutputIssues(shape, value) {
7899
+ return invariants.checkFieldValue({
7900
+ entryType: shape.type,
7901
+ value: value,
7902
+ ...shape.fields !== void 0 ? {
7903
+ fields: shape.fields
7904
+ } : {},
7905
+ ...shape.of !== void 0 ? {
7906
+ of: shape.of
7907
+ } : {},
7908
+ ...shape.options !== void 0 ? {
7909
+ options: shape.options
7910
+ } : {},
7911
+ ...shape.validation !== void 0 ? {
7912
+ validation: shape.validation
7913
+ } : {}
7914
+ });
7915
+ }
7916
+
7704
7917
  function requirePendingEffect(instance, effectKey) {
7705
7918
  const pending = instance.pendingEffects.find(e => e._key === effectKey);
7706
7919
  if (pending !== void 0) return pending;
@@ -7896,7 +8109,7 @@ function idsArm(filter, params) {
7896
8109
  function instancesQuery(args) {
7897
8110
  const {tag: tag, filter: filter = {}} = args;
7898
8111
  if (invariants.validateTag(tag), filter.limit !== void 0 && (!Number.isInteger(filter.limit) || filter.limit <= 0)) throw new invariants.ContractViolationError(`instancesQuery: limit must be a positive integer; got ${JSON.stringify(filter.limit)}`);
7899
- const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, invariants.tagScopeFilter() ], params = {
8112
+ const conditions = [ `_type == "${invariants.WORKFLOW_INSTANCE_TYPE}"`, invariants.tagScopeFilter() ], params = {
7900
8113
  tag: tag
7901
8114
  };
7902
8115
  filter.includeCompleted !== !0 && conditions.push(inFlightFilter()), filter.definition !== void 0 && (conditions.push("definition == $definition"),
@@ -7921,7 +8134,7 @@ async function sortByDependencies({client: client, definitions: definitions, tag
7921
8134
  byName.set(def.name, def);
7922
8135
  }
7923
8136
  const findInBatch = ref => findRefInBatch(byName, ref);
7924
- return await assertCrossBatchRefsDeployed({
8137
+ return await assertSpawnContracts({
7925
8138
  client: client,
7926
8139
  definitions: definitions,
7927
8140
  ctx: {
@@ -7937,31 +8150,76 @@ function findRefInBatch(byName, ref) {
7937
8150
  if (typeof ref.version != "number") return byName.get(ref.name);
7938
8151
  }
7939
8152
 
7940
- async function assertCrossBatchRefsDeployed({client: client, definitions: definitions, ctx: ctx}) {
7941
- const missing = [];
7942
- for (const def of definitions) for (const ref of refsOf(def)) {
7943
- if (ctx.findInBatch(ref) !== void 0) continue;
7944
- const label = await resolveDeployedRefLabel({
8153
+ async function assertSpawnContracts({client: client, definitions: definitions, ctx: ctx}) {
8154
+ const missing = [], invalidHandoffs = [];
8155
+ for (const def of definitions) for (const spawn of spawnSitesOf(def)) {
8156
+ const ref = logicalRefOf(spawn.definition), child = ctx.findInBatch(ref) ?? await resolveDeployedRef({
7945
8157
  client: client,
7946
8158
  ref: ref,
7947
8159
  tag: ctx.tag
7948
8160
  });
7949
- label !== void 0 && missing.push({
7950
- from: def.name,
7951
- ref: label
7952
- });
8161
+ if (child === void 0) {
8162
+ missing.push({
8163
+ reason: "unresolved-definition",
8164
+ from: def.name,
8165
+ ref: logicalRefLabel(ref)
8166
+ });
8167
+ continue;
8168
+ }
8169
+ for (const field of Object.keys(spawn.with ?? {})) {
8170
+ const entry = (child.fields ?? []).find(candidate => candidate.name === field);
8171
+ entry === void 0 ? invalidHandoffs.push({
8172
+ reason: "unconsumable-field",
8173
+ from: def.name,
8174
+ child: child.name,
8175
+ field: field,
8176
+ detail: "the child declares no workflow field with that name"
8177
+ }) : invariants.isInputSourced(entry) || invalidHandoffs.push({
8178
+ reason: "unconsumable-field",
8179
+ from: def.name,
8180
+ child: child.name,
8181
+ field: field,
8182
+ detail: `the child field is ${entry.initialValue?.type ?? "working-memory"}-sourced, not input-sourced`
8183
+ });
8184
+ }
7953
8185
  }
7954
- if (missing.length === 0) return;
7955
- const lines = missing.map(m => ` - ${m.from} → ${m.ref} (neither in batch nor deployed)`);
7956
- throw new Error(`workflow.deployDefinitions: ${missing.length} unresolved reference${missing.length === 1 ? "" : "s"}:\n` + lines.join(`\n`));
8186
+ if (missing.length === 0 && invalidHandoffs.length === 0) return;
8187
+ if (invalidHandoffs.length === 0) {
8188
+ const lines2 = missing.map(issue => ` - ${issue.from} ${issue.ref} (neither in batch nor deployed)`);
8189
+ throw new Error(`workflow.deployDefinitions: ${missing.length} unresolved reference${missing.length === 1 ? "" : "s"}:\n` + lines2.join(`\n`));
8190
+ }
8191
+ const issues = [ ...missing, ...invalidHandoffs ], lines = [ ...missing.map(m => ` - ${m.from} → ${m.ref} (neither in batch nor deployed)`), ...invalidHandoffs.map(issue => ` - ${issue.from} → ${issue.child}: spawn.with["${issue.field}"] cannot be consumed — ${issue.detail}`) ], count = lines.length;
8192
+ throw new invariants.SpawnContractsInvalidError({
8193
+ issues: issues,
8194
+ message: `workflow.deployDefinitions: ${count} invalid spawn contract${count === 1 ? "" : "s"}:\n` + lines.join(`\n`)
8195
+ });
7957
8196
  }
7958
8197
 
7959
- async function resolveDeployedRefLabel({client: client, ref: ref, tag: tag}) {
8198
+ async function resolveDeployedRef({client: client, ref: ref, tag: tag}) {
7960
8199
  const wantsExplicit = typeof ref.version == "number", params = {
7961
8200
  definition: ref.name,
7962
8201
  tag: tag
7963
8202
  };
7964
- if (wantsExplicit && (params.version = ref.version), !await client.fetch(definitionLookupGroq(wantsExplicit), params)) return wantsExplicit ? `${ref.name} v${ref.version}` : ref.name;
8203
+ wantsExplicit && (params.version = ref.version);
8204
+ const doc = await client.fetch(definitionLookupGroq(wantsExplicit), params);
8205
+ if (doc) return parseDefinitionInput(doc, `workflow.deployDefinitions child "${logicalRefLabel(ref)}"`);
8206
+ }
8207
+
8208
+ function logicalRefLabel(ref) {
8209
+ return typeof ref.version == "number" ? `${ref.name} v${ref.version}` : ref.name;
8210
+ }
8211
+
8212
+ function logicalRefOf(ref) {
8213
+ return {
8214
+ name: ref.name,
8215
+ ...ref.version !== void 0 ? {
8216
+ version: ref.version
8217
+ } : {}
8218
+ };
8219
+ }
8220
+
8221
+ function spawnSitesOf(def) {
8222
+ return def.stages.flatMap(stage => stage.activities ?? []).flatMap(activity => activity.actions ?? []).flatMap(action => action.spawn === void 0 ? [] : [ action.spawn ]);
7965
8223
  }
7966
8224
 
7967
8225
  function topoSortDefinitions(definitions, ctx) {
@@ -7981,12 +8239,7 @@ function topoSortDefinitions(definitions, ctx) {
7981
8239
  }
7982
8240
 
7983
8241
  function refsOf(def) {
7984
- return def.stages.flatMap(stage => stage.activities ?? []).flatMap(activity => activity.actions ?? []).flatMap(action => action.spawn === void 0 ? [] : [ action.spawn.definition ]).map(ref => ({
7985
- name: ref.name,
7986
- ...ref.version !== void 0 ? {
7987
- version: ref.version
7988
- } : {}
7989
- }));
8242
+ return spawnSitesOf(def).map(spawn => logicalRefOf(spawn.definition));
7990
8243
  }
7991
8244
 
7992
8245
  function hashDefinitionContent(def) {
@@ -8009,7 +8262,10 @@ function planDefinitionDeploy({def: def, latest: latest, target: target}) {
8009
8262
  tag: target.tag,
8010
8263
  version: version,
8011
8264
  contentHash: contentHash,
8012
- ...MODEL_STAMP
8265
+ ...invariants.modelStampFor({
8266
+ documentType: "definition",
8267
+ document: expanded
8268
+ })
8013
8269
  };
8014
8270
  return {
8015
8271
  status: unchanged ? "unchanged" : "create",
@@ -8083,7 +8339,7 @@ async function loadDefinition({client: client, definition: definition, version:
8083
8339
  definition: definition,
8084
8340
  version: version
8085
8341
  });
8086
- return assertReadableModel(doc);
8342
+ return invariants.assertReadableModel(doc);
8087
8343
  }
8088
8344
  const latest = await loadLatestDeployed({
8089
8345
  client: client,
@@ -8101,14 +8357,14 @@ async function loadLatestDeployed({client: client, definition: definition, tag:
8101
8357
  definition: definition,
8102
8358
  tag: tag
8103
8359
  });
8104
- return doc ? assertReadableModel(doc) : void 0;
8360
+ return doc ? invariants.assertReadableModel(doc) : void 0;
8105
8361
  }
8106
8362
 
8107
8363
  async function loadDefinitionVersions({client: client, definition: definition, tag: tag}) {
8108
8364
  return (await client.fetch(`*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${invariants.tagScopeFilter()}] | order(version desc)`, {
8109
8365
  definition: definition,
8110
8366
  tag: tag
8111
- })).map(assertReadableModel);
8367
+ })).map(invariants.assertReadableModel);
8112
8368
  }
8113
8369
 
8114
8370
  async function loadDefinitionVersionsOrThrow({client: client, definition: definition, tag: tag}) {
@@ -8497,8 +8753,8 @@ async function resumeStart(args) {
8497
8753
  version: args.version
8498
8754
  });
8499
8755
  if (mismatch !== void 0) throw new invariants.ContractViolationError(`startInstance: instanceId "${existing._id}" already exists and ${mismatch}. A supplied instanceId is start's idempotency key — reuse one only to retry the same start.`);
8500
- if (existing.stages.length === 0 && terminalState(existing) !== "in-flight") throw new invariants.ContractViolationError(`startInstance: instanceId "${existing._id}" belongs to a start that was discarded (aborted before it finished starting) — mint a new id to start again.`);
8501
- const completesStart = isUnprimed(existing), emitStarted = () => resolveTelemetry(args.telemetry).log(WorkflowInstanceStarted, instanceStartedDataFor({
8756
+ if (existing.stages.length === 0 && invariants.terminalState(existing) !== "in-flight") throw new invariants.ContractViolationError(`startInstance: instanceId "${existing._id}" belongs to a start that was discarded (aborted before it finished starting) — mint a new id to start again.`);
8757
+ const completesStart = invariants.isUnprimed(existing), emitStarted = () => resolveTelemetry(args.telemetry).log(WorkflowInstanceStarted, instanceStartedDataFor({
8502
8758
  instance: existing,
8503
8759
  initialFieldCount: args.initialFieldCount,
8504
8760
  viaSpawn: !1
@@ -8796,7 +9052,7 @@ async function deleteDefinition(args) {
8796
9052
  async function assertNoSpawnReferrers({client: client, tag: tag, definition: definition, targets: targets, lastVersionGoes: lastVersionGoes}) {
8797
9053
  const deployed = (await client.fetch(`*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && ${invariants.tagScopeFilter()}]`, {
8798
9054
  tag: tag
8799
- })).map(assertReadableModel), targetIds = new Set(targets.map(d => d._id)), targetVersions = new Set(targets.map(d => d.version)), referrers = deployed.filter(d => !targetIds.has(d._id)).filter(d => refsOf(d).some(ref => ref.name === definition && (typeof ref.version == "number" ? targetVersions.has(ref.version) : lastVersionGoes)));
9055
+ })).map(invariants.assertReadableModel), targetIds = new Set(targets.map(d => d._id)), targetVersions = new Set(targets.map(d => d.version)), referrers = deployed.filter(d => !targetIds.has(d._id)).filter(d => refsOf(d).some(ref => ref.name === definition && (typeof ref.version == "number" ? targetVersions.has(ref.version) : lastVersionGoes)));
8800
9056
  if (referrers.length > 0) throw new invariants.DefinitionInUseError({
8801
9057
  definition: definition,
8802
9058
  blockedBy: {
@@ -8811,7 +9067,7 @@ async function assertNoSpawnReferrers({client: client, tag: tag, definition: def
8811
9067
 
8812
9068
  async function nonTerminalInstanceIds({client: client, tag: tag, definition: definition, version: version}) {
8813
9069
  const versionFilter = version === void 0 ? "" : " && pinnedVersion == $version";
8814
- return (await client.fetch(`*[_type == "${WORKFLOW_INSTANCE_TYPE}" && definition == $definition && ${invariants.tagScopeFilter()} && ${inFlightFilter()}${versionFilter}] | order(_id asc){_id}`, {
9070
+ return (await client.fetch(`*[_type == "${invariants.WORKFLOW_INSTANCE_TYPE}" && definition == $definition && ${invariants.tagScopeFilter()} && ${inFlightFilter()}${versionFilter}] | order(_id asc){_id}`, {
8815
9071
  definition: definition,
8816
9072
  tag: tag,
8817
9073
  ...version !== void 0 ? {
@@ -8847,11 +9103,12 @@ async function fetchStartSlice(args) {
8847
9103
  includeCompleted: !0
8848
9104
  }
8849
9105
  });
8850
- return (await client.fetch(query, params)).map(assertReadableModel);
9106
+ return (await client.fetch(query, params)).map(invariants.assertReadableModel);
8851
9107
  }
8852
9108
 
8853
9109
  const workflow = {
8854
9110
  deployDefinitions: async rawArgs => {
9111
+ invariants.assertReaderModelAcknowledgement(rawArgs.expectedMinReaderModel);
8855
9112
  const args = taggedScope(rawArgs, REQUEST_TAG.deploy), {client: client, tag: tag, resourceAliases: resourceAliases} = args;
8856
9113
  invariants.validateTag(tag);
8857
9114
  const definitions = args.definitions.map(def => parseDefinitionInput(def, "workflow.deployDefinitions"));
@@ -8924,8 +9181,8 @@ const workflow = {
8924
9181
  }), result;
8925
9182
  },
8926
9183
  startInstance: async rawArgs => {
8927
- const args = taggedScope(rawArgs, REQUEST_TAG.start), {client: client, tag: tag, workflowResource: workflowResource, definition: definitionName, version: version, initialFields: initialFields, ancestors: ancestors, context: context, instanceId: instanceId, perspective: perspective, executionContext: executionContext} = args, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, seedFields = initialFields ?? [], existing = instanceId !== void 0 ? await getInstanceDocument(client, instanceId) : void 0;
8928
- if (existing !== void 0 && existing.tag === tag) return resumeStart({
9184
+ const args = taggedScope(rawArgs, REQUEST_TAG.start), {client: client, tag: tag, definition: definitionName, version: version, initialFields: initialFields, instanceId: instanceId, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = operationContext, clock = args.clock ?? wallClock, seedFields = initialFields ?? [], existing = instanceId !== void 0 ? await getInstanceDocument(client, instanceId) : void 0;
9185
+ return existing !== void 0 && existing.tag === tag ? resumeStart({
8929
9186
  client: client,
8930
9187
  tag: tag,
8931
9188
  existing: existing,
@@ -8942,105 +9199,12 @@ const workflow = {
8942
9199
  ...args.telemetry !== void 0 ? {
8943
9200
  telemetry: args.telemetry
8944
9201
  } : {}
8945
- });
8946
- const definition = await loadDefinition({
8947
- client: client,
8948
- definition: definitionName,
8949
- version: version,
8950
- tag: tag
8951
- }), id = instanceId ?? instanceDocId(tag);
8952
- if (definition.stages.find(s => s.name === definition.initialStage) === void 0) throw new Error(`Initial stage "${definition.initialStage}" missing in ${definitionName} v${definition.version}`);
8953
- const now = clock();
8954
- if (assertRequiredInputProvided({
8955
- entryDefs: definition.fields ?? [],
8956
- initialFields: seedFields,
8957
- definitionName: definition.name
8958
- }), definition.start?.allowed !== void 0) {
8959
- const insight = await startAllowedInsight({
8960
- client: client,
8961
- tag: tag,
8962
- definition: definition,
8963
- allowed: definition.start.allowed,
8964
- initialFields: seedFields,
8965
- now: now
8966
- });
8967
- if (insight.outcome !== "satisfied") throw new StartNotAllowedError({
8968
- definition: definition.name,
8969
- insight: insight
8970
- });
8971
- }
8972
- const contextEntries = context ? toContextEntries(context) : [], fieldDiscards = [], resolvedFields = await resolveDeclaredFields({
8973
- entryDefs: definition.fields ?? [],
8974
- initialFields: seedFields,
8975
- ctx: {
8976
- client: client,
8977
- now: now,
8978
- selfId: id,
8979
- tag: tag,
8980
- workflowResource: workflowResource,
8981
- definitionName: definition.name,
8982
- refSurface: refSurface,
8983
- ...perspective !== void 0 ? {
8984
- perspective: perspective
8985
- } : {},
8986
- recordDiscard: recordFieldDiscards({
8987
- target: fieldDiscards,
8988
- scope: "workflow",
8989
- at: now
8990
- })
8991
- },
8992
- randomKey: randomKey
8993
- }), effectivePerspective = perspective ?? derivePerspectiveFromFields(resolvedFields), base = buildInstanceBase({
8994
- id: id,
8995
- now: now,
8996
- tag: tag,
8997
- workflowResource: workflowResource,
8998
- definitionName: definition.name,
8999
- pinnedVersion: definition.version,
9000
- pinnedContentHash: definition.contentHash,
9001
- definition: definition,
9002
- fields: resolvedFields,
9003
- context: contextEntries,
9004
- ancestors: ancestors ?? [],
9005
- perspective: effectivePerspective,
9006
- initialStage: definition.initialStage,
9007
- actor: actor,
9008
- executionContext: resolveExecutionContext(executionContext),
9009
- ...fieldDiscards.length > 0 ? {
9010
- extraHistory: fieldDiscards
9011
- } : {}
9012
- });
9013
- await client.create(base, SYNC_COMMIT);
9014
- const cascaded = await settleStart({
9015
- client: client,
9016
- tag: tag,
9017
- instanceId: id,
9018
- actor: actor,
9019
- clientForGdr: clientForGdr,
9020
- refSurface: refSurface,
9202
+ }) : startFreshInstance({
9203
+ args: args,
9204
+ operationContext: operationContext,
9021
9205
  clock: clock,
9022
- ...executionContext !== void 0 ? {
9023
- executionContext: executionContext
9024
- } : {},
9025
- ...args.telemetry !== void 0 ? {
9026
- telemetry: args.telemetry
9027
- } : {},
9028
- emitStarted: () => resolveTelemetry(args.telemetry).log(WorkflowInstanceStarted, instanceStartedData({
9029
- definition: definition,
9030
- instanceId: id,
9031
- initialFieldCount: seedFields.length,
9032
- viaSpawn: !1
9033
- }))
9206
+ seedFields: seedFields
9034
9207
  });
9035
- return {
9036
- instance: await reload({
9037
- client: client,
9038
- instanceId: id,
9039
- tag: tag
9040
- }),
9041
- cascaded: cascaded,
9042
- changed: !0
9043
- };
9044
9208
  },
9045
9209
  fireAction: async rawArgs => {
9046
9210
  const args = taggedScope(rawArgs, REQUEST_TAG.fireAction), {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, activity: activity, action: action, params: params, idempotent: idempotent, resourceClients: resourceClients, grantsFromPath: grantsFromPath, executionContext: executionContext} = args;
@@ -9451,7 +9615,7 @@ const workflow = {
9451
9615
  instance: instance
9452
9616
  }), reserved = await renderConditionScope({
9453
9617
  instance: instance,
9454
- definition: parseDefinitionSnapshot(instance),
9618
+ definition: invariants.parseDefinitionSnapshot(instance),
9455
9619
  snapshot: snapshot,
9456
9620
  now: clock()
9457
9621
  }), tree = groqJs.parse(groq, {
@@ -9526,11 +9690,11 @@ const workflow = {
9526
9690
  return (await client.fetch(query, params)).map(readInstanceDoc).filter(instance => instanceWatchesDocument(instance, document));
9527
9691
  },
9528
9692
  definitionsForDocument: async rawArgs => {
9529
- const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document} = args;
9693
+ const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document, subject: subject} = args;
9530
9694
  invariants.validateTag(tag);
9531
9695
  const clock = args.clock ?? wallClock, deployed = (await client.fetch(definitionsListGroq("desc"), {
9532
9696
  tag: tag
9533
- })).map(assertReadableModel), latest = latestDeployedDefinitions(deployed);
9697
+ })).map(invariants.assertReadableModel), latest = latestDeployedDefinitions(deployed);
9534
9698
  let slice;
9535
9699
  return applicableDefinitions({
9536
9700
  definitions: latest,
@@ -9538,6 +9702,9 @@ const workflow = {
9538
9702
  scope: {
9539
9703
  tag: tag,
9540
9704
  now: clock(),
9705
+ ...subject !== void 0 ? {
9706
+ subject: subject
9707
+ } : {},
9541
9708
  fetchDataset: () => slice ??= fetchStartSlice({
9542
9709
  client: client,
9543
9710
  tag: tag
@@ -9553,20 +9720,29 @@ const workflow = {
9553
9720
  definition: definitionName,
9554
9721
  version: version,
9555
9722
  tag: tag
9723
+ }), invalidInitialFields = initialFieldIssues({
9724
+ workflowFields: definition.fields ?? [],
9725
+ stages: definition.stages,
9726
+ initialFields: initialFields ?? []
9556
9727
  }), missingRequired = missingRequiredInputs({
9557
9728
  entryDefs: definition.fields ?? [],
9558
9729
  initialFields: initialFields ?? []
9559
9730
  }), allowed = definition.start?.allowed;
9560
9731
  if (allowed === void 0) return {
9561
- allowed: !0,
9732
+ allowed: invalidInitialFields.length === 0,
9562
9733
  outcome: "satisfied",
9563
9734
  unboundReads: [],
9564
- missingRequired: missingRequired
9735
+ missingRequired: missingRequired,
9736
+ invalidInitialFields: invalidInitialFields
9565
9737
  };
9566
- const unboundReads = unboundAllowedReads(allowed, startFieldsParam({
9567
- entryDefs: definition.fields ?? [],
9568
- initialFields: initialFields ?? []
9569
- })), insight = await startAllowedInsight({
9738
+ const unboundReads = unboundAllowedReadsWithSubject({
9739
+ allowed: allowed,
9740
+ fields: startFieldsParam({
9741
+ entryDefs: definition.fields ?? [],
9742
+ initialFields: initialFields ?? []
9743
+ }),
9744
+ definition: definition
9745
+ }), insight = await startAllowedInsight({
9570
9746
  client: client,
9571
9747
  tag: tag,
9572
9748
  definition: definition,
@@ -9575,11 +9751,12 @@ const workflow = {
9575
9751
  now: clock()
9576
9752
  }), provisional = unboundReads.length > 0;
9577
9753
  return {
9578
- allowed: !provisional && insight.outcome === "satisfied",
9754
+ allowed: invalidInitialFields.length === 0 && !provisional && insight.outcome === "satisfied",
9579
9755
  outcome: provisional ? "unevaluable" : insight.outcome,
9580
9756
  insight: insight,
9581
9757
  unboundReads: unboundReads,
9582
- missingRequired: missingRequired
9758
+ missingRequired: missingRequired,
9759
+ invalidInitialFields: invalidInitialFields
9583
9760
  };
9584
9761
  },
9585
9762
  permissions: {
@@ -9609,6 +9786,239 @@ async function startAllowedInsight(args) {
9609
9786
  });
9610
9787
  }
9611
9788
 
9789
+ async function startFreshInstance(args) {
9790
+ const {args: startArgs, operationContext: operationContext, clock: clock, seedFields: seedFields} = args, {client: client, tag: tag, workflowResource: workflowResource, definition: definitionName, version: version, ancestors: ancestors, context: context, instanceId: instanceId, perspective: perspective, executionContext: executionContext} = startArgs, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = operationContext, definition = await loadDefinition({
9791
+ client: client,
9792
+ definition: definitionName,
9793
+ version: version,
9794
+ tag: tag
9795
+ }), id = instanceId ?? instanceDocId(tag);
9796
+ assertInitialStageExists(definition);
9797
+ const now = clock();
9798
+ await assertStartInputs({
9799
+ client: client,
9800
+ tag: tag,
9801
+ definition: definition,
9802
+ initialFields: seedFields,
9803
+ now: now
9804
+ });
9805
+ const {resolvedFields: resolvedFields, fieldDiscards: fieldDiscards} = await resolveStartFields({
9806
+ client: client,
9807
+ tag: tag,
9808
+ workflowResource: workflowResource,
9809
+ definition: definition,
9810
+ initialFields: seedFields,
9811
+ id: id,
9812
+ now: now,
9813
+ refSurface: refSurface,
9814
+ ...perspective !== void 0 ? {
9815
+ perspective: perspective
9816
+ } : {}
9817
+ }), base = buildInstanceBase({
9818
+ id: id,
9819
+ now: now,
9820
+ tag: tag,
9821
+ workflowResource: workflowResource,
9822
+ definitionName: definition.name,
9823
+ pinnedVersion: definition.version,
9824
+ pinnedContentHash: definition.contentHash,
9825
+ definition: definition,
9826
+ fields: resolvedFields,
9827
+ context: context ? toContextEntries(context) : [],
9828
+ ancestors: ancestors ?? [],
9829
+ perspective: perspective ?? derivePerspectiveFromFields(resolvedFields),
9830
+ initialStage: definition.initialStage,
9831
+ actor: actor,
9832
+ executionContext: resolveExecutionContext(executionContext),
9833
+ ...fieldDiscards.length > 0 ? {
9834
+ extraHistory: fieldDiscards
9835
+ } : {}
9836
+ });
9837
+ await client.create(base, SYNC_COMMIT);
9838
+ const cascaded = await settleStart({
9839
+ client: client,
9840
+ tag: tag,
9841
+ instanceId: id,
9842
+ actor: actor,
9843
+ clientForGdr: clientForGdr,
9844
+ refSurface: refSurface,
9845
+ clock: clock,
9846
+ ...executionContext !== void 0 ? {
9847
+ executionContext: executionContext
9848
+ } : {},
9849
+ ...startArgs.telemetry !== void 0 ? {
9850
+ telemetry: startArgs.telemetry
9851
+ } : {},
9852
+ emitStarted: () => resolveTelemetry(startArgs.telemetry).log(WorkflowInstanceStarted, instanceStartedData({
9853
+ definition: definition,
9854
+ instanceId: id,
9855
+ initialFieldCount: seedFields.length,
9856
+ viaSpawn: !1
9857
+ }))
9858
+ });
9859
+ return {
9860
+ instance: await reload({
9861
+ client: client,
9862
+ instanceId: id,
9863
+ tag: tag
9864
+ }),
9865
+ cascaded: cascaded,
9866
+ changed: !0
9867
+ };
9868
+ }
9869
+
9870
+ function assertInitialStageExists(definition) {
9871
+ if (!definition.stages.some(stage => stage.name === definition.initialStage)) throw new Error(`Initial stage "${definition.initialStage}" missing in ${definition.name} v${definition.version}`);
9872
+ }
9873
+
9874
+ async function assertStartInputs(args) {
9875
+ const {client: client, tag: tag, definition: definition, initialFields: initialFields, now: now} = args;
9876
+ assertInitialFieldsConsumable({
9877
+ workflowFields: definition.fields ?? [],
9878
+ stages: definition.stages,
9879
+ initialFields: initialFields,
9880
+ definitionName: definition.name
9881
+ }), assertRequiredInputProvided({
9882
+ entryDefs: definition.fields ?? [],
9883
+ initialFields: initialFields,
9884
+ definitionName: definition.name
9885
+ });
9886
+ const allowed = definition.start?.allowed;
9887
+ if (allowed === void 0) return;
9888
+ const insight = await startAllowedInsight({
9889
+ client: client,
9890
+ tag: tag,
9891
+ definition: definition,
9892
+ allowed: allowed,
9893
+ initialFields: initialFields,
9894
+ now: now
9895
+ });
9896
+ if (insight.outcome !== "satisfied") throw new StartNotAllowedError({
9897
+ definition: definition.name,
9898
+ insight: insight
9899
+ });
9900
+ }
9901
+
9902
+ async function resolveStartFields(args) {
9903
+ const {client: client, tag: tag, workflowResource: workflowResource, definition: definition, initialFields: initialFields, id: id, now: now, refSurface: refSurface, perspective: perspective} = args, fieldDiscards = [];
9904
+ return {
9905
+ resolvedFields: await resolveDeclaredFields({
9906
+ entryDefs: definition.fields ?? [],
9907
+ initialFields: initialFields,
9908
+ ctx: {
9909
+ client: client,
9910
+ now: now,
9911
+ selfId: id,
9912
+ tag: tag,
9913
+ workflowResource: workflowResource,
9914
+ definitionName: definition.name,
9915
+ refSurface: refSurface,
9916
+ ...perspective !== void 0 ? {
9917
+ perspective: perspective
9918
+ } : {},
9919
+ recordDiscard: recordFieldDiscards({
9920
+ target: fieldDiscards,
9921
+ scope: "workflow",
9922
+ at: now
9923
+ })
9924
+ },
9925
+ randomKey: randomKey
9926
+ }),
9927
+ fieldDiscards: fieldDiscards
9928
+ };
9929
+ }
9930
+
9931
+ function isRecord(value) {
9932
+ return typeof value == "object" && value !== null && !Array.isArray(value);
9933
+ }
9934
+
9935
+ function isClientProjectUser(value) {
9936
+ return isRecord(value) && typeof value.id == "string" && (value.displayName === void 0 || typeof value.displayName == "string") && (value.email === void 0 || typeof value.email == "string") && (value.imageUrl === void 0 || value.imageUrl === null || typeof value.imageUrl == "string");
9937
+ }
9938
+
9939
+ function objectProperty(value, property) {
9940
+ if (!isRecord(value)) return;
9941
+ const propertyValue = value[property];
9942
+ return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
9943
+ }
9944
+
9945
+ function stringProperty(value, property) {
9946
+ const propertyValue = Reflect.get(value, property);
9947
+ return typeof propertyValue == "string" ? propertyValue : void 0;
9948
+ }
9949
+
9950
+ function apiErrorType(error) {
9951
+ const response = objectProperty(error, "response"), body = objectProperty(response, "body");
9952
+ if (!body) return;
9953
+ const nestedError = objectProperty(body, "error");
9954
+ return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
9955
+ }
9956
+
9957
+ function isProjectUserNotFoundError(error) {
9958
+ return apiErrorType(error) === "projectUserNotFoundError";
9959
+ }
9960
+
9961
+ function clientProjectUserDirectory(client, projectId) {
9962
+ return {
9963
+ findById: async id => {
9964
+ if (!client.request) return {
9965
+ status: "inaccessible",
9966
+ cause: new Error("Project-user resolution requires WorkflowClient.request")
9967
+ };
9968
+ try {
9969
+ const response = await client.request({
9970
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
9971
+ }), candidate = Array.isArray(response) ? response[0] : response;
9972
+ return candidate == null ? {
9973
+ status: "missing"
9974
+ } : isClientProjectUser(candidate) ? {
9975
+ status: "resolved",
9976
+ user: candidate
9977
+ } : {
9978
+ status: "inaccessible",
9979
+ cause: new Error("Project-user response had an invalid shape")
9980
+ };
9981
+ } catch (cause) {
9982
+ return isProjectUserNotFoundError(cause) ? {
9983
+ status: "missing"
9984
+ } : {
9985
+ status: "inaccessible",
9986
+ cause: cause
9987
+ };
9988
+ }
9989
+ }
9990
+ };
9991
+ }
9992
+
9993
+ function resolveClientActor(client, args) {
9994
+ return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
9995
+ }
9996
+
9997
+ async function resolveActor(directory, actor) {
9998
+ if (actor.kind !== "person") return {
9999
+ status: "not-person",
10000
+ actor: actor
10001
+ };
10002
+ const personActor = {
10003
+ ...actor,
10004
+ kind: "person"
10005
+ }, lookup = await directory.findById(personActor.id);
10006
+ return lookup.status === "resolved" ? {
10007
+ status: "resolved",
10008
+ actor: personActor,
10009
+ user: lookup.user
10010
+ } : lookup.status === "inaccessible" ? {
10011
+ status: "inaccessible",
10012
+ actor: personActor,
10013
+ ...lookup.cause === void 0 ? {} : {
10014
+ cause: lookup.cause
10015
+ }
10016
+ } : {
10017
+ status: "missing",
10018
+ actor: personActor
10019
+ };
10020
+ }
10021
+
9612
10022
  const DEFAULT_EFFECT_LEASE_MS = 300 * 1e3;
9613
10023
 
9614
10024
  function isClaimExpired(claim, now) {
@@ -9686,7 +10096,7 @@ async function verifyDeployedDefinitionsInternal(args) {
9686
10096
  invariants.validateTag(tag);
9687
10097
  const log = logger("verifyDeployedDefinitions"), definitions = (await client.fetch(definitionsListGroq("asc"), {
9688
10098
  tag: tag
9689
- })).map(assertReadableModel), seen = [], missingByName = /* @__PURE__ */ new Map;
10099
+ })).map(invariants.assertReadableModel), seen = [], missingByName = /* @__PURE__ */ new Map;
9690
10100
  for (const def of definitions) seen.push({
9691
10101
  name: def.name,
9692
10102
  version: def.version,
@@ -9751,11 +10161,11 @@ function isCancelledCompletion(data) {
9751
10161
  }
9752
10162
 
9753
10163
  async function drainEffectsInternal(args) {
9754
- const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, effectHandlers: effectHandlers, missingHandler: missingHandler, logger: logger} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, routeGdr = buildClientForGdr({
9755
- client: client,
10164
+ const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, effectHandlers: effectHandlers, missingHandler: missingHandler, logger: logger, handlerClient: handlerClient, handlerResourceClients: handlerResourceClients} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, routeGdr = buildClientForGdr({
10165
+ client: handlerClient,
9756
10166
  workflowResource: workflowResource,
9757
- resourceClients: resourceClients
9758
- }), clientFor = ref => withRequestTag(routeGdr(invariants.parseGdr(typeof ref == "string" ? ref : ref.id)), REQUEST_TAG.effect), {actor: drainerActor} = await resolveAccess(client), executionContext = args.executionContext;
10167
+ resourceClients: handlerResourceClients
10168
+ }), clientFor = ref => routeGdr(invariants.parseGdr(typeof ref == "string" ? ref : ref.id)), {actor: drainerActor} = await resolveAccess(client), executionContext = args.executionContext;
9759
10169
  invariants.validateTag(tag);
9760
10170
  const log = logger("drainEffects"), buckets = {
9761
10171
  drained: [],
@@ -9801,6 +10211,7 @@ async function drainEffectsInternal(args) {
9801
10211
  handler: handler,
9802
10212
  candidate: candidate,
9803
10213
  client: client,
10214
+ handlerClient: handlerClient,
9804
10215
  tag: tag,
9805
10216
  workflowResource: workflowResource,
9806
10217
  ...resourceClients !== void 0 ? {
@@ -9826,11 +10237,11 @@ function findClaimableCandidate({instance: instance, now: now, skippedKeys: skip
9826
10237
  }
9827
10238
 
9828
10239
  async function dispatchAndReport(args) {
9829
- const {handler: handler, candidate: candidate, client: client, tag: tag, workflowResource: workflowResource, resourceClients: resourceClients, instanceId: instanceId, clientFor: clientFor, logger: logger, log: log, executionContext: executionContext, telemetry: telemetry, clock: clock} = args, {outputs: outputs, ops: ops, dispatchError: dispatchError} = await dispatchEffect({
10240
+ const {handler: handler, candidate: candidate, client: client, handlerClient: handlerClient, tag: tag, workflowResource: workflowResource, resourceClients: resourceClients, instanceId: instanceId, clientFor: clientFor, logger: logger, log: log, executionContext: executionContext, telemetry: telemetry, clock: clock} = args, {outputs: outputs, ops: ops, dispatchError: dispatchError} = await dispatchEffect({
9830
10241
  handler: handler,
9831
10242
  candidate: candidate,
9832
10243
  ctx: {
9833
- client: client,
10244
+ client: handlerClient,
9834
10245
  clientFor: clientFor,
9835
10246
  instanceId: instanceId,
9836
10247
  logger: logger
@@ -9936,7 +10347,7 @@ async function claimPendingEffect({client: client, instance: instance, candidate
9936
10347
  async function dispatchEffect({handler: handler, candidate: candidate, ctx: ctx}) {
9937
10348
  try {
9938
10349
  const result = await handler(candidate.params, {
9939
- client: withRequestTag(ctx.client, REQUEST_TAG.effect),
10350
+ client: ctx.client,
9940
10351
  clientFor: ctx.clientFor,
9941
10352
  instanceId: ctx.instanceId,
9942
10353
  effectKey: candidate._key,
@@ -10052,7 +10463,7 @@ function createInstanceSession(args) {
10052
10463
  }, uri = invariants.gdrFromResource(owned.resource, owned.doc._id);
10053
10464
  if (uri === selfUri()) {
10054
10465
  const rawStamp = owned.doc._updatedAt;
10055
- !invariants.isParseableInstant(rawStamp) || owned.doc._type !== WORKFLOW_INSTANCE_TYPE ? asHeldInstance(owned.doc) : Date.parse(rawStamp) > Date.parse(instance._updatedAt) && (instance = asHeldInstance(owned.doc));
10466
+ !invariants.isParseableInstant(rawStamp) || owned.doc._type !== invariants.WORKFLOW_INSTANCE_TYPE ? asHeldInstance(owned.doc) : Date.parse(rawStamp) > Date.parse(instance._updatedAt) && (instance = asHeldInstance(owned.doc));
10056
10467
  continue;
10057
10468
  }
10058
10469
  next.set(uri, owned);
@@ -10071,7 +10482,7 @@ function createInstanceSession(args) {
10071
10482
  let parsedDefinition;
10072
10483
  const definitionOf = () => (parsedDefinition?.source !== instance && (parsedDefinition = {
10073
10484
  source: instance,
10074
- definition: parseDefinitionSnapshot(instance)
10485
+ definition: invariants.parseDefinitionSnapshot(instance)
10075
10486
  }), parsedDefinition.definition), siteKey = site => JSON.stringify([ site.scope, site.activity ?? null, site.name ]), stagePreview = ({target: target, mode: mode, value: value}) => {
10076
10487
  const site = previewSiteOf({
10077
10488
  instance: instance,
@@ -10430,6 +10841,7 @@ function createEngine(args) {
10430
10841
  missingHandler: missingHandler,
10431
10842
  logger: logger,
10432
10843
  telemetry: telemetry,
10844
+ resolveActor: rest => resolveClientActor(client, rest),
10433
10845
  deployDefinitions: async rest => {
10434
10846
  const result = await workflow.deployDefinitions(withScope(rest));
10435
10847
  return logDeployWarnings(result, logger), result;
@@ -10470,6 +10882,10 @@ function createEngine(args) {
10470
10882
  drainEffects: async ({instanceId: instanceId}) => {
10471
10883
  const result = await drainEffectsInternal({
10472
10884
  client: client,
10885
+ handlerClient: args.client,
10886
+ ...args.resourceClients !== void 0 ? {
10887
+ handlerResourceClients: args.resourceClients
10888
+ } : {},
10473
10889
  tag: tag,
10474
10890
  workflowResource: workflowResource,
10475
10891
  ...optionalScope,
@@ -10666,6 +11082,7 @@ function attributeMember({member: member, group: group, chain: chain}) {
10666
11082
  }
10667
11083
 
10668
11084
  function diffEntry({def: rawDef, latestRaw: latestRaw, target: target}) {
11085
+ invariants.assertReaderModelAcknowledgement(target.expectedMinReaderModel);
10669
11086
  const def = parseDefinitionInput(rawDef, "diffEntry"), plan = planDefinitionDeploy({
10670
11087
  def: def,
10671
11088
  latest: asLatest(latestRaw),
@@ -10697,6 +11114,7 @@ function asLatest(raw) {
10697
11114
  }
10698
11115
 
10699
11116
  async function computeDiffEntries({client: client, defs: defs, target: target}) {
11117
+ invariants.assertReaderModelAcknowledgement(target.expectedMinReaderModel);
10700
11118
  const entries = [];
10701
11119
  for (const rawDef of defs) {
10702
11120
  const def = parseDefinitionInput(rawDef, "computeDiffEntries"), latest = await loadLatestDeployed({
@@ -10718,7 +11136,7 @@ function buildInitialFields({declared: declared, values: values}) {
10718
11136
  return Object.entries(values).map(([name, value]) => {
10719
11137
  const entry = declared.find(f => f.name === name);
10720
11138
  if (entry === void 0) throw new Error(settable.length > 0 ? `workflow declares no field "${name}" — input fields: ${settableNames}` : `workflow declares no field "${name}" — it declares no input fields at all`);
10721
- if (entry.initialValue?.type !== "input") throw new Error(`field "${name}" is not input-sourced the engine would silently ignore the value. ` + (settable.length > 0 ? `Input fields: ${settableNames}` : "This workflow has no input fields."));
11139
+ if (entry.initialValue?.type !== "input") throw new Error(`field "${name}" is not input-sourced and cannot accept a start value. ` + (settable.length > 0 ? `Input fields: ${settableNames}` : "This workflow has no input fields."));
10722
11140
  return {
10723
11141
  type: entry.type,
10724
11142
  name: name,
@@ -10782,7 +11200,7 @@ const HISTORY_DISPLAY = {
10782
11200
  },
10783
11201
  aborted: {
10784
11202
  title: "Instance aborted",
10785
- description: "An admin hard-stopped the instance — pending effects cancelled, stage guards lifted, abortedAt + completedAt stamped."
11203
+ description: "An admin hard-stopped the instance — pending effects cancelled, stage guards removed, abortedAt + completedAt stamped."
10786
11204
  },
10787
11205
  opApplied: {
10788
11206
  title: "Op applied",
@@ -10818,6 +11236,10 @@ const HISTORY_DISPLAY = {
10818
11236
  title: "Document references",
10819
11237
  description: "Ordered list of GDR pointers — multi-doc selection."
10820
11238
  },
11239
+ subject: {
11240
+ title: "Subject document",
11241
+ description: "Single GDR pointer at THE document the workflow is about — same value as a document reference, elevated so surfaces identify the subject by kind."
11242
+ },
10821
11243
  "release.ref": {
10822
11244
  title: "Content Release reference",
10823
11245
  description: "GDR pointer at a Content Release system doc, carrying the release name the engine derives the instance's read perspective from."
@@ -10929,7 +11351,7 @@ const HISTORY_DISPLAY = {
10929
11351
  title: "Workflow definition",
10930
11352
  description: "An immutable workflow blueprint, addressed by `name` + `version`."
10931
11353
  },
10932
- [WORKFLOW_INSTANCE_TYPE]: {
11354
+ [invariants.WORKFLOW_INSTANCE_TYPE]: {
10933
11355
  title: "Workflow instance",
10934
11356
  description: "A running (or finished) workflow against its declared fields."
10935
11357
  }
@@ -10996,6 +11418,8 @@ function displayDescription(typeKey) {
10996
11418
  if (typeKey) return DISPLAY[typeKey]?.description;
10997
11419
  }
10998
11420
 
11421
+ exports.ACTION_SEMANTICS = invariants.ACTION_SEMANTICS;
11422
+
10999
11423
  exports.ACTIVITY_KINDS = invariants.ACTIVITY_KINDS;
11000
11424
 
11001
11425
  exports.ACTOR_KINDS = invariants.ACTOR_KINDS;
@@ -11004,6 +11428,12 @@ exports.CONDITION_VARS = invariants.CONDITION_VARS;
11004
11428
 
11005
11429
  exports.ContractViolationError = invariants.ContractViolationError;
11006
11430
 
11431
+ exports.DATA_MODEL_CHANGES = invariants.DATA_MODEL_CHANGES;
11432
+
11433
+ exports.DATA_MODEL_MIN_READER = invariants.DATA_MODEL_MIN_READER;
11434
+
11435
+ exports.DATA_MODEL_VERSION = invariants.DATA_MODEL_VERSION;
11436
+
11007
11437
  exports.DEFAULT_TRANSITION_WHEN = invariants.DEFAULT_TRANSITION_WHEN;
11008
11438
 
11009
11439
  exports.DRIVER_KINDS = invariants.DRIVER_KINDS;
@@ -11026,18 +11456,32 @@ exports.GUARD_PREDICATE_VARS = invariants.GUARD_PREDICATE_VARS;
11026
11456
 
11027
11457
  exports.InstanceNotFoundError = invariants.InstanceNotFoundError;
11028
11458
 
11459
+ exports.ModelVersionAheadError = invariants.ModelVersionAheadError;
11460
+
11029
11461
  exports.PersistedDocShapeError = invariants.PersistedDocShapeError;
11030
11462
 
11463
+ exports.READER_MODEL_ROLLOUT_URL = invariants.READER_MODEL_ROLLOUT_URL;
11464
+
11031
11465
  exports.RESERVED_CONDITION_VARS = invariants.RESERVED_CONDITION_VARS;
11032
11466
 
11467
+ exports.ReaderModelAcknowledgementError = invariants.ReaderModelAcknowledgementError;
11468
+
11033
11469
  exports.START_ALLOWED_VARS = invariants.START_ALLOWED_VARS;
11034
11470
 
11035
11471
  exports.START_FILTER_VARS = invariants.START_FILTER_VARS;
11036
11472
 
11473
+ exports.SpawnContractsInvalidError = invariants.SpawnContractsInvalidError;
11474
+
11037
11475
  exports.WORKFLOW_DEFINITION_TYPE = invariants.WORKFLOW_DEFINITION_TYPE;
11038
11476
 
11477
+ exports.WORKFLOW_INSTANCE_TYPE = invariants.WORKFLOW_INSTANCE_TYPE;
11478
+
11039
11479
  exports.WorkflowError = invariants.WorkflowError;
11040
11480
 
11481
+ exports.assertReadableModel = invariants.assertReadableModel;
11482
+
11483
+ exports.assertReaderModelAcknowledgement = invariants.assertReaderModelAcknowledgement;
11484
+
11041
11485
  exports.clientConfigFromResource = invariants.clientConfigFromResource;
11042
11486
 
11043
11487
  exports.conditionFieldReadNames = invariants.conditionFieldReadNames;
@@ -11054,6 +11498,8 @@ exports.errorMessage = invariants.errorMessage;
11054
11498
 
11055
11499
  exports.extractDocumentId = invariants.extractDocumentId;
11056
11500
 
11501
+ exports.fieldTreeShape = invariants.fieldTreeShape;
11502
+
11057
11503
  exports.gdrFromResource = invariants.gdrFromResource;
11058
11504
 
11059
11505
  exports.gdrRef = invariants.gdrRef;
@@ -11066,8 +11512,14 @@ exports.isCascadeFired = invariants.isCascadeFired;
11066
11512
 
11067
11513
  exports.isGdr = invariants.isGdr;
11068
11514
 
11515
+ exports.isInputSourced = invariants.isInputSourced;
11516
+
11069
11517
  exports.isNotesEntry = invariants.isNotesEntry;
11070
11518
 
11519
+ exports.isSingleDocRefEntry = invariants.isSingleDocRefEntry;
11520
+
11521
+ exports.isSingleDocRefKind = invariants.isSingleDocRefKind;
11522
+
11071
11523
  exports.isStartableDefinition = invariants.isStartableDefinition;
11072
11524
 
11073
11525
  exports.isSubjectEntry = invariants.isSubjectEntry;
@@ -11078,6 +11530,16 @@ exports.isTodoListEntry = invariants.isTodoListEntry;
11078
11530
 
11079
11531
  exports.isTodoListItem = invariants.isTodoListItem;
11080
11532
 
11533
+ exports.isUnprimed = invariants.isUnprimed;
11534
+
11535
+ exports.minReaderModelOf = invariants.minReaderModelOf;
11536
+
11537
+ exports.modelVersionOf = invariants.modelVersionOf;
11538
+
11539
+ exports.parentRef = invariants.parentRef;
11540
+
11541
+ exports.parseDefinitionSnapshot = invariants.parseDefinitionSnapshot;
11542
+
11081
11543
  exports.parseGdr = invariants.parseGdr;
11082
11544
 
11083
11545
  exports.parseResourceGdr = invariants.parseResourceGdr;
@@ -11090,6 +11552,8 @@ exports.refDashboard = invariants.refDashboard;
11090
11552
 
11091
11553
  exports.refDataset = invariants.refDataset;
11092
11554
 
11555
+ exports.refKindAcceptsTypes = invariants.refKindAcceptsTypes;
11556
+
11093
11557
  exports.refMediaLibrary = invariants.refMediaLibrary;
11094
11558
 
11095
11559
  exports.rejectedRefTypes = invariants.rejectedRefTypes;
@@ -11098,6 +11562,10 @@ exports.releaseDocId = invariants.releaseDocId;
11098
11562
 
11099
11563
  exports.releaseRef = invariants.releaseRef;
11100
11564
 
11565
+ exports.requiredModelFeatures = invariants.requiredModelFeatures;
11566
+
11567
+ exports.requiredReaderModel = invariants.requiredReaderModel;
11568
+
11101
11569
  exports.resourceAliasesToMap = invariants.resourceAliasesToMap;
11102
11570
 
11103
11571
  exports.resourceFromParsed = invariants.resourceFromParsed;
@@ -11106,12 +11574,16 @@ exports.resourceGdr = invariants.resourceGdr;
11106
11574
 
11107
11575
  exports.sameResource = invariants.sameResource;
11108
11576
 
11577
+ exports.scalarValidationIssues = invariants.scalarValidationIssues;
11578
+
11109
11579
  exports.schemaTreeShape = invariants.schemaTreeShape;
11110
11580
 
11111
11581
  exports.startKindOf = invariants.startKindOf;
11112
11582
 
11113
11583
  exports.tagScopeFilter = invariants.tagScopeFilter;
11114
11584
 
11585
+ exports.terminalState = invariants.terminalState;
11586
+
11115
11587
  exports.toBareId = invariants.toBareId;
11116
11588
 
11117
11589
  exports.tryParseGdr = invariants.tryParseGdr;
@@ -11220,10 +11692,6 @@ exports.ConcurrentEditFieldError = ConcurrentEditFieldError;
11220
11692
 
11221
11693
  exports.ConcurrentFireActionError = ConcurrentFireActionError;
11222
11694
 
11223
- exports.DATA_MODEL_MIN_READER = DATA_MODEL_MIN_READER;
11224
-
11225
- exports.DATA_MODEL_VERSION = DATA_MODEL_VERSION;
11226
-
11227
11695
  exports.DEFAULT_CONTENT_PERSPECTIVE = DEFAULT_CONTENT_PERSPECTIVE;
11228
11696
 
11229
11697
  exports.DEFAULT_EFFECT_LEASE_MS = DEFAULT_EFFECT_LEASE_MS;
@@ -11252,15 +11720,13 @@ exports.GROUP_KIND_DISPLAY = GROUP_KIND_DISPLAY;
11252
11720
 
11253
11721
  exports.GUARD_DOC_TYPE = GUARD_DOC_TYPE;
11254
11722
 
11255
- exports.GUARD_LIFTED_PREDICATE = GUARD_LIFTED_PREDICATE;
11256
-
11257
11723
  exports.GUARD_OWNER = GUARD_OWNER;
11258
11724
 
11259
11725
  exports.HISTORY_DISPLAY = HISTORY_DISPLAY;
11260
11726
 
11261
- exports.MissingHandlerError = MissingHandlerError;
11727
+ exports.InitialFieldsInvalidError = InitialFieldsInvalidError;
11262
11728
 
11263
- exports.ModelVersionAheadError = ModelVersionAheadError;
11729
+ exports.MissingHandlerError = MissingHandlerError;
11264
11730
 
11265
11731
  exports.MutationGuardDeniedError = MutationGuardDeniedError;
11266
11732
 
@@ -11280,8 +11746,6 @@ exports.StartNotPrimedError = StartNotPrimedError;
11280
11746
 
11281
11747
  exports.StartNotSettledError = StartNotSettledError;
11282
11748
 
11283
- exports.WORKFLOW_INSTANCE_TYPE = WORKFLOW_INSTANCE_TYPE;
11284
-
11285
11749
  exports.WorkflowActionFired = WorkflowActionFired;
11286
11750
 
11287
11751
  exports.WorkflowDefinitionDeleted = WorkflowDefinitionDeleted;
@@ -11324,8 +11788,6 @@ exports.activityAutonomyOf = activityAutonomyOf;
11324
11788
 
11325
11789
  exports.applicableDefinitions = applicableDefinitions;
11326
11790
 
11327
- exports.assertReadableModel = assertReadableModel;
11328
-
11329
11791
  exports.autonomySummary = autonomySummary;
11330
11792
 
11331
11793
  exports.availableActions = availableActions;
@@ -11336,6 +11798,8 @@ exports.buildSnapshot = buildSnapshot;
11336
11798
 
11337
11799
  exports.checklistLines = checklistLines;
11338
11800
 
11801
+ exports.clientProjectUserDirectory = clientProjectUserDirectory;
11802
+
11339
11803
  exports.compileGuard = compileGuard;
11340
11804
 
11341
11805
  exports.computeDiffEntries = computeDiffEntries;
@@ -11412,9 +11876,9 @@ exports.evaluateMutationGuard = evaluateMutationGuard;
11412
11876
 
11413
11877
  exports.evaluateStartFilter = evaluateStartFilter;
11414
11878
 
11415
- exports.explainStartAllowed = explainStartAllowed;
11879
+ exports.expandResourceAliases = expandResourceAliases;
11416
11880
 
11417
- exports.fieldTreeShape = fieldTreeShape;
11881
+ exports.explainStartAllowed = explainStartAllowed;
11418
11882
 
11419
11883
  exports.findCurrentActivityEntry = findCurrentActivityEntry;
11420
11884
 
@@ -11434,6 +11898,8 @@ exports.hashDefinitionContent = hashDefinitionContent;
11434
11898
 
11435
11899
  exports.inFlightFilter = inFlightFilter;
11436
11900
 
11901
+ exports.initialFieldIssues = initialFieldIssues;
11902
+
11437
11903
  exports.instanceDocId = instanceDocId;
11438
11904
 
11439
11905
  exports.instanceGuardQuery = instanceGuardQuery;
@@ -11448,36 +11914,26 @@ exports.isDefinitionApplicable = isDefinitionApplicable;
11448
11914
 
11449
11915
  exports.isFilterScopedOut = isFilterScopedOut;
11450
11916
 
11451
- exports.isGuardLifted = isGuardLifted;
11917
+ exports.isProjectUserNotFoundError = isProjectUserNotFoundError;
11452
11918
 
11453
11919
  exports.isTelemetryEnvDenied = isTelemetryEnvDenied;
11454
11920
 
11455
11921
  exports.isTerminalStage = isTerminalStage;
11456
11922
 
11457
- exports.isUnprimed = isUnprimed;
11458
-
11459
11923
  exports.lakeGuardId = lakeGuardId;
11460
11924
 
11461
11925
  exports.latestDeployedDefinitions = latestDeployedDefinitions;
11462
11926
 
11463
11927
  exports.lintEffectOutputs = lintEffectOutputs;
11464
11928
 
11465
- exports.minReaderModelOf = minReaderModelOf;
11466
-
11467
11929
  exports.missingRequiredInputs = missingRequiredInputs;
11468
11930
 
11469
- exports.modelVersionOf = modelVersionOf;
11470
-
11471
11931
  exports.narrateAutonomyWaits = narrateAutonomyWaits;
11472
11932
 
11473
11933
  exports.noopTelemetry = noopTelemetry;
11474
11934
 
11475
- exports.parentRef = parentRef;
11476
-
11477
11935
  exports.parseDefinitionInput = parseDefinitionInput;
11478
11936
 
11479
- exports.parseDefinitionSnapshot = parseDefinitionSnapshot;
11480
-
11481
11937
  exports.parseGuardDocument = parseGuardDocument;
11482
11938
 
11483
11939
  exports.parseInstanceDocument = parseInstanceDocument;
@@ -11490,10 +11946,16 @@ exports.readInstanceDoc = readInstanceDoc;
11490
11946
 
11491
11947
  exports.readsRaw = readsRaw;
11492
11948
 
11949
+ exports.refsOf = refsOf;
11950
+
11493
11951
  exports.remediationsFor = remediationsFor;
11494
11952
 
11495
11953
  exports.resolveAccess = resolveAccess;
11496
11954
 
11955
+ exports.resolveActor = resolveActor;
11956
+
11957
+ exports.resolveClientActor = resolveClientActor;
11958
+
11497
11959
  exports.resolveFieldEntry = resolveFieldEntry$1;
11498
11960
 
11499
11961
  exports.retractStageGuards = retractStageGuards;
@@ -11516,8 +11978,6 @@ exports.subscriptionDocumentsForInstance = subscriptionDocumentsForInstance;
11516
11978
 
11517
11979
  exports.sweepStaleClaims = sweepStaleClaims;
11518
11980
 
11519
- exports.terminalState = terminalState;
11520
-
11521
11981
  exports.unboundAllowedReads = unboundAllowedReads;
11522
11982
 
11523
11983
  exports.unsatisfiedTransitionSummaries = unsatisfiedTransitionSummaries;