@sanity/workflow-engine 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -279,6 +279,10 @@ const HIGH_FREQUENCY_SAMPLE_MS = 6e4, WorkflowDefinitionDeployed = defineWorkflo
279
279
  name: "Editorial Workflows Stage Set",
280
280
  version: 1,
281
281
  description: "The set-stage admin override was invoked (changed: false = already at the target stage)"
282
+ }), WorkflowActivityReset = defineWorkflowEvent({
283
+ name: "Editorial Workflows Activity Reset",
284
+ version: 1,
285
+ description: "The reset-activity admin override was invoked (changed: false = already at the target status, or the instance was terminal)"
282
286
  }), WorkflowDefinitionDeleted = defineWorkflowEvent({
283
287
  name: "Editorial Workflows Definition Deleted",
284
288
  version: 1,
@@ -859,7 +863,7 @@ function toGroqDescribeContext(ctx) {
859
863
  };
860
864
  }
861
865
 
862
- const VAR_LABELS = new Map([ ...invariants.START_ALLOWED_VARS, ...invariants.CONDITION_VARS ].map(entry => [ entry.name, entry.label ]));
866
+ const VAR_LABELS = new Map([ ...invariants.START_REQUIREMENT_VARS, ...invariants.CONDITION_VARS ].map(entry => [ entry.name, entry.label ]));
863
867
 
864
868
  function renderWorkflowRead(read, ctx) {
865
869
  if (read.variable === "fields" && typeof read.path[0] == "string") {
@@ -932,13 +936,6 @@ function clauseVarRequirement(requirement) {
932
936
  params: {},
933
937
  text: "no activity in this stage may have failed"
934
938
  });
935
- if (target.variable === invariants.SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR) return kind === "truthy" ? groqConditionDescribe.phrase("requirement.subject-has-in-flight-instance", {
936
- params: {},
937
- text: "the subject must already have an in-flight workflow"
938
- }) : groqConditionDescribe.phrase("requirement.subject-has-no-in-flight-instance", {
939
- params: {},
940
- text: "the subject must not already have an in-flight workflow"
941
- });
942
939
  }
943
940
  }
944
941
 
@@ -1434,14 +1431,14 @@ function activitySites(activity, stage) {
1434
1431
  activity: activity.name
1435
1432
  },
1436
1433
  condition: activity.filter
1437
- } ] : [], ...Object.entries(activity.requirements ?? {}).map(([requirement, condition]) => ({
1434
+ } ] : [], ...(activity.requirements ?? []).map(requirement => ({
1438
1435
  stage: stage,
1439
1436
  address: {
1440
1437
  kind: "requirement",
1441
1438
  activity: activity.name,
1442
- requirement: requirement
1439
+ requirement: requirement.name
1443
1440
  },
1444
- condition: condition
1441
+ condition: requirement.query
1445
1442
  })), ...(activity.actions ?? []).flatMap(action => [ ...action.filter !== void 0 ? [ {
1446
1443
  stage: stage,
1447
1444
  address: {
@@ -1509,13 +1506,7 @@ async function evaluateStartFilter(args) {
1509
1506
  const {filter: filter, definition: definition, document: document, scope: scope} = args, parsed = parseStartFilter({
1510
1507
  filter: filter,
1511
1508
  definition: definition
1512
- });
1513
- assertFilterSubject({
1514
- readsSubject: parsed.readsSubject,
1515
- definition: definition,
1516
- scope: scope
1517
- });
1518
- const dataset = await filterDataset(parsed.needsDataset, scope);
1509
+ }), dataset = await filterDataset(parsed.needsDataset, scope);
1519
1510
  if (dataset === void 0) return !1;
1520
1511
  try {
1521
1512
  const result = await (await groqJs.evaluate(parsed.tree, {
@@ -1525,8 +1516,7 @@ async function evaluateStartFilter(args) {
1525
1516
  dataset: dataset,
1526
1517
  params: startContextParams({
1527
1518
  definition: definition,
1528
- scope: scope,
1529
- dataset: dataset
1519
+ scope: scope
1530
1520
  })
1531
1521
  })).get();
1532
1522
  return invariants.isUnevaluable(result) ? !1 : !!result;
@@ -1542,11 +1532,9 @@ async function evaluateStartFilter(args) {
1542
1532
  function parseStartFilter(args) {
1543
1533
  const {filter: filter, definition: definition} = args;
1544
1534
  try {
1545
- const readsSubject = readsSubjectHasInFlightInstance(filter);
1546
1535
  return {
1547
1536
  tree: groqJs.parse(filter),
1548
- needsDataset: groqConditionDescribe.analyzeCondition(filter).readsDataset || readsSubject,
1549
- readsSubject: readsSubject
1537
+ needsDataset: groqConditionDescribe.analyzeCondition(filter).readsDataset
1550
1538
  };
1551
1539
  } catch (err) {
1552
1540
  rethrowNamingDefinition({
@@ -1557,70 +1545,81 @@ function parseStartFilter(args) {
1557
1545
  }
1558
1546
  }
1559
1547
 
1560
- function assertFilterSubject(args) {
1561
- 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.`);
1562
- }
1563
-
1564
1548
  async function filterDataset(needsDataset, scope) {
1565
1549
  if (!needsDataset) return [];
1566
1550
  if (scope?.fetchDataset !== void 0) return scope.fetchDataset();
1567
1551
  }
1568
1552
 
1569
- async function explainStartAllowed(args) {
1570
- const {allowed: allowed, definition: definition, fields: fields, scope: scope} = args;
1553
+ async function explainStartRequirement(args) {
1554
+ const {query: query, definition: definition, fields: fields, scope: scope} = args;
1571
1555
  let needsDataset;
1572
1556
  try {
1573
- needsDataset = groqConditionDescribe.analyzeCondition(allowed).readsDataset || readsSubjectHasInFlightInstance(allowed);
1557
+ needsDataset = groqConditionDescribe.analyzeCondition(query).readsDataset;
1574
1558
  } catch (err) {
1575
1559
  rethrowNamingDefinition({
1576
1560
  err: err,
1577
1561
  definition: definition,
1578
- key: "allowed"
1562
+ key: "requirement"
1579
1563
  });
1580
1564
  }
1581
1565
  let dataset = [];
1582
1566
  if (needsDataset) {
1583
- if (scope?.fetchDataset === void 0) throw new invariants.ContractViolationError(`start.allowed on definition "${definition.name ?? "<unnamed>"}" reads the dataset (\`*[...]\`), but the caller supplied no \`scope.fetchDataset\` — evaluating over an empty slice would let a count()-style rule pass vacuously. Supply the instance slice.`);
1567
+ if (scope?.fetchDataset === void 0) throw new invariants.ContractViolationError(`start requirement on definition "${definition.name ?? "<unnamed>"}" reads the dataset (\`*[...]\`), but the caller supplied no \`scope.fetchDataset\` — evaluating over an empty slice would let a count()-style rule pass vacuously. Supply the instance slice.`);
1584
1568
  dataset = await scope.fetchDataset();
1585
1569
  }
1586
1570
  try {
1587
1571
  return await groqConditionDescribe.explainCondition({
1588
- condition: allowed,
1572
+ condition: query,
1589
1573
  dataset: dataset,
1590
1574
  params: startContextParams({
1591
1575
  definition: definition,
1592
1576
  scope: scope,
1593
- fields: fields,
1594
- dataset: dataset
1577
+ fields: fields
1595
1578
  })
1596
1579
  });
1597
1580
  } catch (err) {
1598
1581
  rethrowNamingDefinition({
1599
1582
  err: err,
1600
1583
  definition: definition,
1601
- key: "allowed"
1584
+ key: "requirement"
1602
1585
  });
1603
1586
  }
1604
1587
  }
1605
1588
 
1606
- function unboundAllowedReads(allowed, fields) {
1607
- return [ ...invariants.conditionFieldReadNames(allowed) ].filter(name => !Object.hasOwn(fields, name));
1589
+ function unboundRequirementReads(query, fields) {
1590
+ return [ ...invariants.conditionFieldReadNames(query) ].filter(name => !Object.hasOwn(fields, name));
1591
+ }
1592
+
1593
+ function hasSingleSubjectRequirement(definition) {
1594
+ return definition.start?.requirements?.some(requirement => requirement.type === "singleSubject") ?? !1;
1595
+ }
1596
+
1597
+ async function singleSubjectRequirementRefused(args) {
1598
+ const {definition: definition, fields: fields, scope: scope} = args, name = singleSubjectDefinitionName(definition), subject = (fields !== void 0 ? startSubject(definition, fields) : void 0) ?? scope?.subject;
1599
+ if (subject === void 0) return !1;
1600
+ if (scope?.fetchDataset === void 0) throw new invariants.ContractViolationError(`singleSubject requirement on definition "${name}" scans the tag's instance slice, but the caller supplied no \`scope.fetchDataset\` — evaluating over an empty slice would let the rule pass vacuously. Supply the projected start slice.`);
1601
+ const dataset = await scope.fetchDataset();
1602
+ return sliceHasInFlightSubject({
1603
+ dataset: dataset,
1604
+ subject: subject,
1605
+ definition: name
1606
+ });
1608
1607
  }
1609
1608
 
1610
- function unboundAllowedReadsWithSubject(args) {
1611
- const {allowed: allowed, fields: fields, definition: definition} = args, subjectField = (definition.fields ?? []).find(invariants.isSubjectEntry)?.name, reads = unboundAllowedReads(allowed, fields);
1612
- return subjectField !== void 0 && readsSubjectHasInFlightInstance(allowed) && !Object.hasOwn(fields, subjectField) && !reads.includes(subjectField) && reads.push(subjectField),
1613
- reads;
1609
+ function singleSubjectDefinitionName(definition) {
1610
+ const label = definition.name ?? "<unnamed>";
1611
+ if (definition.start?.requirements?.find(requirement => requirement.type === "singleSubject") === void 0) throw new invariants.ContractViolationError(`singleSubjectRequirementRefused called for definition "${label}", which declares no \`singleSubject\` requirement — evaluate only what the definition declares.`);
1612
+ if (definition.name === void 0) throw new invariants.ContractViolationError("singleSubjectRequirementRefused needs the definition `name` — the rule scopes its in-flight scan to instances of the definition, which a nameless source cannot identify.");
1613
+ return definition.name;
1614
1614
  }
1615
1615
 
1616
1616
  function rethrowNamingDefinition({err: err, definition: definition, key: key}) {
1617
- invariants.rethrowWithContext(err, `start.${key} on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (check the predicate and its evaluation inputs)`);
1617
+ invariants.rethrowWithContext(err, `${key === "filter" ? "start.filter" : "start requirement"} on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (check the predicate and its evaluation inputs)`);
1618
1618
  }
1619
1619
 
1620
- function startContextParams({definition: definition, scope: scope, fields: fields, dataset: dataset}) {
1621
- const subject = fields === void 0 ? scope?.subject : startSubject(definition, fields) ?? scope?.subject, params = baseStartParams(definition, scope);
1622
- return fields !== void 0 && (params.fields = fields), subject !== void 0 && (params[invariants.SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR] = subjectHasInFlightInstance(dataset, subject)),
1623
- params;
1620
+ function startContextParams({definition: definition, scope: scope, fields: fields}) {
1621
+ const params = baseStartParams(definition, scope);
1622
+ return fields !== void 0 && (params.fields = fields), params;
1624
1623
  }
1625
1624
 
1626
1625
  function baseStartParams(definition, scope) {
@@ -1637,10 +1636,6 @@ function baseStartParams(definition, scope) {
1637
1636
  };
1638
1637
  }
1639
1638
 
1640
- function readsSubjectHasInFlightInstance(groq) {
1641
- return invariants.conditionParameterNames(groq).has(invariants.SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR);
1642
- }
1643
-
1644
1639
  function startSubject(definition, fields) {
1645
1640
  const subjectEntry = (definition.fields ?? []).find(invariants.isSubjectEntry);
1646
1641
  if (subjectEntry === void 0) return;
@@ -1648,15 +1643,12 @@ function startSubject(definition, fields) {
1648
1643
  return invariants.isGdr(value) ? value.id : void 0;
1649
1644
  }
1650
1645
 
1651
- function subjectHasInFlightInstance(dataset, subject) {
1646
+ function sliceHasInFlightSubject(args) {
1647
+ const {dataset: dataset, subject: subject, definition: definition} = args;
1652
1648
  return dataset.some(row => {
1653
1649
  if (typeof row != "object" || row === null) return !1;
1654
1650
  const instance = row;
1655
- return instance._type !== invariants.WORKFLOW_INSTANCE_TYPE || instance.completedAt !== void 0 || !Array.isArray(instance.fields) ? !1 : instance.fields.some(entry => {
1656
- if (typeof entry != "object" || entry === null) return !1;
1657
- const field = entry;
1658
- return field._type === "subject" && invariants.isGdr(field.value) && field.value.id === subject;
1659
- });
1651
+ return instance.completedAt != null || instance.definition !== definition ? !1 : instance.subject === subject;
1660
1652
  });
1661
1653
  }
1662
1654
 
@@ -1686,11 +1678,11 @@ function startFilterSyntaxIssues(groq) {
1686
1678
  });
1687
1679
  }
1688
1680
 
1689
- function startAllowedSyntaxIssues(groq) {
1681
+ function startRequirementSyntaxIssues(groq) {
1690
1682
  return startContextSyntaxIssues({
1691
1683
  groq: groq,
1692
- vars: invariants.START_ALLOWED_VARS,
1693
- context: "start-allowed"
1684
+ vars: invariants.START_REQUIREMENT_VARS,
1685
+ context: "start-requirement"
1694
1686
  });
1695
1687
  }
1696
1688
 
@@ -1698,7 +1690,7 @@ function startContextSyntaxIssues({groq: groq, vars: vars, context: context, ski
1698
1690
  try {
1699
1691
  groqJs.parse(groq);
1700
1692
  } catch (err) {
1701
- return [ err instanceof Error ? err.message : String(err) ];
1693
+ return [ invariants.errorMessage(err) ];
1702
1694
  }
1703
1695
  const bound = new Set(vars.map(v2 => v2.name));
1704
1696
  return [ ...invariants.conditionParameterNames(groq) ].filter(name => !bound.has(name) && name !== skip).map(name => `reads $${name}, which the ${context} context does not bind (an unbound variable is GROQ null, so the predicate silently misevaluates — failing closed or passing vacuously by shape). Bound here: ` + vars.map(v2 => `$${v2.name}`).join(", "));
@@ -2538,24 +2530,24 @@ function applyFieldSet(op, ctx) {
2538
2530
  src: op.value,
2539
2531
  ctx: ctx,
2540
2532
  target: entrySlot(entry)
2541
- });
2542
- return invariants.validateFieldValue({
2533
+ }), validated = invariants.validateFieldValue({
2543
2534
  entryType: entry._type,
2544
2535
  entryName: entry.name,
2545
2536
  value: value,
2546
2537
  ...entryShape(entry)
2547
- }), assertRuntimeRefsWithinSurface({
2538
+ });
2539
+ return assertRuntimeRefsWithinSurface({
2548
2540
  entryType: entry._type,
2549
2541
  entryName: entry.name,
2550
- value: value,
2542
+ value: validated,
2551
2543
  ...entryShape(entry),
2552
2544
  ctx: ctx,
2553
2545
  src: op.value
2554
- }), setEntryValue(entry, value), {
2546
+ }), setEntryValue(entry, validated), {
2555
2547
  opType: op.type,
2556
2548
  target: op.target,
2557
2549
  resolved: {
2558
- value: value
2550
+ value: validated
2559
2551
  }
2560
2552
  };
2561
2553
  }
@@ -2616,27 +2608,27 @@ function applyFieldAppend(op, ctx) {
2616
2608
  ...slot !== void 0 ? {
2617
2609
  target: slot
2618
2610
  } : {}
2619
- });
2620
- invariants.validateFieldAppendItem({
2611
+ }), validated = invariants.validateFieldAppendItem({
2621
2612
  entryType: entry._type,
2622
2613
  entryName: entry.name,
2623
2614
  item: item,
2624
2615
  types: entry._type === "doc.refs" ? entry.types : void 0,
2625
2616
  of: entry._type === "array" ? entry.of : void 0
2626
- }), assertRuntimeRefsWithinSurface({
2617
+ });
2618
+ assertRuntimeRefsWithinSurface({
2627
2619
  entryType: entry._type === "doc.refs" ? "doc.ref" : "object",
2628
2620
  entryName: entry.name,
2629
- value: item,
2621
+ value: validated,
2630
2622
  fields: entry._type === "array" ? entry.of : void 0,
2631
2623
  ctx: ctx,
2632
2624
  src: op.value
2633
2625
  });
2634
2626
  const current = Array.isArray(entry.value) ? entry.value : [];
2635
- return setEntryValue(entry, [ ...current, withRowKey(item) ]), {
2627
+ return setEntryValue(entry, [ ...current, withRowKey(validated) ]), {
2636
2628
  opType: op.type,
2637
2629
  target: op.target,
2638
2630
  resolved: {
2639
- item: item
2631
+ item: validated
2640
2632
  }
2641
2633
  };
2642
2634
  }
@@ -2664,8 +2656,9 @@ async function applyFieldUpdateWhere(op, ctx) {
2664
2656
  mode: "value",
2665
2657
  issues: [ `field.updateWhere value must resolve to an object of fields to merge (target "${op.target.field}")` ]
2666
2658
  });
2659
+ const mergeRecord = merge;
2667
2660
  requireNoReservedMergeKeys({
2668
- merge: merge,
2661
+ merge: mergeRecord,
2669
2662
  entry: entry,
2670
2663
  op: op
2671
2664
  });
@@ -2675,25 +2668,27 @@ async function applyFieldUpdateWhere(op, ctx) {
2675
2668
  ctx: ctx
2676
2669
  }), next = rows.map((row, index) => matches[index] ? {
2677
2670
  ...row,
2678
- ...merge
2679
- } : row);
2680
- return invariants.validateFieldValue({
2671
+ ...mergeRecord
2672
+ } : row), validated = invariants.validateFieldValue({
2681
2673
  entryType: entry._type,
2682
2674
  entryName: entry.name,
2683
2675
  value: next,
2684
2676
  of: entry.of
2685
- }), assertRuntimeRefsWithinSurface({
2677
+ });
2678
+ assertRuntimeRefsWithinSurface({
2686
2679
  entryType: "object",
2687
2680
  entryName: entry.name,
2688
2681
  value: merge,
2689
2682
  fields: entry.of,
2690
2683
  ctx: ctx,
2691
2684
  src: op.value
2692
- }), setEntryValue(entry, next), {
2685
+ }), setEntryValue(entry, validated);
2686
+ const firstMatch = matches.indexOf(!0), persistedRows = validated, persistedMerge = firstMatch === -1 ? merge : Object.fromEntries(Object.keys(mergeRecord).map(key => [ key, persistedRows[firstMatch]?.[key] ]));
2687
+ return {
2693
2688
  opType: op.type,
2694
2689
  target: op.target,
2695
2690
  resolved: {
2696
- merge: merge
2691
+ merge: persistedMerge
2697
2692
  }
2698
2693
  };
2699
2694
  }
@@ -2885,8 +2880,8 @@ function validateDefinition(definition) {
2885
2880
  address: address
2886
2881
  }));
2887
2882
  }
2888
- definition.start?.filter !== void 0 && v2.report("start.filter", startFilterSyntaxIssues(definition.start.filter)),
2889
- definition.start?.allowed !== void 0 && v2.report("start.allowed", startAllowedSyntaxIssues(definition.start.allowed));
2883
+ definition.start?.filter !== void 0 && v2.report("start.filter", startFilterSyntaxIssues(definition.start.filter));
2884
+ for (const [index, requirement] of (definition.start?.requirements ?? []).entries()) requirement.type === "groq" && v2.report(`start.requirements[${index}].query`, startRequirementSyntaxIssues(requirement.query));
2890
2885
  for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
2891
2886
  for (const stage of definition.stages) validateStage(v2, stage);
2892
2887
  for (const issue of invariants.checkWorkflowInvariants(definition)) v2.errors.push(` · ${invariants.formatIssuePath(issue.path)}: ${issue.message}`);
@@ -3316,6 +3311,11 @@ function definitionsListGroq(versionOrder) {
3316
3311
  return `*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && ${invariants.tagScopeFilter()}] | order(name asc, version ${versionOrder})`;
3317
3312
  }
3318
3313
 
3314
+ function latestDefinitionsGroq() {
3315
+ const scoped = `_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && ${invariants.tagScopeFilter()}`;
3316
+ return `*[${scoped} && version == math::max(*[${scoped} && name == ^.name].version)] | order(name asc)`;
3317
+ }
3318
+
3319
3319
  function latestDeployedDefinitions(rows) {
3320
3320
  const byName = /* @__PURE__ */ new Map;
3321
3321
  for (const row of rows) {
@@ -3397,7 +3397,7 @@ function blockedState(input) {
3397
3397
  if (blocked !== void 0) return {
3398
3398
  state: "blocked",
3399
3399
  activity: blocked.activity.name,
3400
- requirements: blocked.unmetRequirements ?? [],
3400
+ requirements: (blocked.unmetRequirements ?? []).map(requirement => requirement.name),
3401
3401
  assignees: input.assignees[blocked.activity.name] ?? []
3402
3402
  };
3403
3403
  }
@@ -3440,7 +3440,7 @@ function diagnoseInstance(input) {
3440
3440
  };
3441
3441
  }
3442
3442
 
3443
- const RUNNABLE_VERBS = /* @__PURE__ */ new Set([ "set-stage", "abort", "drain-effects" ]);
3443
+ const RUNNABLE_VERBS = /* @__PURE__ */ new Set([ "set-stage", "abort", "drain-effects", "reset-activity" ]);
3444
3444
 
3445
3445
  function remediationsForCause(cause) {
3446
3446
  switch (cause.kind) {
@@ -3942,7 +3942,7 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3942
3942
  defaultValue: defaultValue
3943
3943
  });
3944
3944
  if (entry.initialValue?.type === "query") {
3945
- const issues = invariants.checkFieldValue({
3945
+ const check = invariants.parseFieldValue({
3946
3946
  entryType: entry.type,
3947
3947
  value: value,
3948
3948
  types: entry.types,
@@ -3951,9 +3951,9 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3951
3951
  options: entry.options,
3952
3952
  validation: entry.validation
3953
3953
  });
3954
- return issues !== void 0 ? (ctx.recordDiscard?.({
3954
+ return "issues" in check ? (ctx.recordDiscard?.({
3955
3955
  field: entry.name,
3956
- detail: issues.join("; ")
3956
+ detail: check.issues.join("; ")
3957
3957
  }), buildResolvedEntry({
3958
3958
  entry: entry,
3959
3959
  value: defaultValue,
@@ -3961,12 +3961,12 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3961
3961
  now: ctx.now
3962
3962
  })) : buildResolvedEntry({
3963
3963
  entry: entry,
3964
- value: value,
3964
+ value: check.output,
3965
3965
  _key: randomKey2(),
3966
3966
  now: ctx.now
3967
3967
  });
3968
3968
  }
3969
- return invariants.validateFieldValue({
3969
+ const validated = invariants.validateFieldValue({
3970
3970
  entryType: entry.type,
3971
3971
  entryName: entry.name,
3972
3972
  value: value,
@@ -3975,16 +3975,17 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3975
3975
  of: entry.of,
3976
3976
  options: entry.options,
3977
3977
  validation: entry.validation
3978
- }), entry.initialValue?.type === "input" && ctx.inputProvenance !== "definition" && assertRefsWithinSurface({
3978
+ });
3979
+ return entry.initialValue?.type === "input" && ctx.inputProvenance !== "definition" && assertRefsWithinSurface({
3979
3980
  entryType: entry.type,
3980
3981
  entryName: entry.name,
3981
- value: value,
3982
+ value: validated,
3982
3983
  fields: entry.fields,
3983
3984
  of: entry.of,
3984
3985
  surface: ctx.refSurface
3985
3986
  }), buildResolvedEntry({
3986
3987
  entry: entry,
3987
- value: value,
3988
+ value: validated,
3988
3989
  _key: randomKey2(),
3989
3990
  now: ctx.now
3990
3991
  });
@@ -4016,12 +4017,27 @@ function assertInputValueShape(entry, value) {
4016
4017
  function assertGdrShape(value, context) {
4017
4018
  if (typeof value != "object" || value === null) throw new invariants.ContractViolationError(`Invalid GDR for ${context}: expected { id: "<scheme>:...", type: "<schema>" }, got ${typeof value}.`);
4018
4019
  const v2 = value;
4019
- if (typeof v2.id != "string" || !invariants.isGdrUri(v2.id)) throw new invariants.ContractViolationError(`Invalid GDR for ${context}: \`id\` must be a GDR URI ("<scheme>:<...id-parts>" with scheme dataset|canvas|media-library|dashboard). Got ${JSON.stringify(v2.id)}. Construct via \`gdrFromResource\` / \`refDataset\` / \`refCanvas\` etc. — bare document ids are not accepted.`);
4020
+ if (typeof v2.id != "string") throw invalidGdrUriError(context, v2.id);
4021
+ try {
4022
+ invariants.parseGdr(v2.id);
4023
+ } catch (error) {
4024
+ throw error instanceof invariants.VersionSpecificDatasetGdrError ? new invariants.ContractViolationError(`Invalid GDR for ${context}: ${error.message}`) : invalidGdrUriError(context, v2.id);
4025
+ }
4020
4026
  if (typeof v2.type != "string" || v2.type.length === 0) throw new invariants.ContractViolationError(`Invalid GDR for ${context}: \`type\` (schema name) must be a non-empty string. Got ${JSON.stringify(v2.type)}.`);
4021
4027
  }
4022
4028
 
4029
+ function invalidGdrUriError(context, id) {
4030
+ return new invariants.ContractViolationError(`Invalid GDR for ${context}: \`id\` must be a GDR URI ("<scheme>:<...id-parts>" with scheme dataset|canvas|media-library|dashboard). Got ${JSON.stringify(id)}. Construct via \`gdrFromResource\` / \`refDataset\` / \`refCanvas\` etc. — bare document ids are not accepted.`);
4031
+ }
4032
+
4023
4033
  function normalizeQueryResult({entryType: entryType, raw: raw, workflowResource: workflowResource}) {
4024
- 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;
4034
+ if (raw == null) return raw;
4035
+ try {
4036
+ return invariants.isSingleDocRefKind(entryType) ? coerceToGdr(raw, workflowResource) : entryType === "doc.refs" ? Array.isArray(raw) ? raw.map(item => coerceToGdr(item, workflowResource)).filter(v2 => v2 !== null) : [] : raw;
4037
+ } catch (error) {
4038
+ if (error instanceof invariants.VersionSpecificDatasetGdrError) return raw;
4039
+ throw error;
4040
+ }
4025
4041
  }
4026
4042
 
4027
4043
  function coerceGdrShape(raw, workflowResource) {
@@ -4363,219 +4379,706 @@ function parseInstanceDocument(doc) {
4363
4379
  });
4364
4380
  }
4365
4381
 
4366
- async function getInstanceDocument(client, instanceId) {
4367
- const doc = await client.getDocument(instanceId);
4368
- return doc ? readInstanceDoc(doc) : void 0;
4369
- }
4382
+ const SYNC_COMMIT = {
4383
+ visibility: "sync"
4384
+ }, ENGINE_API_VERSION = "2026-04-29", REQUEST_TAG = {
4385
+ engine: "workflow",
4386
+ deploy: "workflow.deploy",
4387
+ deleteDefinition: "workflow.delete-definition",
4388
+ start: "workflow.start",
4389
+ fireAction: "workflow.fire-action",
4390
+ editField: "workflow.edit-field",
4391
+ completeEffect: "workflow.complete-effect",
4392
+ commitEffectOps: "workflow.commit-effect-ops",
4393
+ tick: "workflow.tick",
4394
+ evaluate: "workflow.evaluate",
4395
+ evaluateStart: "workflow.evaluate-start",
4396
+ diagnose: "workflow.diagnose",
4397
+ availableActions: "workflow.available-actions",
4398
+ setStage: "workflow.set-stage",
4399
+ abort: "workflow.abort",
4400
+ resetActivity: "workflow.reset-activity",
4401
+ getInstance: "workflow.get-instance",
4402
+ children: "workflow.children",
4403
+ instancesForDocument: "workflow.instances-for-document",
4404
+ definitionsForDocument: "workflow.definitions-for-document",
4405
+ query: "workflow.query",
4406
+ discover: "workflow.discover",
4407
+ guardQuery: "workflow.guard.query",
4408
+ guardDeploy: "workflow.guard.deploy",
4409
+ guardRefresh: "workflow.guard.refresh",
4410
+ guardRetract: "workflow.guard.retract",
4411
+ effectList: "workflow.effect.list",
4412
+ effectFind: "workflow.effect.find",
4413
+ drain: "workflow.drain",
4414
+ effect: "workflow.effect",
4415
+ verifyDefinitions: "workflow.verify-definitions",
4416
+ accessResolveActor: "workflow.access.resolve-actor",
4417
+ accessGrants: "workflow.access.grants"
4418
+ }, RAW_PATCH = /* @__PURE__ */ Symbol("workflow-engine.raw-patch");
4370
4419
 
4371
- function readInstanceDoc(doc) {
4372
- return parseInstanceDocument(invariants.assertReadableModel(doc));
4420
+ function unwrapPatch(patch) {
4421
+ return typeof patch == "object" && patch !== null && RAW_PATCH in patch ? patch[RAW_PATCH] : patch;
4373
4422
  }
4374
4423
 
4375
- async function reload({client: client, instanceId: instanceId, tag: tag}) {
4376
- const doc = await getInstanceDocument(client, instanceId);
4377
- if (!doc) throw new invariants.InstanceNotFoundError({
4378
- instanceId: instanceId
4379
- });
4380
- if (doc.tag !== tag) throw new invariants.InstanceNotFoundError({
4381
- instanceId: instanceId,
4382
- detail: "not visible to this engine: tag mismatch"
4383
- });
4384
- return doc;
4385
- }
4424
+ const wrapperCache = /* @__PURE__ */ new WeakMap, effectHandlerClientCache = /* @__PURE__ */ new WeakMap, RAW_CLIENT = /* @__PURE__ */ Symbol("workflow-engine.raw-client");
4386
4425
 
4387
- function contextEntry(name, value) {
4388
- const _key = randomKey();
4389
- return typeof value == "string" ? {
4390
- _key: _key,
4391
- _type: "context.string",
4392
- name: name,
4393
- value: value
4394
- } : typeof value == "number" ? {
4395
- _key: _key,
4396
- _type: "context.number",
4397
- name: name,
4398
- value: value
4399
- } : typeof value == "boolean" ? {
4400
- _key: _key,
4401
- _type: "context.boolean",
4402
- name: name,
4403
- value: value
4404
- } : {
4405
- _key: _key,
4406
- _type: "context.ref",
4407
- name: name,
4408
- value: value
4409
- };
4426
+ function unwrapRequestTag(client) {
4427
+ let current = client;
4428
+ for (;RAW_CLIENT in current; ) current = current[RAW_CLIENT];
4429
+ return current;
4410
4430
  }
4411
4431
 
4412
- function contextJsonEntry(name, value) {
4413
- return {
4414
- _key: randomKey(),
4415
- _type: "context.json",
4416
- name: name,
4417
- value: serializeContextValue(name, value)
4418
- };
4432
+ function withRequestTag(client, fallback) {
4433
+ let byTag = wrapperCache.get(client);
4434
+ byTag === void 0 && (byTag = /* @__PURE__ */ new Map, wrapperCache.set(client, byTag));
4435
+ const cached = byTag.get(fallback);
4436
+ if (cached !== void 0) return cached;
4437
+ const wrapped = buildTaggedClient(client, fallback);
4438
+ return byTag.set(fallback, wrapped), wrapped;
4419
4439
  }
4420
4440
 
4421
- function serializeContextValue(name, value) {
4422
- let json;
4423
- try {
4424
- json = JSON.stringify(value);
4425
- } catch (err) {
4426
- invariants.rethrowWithContext(err, `context entry "${name}" holds unserializable JSON`);
4441
+ function effectHandlerClient(client) {
4442
+ const cached = effectHandlerClientCache.get(client);
4443
+ if (cached !== void 0) return cached;
4444
+ let tagged;
4445
+ if (client.withConfig === void 0) tagged = withRequestTag(client, REQUEST_TAG.effect); else {
4446
+ const prefix = clientRequestTagPrefix(client), effectTag = composeRequestTag(prefix, REQUEST_TAG.effect);
4447
+ tagged = client.withConfig({
4448
+ requestTagPrefix: effectTag
4449
+ });
4427
4450
  }
4428
- if (json === void 0) throw new Error(`context entry "${name}" holds a non-JSON value (undefined, a function, or a symbol)`);
4429
- return json;
4451
+ return effectHandlerClientCache.set(client, tagged), tagged;
4430
4452
  }
4431
4453
 
4432
- function buildInstanceBase(args) {
4433
- const {id: id, now: now, actor: actor, perspective: perspective, executionContext: executionContext} = args, entries = [ {
4434
- _key: randomKey(),
4435
- _type: "stageEntered",
4436
- at: now,
4437
- stage: args.initialStage,
4438
- ...actor !== void 0 ? {
4439
- actor: actor
4440
- } : {}
4441
- }, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries, body = {
4442
- _id: id,
4443
- _type: invariants.WORKFLOW_INSTANCE_TYPE,
4444
- _rev: "",
4445
- _createdAt: now,
4446
- _updatedAt: now,
4447
- tag: args.tag,
4448
- workflowResource: args.workflowResource,
4449
- definition: args.definitionName,
4450
- pinnedVersion: args.pinnedVersion,
4451
- ...args.pinnedContentHash !== void 0 ? {
4452
- pinnedContentHash: args.pinnedContentHash
4453
- } : {},
4454
- definitionSnapshot: JSON.stringify(args.definition),
4455
- fields: args.fields,
4456
- context: args.context,
4457
- ancestors: args.ancestors,
4458
- ...perspective !== void 0 ? {
4459
- perspective: perspective
4460
- } : {},
4461
- currentStage: args.initialStage,
4462
- stages: [],
4463
- subworkflows: [],
4464
- pendingEffects: [],
4465
- effectHistory: [],
4466
- history: history,
4467
- startedAt: now,
4468
- lastChangedAt: now
4469
- };
4470
- return {
4471
- ...body,
4472
- ...invariants.modelStampFor({
4473
- documentType: "instance",
4474
- document: body
4475
- })
4476
- };
4454
+ function effectHandlerResolver(resolver) {
4455
+ return mapResourceClientResolver(resolver, effectHandlerClient);
4477
4456
  }
4478
4457
 
4479
- async function hydrateSnapshot(args) {
4480
- const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set, loadInto = async (uri, perspective) => {
4481
- if (visited.has(uri)) return;
4482
- const held = overlay?.get(uri);
4483
- if (held !== void 0) {
4484
- loaded.push(held), visited.add(uri);
4485
- return;
4486
- }
4487
- const fetched = await loadByGdr({
4488
- defaultClient: client,
4489
- clientForGdr: clientForGdr,
4490
- defaultResource: instance.workflowResource,
4491
- uri: uri,
4492
- perspective: perspective
4493
- });
4494
- fetched && (loaded.push(fetched), visited.add(uri));
4458
+ function mapResourceClientResolver(resolver, mapClient) {
4459
+ if (resolver !== void 0) return parsed => {
4460
+ const client = resolver(parsed);
4461
+ return client === void 0 ? void 0 : mapClient(client);
4495
4462
  };
4496
- loaded.push({
4497
- doc: instance,
4498
- resource: instance.workflowResource
4499
- }), visited.add(invariants.selfGdr(instance));
4500
- for (const ref of collectWatchRefs(instance)) await loadInto(ref.id, readsRaw(ref) ? "raw" : instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE);
4501
- return buildSnapshot({
4502
- docs: loaded
4503
- });
4504
- }
4505
-
4506
- async function loadByGdr({defaultClient: defaultClient, clientForGdr: clientForGdr, defaultResource: defaultResource, uri: uri, perspective: perspective}) {
4507
- const parsed = invariants.tryParseGdr(uri);
4508
- if (parsed === void 0) {
4509
- const doc2 = await readDoc({
4510
- client: defaultClient,
4511
- id: uri,
4512
- perspective: perspective
4513
- });
4514
- return doc2 ? {
4515
- doc: doc2,
4516
- resource: defaultResource
4517
- } : null;
4518
- }
4519
- const routed = clientForGdr(parsed), doc = await readDoc({
4520
- client: routed,
4521
- id: parsed.documentId,
4522
- perspective: perspective
4523
- });
4524
- return doc ? {
4525
- doc: doc,
4526
- resource: invariants.resourceFromParsed(parsed)
4527
- } : null;
4528
4463
  }
4529
4464
 
4530
- async function readDoc({client: client, id: id, perspective: perspective}) {
4531
- if (perspective === "raw") {
4532
- const doc2 = await client.getDocument(id) ?? null;
4533
- return doc2 ? doc2._type === invariants.WORKFLOW_INSTANCE_TYPE ? readInstanceDoc(doc2) : invariants.assertReadableModel(doc2) : null;
4534
- }
4535
- const {query: query, params: params} = contentDocQuery(id);
4536
- return await client.fetch(query, params, {
4537
- perspective: perspective
4538
- }) ?? null;
4465
+ function isWorkflowsFamilyPrefix(prefix) {
4466
+ return prefix === void 0 ? !1 : prefix === "sanity.workflows" || prefix.startsWith("sanity.workflows.") || prefix === "sanity.workflows-mcp";
4539
4467
  }
4540
4468
 
4541
- function collectEntryDocUris(resolvedFieldEntries) {
4542
- return entryDocRefs(resolvedFieldEntries).map(ref => ref.id);
4469
+ function clientRequestTagPrefix(client) {
4470
+ const probe = client;
4471
+ return typeof probe.config == "function" ? probe.config().requestTagPrefix : void 0;
4543
4472
  }
4544
4473
 
4545
- function isEngineContext(ctx) {
4546
- return "client" in ctx;
4474
+ function composeRequestTag(prefix, tag) {
4475
+ if (prefix === void 0) return tag;
4476
+ const relative = isWorkflowsFamilyPrefix(prefix) ? stripWorkflowRoot(tag) : tag;
4477
+ return `${prefix}.${relative}`;
4547
4478
  }
4548
4479
 
4549
- function loadCallContext({client: client, instanceId: instanceId, options: options}) {
4550
- return loadContext({
4551
- client: client,
4552
- instanceId: instanceId,
4553
- options: {
4554
- clientForGdr: options.clientForGdr,
4555
- refSurface: options.refSurface,
4556
- ...options.actor ? {
4557
- actor: options.actor
4558
- } : {},
4559
- ...options.clock ? {
4560
- clock: options.clock
4561
- } : {},
4562
- ...options.executionContext ? {
4563
- executionContext: options.executionContext
4564
- } : {},
4565
- ...options.telemetry ? {
4566
- telemetry: options.telemetry
4567
- } : {}
4568
- }
4569
- });
4480
+ function stripWorkflowRoot(tag) {
4481
+ return tag.startsWith("workflow.") ? tag.slice(9) : tag;
4570
4482
  }
4571
4483
 
4572
- async function retryOnRevisionConflict(args) {
4573
- const {client: client, instanceId: instanceId, options: options, commit: commit, onExhausted: onExhausted} = args;
4574
- for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
4575
- const ctx = await loadCallContext({
4576
- client: client,
4577
- instanceId: instanceId,
4578
- options: options
4484
+ function buildTaggedClient(client, fallback) {
4485
+ const strip = isWorkflowsFamilyPrefix(clientRequestTagPrefix(unwrapRequestTag(client))), normalize = tag => strip ? stripWorkflowRoot(tag) : tag, stamp = options => options === void 0 ? {
4486
+ tag: normalize(fallback)
4487
+ } : options.tag === void 0 ? {
4488
+ ...options,
4489
+ tag: normalize(fallback)
4490
+ } : typeof options.tag == "string" && options.tag !== normalize(options.tag) ? {
4491
+ ...options,
4492
+ tag: normalize(options.tag)
4493
+ } : options, wrapPatch = patch => {
4494
+ let target = patch;
4495
+ const wrapped = {
4496
+ get [RAW_PATCH]() {
4497
+ return target;
4498
+ },
4499
+ set: props => (target = target.set(props), wrapped),
4500
+ setIfMissing: props => (target = target.setIfMissing(props), wrapped),
4501
+ unset: paths => (target = target.unset(paths), wrapped),
4502
+ ifRevisionId: rev => (target = target.ifRevisionId(rev), wrapped),
4503
+ commit: options => target.commit(stamp(options))
4504
+ };
4505
+ return wrapped;
4506
+ }, wrapTransaction = tx => {
4507
+ let target = tx;
4508
+ const wrapped = {
4509
+ create: doc => (target = target.create(doc), wrapped),
4510
+ patch: patch => (target = target.patch(unwrapPatch(patch)), wrapped),
4511
+ delete: id => (target = target.delete(id), wrapped),
4512
+ commit: options => target.commit(stamp(options))
4513
+ };
4514
+ return wrapped;
4515
+ }, overrides = {
4516
+ [RAW_CLIENT]: unwrapRequestTag(client),
4517
+ fetch: (query, params, options) => client.fetch(query, params, stamp(options)),
4518
+ getDocument: (id, options) => client.getDocument(id, stamp(options)),
4519
+ patch: documentId => wrapPatch(client.patch(documentId)),
4520
+ create: (doc, options) => client.create(doc, stamp(options)),
4521
+ transaction: () => wrapTransaction(client.transaction()),
4522
+ ...client.withConfig !== void 0 ? {
4523
+ withConfig: config => withRequestTag(client.withConfig(config), fallback)
4524
+ } : {},
4525
+ ...client.action !== void 0 ? {
4526
+ action: (action, options) => client.action(action, stamp(options))
4527
+ } : {},
4528
+ ...client.request !== void 0 ? {
4529
+ request: opts => client.request(stamp(opts))
4530
+ } : {}
4531
+ };
4532
+ return new Proxy(client, {
4533
+ has: (target, property) => Object.hasOwn(overrides, property) || property in target,
4534
+ get: (target, property) => {
4535
+ if (Object.hasOwn(overrides, property)) return Reflect.get(overrides, property, overrides);
4536
+ const value = Reflect.get(target, property, target);
4537
+ return typeof value == "function" ? value.bind(target) : value;
4538
+ }
4539
+ });
4540
+ }
4541
+
4542
+ const pinCache = /* @__PURE__ */ new WeakMap, enginePinned = /* @__PURE__ */ new WeakSet;
4543
+
4544
+ function pinApiVersion(client) {
4545
+ const raw = unwrapRequestTag(client);
4546
+ if (enginePinned.has(raw) || raw.withConfig === void 0) return client;
4547
+ let sibling = pinCache.get(raw);
4548
+ return sibling === void 0 && (sibling = raw.withConfig({
4549
+ apiVersion: ENGINE_API_VERSION
4550
+ }), pinCache.set(raw, sibling), enginePinned.add(unwrapRequestTag(sibling))), sibling;
4551
+ }
4552
+
4553
+ function taggedResolver(resolver, fallback) {
4554
+ return mapResourceClientResolver(resolver, client => withRequestTag(pinApiVersion(client), fallback));
4555
+ }
4556
+
4557
+ function taggedScope(args, tag) {
4558
+ const resourceClients = taggedResolver(args.resourceClients, tag);
4559
+ return {
4560
+ ...args,
4561
+ client: withRequestTag(pinApiVersion(args.client), tag),
4562
+ ...resourceClients !== void 0 ? {
4563
+ resourceClients: resourceClients
4564
+ } : {}
4565
+ };
4566
+ }
4567
+
4568
+ const ACTOR_KIND_SET = new Set(invariants.ACTOR_KINDS), resolutionCache = /* @__PURE__ */ new WeakMap, warnedInstances = /* @__PURE__ */ new WeakMap;
4569
+
4570
+ async function normalizeInstanceIdentities(args) {
4571
+ const {client: client, instance: instance} = args, pending = collectLegacyIds(instance);
4572
+ if (pending.size === 0) return instance;
4573
+ const resolved = await resolveLegacyIds({
4574
+ client: client,
4575
+ instance: instance,
4576
+ pending: pending
4577
+ }), unresolved = [ ...pending ].filter(id => !resolved.has(id));
4578
+ return unresolved.length > 0 ? (warnUnresolvedOnce({
4579
+ client: client,
4580
+ instance: instance,
4581
+ unresolved: unresolved
4582
+ }), instance) : rewriteIdentitySlots(instance, id => resolved.get(id));
4583
+ }
4584
+
4585
+ async function resolveLegacyIds(args) {
4586
+ const resolved = /* @__PURE__ */ new Map, directoryIds = [];
4587
+ for (const id of args.pending) {
4588
+ const {globalId: globalId} = invariants.classifyPrincipalId(id);
4589
+ globalId !== void 0 ? resolved.set(id, globalId) : directoryIds.push(id);
4590
+ }
4591
+ const projectId = anchorProjectId(args.instance.workflowResource);
4592
+ if (directoryIds.length === 0 || projectId === void 0) return resolved;
4593
+ const fromDirectory = await resolveThroughDirectory({
4594
+ client: args.client,
4595
+ projectId: projectId,
4596
+ ids: directoryIds
4597
+ });
4598
+ for (const [id, globalId] of fromDirectory) resolved.set(id, globalId);
4599
+ return resolved;
4600
+ }
4601
+
4602
+ function warnUnresolvedOnce(args) {
4603
+ const key = unwrapRequestTag(args.client);
4604
+ let warned = warnedInstances.get(key);
4605
+ warned === void 0 && (warned = /* @__PURE__ */ new Set, warnedInstances.set(key, warned)),
4606
+ !warned.has(args.instance._id) && (warned.add(args.instance._id), console.warn(`workflow: instance "${args.instance._id}" carries ${args.unresolved.length} legacy project-scoped principal id(s) the project-users directory could not resolve (${args.unresolved.join(", ")}). Its identity values are left as stored — comparisons against them fail closed — and the document does not self-heal until they resolve.`));
4607
+ }
4608
+
4609
+ function anchorProjectId(resource) {
4610
+ if (resource.type === "dataset") return invariants.datasetResourceParts(resource.id).projectId;
4611
+ }
4612
+
4613
+ function collectLegacyIds(instance) {
4614
+ const found = /* @__PURE__ */ new Set;
4615
+ return rewriteIdentitySlots(instance, id => {
4616
+ invariants.classifyPrincipalId(id).namespace === "project" && found.add(id);
4617
+ }), found;
4618
+ }
4619
+
4620
+ function rewriteIdentitySlots(instance, visit) {
4621
+ const record = instance, fields = walkEntries(record.fields, visit), stages = walkStages(record.stages, visit);
4622
+ return fields === record.fields && stages === record.stages ? instance : {
4623
+ ...record,
4624
+ fields: fields,
4625
+ stages: stages
4626
+ };
4627
+ }
4628
+
4629
+ function walkStages(stages, visit) {
4630
+ return Array.isArray(stages) ? shareMap(stages, stage => {
4631
+ const walked = walkEntryRow({
4632
+ row: stage,
4633
+ visit: visit
4634
+ });
4635
+ if (walked === null || typeof walked != "object") return walked;
4636
+ const record = walked, activities = walkActivities(record.activities, visit);
4637
+ return activities === record.activities ? walked === stage ? stage : walked : {
4638
+ ...record,
4639
+ activities: activities
4640
+ };
4641
+ }) : stages;
4642
+ }
4643
+
4644
+ function walkActivities(activities, visit) {
4645
+ return Array.isArray(activities) ? shareMap(activities, activity => walkEntryRow({
4646
+ row: activity,
4647
+ visit: visit
4648
+ })) : activities;
4649
+ }
4650
+
4651
+ function walkEntryRow(args) {
4652
+ const {row: row, visit: visit} = args;
4653
+ if (row === null || typeof row != "object") return row;
4654
+ const record = row, patch = {}, fields = walkEntries(record.fields, visit);
4655
+ if (fields !== record.fields && (patch.fields = fields), typeof record.completedBy == "string") {
4656
+ const stamped = visit(record.completedBy) ?? record.completedBy;
4657
+ stamped !== record.completedBy && (patch.completedBy = stamped);
4658
+ }
4659
+ return Object.keys(patch).length === 0 ? row : {
4660
+ ...record,
4661
+ ...patch
4662
+ };
4663
+ }
4664
+
4665
+ function walkEntries(entries, visit) {
4666
+ return Array.isArray(entries) ? shareMap(entries, entry => {
4667
+ if (entry === null || typeof entry != "object") return entry;
4668
+ const record = entry, value = walkDeclaredValue({
4669
+ type: record._type,
4670
+ fields: record.fields,
4671
+ of: record.of,
4672
+ value: record.value,
4673
+ visit: visit
4674
+ });
4675
+ return value === record.value ? entry : {
4676
+ ...record,
4677
+ value: value
4678
+ };
4679
+ }) : entries;
4680
+ }
4681
+
4682
+ function collectDeclaredGlobalIds(args) {
4683
+ const found = /* @__PURE__ */ new Set;
4684
+ return walkDeclaredValue({
4685
+ type: args.type,
4686
+ fields: args.fields,
4687
+ of: args.of,
4688
+ value: args.value,
4689
+ visit: id => {
4690
+ invariants.classifyPrincipalId(id).namespace === "global" && found.add(id);
4691
+ }
4692
+ }), found;
4693
+ }
4694
+
4695
+ function rewriteDeclaredPrincipalIds(args) {
4696
+ return walkDeclaredValue({
4697
+ type: args.type,
4698
+ fields: args.fields,
4699
+ of: args.of,
4700
+ value: args.value,
4701
+ visit: id => args.map.get(id)
4702
+ });
4703
+ }
4704
+
4705
+ function walkDeclaredValue(args) {
4706
+ const {type: type, value: value, visit: visit} = args;
4707
+ return type === "actor" || type === "assignee" ? visitPrincipalObject(value, visit) : type === "assignees" ? Array.isArray(value) ? shareMap(value, item => visitPrincipalObject(item, visit)) : value : type === "object" ? walkDeclaredObject({
4708
+ value: value,
4709
+ shapes: args.fields,
4710
+ visit: visit
4711
+ }) : type === "array" && Array.isArray(value) ? shareMap(value, row => walkDeclaredObject({
4712
+ value: row,
4713
+ shapes: args.of,
4714
+ visit: visit
4715
+ })) : value;
4716
+ }
4717
+
4718
+ function walkDeclaredObject(args) {
4719
+ const {value: value, shapes: shapes, visit: visit} = args;
4720
+ if (value === null || typeof value != "object" || Array.isArray(value) || !Array.isArray(shapes)) return value;
4721
+ const record = value, patch = {};
4722
+ for (const shape of shapes) {
4723
+ const step = walkDeclaredProperty({
4724
+ record: record,
4725
+ shape: shape,
4726
+ visit: visit
4727
+ });
4728
+ step !== void 0 && (patch[step.name] = step.value);
4729
+ }
4730
+ return Object.keys(patch).length === 0 ? value : {
4731
+ ...record,
4732
+ ...patch
4733
+ };
4734
+ }
4735
+
4736
+ function walkDeclaredProperty(args) {
4737
+ const {record: record, shape: shape, visit: visit} = args;
4738
+ if (shape === null || typeof shape != "object") return;
4739
+ const name = shape.name;
4740
+ if (typeof name != "string" || !Object.hasOwn(record, name)) return;
4741
+ const walked = walkDeclaredValue({
4742
+ type: shape.type,
4743
+ fields: shape.fields,
4744
+ of: shape.of,
4745
+ value: record[name],
4746
+ visit: visit
4747
+ });
4748
+ return walked === record[name] ? void 0 : {
4749
+ name: name,
4750
+ value: walked
4751
+ };
4752
+ }
4753
+
4754
+ function visitPrincipalObject(value, visit) {
4755
+ if (value === null || typeof value != "object" || Array.isArray(value)) return value;
4756
+ const record = value;
4757
+ if (typeof record.id != "string" || record.type !== "user" && !(typeof record.kind == "string" && ACTOR_KIND_SET.has(record.kind))) return value;
4758
+ const stamped = visit(record.id) ?? record.id;
4759
+ return stamped === record.id ? value : {
4760
+ ...record,
4761
+ id: stamped
4762
+ };
4763
+ }
4764
+
4765
+ function shareMap(items, fn) {
4766
+ let changed = !1;
4767
+ const next = items.map(item => {
4768
+ const out = fn(item);
4769
+ return out !== item && (changed = !0), out;
4770
+ });
4771
+ return changed ? next : items;
4772
+ }
4773
+
4774
+ const SAFE_ID = /^[A-Za-z0-9_-]+$/;
4775
+
4776
+ async function resolveThroughDirectory(args) {
4777
+ const {client: client, projectId: projectId, ids: ids} = args, cache = cacheFor(unwrapRequestTag(client)), out = /* @__PURE__ */ new Map, safeMisses = ids.filter(id => {
4778
+ const hit = cache.get(id);
4779
+ return typeof hit == "string" && out.set(id, hit), hit === void 0;
4780
+ }).filter(id => SAFE_ID.test(id) && SAFE_ID.test(projectId));
4781
+ if (safeMisses.length === 0 || client.request === void 0) return out;
4782
+ try {
4783
+ const response = await client.request({
4784
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${safeMisses.map(encodeURIComponent).join(",")}`,
4785
+ tag: REQUEST_TAG.accessResolveActor
4786
+ });
4787
+ ingestDirectoryRows({
4788
+ response: response,
4789
+ requested: new Set(safeMisses),
4790
+ cache: cache,
4791
+ out: out
4792
+ });
4793
+ for (const id of safeMisses) out.has(id) || cache.set(id, null);
4794
+ } catch (err) {
4795
+ console.warn(`workflow: project-users directory lookup failed for project "${projectId}" — legacy principal ids stay unresolved this pass. Original error: ${invariants.errorMessage(err)}`);
4796
+ }
4797
+ return out;
4798
+ }
4799
+
4800
+ function cacheFor(client) {
4801
+ let cache = resolutionCache.get(client);
4802
+ return cache === void 0 && (cache = /* @__PURE__ */ new Map, resolutionCache.set(client, cache)),
4803
+ cache;
4804
+ }
4805
+
4806
+ function ingestDirectoryRows(args) {
4807
+ const records = Array.isArray(args.response) ? args.response : [ args.response ];
4808
+ for (const row of records) typeof row?.id != "string" || typeof row.sanityUserId != "string" || args.requested.has(row.id) && invariants.classifyPrincipalId(row.sanityUserId).namespace === "global" && (args.cache.set(row.id, row.sanityUserId),
4809
+ args.out.set(row.id, row.sanityUserId));
4810
+ }
4811
+
4812
+ async function getInstanceDocument(client, instanceId) {
4813
+ const doc = await client.getDocument(instanceId);
4814
+ if (doc) return normalizeInstanceIdentities({
4815
+ client: client,
4816
+ instance: readInstanceDoc(doc)
4817
+ });
4818
+ }
4819
+
4820
+ function readInstanceDoc(doc) {
4821
+ return parseInstanceDocument(invariants.assertReadableModel(doc));
4822
+ }
4823
+
4824
+ async function reload({client: client, instanceId: instanceId, tag: tag}) {
4825
+ const doc = await getInstanceDocument(client, instanceId);
4826
+ if (!doc) throw new invariants.InstanceNotFoundError({
4827
+ instanceId: instanceId
4828
+ });
4829
+ if (doc.tag !== tag) throw new invariants.InstanceNotFoundError({
4830
+ instanceId: instanceId,
4831
+ detail: "not visible to this engine: tag mismatch"
4832
+ });
4833
+ return doc;
4834
+ }
4835
+
4836
+ function contextEntry(name, value) {
4837
+ const _key = randomKey();
4838
+ return typeof value == "string" ? {
4839
+ _key: _key,
4840
+ _type: "context.string",
4841
+ name: name,
4842
+ value: value
4843
+ } : typeof value == "number" ? {
4844
+ _key: _key,
4845
+ _type: "context.number",
4846
+ name: name,
4847
+ value: value
4848
+ } : typeof value == "boolean" ? {
4849
+ _key: _key,
4850
+ _type: "context.boolean",
4851
+ name: name,
4852
+ value: value
4853
+ } : {
4854
+ _key: _key,
4855
+ _type: "context.ref",
4856
+ name: name,
4857
+ value: value
4858
+ };
4859
+ }
4860
+
4861
+ function contextJsonEntry(name, value) {
4862
+ return {
4863
+ _key: randomKey(),
4864
+ _type: "context.json",
4865
+ name: name,
4866
+ value: serializeContextValue(name, value)
4867
+ };
4868
+ }
4869
+
4870
+ function serializeContextValue(name, value) {
4871
+ let json;
4872
+ try {
4873
+ json = JSON.stringify(value);
4874
+ } catch (err) {
4875
+ invariants.rethrowWithContext(err, `context entry "${name}" holds unserializable JSON`);
4876
+ }
4877
+ if (json === void 0) throw new Error(`context entry "${name}" holds a non-JSON value (undefined, a function, or a symbol)`);
4878
+ return json;
4879
+ }
4880
+
4881
+ function buildInstanceBase(args) {
4882
+ const {id: id, now: now, actor: actor, perspective: perspective, executionContext: executionContext} = args, entries = [ {
4883
+ _key: randomKey(),
4884
+ _type: "stageEntered",
4885
+ at: now,
4886
+ stage: args.initialStage,
4887
+ ...actor !== void 0 ? {
4888
+ actor: actor
4889
+ } : {}
4890
+ }, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries, body = {
4891
+ _id: id,
4892
+ _type: invariants.WORKFLOW_INSTANCE_TYPE,
4893
+ _rev: "",
4894
+ _createdAt: now,
4895
+ _updatedAt: now,
4896
+ tag: args.tag,
4897
+ workflowResource: args.workflowResource,
4898
+ definition: args.definitionName,
4899
+ pinnedVersion: args.pinnedVersion,
4900
+ ...args.pinnedContentHash !== void 0 ? {
4901
+ pinnedContentHash: args.pinnedContentHash
4902
+ } : {},
4903
+ definitionSnapshot: JSON.stringify(args.definition),
4904
+ fields: args.fields,
4905
+ context: args.context,
4906
+ ancestors: args.ancestors,
4907
+ ...perspective !== void 0 ? {
4908
+ perspective: perspective
4909
+ } : {},
4910
+ currentStage: args.initialStage,
4911
+ stages: [],
4912
+ subworkflows: [],
4913
+ pendingEffects: [],
4914
+ effectHistory: [],
4915
+ history: history,
4916
+ startedAt: now,
4917
+ lastChangedAt: now
4918
+ };
4919
+ return {
4920
+ ...body,
4921
+ ...invariants.modelStampFor({
4922
+ documentType: "instance",
4923
+ document: body
4924
+ })
4925
+ };
4926
+ }
4927
+
4928
+ async function hydrateSnapshot(args) {
4929
+ const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set;
4930
+ loaded.push({
4931
+ doc: instance,
4932
+ resource: instance.workflowResource
4933
+ }), visited.add(invariants.selfGdr(instance));
4934
+ const {pending: pending, ordered: ordered} = planReads({
4935
+ client: client,
4936
+ clientForGdr: clientForGdr,
4937
+ instance: instance,
4938
+ overlay: overlay,
4939
+ visited: visited
4940
+ }), fetched = await readPending(pending);
4941
+ for (const entry of ordered) if (!isPendingRead(entry)) loaded.push(entry); else {
4942
+ const doc = fetched.get(entry);
4943
+ doc !== void 0 && loaded.push({
4944
+ doc: doc,
4945
+ resource: entry.resource
4946
+ });
4947
+ }
4948
+ return buildSnapshot({
4949
+ docs: loaded
4950
+ });
4951
+ }
4952
+
4953
+ function planReads(args) {
4954
+ const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay, visited: visited} = args, pending = [], ordered = [];
4955
+ for (const ref of collectWatchRefs(instance)) {
4956
+ if (visited.has(ref.id)) continue;
4957
+ visited.add(ref.id);
4958
+ const held = overlay?.get(ref.id);
4959
+ if (held !== void 0) ordered.push(held); else {
4960
+ const read = routeRead({
4961
+ defaultClient: client,
4962
+ clientForGdr: clientForGdr,
4963
+ defaultResource: instance.workflowResource,
4964
+ uri: ref.id,
4965
+ perspective: readsRaw(ref) ? "raw" : instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE
4966
+ });
4967
+ pending.push(read), ordered.push(read);
4968
+ }
4969
+ }
4970
+ return {
4971
+ pending: pending,
4972
+ ordered: ordered
4973
+ };
4974
+ }
4975
+
4976
+ function isPendingRead(entry) {
4977
+ return "client" in entry;
4978
+ }
4979
+
4980
+ const SNAPSHOT_READ_BATCH_SIZE = 100, SNAPSHOT_READ_CONCURRENCY = 4, snapshotDocsQuery = "*[_id in $ids]";
4981
+
4982
+ function routeRead({defaultClient: defaultClient, clientForGdr: clientForGdr, defaultResource: defaultResource, uri: uri, perspective: perspective}) {
4983
+ const parsed = invariants.tryParseGdr(uri);
4984
+ return parsed === void 0 ? {
4985
+ client: defaultClient,
4986
+ id: uri,
4987
+ perspective: perspective,
4988
+ resource: defaultResource
4989
+ } : {
4990
+ client: clientForGdr(parsed),
4991
+ id: parsed.documentId,
4992
+ perspective: perspective,
4993
+ resource: invariants.resourceFromParsed(parsed)
4994
+ };
4995
+ }
4996
+
4997
+ async function readPending(reads) {
4998
+ const batches = groupReads(reads).flatMap(group => chunks(group.reads, SNAPSHOT_READ_BATCH_SIZE).map(batch => ({
4999
+ group: group,
5000
+ batch: batch
5001
+ }))), results = [];
5002
+ for (const wave of chunks(batches, SNAPSHOT_READ_CONCURRENCY)) results.push(...await Promise.all(wave.map(readBatch)));
5003
+ return new Map(results.flat());
5004
+ }
5005
+
5006
+ async function readBatch(args) {
5007
+ const {group: group, batch: batch} = args, ids = batch.map(read => read.id), docs = await group.client.fetch(snapshotDocsQuery, {
5008
+ ids: ids
5009
+ }, {
5010
+ perspective: group.perspective
5011
+ }), byId = new Map(docs.map(doc => [ doc._id, validateRawDoc(doc, group.perspective) ]));
5012
+ return batch.flatMap(read => {
5013
+ const doc = byId.get(read.id);
5014
+ return doc === void 0 ? [] : [ [ read, doc ] ];
5015
+ });
5016
+ }
5017
+
5018
+ function validateRawDoc(doc, perspective) {
5019
+ return perspective !== "raw" ? doc : doc._type === invariants.WORKFLOW_INSTANCE_TYPE ? readInstanceDoc(doc) : invariants.assertReadableModel(doc);
5020
+ }
5021
+
5022
+ function groupReads(reads) {
5023
+ const byClient = /* @__PURE__ */ new Map;
5024
+ for (const read of reads) {
5025
+ let clientGroups = byClient.get(read.client);
5026
+ clientGroups === void 0 && (clientGroups = /* @__PURE__ */ new Map, byClient.set(read.client, clientGroups));
5027
+ const key = JSON.stringify(read.perspective);
5028
+ let group = clientGroups.get(key);
5029
+ group === void 0 && (group = {
5030
+ client: read.client,
5031
+ perspective: read.perspective,
5032
+ reads: []
5033
+ }, clientGroups.set(key, group)), group.reads.push(read);
5034
+ }
5035
+ return [ ...byClient.values() ].flatMap(groups => [ ...groups.values() ]);
5036
+ }
5037
+
5038
+ function chunks(values, size) {
5039
+ return Array.from({
5040
+ length: Math.ceil(values.length / size)
5041
+ }, (_, index) => values.slice(index * size, (index + 1) * size));
5042
+ }
5043
+
5044
+ function collectEntryDocUris(resolvedFieldEntries) {
5045
+ return entryDocRefs(resolvedFieldEntries).map(ref => ref.id);
5046
+ }
5047
+
5048
+ function isEngineContext(ctx) {
5049
+ return "client" in ctx;
5050
+ }
5051
+
5052
+ function loadCallContext({client: client, instanceId: instanceId, options: options}) {
5053
+ return loadContext({
5054
+ client: client,
5055
+ instanceId: instanceId,
5056
+ options: {
5057
+ clientForGdr: options.clientForGdr,
5058
+ refSurface: options.refSurface,
5059
+ ...options.actor ? {
5060
+ actor: options.actor
5061
+ } : {},
5062
+ ...options.clock ? {
5063
+ clock: options.clock
5064
+ } : {},
5065
+ ...options.executionContext ? {
5066
+ executionContext: options.executionContext
5067
+ } : {},
5068
+ ...options.telemetry ? {
5069
+ telemetry: options.telemetry
5070
+ } : {}
5071
+ }
5072
+ });
5073
+ }
5074
+
5075
+ async function retryOnRevisionConflict(args) {
5076
+ const {client: client, instanceId: instanceId, options: options, commit: commit, onExhausted: onExhausted} = args;
5077
+ for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
5078
+ const ctx = await loadCallContext({
5079
+ client: client,
5080
+ instanceId: instanceId,
5081
+ options: options
4579
5082
  });
4580
5083
  try {
4581
5084
  return await commit(ctx);
@@ -4591,29 +5094,31 @@ async function loadContext({client: client, instanceId: instanceId, options: opt
4591
5094
  if (!instance) throw new invariants.InstanceNotFoundError({
4592
5095
  instanceId: instanceId
4593
5096
  });
4594
- const definition = invariants.parseDefinitionSnapshot(instance), {clientForGdr: clientForGdr, refSurface: refSurface} = options, clock = options.clock ?? wallClock, snapshot = await hydrateSnapshot({
5097
+ return buildEngineContext({
4595
5098
  client: client,
4596
- clientForGdr: clientForGdr,
5099
+ clientForGdr: options.clientForGdr,
5100
+ refSurface: options.refSurface,
4597
5101
  instance: instance,
5102
+ definition: invariants.parseDefinitionSnapshot(instance),
5103
+ ...options.actor !== void 0 ? {
5104
+ actor: options.actor
5105
+ } : {},
5106
+ ...options.clock !== void 0 ? {
5107
+ clock: options.clock
5108
+ } : {},
5109
+ ...options.executionContext !== void 0 ? {
5110
+ executionContext: options.executionContext
5111
+ } : {},
5112
+ ...options.telemetry !== void 0 ? {
5113
+ telemetry: options.telemetry
5114
+ } : {},
4598
5115
  ...options.overlay !== void 0 ? {
4599
5116
  overlay: options.overlay
5117
+ } : {},
5118
+ ...options.settlingCohorts !== void 0 ? {
5119
+ settlingCohorts: options.settlingCohorts
4600
5120
  } : {}
4601
5121
  });
4602
- return {
4603
- client: client,
4604
- clientForGdr: clientForGdr,
4605
- refSurface: refSurface,
4606
- clock: clock,
4607
- now: clock(),
4608
- executionContext: resolveExecutionContext(options?.executionContext),
4609
- telemetry: resolveTelemetry(options.telemetry),
4610
- ...options?.actor !== void 0 ? {
4611
- actor: options.actor
4612
- } : {},
4613
- instance: instance,
4614
- definition: definition,
4615
- snapshot: snapshot
4616
- };
4617
5122
  }
4618
5123
 
4619
5124
  async function ctxConditionParams(ctx, opts) {
@@ -4651,6 +5156,9 @@ async function buildEngineContext(args) {
4651
5156
  now: clock(),
4652
5157
  executionContext: resolveExecutionContext(args.executionContext),
4653
5158
  telemetry: resolveTelemetry(args.telemetry),
5159
+ ...args.settlingCohorts !== void 0 ? {
5160
+ settlingCohorts: args.settlingCohorts
5161
+ } : {},
4654
5162
  ...args.actor !== void 0 ? {
4655
5163
  actor: args.actor
4656
5164
  } : {},
@@ -4659,7 +5167,10 @@ async function buildEngineContext(args) {
4659
5167
  snapshot: await hydrateSnapshot({
4660
5168
  client: client,
4661
5169
  clientForGdr: clientForGdr,
4662
- instance: instance
5170
+ instance: instance,
5171
+ ...args.overlay !== void 0 ? {
5172
+ overlay: args.overlay
5173
+ } : {}
4663
5174
  })
4664
5175
  };
4665
5176
  }
@@ -4782,10 +5293,6 @@ function recordProcessedRequest({mutation: mutation, record: record, now: now})
4782
5293
  } ];
4783
5294
  }
4784
5295
 
4785
- const SYNC_COMMIT = {
4786
- visibility: "sync"
4787
- }, ENGINE_API_VERSION = "2026-04-29";
4788
-
4789
5296
  function resolveGuardRead(args) {
4790
5297
  const {expr: expr, ctx: ctx, deref: deref} = args;
4791
5298
  if (expr === "$self") return invariants.selfGdr(ctx.instance);
@@ -4839,32 +5346,311 @@ function resolveIdRefTargets(idRefs, ctx) {
4839
5346
  return targets.length === 0 ? null : targets;
4840
5347
  }
4841
5348
 
4842
- function assertSingleResource(targets) {
4843
- const resource = invariants.resourceFromParsed(targets[0].parsed);
4844
- for (const g of targets) {
4845
- const r = invariants.resourceFromParsed(g.parsed);
4846
- if (r.type !== resource.type || r.id !== resource.id) throw new Error(`Guard targets span multiple resources (${resource.type}:${resource.id} vs ${r.type}:${r.id}); a guard is single-resource.`);
4847
- }
4848
- return resource;
5349
+ function assertSingleResource(targets) {
5350
+ const resource = invariants.resourceFromParsed(targets[0].parsed);
5351
+ for (const g of targets) {
5352
+ const r = invariants.resourceFromParsed(g.parsed);
5353
+ if (r.type !== resource.type || r.id !== resource.id) throw new Error(`Guard targets span multiple resources (${resource.type}:${resource.id} vs ${r.type}:${r.id}); a guard is single-resource.`);
5354
+ }
5355
+ return resource;
5356
+ }
5357
+
5358
+ function bareIdRefs(targets) {
5359
+ return targets.flatMap(g => [ g.parsed.documentId, `drafts.${g.parsed.documentId}` ]);
5360
+ }
5361
+
5362
+ function resolveMatchTypes(targets, authorTypes) {
5363
+ if (authorTypes !== void 0) return authorTypes;
5364
+ const inferred = [ ...new Set(targets.map(g => g.type).filter(t => !!t)) ];
5365
+ return inferred.length > 0 ? inferred : void 0;
5366
+ }
5367
+
5368
+ function resolveMetadata(metadata, ctx) {
5369
+ const out = {};
5370
+ for (const [k, expr] of Object.entries(metadata ?? {})) out[k] = resolveGuardRead({
5371
+ expr: expr,
5372
+ ctx: ctx,
5373
+ deref: !0
5374
+ });
5375
+ return out;
5376
+ }
5377
+
5378
+ function isRecord(value) {
5379
+ return typeof value == "object" && value !== null && !Array.isArray(value);
5380
+ }
5381
+
5382
+ function isClientProjectUser(value) {
5383
+ return isRecord(value) && typeof value.id == "string" && (value.sanityUserId === void 0 || typeof value.sanityUserId == "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");
5384
+ }
5385
+
5386
+ function objectProperty(value, property) {
5387
+ if (!isRecord(value)) return;
5388
+ const propertyValue = value[property];
5389
+ return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
5390
+ }
5391
+
5392
+ function stringProperty(value, property) {
5393
+ const propertyValue = Reflect.get(value, property);
5394
+ return typeof propertyValue == "string" ? propertyValue : void 0;
5395
+ }
5396
+
5397
+ function apiErrorType(error) {
5398
+ const response = objectProperty(error, "response"), body = objectProperty(response, "body");
5399
+ if (!body) return;
5400
+ const nestedError = objectProperty(body, "error");
5401
+ return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
5402
+ }
5403
+
5404
+ function isProjectUserNotFoundError(error) {
5405
+ return apiErrorType(error) === "projectUserNotFoundError";
5406
+ }
5407
+
5408
+ const memberJoinCache = /* @__PURE__ */ new WeakMap;
5409
+
5410
+ function joinCacheFor(client) {
5411
+ let cache = memberJoinCache.get(client);
5412
+ return cache === void 0 && (cache = /* @__PURE__ */ new Map, memberJoinCache.set(client, cache)),
5413
+ cache;
5414
+ }
5415
+
5416
+ async function projectMemberIds(request, projectId) {
5417
+ const project = await request({
5418
+ uri: `/projects/${encodeURIComponent(projectId)}`
5419
+ }), members = isRecord(project) ? project.members : void 0;
5420
+ return Array.isArray(members) ? members.map(member => isRecord(member) ? member.id : void 0).filter(id => typeof id == "string") : [];
5421
+ }
5422
+
5423
+ async function resolveGlobalProjectUser(args) {
5424
+ const {client: client, request: request, projectId: projectId, id: id} = args, cache = joinCacheFor(client), key = `${projectId}:${id}`, hit = cache.get(key);
5425
+ if (hit !== void 0) return hit === null ? {
5426
+ status: "missing"
5427
+ } : {
5428
+ status: "resolved",
5429
+ user: hit
5430
+ };
5431
+ try {
5432
+ await ingestMemberJoin({
5433
+ request: request,
5434
+ projectId: projectId,
5435
+ cache: cache
5436
+ });
5437
+ const found = cache.get(key);
5438
+ return found != null ? {
5439
+ status: "resolved",
5440
+ user: found
5441
+ } : (cache.set(key, null), {
5442
+ status: "missing"
5443
+ });
5444
+ } catch (cause) {
5445
+ return {
5446
+ status: "inaccessible",
5447
+ cause: cause
5448
+ };
5449
+ }
5450
+ }
5451
+
5452
+ async function ingestMemberJoin(args) {
5453
+ const {request: request, projectId: projectId, cache: cache} = args, memberIds = await projectMemberIds(request, projectId);
5454
+ if (memberIds.length === 0) return;
5455
+ const response = await request({
5456
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${memberIds.map(encodeURIComponent).join(",")}`
5457
+ });
5458
+ for (const row of Array.isArray(response) ? response : [ response ]) isClientProjectUser(row) && typeof row.sanityUserId == "string" && cache.set(`${projectId}:${row.sanityUserId}`, row);
5459
+ }
5460
+
5461
+ function clientProjectUserDirectory(client, projectId) {
5462
+ return {
5463
+ findById: async id => {
5464
+ if (!client.request) return {
5465
+ status: "inaccessible",
5466
+ cause: new Error("Project-user resolution requires WorkflowClient.request")
5467
+ };
5468
+ if (invariants.classifyPrincipalId(id).namespace === "global") return resolveGlobalProjectUser({
5469
+ client: client,
5470
+ request: client.request,
5471
+ projectId: projectId,
5472
+ id: id
5473
+ });
5474
+ try {
5475
+ const response = await client.request({
5476
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
5477
+ }), candidate = Array.isArray(response) ? response[0] : response;
5478
+ return candidate == null ? {
5479
+ status: "missing"
5480
+ } : isClientProjectUser(candidate) ? {
5481
+ status: "resolved",
5482
+ user: candidate
5483
+ } : {
5484
+ status: "inaccessible",
5485
+ cause: new Error("Project-user response had an invalid shape")
5486
+ };
5487
+ } catch (cause) {
5488
+ return isProjectUserNotFoundError(cause) ? {
5489
+ status: "missing"
5490
+ } : {
5491
+ status: "inaccessible",
5492
+ cause: cause
5493
+ };
5494
+ }
5495
+ }
5496
+ };
5497
+ }
5498
+
5499
+ function resolveClientActor(client, args) {
5500
+ return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
5501
+ }
5502
+
5503
+ async function resolveActor(directory, actor) {
5504
+ if (actor.kind !== "person") return {
5505
+ status: "not-person",
5506
+ actor: actor
5507
+ };
5508
+ const personActor = {
5509
+ ...actor,
5510
+ kind: "person"
5511
+ }, lookup = await directory.findById(personActor.id);
5512
+ return lookup.status === "resolved" ? {
5513
+ status: "resolved",
5514
+ actor: personActor,
5515
+ user: lookup.user
5516
+ } : lookup.status === "inaccessible" ? {
5517
+ status: "inaccessible",
5518
+ actor: personActor,
5519
+ ...lookup.cause === void 0 ? {} : {
5520
+ cause: lookup.cause
5521
+ }
5522
+ } : {
5523
+ status: "missing",
5524
+ actor: personActor
5525
+ };
5526
+ }
5527
+
5528
+ const IDENTITY_KINDS = /* @__PURE__ */ new Set([ "actor", "assignee", "assignees" ]);
5529
+
5530
+ async function localizeGuardMetadata(args) {
5531
+ const {doc: doc, guard: guard, client: client, instance: instance, definition: definition} = args;
5532
+ if (doc.resourceType !== "dataset" || client.request === void 0) return doc;
5533
+ const request = client.request, {projectId: projectId} = invariants.datasetResourceParts(doc.resourceId), shapes = metadataShapes({
5534
+ guard: guard,
5535
+ instance: instance,
5536
+ definition: definition
5537
+ }), globals = collectMetadataGlobals({
5538
+ metadata: doc.metadata,
5539
+ shapes: shapes
5540
+ });
5541
+ if (globals.size === 0) return doc;
5542
+ const map = /* @__PURE__ */ new Map;
5543
+ for (const id of globals) {
5544
+ const lookup = await resolveGlobalProjectUser({
5545
+ client: client,
5546
+ request: request,
5547
+ projectId: projectId,
5548
+ id: id
5549
+ });
5550
+ lookup.status === "resolved" && map.set(id, lookup.user.id);
5551
+ }
5552
+ return map.size === 0 ? doc : {
5553
+ ...doc,
5554
+ metadata: rewriteMetadata({
5555
+ metadata: doc.metadata,
5556
+ shapes: shapes,
5557
+ map: map
5558
+ })
5559
+ };
5560
+ }
5561
+
5562
+ function metadataShapes(args) {
5563
+ const out = /* @__PURE__ */ new Map;
5564
+ for (const [key, expr] of Object.entries(args.guard.metadata ?? {})) {
5565
+ const shape = shapeOfRead({
5566
+ expr: expr,
5567
+ instance: args.instance,
5568
+ definition: args.definition
5569
+ });
5570
+ shape !== void 0 && out.set(key, shape);
5571
+ }
5572
+ return out;
5573
+ }
5574
+
5575
+ function shapeOfRead(args) {
5576
+ const fieldRead = invariants.FIELD_READ.exec(args.expr);
5577
+ if (fieldRead !== null) return shapeOfFieldRead(args.instance, fieldRead);
5578
+ const effectsRead = invariants.EFFECTS_READ.exec(args.expr);
5579
+ if (effectsRead !== null) return shapeOfEffectsRead(args.definition, effectsRead);
5580
+ }
5581
+
5582
+ function shapeOfFieldRead(instance, fieldRead) {
5583
+ const entry = (instance.fields ?? []).find(s => s.name === fieldRead[1]);
5584
+ if (entry !== void 0) return narrowByPath({
5585
+ type: entry._type,
5586
+ fields: Reflect.get(entry, "fields"),
5587
+ of: Reflect.get(entry, "of")
5588
+ }, fieldRead[2]);
4849
5589
  }
4850
5590
 
4851
- function bareIdRefs(targets) {
4852
- return targets.flatMap(g => [ g.parsed.documentId, `drafts.${g.parsed.documentId}` ]);
5591
+ function shapeOfEffectsRead(definition, effectsRead) {
5592
+ const outputs = findEffect(definition, effectsRead[1])?.outputs ?? [], path = effectsRead[2];
5593
+ if (path === void 0) return;
5594
+ const [head, ...rest] = path.split("."), output = outputs.find(shape => shape.name === head);
5595
+ if (output !== void 0) return narrowByPath({
5596
+ type: output.type,
5597
+ fields: output.fields,
5598
+ of: output.of
5599
+ }, rest.length > 0 ? rest.join(".") : void 0);
4853
5600
  }
4854
5601
 
4855
- function resolveMatchTypes(targets, authorTypes) {
4856
- if (authorTypes !== void 0) return authorTypes;
4857
- const inferred = [ ...new Set(targets.map(g => g.type).filter(t => !!t)) ];
4858
- return inferred.length > 0 ? inferred : void 0;
5602
+ function narrowByPath(shape, path) {
5603
+ if (path === void 0) return shape;
5604
+ if (typeof shape.type != "string" || !IDENTITY_KINDS.has(shape.type)) return;
5605
+ const steps = identityPathSteps(shape.type, path);
5606
+ if (steps === void 0) return;
5607
+ const itemType = shape.type === "assignees" ? "assignee" : shape.type;
5608
+ if (steps.length === 0) return {
5609
+ type: itemType
5610
+ };
5611
+ if (steps.length === 1 && steps[0] === "id") return {
5612
+ type: itemType,
5613
+ idLeaf: !0
5614
+ };
4859
5615
  }
4860
5616
 
4861
- function resolveMetadata(metadata, ctx) {
4862
- const out = {};
4863
- for (const [k, expr] of Object.entries(metadata ?? {})) out[k] = resolveGuardRead({
4864
- expr: expr,
4865
- ctx: ctx,
4866
- deref: !0
4867
- });
5617
+ function identityPathSteps(kind, path) {
5618
+ const segments = path.split(".");
5619
+ return kind !== "assignees" ? segments : /^\d+$/.test(segments[0] ?? "") ? segments.slice(1) : void 0;
5620
+ }
5621
+
5622
+ function collectMetadataGlobals(args) {
5623
+ const found = /* @__PURE__ */ new Set;
5624
+ for (const [key, shape] of args.shapes) {
5625
+ const value = args.metadata[key];
5626
+ if (shape.idLeaf === !0) {
5627
+ typeof value == "string" && invariants.classifyPrincipalId(value).namespace === "global" && found.add(value);
5628
+ continue;
5629
+ }
5630
+ for (const id of collectDeclaredGlobalIds({
5631
+ ...shape,
5632
+ value: value
5633
+ })) found.add(id);
5634
+ }
5635
+ return found;
5636
+ }
5637
+
5638
+ function rewriteMetadata(args) {
5639
+ const out = {
5640
+ ...args.metadata
5641
+ };
5642
+ for (const [key, shape] of args.shapes) {
5643
+ const value = out[key];
5644
+ if (shape.idLeaf === !0) {
5645
+ typeof value == "string" && (out[key] = args.map.get(value) ?? value);
5646
+ continue;
5647
+ }
5648
+ out[key] = rewriteDeclaredPrincipalIds({
5649
+ ...shape,
5650
+ value: value,
5651
+ map: args.map
5652
+ });
5653
+ }
4868
5654
  return out;
4869
5655
  }
4870
5656
 
@@ -4900,151 +5686,6 @@ function parseGuardDocument(doc) {
4900
5686
  });
4901
5687
  }
4902
5688
 
4903
- const REQUEST_TAG = {
4904
- engine: "workflow",
4905
- deploy: "workflow.deploy",
4906
- deleteDefinition: "workflow.delete-definition",
4907
- start: "workflow.start",
4908
- fireAction: "workflow.fire-action",
4909
- editField: "workflow.edit-field",
4910
- completeEffect: "workflow.complete-effect",
4911
- commitEffectOps: "workflow.commit-effect-ops",
4912
- tick: "workflow.tick",
4913
- evaluate: "workflow.evaluate",
4914
- evaluateStart: "workflow.evaluate-start",
4915
- diagnose: "workflow.diagnose",
4916
- availableActions: "workflow.available-actions",
4917
- setStage: "workflow.set-stage",
4918
- abort: "workflow.abort",
4919
- getInstance: "workflow.get-instance",
4920
- children: "workflow.children",
4921
- instancesForDocument: "workflow.instances-for-document",
4922
- definitionsForDocument: "workflow.definitions-for-document",
4923
- query: "workflow.query",
4924
- discover: "workflow.discover",
4925
- guardQuery: "workflow.guard.query",
4926
- guardDeploy: "workflow.guard.deploy",
4927
- guardRefresh: "workflow.guard.refresh",
4928
- guardRetract: "workflow.guard.retract",
4929
- effectList: "workflow.effect.list",
4930
- effectFind: "workflow.effect.find",
4931
- drain: "workflow.drain",
4932
- effect: "workflow.effect",
4933
- verifyDefinitions: "workflow.verify-definitions",
4934
- accessResolveActor: "workflow.access.resolve-actor",
4935
- accessGrants: "workflow.access.grants"
4936
- }, RAW_PATCH = /* @__PURE__ */ Symbol("workflow-engine.raw-patch");
4937
-
4938
- function unwrapPatch(patch) {
4939
- return typeof patch == "object" && patch !== null && RAW_PATCH in patch ? patch[RAW_PATCH] : patch;
4940
- }
4941
-
4942
- const wrapperCache = /* @__PURE__ */ new WeakMap, RAW_CLIENT = /* @__PURE__ */ Symbol("workflow-engine.raw-client");
4943
-
4944
- function unwrapRequestTag(client) {
4945
- let current = client;
4946
- for (;RAW_CLIENT in current; ) current = current[RAW_CLIENT];
4947
- return current;
4948
- }
4949
-
4950
- function withRequestTag(client, fallback) {
4951
- let byTag = wrapperCache.get(client);
4952
- byTag === void 0 && (byTag = /* @__PURE__ */ new Map, wrapperCache.set(client, byTag));
4953
- const cached = byTag.get(fallback);
4954
- if (cached !== void 0) return cached;
4955
- const wrapped = buildTaggedClient(client, fallback);
4956
- return byTag.set(fallback, wrapped), wrapped;
4957
- }
4958
-
4959
- function isWorkflowsFamilyPrefix(prefix) {
4960
- return prefix === void 0 ? !1 : prefix === "sanity.workflows" || prefix.startsWith("sanity.workflows.") || prefix === "sanity.workflows-mcp";
4961
- }
4962
-
4963
- function clientRequestTagPrefix(client) {
4964
- const probe = client;
4965
- return typeof probe.config == "function" ? probe.config().requestTagPrefix : void 0;
4966
- }
4967
-
4968
- function buildTaggedClient(client, fallback) {
4969
- const strip = isWorkflowsFamilyPrefix(clientRequestTagPrefix(unwrapRequestTag(client))), normalize = tag => strip && tag.startsWith("workflow.") ? tag.slice(9) : tag, stamp = options => options === void 0 ? {
4970
- tag: normalize(fallback)
4971
- } : options.tag === void 0 ? {
4972
- ...options,
4973
- tag: normalize(fallback)
4974
- } : typeof options.tag == "string" && options.tag !== normalize(options.tag) ? {
4975
- ...options,
4976
- tag: normalize(options.tag)
4977
- } : options, wrapPatch = patch => {
4978
- let target = patch;
4979
- const wrapped = {
4980
- get [RAW_PATCH]() {
4981
- return target;
4982
- },
4983
- set: props => (target = target.set(props), wrapped),
4984
- setIfMissing: props => (target = target.setIfMissing(props), wrapped),
4985
- unset: paths => (target = target.unset(paths), wrapped),
4986
- ifRevisionId: rev => (target = target.ifRevisionId(rev), wrapped),
4987
- commit: options => target.commit(stamp(options))
4988
- };
4989
- return wrapped;
4990
- }, wrapTransaction = tx => {
4991
- let target = tx;
4992
- const wrapped = {
4993
- create: doc => (target = target.create(doc), wrapped),
4994
- patch: patch => (target = target.patch(unwrapPatch(patch)), wrapped),
4995
- delete: id => (target = target.delete(id), wrapped),
4996
- commit: options => target.commit(stamp(options))
4997
- };
4998
- return wrapped;
4999
- };
5000
- return {
5001
- [RAW_CLIENT]: unwrapRequestTag(client),
5002
- fetch: (query, params, options) => client.fetch(query, params, stamp(options)),
5003
- getDocument: (id, options) => client.getDocument(id, stamp(options)),
5004
- patch: documentId => wrapPatch(client.patch(documentId)),
5005
- create: (doc, options) => client.create(doc, stamp(options)),
5006
- transaction: () => wrapTransaction(client.transaction()),
5007
- ...client.withConfig !== void 0 ? {
5008
- withConfig: config => withRequestTag(client.withConfig(config), fallback)
5009
- } : {},
5010
- ...client.action !== void 0 ? {
5011
- action: (action, options) => client.action(action, stamp(options))
5012
- } : {},
5013
- ...client.request !== void 0 ? {
5014
- request: opts => client.request(stamp(opts))
5015
- } : {}
5016
- };
5017
- }
5018
-
5019
- const pinCache = /* @__PURE__ */ new WeakMap, enginePinned = /* @__PURE__ */ new WeakSet;
5020
-
5021
- function pinApiVersion(client) {
5022
- const raw = unwrapRequestTag(client);
5023
- if (enginePinned.has(raw) || raw.withConfig === void 0) return client;
5024
- let sibling = pinCache.get(raw);
5025
- return sibling === void 0 && (sibling = raw.withConfig({
5026
- apiVersion: ENGINE_API_VERSION
5027
- }), pinCache.set(raw, sibling), enginePinned.add(unwrapRequestTag(sibling))), sibling;
5028
- }
5029
-
5030
- function taggedResolver(resolver, fallback) {
5031
- if (resolver !== void 0) return parsed => {
5032
- const client = resolver(parsed);
5033
- return client === void 0 ? void 0 : withRequestTag(pinApiVersion(client), fallback);
5034
- };
5035
- }
5036
-
5037
- function taggedScope(args, tag) {
5038
- const resourceClients = taggedResolver(args.resourceClients, tag);
5039
- return {
5040
- ...args,
5041
- client: withRequestTag(pinApiVersion(args.client), tag),
5042
- ...resourceClients !== void 0 ? {
5043
- resourceClients: resourceClients
5044
- } : {}
5045
- };
5046
- }
5047
-
5048
5689
  async function fetchGuards(args) {
5049
5690
  return (await args.client.fetch(args.query, args.params, {
5050
5691
  tag: REQUEST_TAG.guardQuery
@@ -5061,16 +5702,20 @@ function guardsForResource(client) {
5061
5702
  });
5062
5703
  }
5063
5704
 
5064
- function instanceGuardQuery(instanceId) {
5705
+ function instancesGuardQuery(instanceIds) {
5065
5706
  return {
5066
- query: "*[_type == $guardType && sourceInstanceId == $instanceId]",
5707
+ query: "*[_type == $guardType && sourceInstanceId in $instanceIds] | order(_id asc)",
5067
5708
  params: {
5068
5709
  guardType: GUARD_DOC_TYPE,
5069
- instanceId: instanceId
5710
+ instanceIds: [ ...instanceIds ]
5070
5711
  }
5071
5712
  };
5072
5713
  }
5073
5714
 
5715
+ function instanceGuardQuery(instanceId) {
5716
+ return instancesGuardQuery([ instanceId ]);
5717
+ }
5718
+
5074
5719
  function verdictGuardsForInstance(client, instanceId) {
5075
5720
  const {query: query, params: params} = instanceGuardQuery(instanceId);
5076
5721
  return fetchGuards({
@@ -5172,7 +5817,7 @@ function dedupById(guards) {
5172
5817
  return [ ...new Map(guards.map(g => [ g._id, g ])).values() ].sort((a, b) => a._id.localeCompare(b._id));
5173
5818
  }
5174
5819
 
5175
- const GUARD_OWNER = "robot:workflow-engine";
5820
+ const GUARD_OWNER = "robot:workflow-engine", DELETE_CHUNK_SIZE = 200;
5176
5821
 
5177
5822
  function resolveGuardRoute(guard, ctx) {
5178
5823
  const targets = resolveIdRefTargets(guard.match.idRefs, ctx);
@@ -5225,10 +5870,9 @@ function resolveGuard({guard: guard, instance: instance, stageName: stageName, n
5225
5870
  };
5226
5871
  }
5227
5872
 
5228
- async function upsertGuard(client, doc) {
5229
- if (!await client.getDocument(doc._id, {
5230
- tag: REQUEST_TAG.guardDeploy
5231
- })) {
5873
+ async function upsertGuard(args) {
5874
+ const {client: client, doc: doc, exists: exists} = args;
5875
+ if (!exists) {
5232
5876
  await client.create(doc, {
5233
5877
  ...SYNC_COMMIT,
5234
5878
  tag: REQUEST_TAG.guardDeploy
@@ -5254,7 +5898,9 @@ function resolvedStageGuards(args) {
5254
5898
  });
5255
5899
  resolved !== null && out.push({
5256
5900
  client: args.clientForGdr(resolved.routeGdr),
5257
- doc: resolved.doc
5901
+ resourceKey: invariants.resourceGdr(invariants.resourceFromParsed(resolved.routeGdr)),
5902
+ value: resolved.doc,
5903
+ guard: guard
5258
5904
  });
5259
5905
  }
5260
5906
  return out;
@@ -5271,12 +5917,54 @@ function resolvedStageGuardRoutes(args) {
5271
5917
  const route = resolveGuardRoute(guard, ctx);
5272
5918
  route !== null && out.push({
5273
5919
  client: args.clientForGdr(route.routeGdr),
5274
- guardId: route.guardId
5920
+ resourceKey: invariants.resourceGdr(invariants.resourceFromParsed(route.routeGdr)),
5921
+ value: route.guardId
5275
5922
  });
5276
5923
  }
5277
5924
  return out;
5278
5925
  }
5279
5926
 
5927
+ function groupByResource(routed) {
5928
+ const groups = /* @__PURE__ */ new Map;
5929
+ for (const item of routed) {
5930
+ const existing = groups.get(item.resourceKey);
5931
+ if (existing !== void 0) {
5932
+ existing.values.push(item.value);
5933
+ continue;
5934
+ }
5935
+ groups.set(item.resourceKey, {
5936
+ client: item.client,
5937
+ resourceKey: item.resourceKey,
5938
+ values: [ item.value ]
5939
+ });
5940
+ }
5941
+ return [ ...groups.values() ];
5942
+ }
5943
+
5944
+ async function existingGuardIds(groups) {
5945
+ const entries = await Promise.all(groups.map(async ({client: client, resourceKey: resourceKey, values: values}) => {
5946
+ const ids = values.map(doc => doc._id), existing = await client.fetch("*[_id in $ids]._id", {
5947
+ ids: ids
5948
+ }, {
5949
+ tag: REQUEST_TAG.guardDeploy
5950
+ });
5951
+ return [ resourceKey, new Set(existing) ];
5952
+ }));
5953
+ return new Map(entries);
5954
+ }
5955
+
5956
+ async function observedGuards(groups) {
5957
+ const entries = await Promise.all(groups.map(async ({client: client, resourceKey: resourceKey, values: ids}) => {
5958
+ const guards = await client.fetch("*[_id in $ids]{_id, predicate, _rev}", {
5959
+ ids: ids
5960
+ }, {
5961
+ tag: REQUEST_TAG.guardRetract
5962
+ });
5963
+ return [ resourceKey, new Map(guards.map(guard => [ guard._id, guard ])) ];
5964
+ }));
5965
+ return new Map(entries);
5966
+ }
5967
+
5280
5968
  async function committedInstance(args) {
5281
5969
  return getInstanceDocument(args.client, args.instance._id);
5282
5970
  }
@@ -5284,10 +5972,22 @@ async function committedInstance(args) {
5284
5972
  async function deployStageGuards(args) {
5285
5973
  const live = await committedInstance(args);
5286
5974
  if (live === void 0 || live.currentStage !== args.stageName || live.abortedAt !== void 0) return;
5975
+ const routed = resolvedStageGuards(args), existingByResource = await existingGuardIds(groupByResource(routed));
5287
5976
  let deployed = 0;
5288
- for (const {client: client, doc: doc} of resolvedStageGuards(args)) {
5977
+ for (const {client: client, resourceKey: resourceKey, value: doc, guard: guard} of routed) {
5289
5978
  try {
5290
- await upsertGuard(client, doc);
5979
+ const localized = await localizeGuardMetadata({
5980
+ doc: doc,
5981
+ guard: guard,
5982
+ client: client,
5983
+ instance: args.instance,
5984
+ definition: args.definition
5985
+ });
5986
+ await upsertGuard({
5987
+ client: client,
5988
+ doc: localized,
5989
+ exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
5990
+ });
5291
5991
  } catch (cause) {
5292
5992
  throw deployed > 0 ? new PartialGuardDeployError({
5293
5993
  stageName: args.stageName,
@@ -5300,13 +6000,11 @@ async function deployStageGuards(args) {
5300
6000
  }
5301
6001
 
5302
6002
  async function retractStageGuards(args) {
5303
- const observed = [];
5304
- for (const {client: client, guardId: guardId} of resolvedStageGuardRoutes(args)) {
5305
- const guard = await client.getDocument(guardId, {
5306
- tag: REQUEST_TAG.guardRetract
5307
- });
6003
+ const observed = [], routed = resolvedStageGuardRoutes(args), guardsByResource = await observedGuards(groupByResource(routed));
6004
+ for (const {client: client, resourceKey: resourceKey, value: guardId} of routed) {
6005
+ const guard = guardsByResource.get(resourceKey)?.get(guardId);
5308
6006
  if (guard) {
5309
- if (guard._rev === void 0) throw new Error(`Cannot retract guard ${guardId}: persisted document has no revision`);
6007
+ if (guard._rev === void 0 || guard._rev === null) throw new Error(`Cannot retract guard ${guardId}: persisted document has no revision`);
5310
6008
  observed.push({
5311
6009
  client: client,
5312
6010
  guardId: guardId,
@@ -5337,10 +6035,11 @@ async function deleteOrphanedDefinitionGuards(args) {
5337
6035
  let count = 0;
5338
6036
  for (const {client: resourceClient, guards: guards} of perClient) {
5339
6037
  const own = guards.filter(guard => guard.sourceInstanceId.startsWith(ownPartitionPrefix));
5340
- if (own.length === 0) continue;
5341
- const tx = resourceClient.transaction();
5342
- for (const guard of own) tx.delete(guard._id);
5343
- await tx.commit(), count += own.length;
6038
+ if (own.length !== 0) for (let start = 0; start < own.length; start += DELETE_CHUNK_SIZE) {
6039
+ const chunk = own.slice(start, start + DELETE_CHUNK_SIZE), tx = resourceClient.transaction();
6040
+ for (const guard of chunk) tx.delete(guard._id);
6041
+ await tx.commit(), count += chunk.length;
6042
+ }
5344
6043
  }
5345
6044
  return count;
5346
6045
  }
@@ -5406,7 +6105,8 @@ async function commitAbort({ctx: ctx, reason: reason, requestRecord: requestReco
5406
6105
  ...actor !== void 0 ? {
5407
6106
  actor: actor
5408
6107
  } : {}
5409
- }), mutation.completedAt = at, mutation.abortedAt = at, await persist(ctx, mutation),
6108
+ }), mutation.completedAt = at, mutation.abortedAt = at;
6109
+ const instance = readInstanceDoc(await persist(ctx, mutation));
5410
6110
  await retractStageGuards({
5411
6111
  client: ctx.client,
5412
6112
  clientForGdr: ctx.clientForGdr,
@@ -5426,7 +6126,8 @@ async function commitAbort({ctx: ctx, reason: reason, requestRecord: requestReco
5426
6126
  });
5427
6127
  return {
5428
6128
  fired: !0,
5429
- stage: stage.name
6129
+ stage: stage.name,
6130
+ instance: instance
5430
6131
  };
5431
6132
  }
5432
6133
 
@@ -6178,18 +6879,42 @@ async function fetchGrants(args) {
6178
6879
  });
6179
6880
  }
6180
6881
 
6181
- async function advisoryCan({instance: instance, actor: actor, grants: grants}) {
6882
+ async function advisoryCanForCall(args) {
6883
+ const actor = args.options?.actor;
6884
+ if (!(args.options?.grants === void 0 || actor === void 0)) return advisoryCan({
6885
+ instance: args.instance,
6886
+ identity: invariants.lakePrincipalId({
6887
+ actor: actor,
6888
+ localPrincipalId: args.options.localPrincipalId
6889
+ }),
6890
+ grants: args.options.grants
6891
+ });
6892
+ }
6893
+
6894
+ async function advisoryCan({instance: instance, identity: identity, grants: grants}) {
6182
6895
  if (grants === void 0) return;
6183
6896
  const can = {};
6184
6897
  for (const permission of invariants.DOCUMENT_VALUE_PERMISSIONS) can[permission] = await grantsPermissionOn({
6185
6898
  document: instance,
6186
6899
  grants: grants,
6187
6900
  permission: permission,
6188
- userId: actor.id
6901
+ userId: identity
6189
6902
  });
6190
6903
  return can;
6191
6904
  }
6192
6905
 
6906
+ function requirementDescriptor(requirement) {
6907
+ return {
6908
+ name: requirement.name,
6909
+ ...requirement.title !== void 0 ? {
6910
+ title: requirement.title
6911
+ } : {},
6912
+ ...requirement.description !== void 0 ? {
6913
+ description: requirement.description
6914
+ } : {}
6915
+ };
6916
+ }
6917
+
6193
6918
  function subjectDenialLabels(denied) {
6194
6919
  return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
6195
6920
  }
@@ -6210,10 +6935,10 @@ class ActionDisabledError extends invariants.WorkflowError {
6210
6935
 
6211
6936
  class StartNotAllowedError extends invariants.WorkflowError {
6212
6937
  definition;
6213
- insight;
6938
+ unmetRequirements;
6214
6939
  constructor(args) {
6215
- super("start-not-allowed", `startInstance refused: start.allowed on definition "${args.definition}" evaluated ` + (args.insight.outcome === "unevaluable" ? `GROQ null ("can't decide" — fail-closed)` : "false") + " for the supplied initialFields. Pre-flight the verdict with evaluateStart."),
6216
- this.name = "StartNotAllowedError", this.definition = args.definition, this.insight = args.insight;
6940
+ super("start-not-allowed", `startInstance refused definition "${args.definition}": ${args.unmetRequirements.map(requirement => requirement.title ?? requirement.name).join(", ")}. Pre-flight the verdict with evaluateStart.`),
6941
+ this.name = "StartNotAllowedError", this.definition = args.definition, this.unmetRequirements = args.unmetRequirements;
6217
6942
  }
6218
6943
  }
6219
6944
 
@@ -6229,7 +6954,7 @@ const disabledReasonDetail = {
6229
6954
  "stage-terminal": r => `stage "${r.stage}" is terminal`,
6230
6955
  "instance-completed": r => `instance completed at ${r.completedAt}`,
6231
6956
  "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
6232
- "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`,
6957
+ "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
6233
6958
  "subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
6234
6959
  };
6235
6960
 
@@ -6298,11 +7023,10 @@ async function resolveActionCommit({ctx: ctx, activityName: activityName, action
6298
7023
  }
6299
7024
  });
6300
7025
  if (action.filter !== void 0) {
6301
- const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({
7026
+ const can = await advisoryCanForCall({
6302
7027
  instance: ctx.instance,
6303
- actor: actor,
6304
- grants: options.grants
6305
- }) : void 0;
7028
+ options: options
7029
+ });
6306
7030
  if (!await ctxEvaluateCondition({
6307
7031
  ctx: ctx,
6308
7032
  condition: action.filter,
@@ -6543,42 +7267,24 @@ async function fireTriggeredAction({ctx: ctx, mutation: mutation, activity: acti
6543
7267
  entry.firedActions = [ ...entry.firedActions ?? [], action.name ];
6544
7268
  const {ranOps: ranOps} = await applyActionFire({
6545
7269
  ctx: ctx,
6546
- mutation: mutation,
6547
- activity: activity,
6548
- action: action,
6549
- params: {},
6550
- actor: ctx.actor,
6551
- triggered: !0
6552
- });
6553
- return {
6554
- ranFieldOps: ranOps.some(isFieldOp)
6555
- };
6556
- }
6557
-
6558
- async function primeInitialStage({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
6559
- const instance = await getInstanceDocument(client, instanceId);
6560
- if (!instance || instance.stages.length > 0) return;
6561
- const definition = invariants.parseDefinitionSnapshot(instance), stage = definition.stages.find(s => s.name === instance.currentStage);
6562
- if (stage === void 0) return;
6563
- const ctx = await buildEngineContext({
6564
- client: client,
6565
- clientForGdr: clientForGdr,
6566
- refSurface: refSurface,
6567
- instance: instance,
6568
- definition: definition,
6569
- ...clock ? {
6570
- clock: clock
6571
- } : {},
6572
- ...actor ? {
6573
- actor: actor
6574
- } : {},
6575
- ...executionContext ? {
6576
- executionContext: executionContext
6577
- } : {},
6578
- ...telemetry ? {
6579
- telemetry: telemetry
6580
- } : {}
6581
- }), now = ctx.now, discards = [], initialStageEntry = {
7270
+ mutation: mutation,
7271
+ activity: activity,
7272
+ action: action,
7273
+ params: {},
7274
+ actor: ctx.actor,
7275
+ triggered: !0
7276
+ });
7277
+ return {
7278
+ ranFieldOps: ranOps.some(isFieldOp)
7279
+ };
7280
+ }
7281
+
7282
+ async function primeInitialStage(args) {
7283
+ const {client: client, instanceId: instanceId, refSurface: refSurface, clientForGdr: clientForGdr} = args, instance = await getInstanceDocument(client, instanceId);
7284
+ if (!instance || instance.stages.length > 0) return;
7285
+ const ctx = await buildCascadeStepContext(args, instance), {definition: definition} = ctx, stage = definition.stages.find(s => s.name === instance.currentStage);
7286
+ if (stage === void 0) return;
7287
+ const now = ctx.now, discards = [], initialStageEntry = {
6582
7288
  _key: randomKey(),
6583
7289
  name: stage.name,
6584
7290
  enteredAt: now,
@@ -6647,29 +7353,37 @@ async function primeInitialStage({client: client, instanceId: instanceId, actor:
6647
7353
 
6648
7354
  const CASCADE_LIMIT = 100;
6649
7355
 
6650
- async function runCascadeHop({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
6651
- const ctx = await loadContext({
7356
+ async function runCascadeHop({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts, instance: instance}) {
7357
+ const contextOptions = {
7358
+ clientForGdr: clientForGdr,
7359
+ refSurface: refSurface,
7360
+ ...actor ? {
7361
+ actor: actor
7362
+ } : {},
7363
+ ...clock ? {
7364
+ clock: clock
7365
+ } : {},
7366
+ ...executionContext ? {
7367
+ executionContext: executionContext
7368
+ } : {},
7369
+ ...telemetry ? {
7370
+ telemetry: telemetry
7371
+ } : {},
7372
+ ...overlay ? {
7373
+ overlay: overlay
7374
+ } : {},
7375
+ ...settlingCohorts ? {
7376
+ settlingCohorts: settlingCohorts
7377
+ } : {}
7378
+ }, ctx = instance === void 0 ? await loadContext({
6652
7379
  client: client,
6653
7380
  instanceId: instanceId,
6654
- options: {
6655
- clientForGdr: clientForGdr,
6656
- refSurface: refSurface,
6657
- ...actor ? {
6658
- actor: actor
6659
- } : {},
6660
- ...clock ? {
6661
- clock: clock
6662
- } : {},
6663
- ...executionContext ? {
6664
- executionContext: executionContext
6665
- } : {},
6666
- ...telemetry ? {
6667
- telemetry: telemetry
6668
- } : {},
6669
- ...overlay ? {
6670
- overlay: overlay
6671
- } : {}
6672
- }
7381
+ options: contextOptions
7382
+ }) : await buildEngineContext({
7383
+ client: client,
7384
+ instance: instance,
7385
+ definition: invariants.parseDefinitionSnapshot(instance),
7386
+ ...contextOptions
6673
7387
  });
6674
7388
  if (isTerminal(ctx)) return {
6675
7389
  moved: !1
@@ -6699,10 +7413,10 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
6699
7413
  });
6700
7414
  }
6701
7415
 
6702
- async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
7416
+ async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts}) {
6703
7417
  let count = 0;
6704
7418
  for (;;) {
6705
- if (await drainCondemnedChildren({
7419
+ const drained = await drainCondemnedChildren({
6706
7420
  client: client,
6707
7421
  instanceId: instanceId,
6708
7422
  actor: actor,
@@ -6711,7 +7425,8 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6711
7425
  clock: clock,
6712
7426
  executionContext: executionContext,
6713
7427
  telemetry: telemetry
6714
- }), !(await runCascadeHop({
7428
+ });
7429
+ if (!(await runCascadeHop({
6715
7430
  client: client,
6716
7431
  instanceId: instanceId,
6717
7432
  actor: actor,
@@ -6720,7 +7435,11 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6720
7435
  clock: clock,
6721
7436
  executionContext: executionContext,
6722
7437
  telemetry: telemetry,
6723
- overlay: overlay
7438
+ overlay: overlay,
7439
+ settlingCohorts: settlingCohorts,
7440
+ ...drained.drained ? {} : {
7441
+ instance: drained.instance
7442
+ }
6724
7443
  })).moved) return count;
6725
7444
  if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
6726
7445
  instanceId: instanceId,
@@ -6729,39 +7448,50 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6729
7448
  }
6730
7449
  }
6731
7450
 
6732
- async function drainCondemnedChildren({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining = /* @__PURE__ */ new Set}) {
6733
- if (draining.has(instanceId)) return;
7451
+ async function drainCondemnedChildren({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining = /* @__PURE__ */ new Set, instance: preloadedInstance}) {
7452
+ if (draining.has(instanceId)) return {
7453
+ instance: preloadedInstance,
7454
+ drained: !1
7455
+ };
6734
7456
  draining.add(instanceId);
6735
- const instance = await getInstanceDocument(client, instanceId);
6736
- if (!instance) return;
7457
+ const instance = preloadedInstance ?? await getInstanceDocument(client, instanceId);
7458
+ if (!instance) return {
7459
+ instance: void 0,
7460
+ drained: !1
7461
+ };
6737
7462
  const condemned = condemnedSubworkflows(instance);
6738
- if (condemned.length !== 0) {
6739
- for (const row of condemned) await settleCondemnedRow({
6740
- client: client,
6741
- ownerId: instanceId,
6742
- row: row,
6743
- actor: actor,
6744
- clientForGdr: clientForGdr,
6745
- refSurface: refSurface,
6746
- clock: clock,
6747
- executionContext: executionContext,
6748
- telemetry: telemetry,
6749
- draining: draining
6750
- });
6751
- await stampDrainedRows({
6752
- client: client,
6753
- instance: instance,
6754
- condemned: condemned,
6755
- clock: clock,
6756
- executionContext: executionContext
6757
- });
6758
- }
7463
+ if (condemned.length === 0) return {
7464
+ instance: instance,
7465
+ drained: !1
7466
+ };
7467
+ for (const row of condemned) await settleCondemnedRow({
7468
+ client: client,
7469
+ ownerId: instanceId,
7470
+ row: row,
7471
+ actor: actor,
7472
+ clientForGdr: clientForGdr,
7473
+ refSurface: refSurface,
7474
+ clock: clock,
7475
+ executionContext: executionContext,
7476
+ telemetry: telemetry,
7477
+ draining: draining
7478
+ });
7479
+ return await stampDrainedRows({
7480
+ client: client,
7481
+ instance: instance,
7482
+ condemned: condemned,
7483
+ clock: clock,
7484
+ executionContext: executionContext
7485
+ }), {
7486
+ instance: instance,
7487
+ drained: !0
7488
+ };
6759
7489
  }
6760
7490
 
6761
7491
  async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining}) {
6762
7492
  const childId = invariants.toBareId(row.ref.id);
6763
7493
  try {
6764
- await abortInstance({
7494
+ const result = await abortInstance({
6765
7495
  client: client,
6766
7496
  instanceId: childId,
6767
7497
  reason: row.abortPending?.reason ?? "condemned by parent",
@@ -6781,7 +7511,8 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6781
7511
  telemetry: telemetry
6782
7512
  } : {}
6783
7513
  }
6784
- }), await drainCondemnedChildren({
7514
+ });
7515
+ await drainCondemnedChildren({
6785
7516
  client: client,
6786
7517
  instanceId: childId,
6787
7518
  actor: actor,
@@ -6790,7 +7521,10 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6790
7521
  clock: clock,
6791
7522
  executionContext: executionContext,
6792
7523
  telemetry: telemetry,
6793
- draining: draining
7524
+ draining: draining,
7525
+ ...result.fired ? {
7526
+ instance: result.instance
7527
+ } : {}
6794
7528
  });
6795
7529
  } catch (cause) {
6796
7530
  if (cause instanceof invariants.InstanceNotFoundError) return;
@@ -6803,9 +7537,7 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6803
7537
  }
6804
7538
 
6805
7539
  async function stampDrainedRows({client: client, instance: instance, condemned: condemned, clock: clock, executionContext: executionContext}) {
6806
- 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}", {
6807
- ids: ids
6808
- }), 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 => {
7540
+ const now = (clock ?? wallClock)(), stamp = resolveExecutionContext(executionContext), ids = condemned.map(row => invariants.toBareId(row.ref.id)), byId = await loadInstancesById(client, ids), condemnedKeys = new Set(condemned.map(row => row._key)), history = [ ...instance.history ], subworkflows = (instance.subworkflows ?? []).map(row => {
6809
7541
  if (row.resolved !== void 0 || !condemnedKeys.has(row._key)) return row;
6810
7542
  const child = byId.get(invariants.toBareId(row.ref.id)), resolved = child !== void 0 ? terminalResolution(child) : {
6811
7543
  at: now,
@@ -6827,6 +7559,16 @@ async function stampDrainedRows({client: client, instance: instance, condemned:
6827
7559
  }).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
6828
7560
  }
6829
7561
 
7562
+ async function loadInstancesById(client, ids) {
7563
+ const children = await client.fetch("*[_id in $ids]", {
7564
+ ids: ids
7565
+ });
7566
+ return new Map(children.filter(child => child._type === invariants.WORKFLOW_INSTANCE_TYPE).map(child => {
7567
+ const instance = readInstanceDoc(child);
7568
+ return [ instance._id, instance ];
7569
+ }));
7570
+ }
7571
+
6830
7572
  function terminalResolution(child) {
6831
7573
  const status = resolvedChildStatus(child);
6832
7574
  if (!(status === void 0 || child.completedAt === void 0 || child.completedAt === null)) return {
@@ -6849,28 +7591,18 @@ function subworkflowResolvedEntry({row: row, at: at, status: status}) {
6849
7591
  };
6850
7592
  }
6851
7593
 
6852
- async function propagateToAncestors({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
6853
- const loaded = await loadPropagationPair(client, instanceId);
6854
- if (loaded === void 0) return;
6855
- const {child: child, parent: parent} = loaded, definition = invariants.parseDefinitionSnapshot(parent), ctx = await buildEngineContext({
7594
+ async function propagateToAncestors(args) {
7595
+ const {instance: instance, ...stepArgs} = args, {client: client, instanceId: instanceId} = stepArgs, loaded = await loadPropagationPair({
6856
7596
  client: client,
6857
- clientForGdr: clientForGdr,
6858
- refSurface: refSurface,
6859
- instance: parent,
6860
- definition: definition,
6861
- ...clock ? {
6862
- clock: clock
6863
- } : {},
6864
- ...actor ? {
6865
- actor: actor
6866
- } : {},
6867
- ...executionContext ? {
6868
- executionContext: executionContext
6869
- } : {},
6870
- ...telemetry ? {
6871
- telemetry: telemetry
7597
+ instanceId: instanceId,
7598
+ ...instance !== void 0 ? {
7599
+ instance: instance
6872
7600
  } : {}
6873
- }), mutation = startMutation(parent), row = mutation.subworkflows.find(r => invariants.toBareId(r.ref.id) === child._id);
7601
+ });
7602
+ if (loaded === void 0) return;
7603
+ const {child: child, parent: parent} = loaded;
7604
+ if (stepArgs.settlingCohorts?.has(parent._id)) return;
7605
+ const ctx = await buildCascadeStepContext(stepArgs, parent), mutation = startMutation(parent), row = mutation.subworkflows.find(r => invariants.toBareId(r.ref.id) === child._id);
6874
7606
  if (row === void 0) {
6875
7607
  await recordOrphanedPropagation({
6876
7608
  ctx: ctx,
@@ -6885,29 +7617,11 @@ async function propagateToAncestors({client: client, instanceId: instanceId, act
6885
7617
  child: child,
6886
7618
  now: ctx.now
6887
7619
  }), parentTerminal = parent.completedAt !== void 0;
6888
- changed && await persist(ctx, mutation), !parentTerminal && (await cascadeAutoTransitions({
6889
- client: client,
6890
- instanceId: parent._id,
6891
- actor: actor,
6892
- clientForGdr: clientForGdr,
6893
- refSurface: refSurface,
6894
- clock: clock,
6895
- executionContext: executionContext,
6896
- telemetry: telemetry
6897
- }), await propagateToAncestors({
6898
- client: client,
6899
- instanceId: parent._id,
6900
- actor: actor,
6901
- clientForGdr: clientForGdr,
6902
- refSurface: refSurface,
6903
- clock: clock,
6904
- executionContext: executionContext,
6905
- telemetry: telemetry
6906
- }));
7620
+ changed && await persist(ctx, mutation), !parentTerminal && await cascadeAndPropagateParent(stepArgs, parent);
6907
7621
  }
6908
7622
 
6909
- async function loadPropagationPair(client, instanceId) {
6910
- const child = await getInstanceDocument(client, instanceId);
7623
+ async function loadPropagationPair({client: client, instanceId: instanceId, instance: instance}) {
7624
+ const child = instance ?? await getInstanceDocument(client, instanceId);
6911
7625
  if (!child) return;
6912
7626
  const parentGdr = invariants.parentRef(child);
6913
7627
  if (parentGdr === void 0) return;
@@ -6918,6 +7632,83 @@ async function loadPropagationPair(client, instanceId) {
6918
7632
  };
6919
7633
  }
6920
7634
 
7635
+ async function propagateSpawnBatch(args) {
7636
+ const {childIds: childIds, ...stepArgs} = args, {client: client, instanceId: instanceId} = stepArgs, parent = await getInstanceDocument(client, instanceId);
7637
+ if (parent === void 0) return;
7638
+ const children = await loadInstancesById(client, childIds);
7639
+ if (children.size !== childIds.length) throw new Error(`Spawn batch for ${instanceId} could not reload every child`);
7640
+ const ctx = await buildCascadeStepContext(stepArgs, parent), mutation = startMutation(parent);
7641
+ stampSpawnBatch({
7642
+ mutation: mutation,
7643
+ parent: parent,
7644
+ children: children,
7645
+ childIds: childIds,
7646
+ now: ctx.now
7647
+ }) && await persist(ctx, mutation), parent.completedAt === void 0 && await cascadeAndPropagateParent(stepArgs, parent);
7648
+ }
7649
+
7650
+ async function buildCascadeStepContext(args, instance) {
7651
+ return buildEngineContext({
7652
+ client: args.client,
7653
+ clientForGdr: args.clientForGdr,
7654
+ refSurface: args.refSurface,
7655
+ instance: instance,
7656
+ definition: invariants.parseDefinitionSnapshot(instance),
7657
+ ...args.clock ? {
7658
+ clock: args.clock
7659
+ } : {},
7660
+ ...args.actor ? {
7661
+ actor: args.actor
7662
+ } : {},
7663
+ ...args.executionContext ? {
7664
+ executionContext: args.executionContext
7665
+ } : {},
7666
+ ...args.telemetry ? {
7667
+ telemetry: args.telemetry
7668
+ } : {},
7669
+ ...args.settlingCohorts ? {
7670
+ settlingCohorts: args.settlingCohorts
7671
+ } : {}
7672
+ });
7673
+ }
7674
+
7675
+ async function cascadeAndPropagateParent(args, parent) {
7676
+ await cascadeAutoTransitions({
7677
+ ...args,
7678
+ instanceId: parent._id
7679
+ }), await propagateToAncestors({
7680
+ ...args,
7681
+ instanceId: parent._id
7682
+ });
7683
+ }
7684
+
7685
+ function stampSpawnBatch({mutation: mutation, parent: parent, children: children, childIds: childIds, now: now}) {
7686
+ let changed = !1;
7687
+ for (const childId of childIds) {
7688
+ const child = requireSpawnBatchChild({
7689
+ parent: parent,
7690
+ children: children,
7691
+ childId: childId
7692
+ }), row = mutation.subworkflows.find(entry => invariants.toBareId(entry.ref.id) === childId);
7693
+ if (row === void 0) throw new Error(`Spawn batch child ${childId} has no registry row on parent ${parent._id}`);
7694
+ changed = stampResolvedChild({
7695
+ mutation: mutation,
7696
+ row: row,
7697
+ child: child,
7698
+ now: now
7699
+ }) || changed;
7700
+ }
7701
+ return changed;
7702
+ }
7703
+
7704
+ function requireSpawnBatchChild({parent: parent, children: children, childId: childId}) {
7705
+ const child = children.get(childId);
7706
+ if (child === void 0) throw new Error(`Spawn batch child ${childId} disappeared`);
7707
+ const expectedParent = invariants.parentRef(child);
7708
+ if (expectedParent === void 0 || invariants.toBareId(expectedParent.id) !== parent._id) throw new Error(`Spawn batch child ${childId} does not belong to parent ${parent._id}`);
7709
+ return child;
7710
+ }
7711
+
6921
7712
  function stampResolvedChild({mutation: mutation, row: row, child: child, now: now}) {
6922
7713
  if (row.resolved !== void 0) return !1;
6923
7714
  const resolved = terminalResolution(child);
@@ -7053,36 +7844,14 @@ async function persist(ctx, mutation) {
7053
7844
  for (const {body: body} of pendingCreates) tx.create(body);
7054
7845
  tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)),
7055
7846
  await tx.commit(), mutation.pendingCreates = [];
7056
- const actorForPriming = ctx.actor;
7057
- for (const {body: body, started: started} of pendingCreates) try {
7058
- await primeInitialStage({
7059
- client: ctx.client,
7060
- instanceId: body._id,
7061
- actor: actorForPriming,
7062
- clientForGdr: ctx.clientForGdr,
7063
- refSurface: ctx.refSurface,
7064
- clock: ctx.clock,
7065
- executionContext: ctx.executionContext,
7066
- telemetry: ctx.telemetry
7067
- }), await cascadeAutoTransitions({
7068
- client: ctx.client,
7069
- instanceId: body._id,
7070
- actor: actorForPriming,
7071
- clientForGdr: ctx.clientForGdr,
7072
- refSurface: ctx.refSurface,
7073
- clock: ctx.clock,
7074
- executionContext: ctx.executionContext,
7075
- telemetry: ctx.telemetry
7076
- }), await propagateToAncestors({
7077
- client: ctx.client,
7847
+ const actorForPriming = ctx.actor, settlingCohorts = new Set(ctx.settlingCohorts).add(ctx.instance._id);
7848
+ for (const {body: body} of pendingCreates) try {
7849
+ const stepArgs = spawnStepArgs(ctx, {
7078
7850
  instanceId: body._id,
7079
7851
  actor: actorForPriming,
7080
- clientForGdr: ctx.clientForGdr,
7081
- refSurface: ctx.refSurface,
7082
- clock: ctx.clock,
7083
- executionContext: ctx.executionContext,
7084
- telemetry: ctx.telemetry
7085
- }), ctx.telemetry.log(WorkflowInstanceStarted, started);
7852
+ settlingCohorts: settlingCohorts
7853
+ });
7854
+ await primeInitialStage(stepArgs), await cascadeAutoTransitions(stepArgs);
7086
7855
  } catch (cause) {
7087
7856
  throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
7088
7857
  instanceId: ctx.instance._id,
@@ -7090,11 +7859,50 @@ async function persist(ctx, mutation) {
7090
7859
  reason: `spawned child "${body._id}" failed to settle after the spawn transaction committed`
7091
7860
  });
7092
7861
  }
7862
+ await settleSpawnBatch(ctx, {
7863
+ pendingCreates: pendingCreates,
7864
+ actor: actorForPriming,
7865
+ settlingCohorts: settlingCohorts
7866
+ });
7867
+ for (const {started: started} of pendingCreates) ctx.telemetry.log(WorkflowInstanceStarted, started);
7093
7868
  const reloaded = await getInstanceDocument(ctx.client, ctx.instance._id);
7094
7869
  if (!reloaded) throw new Error(`Instance ${ctx.instance._id} disappeared after transaction commit`);
7095
7870
  return reloaded;
7096
7871
  }
7097
7872
 
7873
+ async function settleSpawnBatch(ctx, args) {
7874
+ try {
7875
+ await propagateSpawnBatch({
7876
+ ...spawnStepArgs(ctx, {
7877
+ instanceId: ctx.instance._id,
7878
+ actor: args.actor,
7879
+ settlingCohorts: args.settlingCohorts
7880
+ }),
7881
+ childIds: args.pendingCreates.map(({body: body}) => body._id)
7882
+ });
7883
+ } catch (cause) {
7884
+ throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
7885
+ instanceId: ctx.instance._id,
7886
+ guardError: cause,
7887
+ reason: "spawned children failed to propagate after the spawn transaction committed"
7888
+ });
7889
+ }
7890
+ }
7891
+
7892
+ function spawnStepArgs(ctx, args) {
7893
+ return {
7894
+ client: ctx.client,
7895
+ instanceId: args.instanceId,
7896
+ actor: args.actor,
7897
+ clientForGdr: ctx.clientForGdr,
7898
+ refSurface: ctx.refSurface,
7899
+ clock: ctx.clock,
7900
+ executionContext: ctx.executionContext,
7901
+ telemetry: ctx.telemetry,
7902
+ settlingCohorts: args.settlingCohorts ?? ctx.settlingCohorts
7903
+ };
7904
+ }
7905
+
7098
7906
  function stampExecutionContext(ctx, mutation) {
7099
7907
  const stamp = ctx.executionContext, appended = mutation.history.slice(ctx.instance.history.length);
7100
7908
  mutation.history = [ ...mutation.history.slice(0, ctx.instance.history.length), ...stampHistoryEntries(appended, stamp) ];
@@ -7222,24 +8030,25 @@ function buildEffectSettlement(pending, outcome) {
7222
8030
  }
7223
8031
 
7224
8032
  function validateEffectOutputs(args) {
7225
- const {outputs: outputs, declared: declared, effectName: effectName} = args, byName = new Map(declared.map(shape => [ shape.name, shape ])), issues = [];
8033
+ const {outputs: outputs, declared: declared, effectName: effectName} = args, byName = new Map(declared.map(shape => [ shape.name, shape ])), issues = [], normalized = {};
7226
8034
  for (const [key, value] of Object.entries(outputs)) {
7227
8035
  const shape = byName.get(key);
7228
8036
  if (shape === void 0) {
7229
8037
  issues.push(`"${key}" is not a declared output`);
7230
8038
  continue;
7231
8039
  }
7232
- const shapeIssues = effectOutputIssues(shape, value);
7233
- shapeIssues !== void 0 && issues.push(...shapeIssues.map(i => `"${key}": ${i}`));
8040
+ const check = effectOutputCheck(shape, value);
8041
+ "issues" in check ? issues.push(...check.issues.map(i => `"${key}": ${i}`)) : normalized[key] = check.output;
7234
8042
  }
7235
8043
  if (issues.length > 0) throw new EffectOutputsInvalidError({
7236
8044
  effect: effectName,
7237
8045
  issues: issues
7238
8046
  });
8047
+ return normalized;
7239
8048
  }
7240
8049
 
7241
- function effectOutputIssues(shape, value) {
7242
- return invariants.checkFieldValue({
8050
+ function effectOutputCheck(shape, value) {
8051
+ return invariants.parseFieldValue({
7243
8052
  entryType: shape.type,
7244
8053
  value: value,
7245
8054
  ...shape.fields !== void 0 ? {
@@ -7285,15 +8094,15 @@ function validateCompletionInput({pending: pending, definition: definition, stat
7285
8094
  effect: pending.name,
7286
8095
  issues: [ "outputs cannot accompany a failed completion — report outputs on a done completion instead" ]
7287
8096
  });
7288
- if (outputs !== void 0) {
7289
- const declared = findEffect(definition, pending.name)?.outputs ?? [];
7290
- validateEffectOutputs({
7291
- outputs: outputs,
7292
- declared: declared,
7293
- effectName: pending.name
7294
- });
7295
- }
7296
- return ops !== void 0 ? validateEffectOps(ops, pending.name) : [];
8097
+ const normalizedOutputs = outputs !== void 0 ? validateEffectOutputs({
8098
+ outputs: outputs,
8099
+ declared: findEffect(definition, pending.name)?.outputs ?? [],
8100
+ effectName: pending.name
8101
+ }) : void 0;
8102
+ return {
8103
+ ops: ops !== void 0 ? validateEffectOps(ops, pending.name) : [],
8104
+ outputs: normalizedOutputs
8105
+ };
7297
8106
  }
7298
8107
 
7299
8108
  async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, requestRecord: requestRecord, actor: actor}) {
@@ -7302,7 +8111,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
7302
8111
  record: requestRecord,
7303
8112
  now: ctx.now
7304
8113
  });
7305
- const pending = requirePendingEffect(ctx.instance, effectKey), validatedOps = validateCompletionInput({
8114
+ const pending = requirePendingEffect(ctx.instance, effectKey), {ops: validatedOps, outputs: validatedOutputs} = validateCompletionInput({
7306
8115
  pending: pending,
7307
8116
  definition: ctx.definition,
7308
8117
  status: status,
@@ -7321,7 +8130,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
7321
8130
  detail: detail,
7322
8131
  error: error,
7323
8132
  durationMs: durationMs,
7324
- outputs: outputs
8133
+ outputs: validatedOutputs
7325
8134
  });
7326
8135
  mutation.effectHistory.push(settlement.run);
7327
8136
  const wroteEffectOutputs = status === "done" && outputs !== void 0;
@@ -7519,6 +8328,64 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
7519
8328
  };
7520
8329
  }
7521
8330
 
8331
+ const RESET_ACTIVITY_TARGETS = [ "active", "skipped" ];
8332
+
8333
+ function isResetActivityTarget(value) {
8334
+ return RESET_ACTIVITY_TARGETS.includes(value);
8335
+ }
8336
+
8337
+ async function resetActivity(args) {
8338
+ const {client: client, instanceId: instanceId, activity: activity, to: to, requestRecord: requestRecord, options: options} = args, ctx = await loadCallContext({
8339
+ client: client,
8340
+ instanceId: instanceId,
8341
+ options: options
8342
+ });
8343
+ return commitResetActivity({
8344
+ ctx: ctx,
8345
+ activity: activity,
8346
+ to: to,
8347
+ requestRecord: requestRecord,
8348
+ actor: options?.actor
8349
+ });
8350
+ }
8351
+
8352
+ async function commitResetActivity({ctx: ctx, activity: activity, to: to, requestRecord: requestRecord, actor: actor}) {
8353
+ if (assertRequestUnprocessed({
8354
+ instance: ctx.instance,
8355
+ record: requestRecord,
8356
+ now: ctx.now
8357
+ }), isTerminal(ctx)) return {
8358
+ fired: !1
8359
+ };
8360
+ const mutation = startMutation(ctx.instance), openStage2 = findOpenStageEntry(mutation), entry = findCurrentActivityEntry(mutation, activity);
8361
+ if (openStage2 === void 0 || entry === void 0) throw new invariants.ContractViolationError(`resetActivity: activity "${activity}" is not in the current stage of instance "${ctx.instance._id}"`);
8362
+ const from = entry.status;
8363
+ if (from === to) return {
8364
+ fired: !1
8365
+ };
8366
+ if (!invariants.isTerminalActivityStatus(from)) throw new invariants.ContractViolationError(`resetActivity: activity "${activity}" is "${from}", not a terminal status — a reset only recovers a resolved (typically failed) activity. Use setStage to force past a live stage.`);
8367
+ return recordProcessedRequest({
8368
+ mutation: mutation,
8369
+ record: requestRecord,
8370
+ now: ctx.now
8371
+ }), applyActivityStatusChange({
8372
+ entry: entry,
8373
+ history: mutation.history,
8374
+ stage: openStage2.name,
8375
+ to: to,
8376
+ at: ctx.now,
8377
+ ...actor !== void 0 ? {
8378
+ actor: actor
8379
+ } : {}
8380
+ }), await persist(ctx, mutation), {
8381
+ fired: !0,
8382
+ stage: openStage2.name,
8383
+ activity: activity,
8384
+ from: from,
8385
+ to: to
8386
+ };
8387
+ }
8388
+
7522
8389
  const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ new WeakMap;
7523
8390
 
7524
8391
  async function resolveAccess(taggedClient, args = {}) {
@@ -7528,10 +8395,13 @@ async function resolveAccess(taggedClient, args = {}) {
7528
8395
  client: client,
7529
8396
  requestFn: requestFn,
7530
8397
  resourcePath: args.grantsFromPath
7531
- }) : Promise.resolve(void 0), [actor, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
7532
- if (actor === void 0) throw new invariants.ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
8398
+ }) : Promise.resolve(void 0), [identity, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
8399
+ if (identity === void 0) throw new invariants.ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
7533
8400
  return {
7534
- actor: actor,
8401
+ actor: identity.actor,
8402
+ ...identity.localPrincipalId !== void 0 ? {
8403
+ localPrincipalId: identity.localPrincipalId
8404
+ } : {},
7535
8405
  ...grants !== void 0 ? {
7536
8406
  grants: grants
7537
8407
  } : {}
@@ -7541,7 +8411,7 @@ async function resolveAccess(taggedClient, args = {}) {
7541
8411
  function cachedActor(client, requestFn) {
7542
8412
  const cached = actorCache.get(client);
7543
8413
  if (cached !== void 0) return cached;
7544
- const pending = fetchActor(requestFn).catch(err => {
8414
+ const pending = fetchActor(client, requestFn).catch(err => {
7545
8415
  throw actorCache.get(client) === pending && actorCache.delete(client), err;
7546
8416
  });
7547
8417
  return actorCache.set(client, pending), pending;
@@ -7568,27 +8438,90 @@ function cachedGrants({client: client, requestFn: requestFn, resourcePath: resou
7568
8438
  byPath.set(resourcePath, cached)), cached;
7569
8439
  }
7570
8440
 
7571
- async function fetchActor(requestFn) {
7572
- let user;
8441
+ async function fetchActor(client, requestFn) {
8442
+ const [resourceUser, globalUser] = await Promise.all([ fetchCurrentUser(requestFn, "the workflow resource host"), fetchGlobalUser(client) ]), resourceId = usableId(resourceUser), globalId = usableId(globalUser.user), id = globalId ?? resourceId;
8443
+ if (id === void 0) return;
8444
+ refuseProjectPrincipalWithoutGlobal({
8445
+ resourceId: resourceId,
8446
+ globalId: globalId,
8447
+ globalUser: globalUser
8448
+ });
8449
+ const roleNames = (resourceUser?.roles?.length ? resourceUser : globalUser.user)?.roles?.map(r => r.name).filter(n => !!n) ?? [];
8450
+ return {
8451
+ actor: {
8452
+ kind: "person",
8453
+ id: id,
8454
+ ...roleNames.length > 0 ? {
8455
+ roles: roleNames
8456
+ } : {}
8457
+ },
8458
+ ...resourceId !== void 0 && resourceId !== id ? {
8459
+ localPrincipalId: resourceId
8460
+ } : {}
8461
+ };
8462
+ }
8463
+
8464
+ function refuseProjectPrincipalWithoutGlobal(args) {
8465
+ const {resourceId: resourceId, globalId: globalId, globalUser: globalUser} = args;
8466
+ if (globalId !== void 0 || resourceId === void 0 || invariants.classifyPrincipalId(resourceId).namespace !== "project") return;
8467
+ const reason = "reason" in globalUser ? globalUser.reason : "no record";
8468
+ throw new Error(`workflow: the caller's identity on the workflow resource host is project-scoped ("${resourceId}") and the account-global record could not be resolved (${reason}). The engine speaks account-global user ids only and never acts as a project-scoped principal.`, "cause" in globalUser ? {
8469
+ cause: globalUser.cause
8470
+ } : void 0);
8471
+ }
8472
+
8473
+ function usableId(user) {
8474
+ if (!(!user || typeof user.id != "string" || user.id.length === 0)) return user.id;
8475
+ }
8476
+
8477
+ async function fetchCurrentUser(requestFn, hostDescription) {
7573
8478
  try {
7574
- user = await requestFn({
8479
+ return await requestFn({
7575
8480
  uri: "/users/me",
7576
8481
  tag: REQUEST_TAG.accessResolveActor
7577
8482
  });
7578
8483
  } catch (err) {
7579
- throw new Error('workflow: /users/me request failed. The engine resolves the actor from the client\'s token via `client.request({ uri: "/users/me" })`. Check the token/connectivity.', {
8484
+ throw new Error(`workflow: /users/me request against ${hostDescription} failed. The engine resolves the actor from the client's token via \`client.request({ uri: '/users/me' })\`. Check the token/connectivity.`, {
7580
8485
  cause: err
7581
8486
  });
7582
8487
  }
7583
- if (!user || typeof user.id != "string" || user.id.length === 0) return;
7584
- const roleNames = user.roles?.map(r => r.name).filter(n => !!n) ?? [];
7585
- return {
7586
- kind: "person",
7587
- id: user.id,
7588
- ...roleNames.length > 0 ? {
7589
- roles: roleNames
7590
- } : {}
8488
+ }
8489
+
8490
+ async function fetchGlobalUser(client) {
8491
+ if (typeof client.withConfig != "function") return {
8492
+ user: void 0,
8493
+ reason: "the client cannot reach the global API host (no withConfig)"
8494
+ };
8495
+ let globalRequest, globalClient;
8496
+ try {
8497
+ globalClient = client.withConfig({
8498
+ useProjectHostname: !1
8499
+ }), globalRequest = lazyRequest(globalClient);
8500
+ } catch (err) {
8501
+ return {
8502
+ user: void 0,
8503
+ reason: `building the global-host sibling client failed: ${invariants.errorMessage(err)}`,
8504
+ cause: err
8505
+ };
8506
+ }
8507
+ if (globalRequest === void 0) return {
8508
+ user: void 0,
8509
+ reason: "the global-host sibling client cannot issue requests"
7591
8510
  };
8511
+ try {
8512
+ return {
8513
+ user: await globalRequest({
8514
+ uri: "/users/me",
8515
+ tag: REQUEST_TAG.accessResolveActor
8516
+ })
8517
+ };
8518
+ } catch (err) {
8519
+ return {
8520
+ user: void 0,
8521
+ reason: `global-host /users/me failed: ${invariants.errorMessage(err)}`,
8522
+ cause: err
8523
+ };
8524
+ }
7592
8525
  }
7593
8526
 
7594
8527
  async function fetchGrantsCached(requestFn, resourcePath) {
@@ -7764,35 +8697,48 @@ async function subjectResourceGrants(args) {
7764
8697
  const resolved = await Promise.all([ ...clients.entries() ].map(async ([key, entry]) => {
7765
8698
  const path = aclPathForResource(entry.resource);
7766
8699
  if (path === void 0) return;
7767
- const grants = await grantsForClientPath(entry.client, path);
7768
- return grants === void 0 ? void 0 : [ key, grants ];
8700
+ const [grants, actorId] = await Promise.all([ grantsForClientPath(entry.client, path), resourceActorId(entry.client) ]);
8701
+ if (!(grants === void 0 || actorId === void 0)) return [ key, {
8702
+ grants: grants,
8703
+ actorId: actorId
8704
+ } ];
7769
8705
  }));
7770
8706
  return new Map(resolved.filter(entry => entry !== void 0));
7771
8707
  }
7772
8708
 
8709
+ async function resourceActorId(client) {
8710
+ try {
8711
+ const access = await resolveAccess(client);
8712
+ return invariants.lakePrincipalId(access);
8713
+ } catch (err) {
8714
+ console.warn(`workflow: failed to resolve the actor identity on a subject resource; the subject-write forecast omits this resource (degrade open — the lake still enforces writes). Original error: ${invariants.errorMessage(err)}`);
8715
+ return;
8716
+ }
8717
+ }
8718
+
7773
8719
  async function evaluateInstance(args) {
7774
8720
  const {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, resourceClients: resourceClients} = args, now = (args.clock ?? wallClock)();
7775
8721
  invariants.validateTag(tag);
7776
- const {actor: actor, grants: grants} = await resolveAccess(client, {
8722
+ const [access, instance] = await Promise.all([ resolveAccess(client, {
7777
8723
  ...args.grantsFromPath !== void 0 ? {
7778
8724
  grantsFromPath: args.grantsFromPath
7779
8725
  } : {}
7780
- }), instance = await reload({
8726
+ }), reload({
7781
8727
  client: client,
7782
8728
  instanceId: instanceId,
7783
8729
  tag: tag
7784
- }), definition = invariants.parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
8730
+ }) ]), {actor: actor, grants: grants, localPrincipalId: localPrincipalId} = access, definition = invariants.parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
7785
8731
  client: client,
7786
8732
  workflowResource: workflowResource,
7787
8733
  resourceClients: resourceClients
7788
- }), snapshot = await hydrateSnapshot({
8734
+ }), [snapshot, guards, resourceGrants] = await Promise.all([ hydrateSnapshot({
7789
8735
  client: client,
7790
8736
  clientForGdr: clientForGdr,
7791
8737
  instance: instance
7792
- }), guards = await verdictGuardsForInstance(client, instance._id), resourceGrants = await subjectResourceGrants({
8738
+ }), verdictGuardsForInstance(client, instance._id), subjectResourceGrants({
7793
8739
  clientForGdr: clientForGdr,
7794
8740
  instance: instance
7795
- });
8741
+ }) ]);
7796
8742
  return evaluateFromSnapshot({
7797
8743
  instance: instance,
7798
8744
  definition: definition,
@@ -7801,6 +8747,9 @@ async function evaluateInstance(args) {
7801
8747
  guards: guards,
7802
8748
  now: now,
7803
8749
  resourceGrants: resourceGrants,
8750
+ ...localPrincipalId !== void 0 ? {
8751
+ localPrincipalId: localPrincipalId
8752
+ } : {},
7804
8753
  ...grants !== void 0 ? {
7805
8754
  grants: grants
7806
8755
  } : {}
@@ -7856,9 +8805,12 @@ async function evaluateFromSnapshot(args) {
7856
8805
  definition: definition,
7857
8806
  snapshot: snapshot,
7858
8807
  now: now
7859
- }, can = await advisoryCan({
7860
- instance: instance,
8808
+ }, anchorIdentity = invariants.lakePrincipalId({
7861
8809
  actor: actor,
8810
+ localPrincipalId: args.localPrincipalId
8811
+ }), can = await advisoryCan({
8812
+ instance: instance,
8813
+ identity: anchorIdentity,
7862
8814
  grants: grants
7863
8815
  }), scope = await renderConditionScope(scopeSource, {
7864
8816
  actor: actor,
@@ -7875,11 +8827,10 @@ async function evaluateFromSnapshot(args) {
7875
8827
  activityName: activityName
7876
8828
  })), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
7877
8829
  instance: instance,
7878
- actor: actor,
8830
+ identity: anchorIdentity,
7879
8831
  guards: args.guards
7880
8832
  }), subjectDenials = await forecastSubjectDenials({
7881
8833
  instance: instance,
7882
- actor: actor,
7883
8834
  snapshot: snapshot,
7884
8835
  resourceGrants: args.resourceGrants
7885
8836
  }), subjectPermissionReason = subjectDenials.length > 0 ? {
@@ -8023,20 +8974,20 @@ async function editPredicateInsight(args) {
8023
8974
  }
8024
8975
 
8025
8976
  async function forecastSubjectDenials(args) {
8026
- const {instance: instance, actor: actor, snapshot: snapshot, resourceGrants: resourceGrants} = args;
8977
+ const {instance: instance, snapshot: snapshot, resourceGrants: resourceGrants} = args;
8027
8978
  if (resourceGrants === void 0 || resourceGrants.size === 0) return [];
8028
8979
  const denials = [], seen = /* @__PURE__ */ new Set;
8029
8980
  for (const {ref: ref, parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
8030
8981
  if (seen.has(ref.id)) continue;
8031
8982
  seen.add(ref.id);
8032
- const grants = resourceGrants.get(invariants.resourceGdr(resource));
8033
- if (grants === void 0) continue;
8983
+ const access = resourceGrants.get(invariants.resourceGdr(resource));
8984
+ if (access === void 0) continue;
8034
8985
  const doc = snapshot.docs.find(d => d._id === ref.id);
8035
8986
  doc !== void 0 && await subjectUpdateAllowed({
8036
8987
  doc: doc,
8037
8988
  parsed: parsed,
8038
- grants: grants,
8039
- actorId: actor.id
8989
+ grants: access.grants,
8990
+ actorId: access.actorId
8040
8991
  }) === !1 && denials.push({
8041
8992
  subject: ref.id,
8042
8993
  resource: invariants.resourceGdr(resource),
@@ -8117,12 +9068,12 @@ async function explainActivityConditions({activity: activity, activityScope: act
8117
9068
  snapshot: snapshot,
8118
9069
  sites: sites
8119
9070
  }), requirementEntries = [];
8120
- for (const [name, condition] of Object.entries(activity.requirements ?? {})) requirementEntries.push([ name, await explainAt({
9071
+ for (const requirement of activity.requirements ?? []) requirementEntries.push([ requirement, await explainAt({
8121
9072
  kind: "requirement",
8122
9073
  activity: activity.name,
8123
- requirement: name
8124
- }, condition) ]);
8125
- const requirementInsights = Object.fromEntries(requirementEntries), unmetRequirements = requirementEntries.filter(([, insight]) => insight.outcome !== "satisfied").map(([name]) => name), filterInsight = activity.filter !== void 0 ? await explainSite({
9074
+ requirement: requirement.name
9075
+ }, requirement.query) ]);
9076
+ const requirementInsights = Object.fromEntries(requirementEntries.map(([requirement, insight]) => [ requirement.name, insight ])), unmetRequirements = requirementEntries.filter(([, insight]) => insight.outcome !== "satisfied").map(([requirement]) => requirementDescriptor(requirement)), filterInsight = activity.filter !== void 0 ? await explainSite({
8126
9077
  site: {
8127
9078
  kind: "activity-filter",
8128
9079
  activity: activity.name
@@ -8268,12 +9219,12 @@ function actionEvaluationIdentity(action) {
8268
9219
  };
8269
9220
  }
8270
9221
 
8271
- async function instanceGuardReason({instance: instance, actor: actor, guards: guards}) {
9222
+ async function instanceGuardReason({instance: instance, identity: identity, guards: guards}) {
8272
9223
  if (guards === void 0 || guards.length === 0) return;
8273
9224
  const denied = await instanceWriteDenials({
8274
9225
  instance: instance,
8275
9226
  guards: guards,
8276
- identity: actor.id
9227
+ identity: identity
8277
9228
  });
8278
9229
  if (denied.length !== 0) return {
8279
9230
  kind: "mutation-guard-denied",
@@ -8312,24 +9263,24 @@ function inFlightFilter() {
8312
9263
  return "!defined(completedAt)";
8313
9264
  }
8314
9265
 
8315
- const FIELD_REFS_DOC = "count(fields[value.id in $documents]) > 0 || count(fields[count(value[id in $documents]) > 0]) > 0", ACTIVITY_REFS_DOC = `count(stages[count(activities[${FIELD_REFS_DOC}]) > 0]) > 0`;
9266
+ const FIELD_REFS_DOC = "count(fields[value.id in $documents]) > 0 || count(fields[count(value[id in $documents]) > 0]) > 0", OPEN_STAGE_REFS_DOC = `count(stages[!defined(exitedAt) && (${FIELD_REFS_DOC} || count(activities[${FIELD_REFS_DOC}]) > 0)]) > 0`, LIVE_SUBWORKFLOW_REFS_DOC = "count(subworkflows[!defined(resolved) && ref.id in $documents]) > 0";
8316
9267
 
8317
9268
  function documentPrefilter(documents, params) {
8318
9269
  for (const document of documents) if (!invariants.isGdrUri(document)) throw new invariants.ContractViolationError(`every document must be a resource-qualified GDR URI (e.g. "dataset:project:dataset:doc-id"); got: ${JSON.stringify(document)}`);
8319
9270
  return params.documents = [ ...documents ], params.bareIds = documents.map(invariants.extractDocumentId),
8320
- `_id in $bareIds || count(ancestors[id in $documents]) > 0 || ${FIELD_REFS_DOC} || count(stages[${FIELD_REFS_DOC}]) > 0 || ${ACTIVITY_REFS_DOC}`;
9271
+ `_id in $bareIds || count(ancestors[id in $documents]) > 0 || ${LIVE_SUBWORKFLOW_REFS_DOC} || ${FIELD_REFS_DOC} || ${OPEN_STAGE_REFS_DOC}`;
8321
9272
  }
8322
9273
 
8323
9274
  function documentArm(filter, params) {
8324
9275
  if (filter.documents === void 0 && filter.document === void 0) return;
8325
- const documents = [ ...filter.documents ?? [], ...filter.document !== void 0 ? [ filter.document ] : [] ];
9276
+ const documents = [ .../* @__PURE__ */ new Set([ ...filter.documents ?? [], ...filter.document !== void 0 ? [ filter.document ] : [] ]) ].sort();
8326
9277
  return documentPrefilter(documents, params);
8327
9278
  }
8328
9279
 
8329
9280
  function idsArm(filter, params) {
8330
9281
  if (filter.ids !== void 0) {
8331
9282
  for (const id of filter.ids) if (invariants.isGdrUri(id)) throw new invariants.ContractViolationError(`instancesQuery: every id must be a bare instance document id (an instance's _id is never resource-qualified); got a GDR URI: ${JSON.stringify(id)}`);
8332
- return params.ids = [ ...filter.ids ], "_id in $ids";
9283
+ return params.ids = [ ...new Set(filter.ids) ].sort(), "_id in $ids";
8333
9284
  }
8334
9285
  }
8335
9286
 
@@ -8743,11 +9694,10 @@ async function commitEdit({ctx: ctx, target: target, mode: mode, value: value, r
8743
9694
  }
8744
9695
 
8745
9696
  async function assertFieldEditable({ctx: ctx, site: site, options: options}) {
8746
- const actor = options?.actor, window = fieldWindowOpen(ctx.instance, site), can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({
9697
+ const actor = options?.actor, window = fieldWindowOpen(ctx.instance, site), can = await advisoryCanForCall({
8747
9698
  instance: ctx.instance,
8748
- actor: actor,
8749
- grants: options.grants
8750
- }) : void 0, predicateSatisfied = window.open && site.effective !== !0 && site.effective !== void 0 ? await ctxEvaluateCondition({
9699
+ options: options
9700
+ }), predicateSatisfied = window.open && site.effective !== !0 && site.effective !== void 0 ? await ctxEvaluateCondition({
8751
9701
  ctx: ctx,
8752
9702
  condition: site.effective,
8753
9703
  opts: {
@@ -9105,8 +10055,7 @@ async function abortAndPropagate(args) {
9105
10055
  executionContext: executionContext,
9106
10056
  telemetry: telemetry
9107
10057
  })
9108
- });
9109
- return await drainCondemnedChildren({
10058
+ }), drained = await drainCondemnedChildren({
9110
10059
  client: client,
9111
10060
  instanceId: instanceId,
9112
10061
  actor: actor,
@@ -9118,8 +10067,12 @@ async function abortAndPropagate(args) {
9118
10067
  } : {},
9119
10068
  ...telemetry !== void 0 ? {
9120
10069
  telemetry: telemetry
10070
+ } : {},
10071
+ ...result.fired ? {
10072
+ instance: result.instance
9121
10073
  } : {}
9122
- }), await propagateToAncestors({
10074
+ });
10075
+ return await propagateToAncestors({
9123
10076
  client: client,
9124
10077
  instanceId: instanceId,
9125
10078
  actor: actor,
@@ -9131,6 +10084,9 @@ async function abortAndPropagate(args) {
9131
10084
  } : {},
9132
10085
  ...telemetry !== void 0 ? {
9133
10086
  telemetry: telemetry
10087
+ } : {},
10088
+ ...result.fired && !drained.drained ? {
10089
+ instance: drained.instance
9134
10090
  } : {}
9135
10091
  }), result.fired;
9136
10092
  }
@@ -9238,6 +10194,9 @@ function buildEngineCallOptions(args) {
9238
10194
  actor: args.actor,
9239
10195
  clientForGdr: args.clientForGdr,
9240
10196
  refSurface: args.refSurface,
10197
+ ...args.localPrincipalId !== void 0 ? {
10198
+ localPrincipalId: args.localPrincipalId
10199
+ } : {},
9241
10200
  ...args.grants !== void 0 ? {
9242
10201
  grants: args.grants
9243
10202
  } : {},
@@ -9379,6 +10338,15 @@ async function abortInstances(args) {
9379
10338
  return aborted;
9380
10339
  }
9381
10340
 
10341
+ function projectStartSliceRow(instance) {
10342
+ const subject = (instance.fields ?? []).find(field => field._type === "subject")?.value;
10343
+ return {
10344
+ definition: instance.definition,
10345
+ subject: typeof subject == "object" && subject !== null && "id" in subject && invariants.isGdrUri(subject.id) ? subject.id : null,
10346
+ completedAt: instance.completedAt ?? null
10347
+ };
10348
+ }
10349
+
9382
10350
  async function fetchStartSlice(args) {
9383
10351
  const {client: client, tag: tag} = args, {query: query, params: params} = instancesQuery({
9384
10352
  tag: tag,
@@ -9386,7 +10354,7 @@ async function fetchStartSlice(args) {
9386
10354
  includeCompleted: !0
9387
10355
  }
9388
10356
  });
9389
- return (await client.fetch(query, params)).map(invariants.assertReadableModel);
10357
+ return (await client.fetch(query, params)).map(invariants.assertReadableModel).map(projectStartSliceRow);
9390
10358
  }
9391
10359
 
9392
10360
  const workflow = {
@@ -9535,6 +10503,9 @@ const workflow = {
9535
10503
  ...access.grants !== void 0 ? {
9536
10504
  grants: access.grants
9537
10505
  } : {},
10506
+ ...access.localPrincipalId !== void 0 ? {
10507
+ localPrincipalId: access.localPrincipalId
10508
+ } : {},
9538
10509
  clock: clock,
9539
10510
  clientForGdr: clientForGdr,
9540
10511
  refSurface: refSurface,
@@ -9603,6 +10574,9 @@ const workflow = {
9603
10574
  ...access.grants !== void 0 ? {
9604
10575
  grants: access.grants
9605
10576
  } : {},
10577
+ ...access.localPrincipalId !== void 0 ? {
10578
+ localPrincipalId: access.localPrincipalId
10579
+ } : {},
9606
10580
  clock: clock,
9607
10581
  clientForGdr: clientForGdr,
9608
10582
  refSurface: refSurface,
@@ -9762,7 +10736,7 @@ const workflow = {
9762
10736
  });
9763
10737
  },
9764
10738
  tick: async rawArgs => {
9765
- const args = taggedScope(rawArgs, REQUEST_TAG.tick), {client: client, tag: tag, instanceId: instanceId, executionContext: executionContext} = args, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, current = await reload({
10739
+ const args = taggedScope(rawArgs, REQUEST_TAG.tick), {client: client, tag: tag, instanceId: instanceId, executionContext: executionContext} = args, {access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, current = await reload({
9766
10740
  client: client,
9767
10741
  instanceId: instanceId,
9768
10742
  tag: tag
@@ -9770,7 +10744,7 @@ const workflow = {
9770
10744
  await assertInstanceWriteAllowed({
9771
10745
  instance: current,
9772
10746
  guards: guards,
9773
- identity: actor.id
10747
+ identity: invariants.lakePrincipalId(access)
9774
10748
  });
9775
10749
  const {cascaded: cascaded, instance: instance} = await cascadeAndReload({
9776
10750
  client: client,
@@ -9887,6 +10861,59 @@ const workflow = {
9887
10861
  }
9888
10862
  });
9889
10863
  },
10864
+ resetActivity: async rawArgs => {
10865
+ const args = taggedScope(rawArgs, REQUEST_TAG.resetActivity), {client: client, tag: tag, instanceId: instanceId, activity: activity, executionContext: executionContext} = args, to = args.to ?? "active";
10866
+ if (!isResetActivityTarget(to)) throw new invariants.ContractViolationError(`resetActivity: "to" must be "active" or "skipped"; got ${JSON.stringify(args.to)}`);
10867
+ return runCommitVerb({
10868
+ args: args,
10869
+ op: "resetActivity",
10870
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, record: record, before: before}) => {
10871
+ const result = await resetActivity({
10872
+ client: client,
10873
+ instanceId: instanceId,
10874
+ activity: activity,
10875
+ to: to,
10876
+ ...record !== void 0 ? {
10877
+ requestRecord: record
10878
+ } : {},
10879
+ options: engineOptionsForActor({
10880
+ actor: actor,
10881
+ clock: clock,
10882
+ clientForGdr: clientForGdr,
10883
+ refSurface: refSurface,
10884
+ executionContext: executionContext,
10885
+ telemetry: args.telemetry
10886
+ })
10887
+ }), cascaded = result.fired ? await cascade({
10888
+ client: client,
10889
+ instanceId: instanceId,
10890
+ actor: actor,
10891
+ clientForGdr: clientForGdr,
10892
+ refSurface: refSurface,
10893
+ clock: clock,
10894
+ ...executionContext !== void 0 ? {
10895
+ executionContext: executionContext
10896
+ } : {},
10897
+ ...args.telemetry !== void 0 ? {
10898
+ telemetry: args.telemetry
10899
+ } : {}
10900
+ }) : 0;
10901
+ return resolveTelemetry(args.telemetry).log(WorkflowActivityReset, {
10902
+ ...definitionHashFragment(before.pinnedContentHash),
10903
+ instanceId: instanceId,
10904
+ changed: result.fired
10905
+ }), {
10906
+ instance: await reload({
10907
+ client: client,
10908
+ instanceId: instanceId,
10909
+ tag: tag
10910
+ }),
10911
+ cascaded: cascaded,
10912
+ changed: result.fired
10913
+ };
10914
+ }
10915
+ });
10916
+ },
9890
10917
  getInstance: async rawArgs => {
9891
10918
  const args = taggedScope(rawArgs, REQUEST_TAG.getInstance), {client: client, tag: tag, instanceId: instanceId} = args;
9892
10919
  return invariants.validateTag(tag), reload({
@@ -10033,21 +11060,18 @@ const workflow = {
10033
11060
  return (await client.fetch(query, params)).map(readInstanceDoc).filter(instance => instanceWatchesDocument(instance, document));
10034
11061
  },
10035
11062
  definitionsForDocument: async rawArgs => {
10036
- const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document, subject: subject} = args;
11063
+ const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document} = args;
10037
11064
  invariants.validateTag(tag);
10038
- const clock = args.clock ?? wallClock, deployed = (await client.fetch(definitionsListGroq("desc"), {
11065
+ const clock = args.clock ?? wallClock, deployed = (await client.fetch(latestDefinitionsGroq(), {
10039
11066
  tag: tag
10040
- })).map(invariants.assertReadableModel), latest = latestDeployedDefinitions(deployed);
11067
+ })).map(invariants.assertReadableModel);
10041
11068
  let slice;
10042
11069
  return applicableDefinitions({
10043
- definitions: latest,
11070
+ definitions: deployed,
10044
11071
  document: document,
10045
11072
  scope: {
10046
11073
  tag: tag,
10047
11074
  now: clock(),
10048
- ...subject !== void 0 ? {
10049
- subject: subject
10050
- } : {},
10051
11075
  fetchDataset: () => slice ??= fetchStartSlice({
10052
11076
  client: client,
10053
11077
  tag: tag
@@ -10070,34 +11094,17 @@ const workflow = {
10070
11094
  }), missingRequired = missingRequiredInputs({
10071
11095
  entryDefs: definition.fields ?? [],
10072
11096
  initialFields: initialFields ?? []
10073
- }), allowed = definition.start?.allowed;
10074
- if (allowed === void 0) return {
10075
- allowed: invalidInitialFields.length === 0,
10076
- outcome: "satisfied",
10077
- unboundReads: [],
10078
- missingRequired: missingRequired,
10079
- invalidInitialFields: invalidInitialFields
10080
- };
10081
- const unboundReads = unboundAllowedReadsWithSubject({
10082
- allowed: allowed,
10083
- fields: startFieldsParam({
10084
- entryDefs: definition.fields ?? [],
10085
- initialFields: initialFields ?? []
10086
- }),
10087
- definition: definition
10088
- }), insight = await startAllowedInsight({
11097
+ }), verdict = await startRequirementVerdict({
10089
11098
  client: client,
10090
11099
  tag: tag,
10091
11100
  definition: definition,
10092
- allowed: allowed,
10093
11101
  initialFields: initialFields ?? [],
10094
- now: clock()
10095
- }), provisional = unboundReads.length > 0;
11102
+ now: clock(),
11103
+ preflight: !0
11104
+ });
10096
11105
  return {
10097
- allowed: invalidInitialFields.length === 0 && !provisional && insight.outcome === "satisfied",
10098
- outcome: provisional ? "unevaluable" : insight.outcome,
10099
- insight: insight,
10100
- unboundReads: unboundReads,
11106
+ ...verdict,
11107
+ allowed: invalidInitialFields.length === 0 && verdict.allowed,
10101
11108
  missingRequired: missingRequired,
10102
11109
  invalidInitialFields: invalidInitialFields
10103
11110
  };
@@ -10109,11 +11116,10 @@ const workflow = {
10109
11116
  }
10110
11117
  };
10111
11118
 
10112
- async function startAllowedInsight(args) {
10113
- const {client: client, tag: tag, definition: definition, allowed: allowed, initialFields: initialFields, now: now} = args;
10114
- return explainStartAllowed({
10115
- allowed: allowed,
10116
- definition: definition,
11119
+ function startPredicateContext(args) {
11120
+ const {client: client, tag: tag, definition: definition, initialFields: initialFields, now: now} = args;
11121
+ let slice;
11122
+ return {
10117
11123
  fields: startFieldsParam({
10118
11124
  entryDefs: definition.fields ?? [],
10119
11125
  initialFields: initialFields
@@ -10121,12 +11127,71 @@ async function startAllowedInsight(args) {
10121
11127
  scope: {
10122
11128
  tag: tag,
10123
11129
  now: now,
10124
- fetchDataset: () => fetchStartSlice({
11130
+ fetchDataset: () => slice ??= fetchStartSlice({
10125
11131
  client: client,
10126
11132
  tag: tag
10127
11133
  })
10128
11134
  }
10129
- });
11135
+ };
11136
+ }
11137
+
11138
+ async function startRequirementVerdict(args) {
11139
+ const {client: client, tag: tag, definition: definition, initialFields: initialFields, now: now, preflight: preflight = !1} = args, declared = definition.start?.requirements ?? [];
11140
+ if (declared.length === 0) return {
11141
+ allowed: !0,
11142
+ outcome: "satisfied",
11143
+ requirements: [],
11144
+ unboundReads: []
11145
+ };
11146
+ const {fields: fields, scope: scope} = startPredicateContext({
11147
+ client: client,
11148
+ tag: tag,
11149
+ definition: definition,
11150
+ initialFields: initialFields,
11151
+ now: now
11152
+ }), subjectEntry = (definition.fields ?? []).find(invariants.isSubjectEntry)?.name, requirements = [], unbound = /* @__PURE__ */ new Set;
11153
+ for (const requirement of declared) {
11154
+ const descriptor = requirementDescriptor(requirement);
11155
+ if (requirement.type === "singleSubject") {
11156
+ if (preflight && subjectEntry !== void 0 && !Object.hasOwn(fields, subjectEntry)) unbound.add(subjectEntry),
11157
+ requirements.push({
11158
+ ...descriptor,
11159
+ outcome: "unevaluable"
11160
+ }); else {
11161
+ const refused = await singleSubjectRequirementRefused({
11162
+ definition: definition,
11163
+ fields: fields,
11164
+ scope: scope
11165
+ });
11166
+ requirements.push({
11167
+ ...descriptor,
11168
+ outcome: refused ? "unsatisfied" : "satisfied"
11169
+ });
11170
+ }
11171
+ continue;
11172
+ }
11173
+ const missing = unboundRequirementReads(requirement.query, fields);
11174
+ missing.forEach(name => unbound.add(name));
11175
+ const insight = await explainStartRequirement({
11176
+ query: requirement.query,
11177
+ definition: definition,
11178
+ fields: fields,
11179
+ scope: scope
11180
+ });
11181
+ requirements.push({
11182
+ ...descriptor,
11183
+ outcome: preflight && missing.length > 0 ? "unevaluable" : insight.outcome,
11184
+ insight: insight
11185
+ });
11186
+ }
11187
+ let outcome = "satisfied";
11188
+ return requirements.some(requirement => requirement.outcome === "unsatisfied") ? outcome = "unsatisfied" : requirements.some(requirement => requirement.outcome === "unevaluable") && (outcome = "unevaluable"),
11189
+ {
11190
+ allowed: outcome === "satisfied",
11191
+ outcome: outcome,
11192
+ requirements: requirements,
11193
+ unboundReads: [ ...unbound ]
11194
+ };
10130
11195
  }
10131
11196
 
10132
11197
  async function startFreshInstance(args) {
@@ -10226,19 +11291,16 @@ async function assertStartInputs(args) {
10226
11291
  initialFields: initialFields,
10227
11292
  definitionName: definition.name
10228
11293
  });
10229
- const allowed = definition.start?.allowed;
10230
- if (allowed === void 0) return;
10231
- const insight = await startAllowedInsight({
11294
+ const unmetRequirements = (await startRequirementVerdict({
10232
11295
  client: client,
10233
11296
  tag: tag,
10234
11297
  definition: definition,
10235
- allowed: allowed,
10236
11298
  initialFields: initialFields,
10237
11299
  now: now
10238
- });
10239
- if (insight.outcome !== "satisfied") throw new StartNotAllowedError({
11300
+ })).requirements.filter(requirement => requirement.outcome !== "satisfied").map(requirementDescriptor);
11301
+ if (unmetRequirements.length > 0) throw new StartNotAllowedError({
10240
11302
  definition: definition.name,
10241
- insight: insight
11303
+ unmetRequirements: unmetRequirements
10242
11304
  });
10243
11305
  }
10244
11306
 
@@ -10271,97 +11333,6 @@ async function resolveStartFields(args) {
10271
11333
  };
10272
11334
  }
10273
11335
 
10274
- function isRecord(value) {
10275
- return typeof value == "object" && value !== null && !Array.isArray(value);
10276
- }
10277
-
10278
- function isClientProjectUser(value) {
10279
- 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");
10280
- }
10281
-
10282
- function objectProperty(value, property) {
10283
- if (!isRecord(value)) return;
10284
- const propertyValue = value[property];
10285
- return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
10286
- }
10287
-
10288
- function stringProperty(value, property) {
10289
- const propertyValue = Reflect.get(value, property);
10290
- return typeof propertyValue == "string" ? propertyValue : void 0;
10291
- }
10292
-
10293
- function apiErrorType(error) {
10294
- const response = objectProperty(error, "response"), body = objectProperty(response, "body");
10295
- if (!body) return;
10296
- const nestedError = objectProperty(body, "error");
10297
- return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
10298
- }
10299
-
10300
- function isProjectUserNotFoundError(error) {
10301
- return apiErrorType(error) === "projectUserNotFoundError";
10302
- }
10303
-
10304
- function clientProjectUserDirectory(client, projectId) {
10305
- return {
10306
- findById: async id => {
10307
- if (!client.request) return {
10308
- status: "inaccessible",
10309
- cause: new Error("Project-user resolution requires WorkflowClient.request")
10310
- };
10311
- try {
10312
- const response = await client.request({
10313
- uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
10314
- }), candidate = Array.isArray(response) ? response[0] : response;
10315
- return candidate == null ? {
10316
- status: "missing"
10317
- } : isClientProjectUser(candidate) ? {
10318
- status: "resolved",
10319
- user: candidate
10320
- } : {
10321
- status: "inaccessible",
10322
- cause: new Error("Project-user response had an invalid shape")
10323
- };
10324
- } catch (cause) {
10325
- return isProjectUserNotFoundError(cause) ? {
10326
- status: "missing"
10327
- } : {
10328
- status: "inaccessible",
10329
- cause: cause
10330
- };
10331
- }
10332
- }
10333
- };
10334
- }
10335
-
10336
- function resolveClientActor(client, args) {
10337
- return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
10338
- }
10339
-
10340
- async function resolveActor(directory, actor) {
10341
- if (actor.kind !== "person") return {
10342
- status: "not-person",
10343
- actor: actor
10344
- };
10345
- const personActor = {
10346
- ...actor,
10347
- kind: "person"
10348
- }, lookup = await directory.findById(personActor.id);
10349
- return lookup.status === "resolved" ? {
10350
- status: "resolved",
10351
- actor: personActor,
10352
- user: lookup.user
10353
- } : lookup.status === "inaccessible" ? {
10354
- status: "inaccessible",
10355
- actor: personActor,
10356
- ...lookup.cause === void 0 ? {} : {
10357
- cause: lookup.cause
10358
- }
10359
- } : {
10360
- status: "missing",
10361
- actor: personActor
10362
- };
10363
- }
10364
-
10365
11336
  const EFFECT_COMMIT_QUEUE_DEPTH = 32, EFFECT_COMMIT_DISPATCH_CAP = 200;
10366
11337
 
10367
11338
  class EffectCommitQueueOverflowError extends invariants.WorkflowError {
@@ -10540,7 +11511,7 @@ function isCancelledCompletion(data) {
10540
11511
  }
10541
11512
 
10542
11513
  async function drainEffectsInternal(args) {
10543
- 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({
11514
+ const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, effectHandlers: effectHandlers, missingHandler: missingHandler, logger: logger, handlerClient: rawHandlerClient, handlerResourceClients: rawHandlerResourceClients} = 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, handlerClient = effectHandlerClient(rawHandlerClient), handlerResourceClients = effectHandlerResolver(rawHandlerResourceClients), routeGdr = buildClientForGdr({
10544
11515
  client: handlerClient,
10545
11516
  workflowResource: workflowResource,
10546
11517
  resourceClients: handlerResourceClients
@@ -10906,22 +11877,30 @@ function createInstanceSession(args) {
10906
11877
  }, tickScope = opScope(REQUEST_TAG.tick), fireScope = opScope(REQUEST_TAG.fireAction), editScope = opScope(REQUEST_TAG.editField), evalScope = opScope(REQUEST_TAG.evaluate);
10907
11878
  let overlay = /* @__PURE__ */ new Map;
10908
11879
  const previews = /* @__PURE__ */ new Map;
10909
- let heldGuards = [], committing = !1, buffered, deferredUpdateError;
10910
- const selfUri = () => invariants.gdrFromResource(instance.workflowResource, instance._id), applyUpdate = docs => {
10911
- const next = /* @__PURE__ */ new Map;
10912
- for (const ld of docs) {
10913
- const owned = {
10914
- doc: structuredClone(ld.doc),
10915
- resource: ld.resource
10916
- }, uri = invariants.gdrFromResource(owned.resource, owned.doc._id);
10917
- if (uri === selfUri()) {
10918
- const rawStamp = owned.doc._updatedAt;
10919
- !invariants.isParseableInstant(rawStamp) || owned.doc._type !== invariants.WORKFLOW_INSTANCE_TYPE ? asHeldInstance(owned.doc) : Date.parse(rawStamp) > Date.parse(instance._updatedAt) && (instance = asHeldInstance(owned.doc));
10920
- continue;
10921
- }
10922
- next.set(uri, owned);
11880
+ let heldGuards = [], committing = !1, buffered;
11881
+ const bufferedDocuments = /* @__PURE__ */ new Map;
11882
+ let deferredUpdateError;
11883
+ const flushDeferredError = () => {
11884
+ if (deferredUpdateError === void 0) return;
11885
+ const failure = deferredUpdateError;
11886
+ throw deferredUpdateError = void 0, failure;
11887
+ }, selfUri = () => invariants.gdrFromResource(instance.workflowResource, instance._id), applyDocument = (ld, target) => {
11888
+ const owned = {
11889
+ doc: structuredClone(ld.doc),
11890
+ resource: ld.resource
11891
+ }, uri = invariants.gdrFromResource(owned.resource, owned.doc._id);
11892
+ if (uri === selfUri()) {
11893
+ const rawStamp = owned.doc._updatedAt;
11894
+ !invariants.isParseableInstant(rawStamp) || owned.doc._type !== invariants.WORKFLOW_INSTANCE_TYPE ? asHeldInstance(owned.doc) : Date.parse(rawStamp) > Date.parse(instance._updatedAt) && (instance = asHeldInstance(owned.doc));
11895
+ return;
10923
11896
  }
11897
+ target.set(uri, owned);
11898
+ }, applyUpdate = docs => {
11899
+ const next = /* @__PURE__ */ new Map;
11900
+ for (const ld of docs) applyDocument(ld, next);
10924
11901
  overlay = next;
11902
+ }, applyDocumentUpdate = doc => {
11903
+ applyDocument(doc, overlay);
10925
11904
  }, access = () => resolveAccess(client, {
10926
11905
  ...args.grantsFromPath !== void 0 ? {
10927
11906
  grantsFromPath: args.grantsFromPath
@@ -10979,17 +11958,23 @@ function createInstanceSession(args) {
10979
11958
  refSurface: evalScope.refSurface
10980
11959
  });
10981
11960
  }, evaluateWith = async ({held: held, guards: guards, self: self}) => {
10982
- const {actor: actor, grants: grants} = await access(), resourceGrants = await subjectResourceGrants({
11961
+ const {actor: actor, localPrincipalId: localPrincipalId, grants: grants} = await access(), resourceGrants = await subjectResourceGrants({
10983
11962
  clientForGdr: evalScope.clientForGdr,
10984
11963
  instance: instance
11964
+ }), normalizedSelf = await normalizeInstanceIdentities({
11965
+ client: evalScope.client,
11966
+ instance: self
10985
11967
  });
10986
11968
  return evaluateFromSnapshot({
10987
- instance: self,
11969
+ instance: normalizedSelf,
10988
11970
  definition: definitionOf(),
10989
11971
  actor: actor,
10990
- snapshot: snapshotFrom(held, self),
11972
+ snapshot: snapshotFrom(held, normalizedSelf),
10991
11973
  guards: guards,
10992
11974
  resourceGrants: resourceGrants,
11975
+ ...localPrincipalId !== void 0 ? {
11976
+ localPrincipalId: localPrincipalId
11977
+ } : {},
10993
11978
  ...clock !== void 0 ? {
10994
11979
  now: clock()
10995
11980
  } : {},
@@ -11035,8 +12020,13 @@ function createInstanceSession(args) {
11035
12020
  committing = !0;
11036
12021
  const held = new Map(overlay);
11037
12022
  try {
11038
- const {actor: actor} = await access();
11039
- return await run(actor, held);
12023
+ const {actor: actor, localPrincipalId: localPrincipalId} = await access();
12024
+ return await run({
12025
+ actor: actor,
12026
+ ...localPrincipalId !== void 0 ? {
12027
+ localPrincipalId: localPrincipalId
12028
+ } : {}
12029
+ }, held);
11040
12030
  } finally {
11041
12031
  if (committing = !1, buffered !== void 0) {
11042
12032
  const next = buffered;
@@ -11047,6 +12037,12 @@ function createInstanceSession(args) {
11047
12037
  deferredUpdateError = err;
11048
12038
  }
11049
12039
  }
12040
+ for (const next of bufferedDocuments.values()) try {
12041
+ applyDocumentUpdate(next);
12042
+ } catch (err) {
12043
+ deferredUpdateError = err;
12044
+ }
12045
+ bufferedDocuments.clear();
11050
12046
  }
11051
12047
  };
11052
12048
  return {
@@ -11054,15 +12050,26 @@ function createInstanceSession(args) {
11054
12050
  return subscriptionDocumentsForInstance(instance);
11055
12051
  },
11056
12052
  update(docs) {
11057
- if (committing) buffered = docs; else try {
12053
+ if (committing) buffered = docs, bufferedDocuments.clear(); else try {
11058
12054
  applyUpdate(docs);
11059
12055
  } catch (err) {
11060
12056
  throw deferredUpdateError = void 0, err;
11061
12057
  }
11062
- if (deferredUpdateError !== void 0) {
11063
- const failure = deferredUpdateError;
11064
- throw deferredUpdateError = void 0, failure;
12058
+ flushDeferredError();
12059
+ },
12060
+ updateDocument(doc) {
12061
+ if (committing) {
12062
+ const uri = invariants.gdrFromResource(doc.resource, doc.doc._id);
12063
+ bufferedDocuments.set(uri, {
12064
+ doc: structuredClone(doc.doc),
12065
+ resource: doc.resource
12066
+ });
12067
+ } else try {
12068
+ applyDocumentUpdate(doc);
12069
+ } catch (err) {
12070
+ throw deferredUpdateError = void 0, err;
11065
12071
  }
12072
+ flushDeferredError();
11066
12073
  },
11067
12074
  updateGuards(guards) {
11068
12075
  heldGuards = guards.map(guard => parseGuardDocument(structuredClone(guard)));
@@ -11094,11 +12101,14 @@ function createInstanceSession(args) {
11094
12101
  site !== void 0 && previews.delete(siteKey(site));
11095
12102
  },
11096
12103
  tick() {
11097
- return commit(async (actor, held) => {
12104
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11098
12105
  await assertInstanceWriteAllowed({
11099
12106
  instance: instance,
11100
12107
  guards: heldGuards,
11101
- identity: actor.id
12108
+ identity: invariants.lakePrincipalId({
12109
+ actor: actor,
12110
+ localPrincipalId: localPrincipalId
12111
+ })
11102
12112
  });
11103
12113
  const before = await reload({
11104
12114
  client: tickScope.client,
@@ -11125,7 +12135,7 @@ function createInstanceSession(args) {
11125
12135
  });
11126
12136
  },
11127
12137
  fireAction({activity: activity, action: action, params: params}) {
11128
- return commit(async (actor, held) => {
12138
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11129
12139
  assertActionAllowed({
11130
12140
  evaluation: await evaluateWith({
11131
12141
  held: held,
@@ -11141,6 +12151,9 @@ function createInstanceSession(args) {
11141
12151
  activity: activity,
11142
12152
  action: action,
11143
12153
  actor: actor,
12154
+ ...localPrincipalId !== void 0 ? {
12155
+ localPrincipalId: localPrincipalId
12156
+ } : {},
11144
12157
  clientForGdr: fireScope.clientForGdr,
11145
12158
  refSurface: fireScope.refSurface,
11146
12159
  ...grants !== void 0 ? {
@@ -11183,7 +12196,7 @@ function createInstanceSession(args) {
11183
12196
  } catch (err) {
11184
12197
  return Promise.reject(err);
11185
12198
  }
11186
- return commit(async (actor, held) => {
12199
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11187
12200
  assertEditAllowed(await evaluateWith({
11188
12201
  held: held,
11189
12202
  guards: heldGuards,
@@ -11200,6 +12213,9 @@ function createInstanceSession(args) {
11200
12213
  value: value
11201
12214
  } : {},
11202
12215
  actor: actor,
12216
+ ...localPrincipalId !== void 0 ? {
12217
+ localPrincipalId: localPrincipalId
12218
+ } : {},
11203
12219
  clientForGdr: editScope.clientForGdr,
11204
12220
  refSurface: editScope.refSurface,
11205
12221
  ...grants !== void 0 ? {
@@ -11310,6 +12326,7 @@ function createEngine(args) {
11310
12326
  availableActions: rest => workflow.availableActions(withScope(rest)),
11311
12327
  setStage: rest => workflow.setStage(withScope(rest)),
11312
12328
  abortInstance: rest => workflow.abortInstance(withScope(rest)),
12329
+ resetActivity: rest => workflow.resetActivity(withScope(rest)),
11313
12330
  deleteDefinition: rest => workflow.deleteDefinition(withScope(rest)),
11314
12331
  getInstance: rest => workflow.getInstance(withScope(rest)),
11315
12332
  subscriptionDocumentsForInstance: rest => workflow.getInstance(withScope(rest)).then(subscriptionDocumentsForInstance),
@@ -11882,6 +12899,8 @@ exports.ACTIVITY_KINDS = invariants.ACTIVITY_KINDS;
11882
12899
 
11883
12900
  exports.ACTOR_KINDS = invariants.ACTOR_KINDS;
11884
12901
 
12902
+ exports.ANONYMOUS_IDENTITY = invariants.ANONYMOUS_IDENTITY;
12903
+
11885
12904
  exports.CONDITION_VARS = invariants.CONDITION_VARS;
11886
12905
 
11887
12906
  exports.ContractViolationError = invariants.ContractViolationError;
@@ -11924,10 +12943,12 @@ exports.RESERVED_CONDITION_VARS = invariants.RESERVED_CONDITION_VARS;
11924
12943
 
11925
12944
  exports.ReaderModelAcknowledgementError = invariants.ReaderModelAcknowledgementError;
11926
12945
 
11927
- exports.START_ALLOWED_VARS = invariants.START_ALLOWED_VARS;
11928
-
11929
12946
  exports.START_FILTER_VARS = invariants.START_FILTER_VARS;
11930
12947
 
12948
+ exports.START_REQUIREMENT_VARS = invariants.START_REQUIREMENT_VARS;
12949
+
12950
+ exports.SYSTEM_IDENTITY = invariants.SYSTEM_IDENTITY;
12951
+
11931
12952
  exports.SpawnContractsInvalidError = invariants.SpawnContractsInvalidError;
11932
12953
 
11933
12954
  exports.WORKFLOW_DEFINITION_TYPE = invariants.WORKFLOW_DEFINITION_TYPE;
@@ -11940,6 +12961,8 @@ exports.assertReadableModel = invariants.assertReadableModel;
11940
12961
 
11941
12962
  exports.assertReaderModelAcknowledgement = invariants.assertReaderModelAcknowledgement;
11942
12963
 
12964
+ exports.classifyPrincipalId = invariants.classifyPrincipalId;
12965
+
11943
12966
  exports.clientConfigFromResource = invariants.clientConfigFromResource;
11944
12967
 
11945
12968
  exports.conditionFieldReadNames = invariants.conditionFieldReadNames;
@@ -11990,6 +13013,8 @@ exports.isTodoListItem = invariants.isTodoListItem;
11990
13013
 
11991
13014
  exports.isUnprimed = invariants.isUnprimed;
11992
13015
 
13016
+ exports.lakePrincipalId = invariants.lakePrincipalId;
13017
+
11993
13018
  exports.minReaderModelOf = invariants.minReaderModelOf;
11994
13019
 
11995
13020
  exports.modelVersionOf = invariants.modelVersionOf;
@@ -12216,6 +13241,8 @@ exports.StartNotSettledError = StartNotSettledError;
12216
13241
 
12217
13242
  exports.WorkflowActionFired = WorkflowActionFired;
12218
13243
 
13244
+ exports.WorkflowActivityReset = WorkflowActivityReset;
13245
+
12219
13246
  exports.WorkflowDefinitionDeleted = WorkflowDefinitionDeleted;
12220
13247
 
12221
13248
  exports.WorkflowDefinitionDeployed = WorkflowDefinitionDeployed;
@@ -12348,7 +13375,7 @@ exports.evaluateStartFilter = evaluateStartFilter;
12348
13375
 
12349
13376
  exports.expandResourceAliases = expandResourceAliases;
12350
13377
 
12351
- exports.explainStartAllowed = explainStartAllowed;
13378
+ exports.explainStartRequirement = explainStartRequirement;
12352
13379
 
12353
13380
  exports.findCurrentActivityEntry = findCurrentActivityEntry;
12354
13381
 
@@ -12364,6 +13391,8 @@ exports.guardsForInstance = guardsForInstance;
12364
13391
 
12365
13392
  exports.guardsForResource = guardsForResource;
12366
13393
 
13394
+ exports.hasSingleSubjectRequirement = hasSingleSubjectRequirement;
13395
+
12367
13396
  exports.hashDefinitionContent = hashDefinitionContent;
12368
13397
 
12369
13398
  exports.inFlightFilter = inFlightFilter;
@@ -12376,10 +13405,14 @@ exports.instanceGuardQuery = instanceGuardQuery;
12376
13405
 
12377
13406
  exports.instanceWatchesDocument = instanceWatchesDocument;
12378
13407
 
13408
+ exports.instancesGuardQuery = instancesGuardQuery;
13409
+
12379
13410
  exports.instancesQuery = instancesQuery;
12380
13411
 
12381
13412
  exports.isClaimExpired = isClaimExpired;
12382
13413
 
13414
+ exports.isClientProjectUser = isClientProjectUser;
13415
+
12383
13416
  exports.isDefinitionApplicable = isDefinitionApplicable;
12384
13417
 
12385
13418
  exports.isFilterScopedOut = isFilterScopedOut;
@@ -12392,6 +13425,8 @@ exports.isTerminalStage = isTerminalStage;
12392
13425
 
12393
13426
  exports.lakeGuardId = lakeGuardId;
12394
13427
 
13428
+ exports.latestDefinitionsGroq = latestDefinitionsGroq;
13429
+
12395
13430
  exports.latestDeployedDefinitions = latestDeployedDefinitions;
12396
13431
 
12397
13432
  exports.lintEffectOutputs = lintEffectOutputs;
@@ -12410,6 +13445,8 @@ exports.parseInstanceDocument = parseInstanceDocument;
12410
13445
 
12411
13446
  exports.processShellUserProperties = processShellUserProperties;
12412
13447
 
13448
+ exports.projectStartSliceRow = projectStartSliceRow;
13449
+
12413
13450
  exports.projectToWatchRef = projectToWatchRef;
12414
13451
 
12415
13452
  exports.readInstanceDoc = readInstanceDoc;
@@ -12432,6 +13469,8 @@ exports.retractStageGuards = retractStageGuards;
12432
13469
 
12433
13470
  exports.silentLogger = silentLogger;
12434
13471
 
13472
+ exports.singleSubjectRequirementRefused = singleSubjectRequirementRefused;
13473
+
12435
13474
  exports.stageAutonomyOf = stageAutonomyOf;
12436
13475
 
12437
13476
  exports.startFieldsParam = startFieldsParam;
@@ -12448,7 +13487,7 @@ exports.subscriptionDocumentsForInstance = subscriptionDocumentsForInstance;
12448
13487
 
12449
13488
  exports.sweepStaleClaims = sweepStaleClaims;
12450
13489
 
12451
- exports.unboundAllowedReads = unboundAllowedReads;
13490
+ exports.unboundRequirementReads = unboundRequirementReads;
12452
13491
 
12453
13492
  exports.unsatisfiedTransitionSummaries = unsatisfiedTransitionSummaries;
12454
13493