@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.js CHANGED
@@ -1,6 +1,6 @@
1
- import { terminalState, andConditions, deriveActivityKind, parseDefinitionSnapshot, CALLER_BOUND_VARS, START_ALLOWED_VARS, CONDITION_VARS, SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, rethrowWithContext, conditionParameterNames, isGdr, WORKFLOW_INSTANCE_TYPE, START_FILTER_VARS, gdrFromResource, selfGdr, isSingleDocRefKind, parentRef, actorFulfillsRole, toBareId, WorkflowError, sameResource, resourceFromParsed, tryParseGdr, isTerminalActivityStatus, errorMessage, FieldValueShapeError, validateFieldValue, validateFieldAppendItem, evaluateCondition, isSingleDocRefEntry, choiceValueIssues, scalarValidationIssues, StoredFieldOpSchema, formatIssues, conditionSyntaxIssues, checkWorkflowInvariants, formatIssuePath, isGuardReadExpr, conditionEffectReads, EFFECTS_READ, datasetResourceParts, evaluatePredicates, WORKFLOW_DEFINITION_TYPE, tagScopeFilter, isCascadeFired, parseGdr, isGdrUri, gdrRef, isInputSourced, checkFieldValue, toPhysicalGdr, isBareSeedId, tolerantEntries, NonEmptyString, FIELD_VALUE_KINDS, tolerantObject, ActorShape, IsoTimestamp, ACTIVITY_STATUSES, GdrShape, DRIVER_KINDS, FIELD_SCOPES, fieldValueSchemas, parsePersistedDoc, assertReadableModel, InstanceNotFoundError, modelStampFor, evaluateConditionOutcome, runGroq, FIELD_READ, MUTATION_GUARD_ACTIONS, resourceGdr, minReaderModelOf, resourceFromGdrUri, refTypeIssues, rejectedRefTypes, DOCUMENT_VALUE_PERMISSIONS, driverKind, EffectNotFoundError, deriveExecutorClassification, validateTag, extractDocumentId, parseStoredDefinition, validateResourceAliasName, labelFor, DefinitionNotFoundError, definitionDocId, SpawnContractsInvalidError, gdrResourcePrefix, RESOURCE_ALIAS_NAME_SOURCE, isUnprimed, DefinitionInUseError, assertReaderModelAcknowledgement, isParseableInstant, groupMembershipNames } from "./_chunks-es/invariants.js";
1
+ import { terminalState, andConditions, deriveActivityKind, parseDefinitionSnapshot, CALLER_BOUND_VARS, START_REQUIREMENT_VARS, CONDITION_VARS, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, rethrowWithContext, isGdr, errorMessage, conditionParameterNames, START_FILTER_VARS, gdrFromResource, selfGdr, isSingleDocRefKind, parentRef, actorFulfillsRole, toBareId, WorkflowError, sameResource, resourceFromParsed, tryParseGdr, isTerminalActivityStatus, FieldValueShapeError, validateFieldValue, validateFieldAppendItem, evaluateCondition, isSingleDocRefEntry, choiceValueIssues, scalarValidationIssues, StoredFieldOpSchema, formatIssues, conditionSyntaxIssues, checkWorkflowInvariants, formatIssuePath, isGuardReadExpr, conditionEffectReads, EFFECTS_READ, datasetResourceParts, evaluatePredicates, WORKFLOW_DEFINITION_TYPE, tagScopeFilter, isCascadeFired, WORKFLOW_INSTANCE_TYPE, parseGdr, isGdrUri, gdrRef, isInputSourced, parseFieldValue, VersionSpecificDatasetGdrError, toPhysicalGdr, isBareSeedId, tolerantEntries, NonEmptyString, FIELD_VALUE_KINDS, tolerantObject, ActorShape, IsoTimestamp, ACTIVITY_STATUSES, GdrShape, DRIVER_KINDS, FIELD_SCOPES, fieldValueSchemas, parsePersistedDoc, ACTOR_KINDS, classifyPrincipalId, assertReadableModel, InstanceNotFoundError, modelStampFor, evaluateConditionOutcome, runGroq, FIELD_READ, MUTATION_GUARD_ACTIONS, resourceGdr, minReaderModelOf, resourceFromGdrUri, refTypeIssues, rejectedRefTypes, DOCUMENT_VALUE_PERMISSIONS, lakePrincipalId, driverKind, EffectNotFoundError, deriveExecutorClassification, validateTag, extractDocumentId, parseStoredDefinition, validateResourceAliasName, labelFor, DefinitionNotFoundError, definitionDocId, SpawnContractsInvalidError, gdrResourcePrefix, RESOURCE_ALIAS_NAME_SOURCE, isUnprimed, DefinitionInUseError, assertReaderModelAcknowledgement, isParseableInstant, groupMembershipNames } from "./_chunks-es/invariants.js";
2
2
 
3
- import { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTOR_KINDS, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, ModelVersionAheadError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, clientConfigFromResource, fieldTreeShape, gdrUri, isNotesEntry, isTodoListEntry, isTodoListItem, modelVersionOf, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, releaseDocId, releaseRef, requiredModelFeatures, requiredReaderModel, resourceAliasesToMap, schemaTreeShape, startKindOf } from "./_chunks-es/invariants.js";
3
+ import { ACTION_SEMANTICS, ACTIVITY_KINDS, ANONYMOUS_IDENTITY, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, ModelVersionAheadError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, SYSTEM_IDENTITY, clientConfigFromResource, fieldTreeShape, gdrUri, isNotesEntry, isTodoListEntry, isTodoListItem, modelVersionOf, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, releaseDocId, releaseRef, requiredModelFeatures, requiredReaderModel, resourceAliasesToMap, schemaTreeShape, startKindOf } from "./_chunks-es/invariants.js";
4
4
 
5
5
  import { atomNode, dedupeBy, analyzeCondition, atomReadsDataset, checklistLines as checklistLines$1, phrase, quoted, listAnd, describeAtom as describeAtom$1, describeCondition as describeCondition$1, guillemets, formatValue, humanize, listOr, scopeReadOf, describeRead, explainCondition, dedupeReads, whatIfCondition, MAX_COUNTERFACTUAL_INDEX } from "@sanity/groq-condition-describe";
6
6
 
@@ -265,6 +265,10 @@ const HIGH_FREQUENCY_SAMPLE_MS = 6e4, WorkflowDefinitionDeployed = defineWorkflo
265
265
  name: "Editorial Workflows Stage Set",
266
266
  version: 1,
267
267
  description: "The set-stage admin override was invoked (changed: false = already at the target stage)"
268
+ }), WorkflowActivityReset = defineWorkflowEvent({
269
+ name: "Editorial Workflows Activity Reset",
270
+ version: 1,
271
+ description: "The reset-activity admin override was invoked (changed: false = already at the target status, or the instance was terminal)"
268
272
  }), WorkflowDefinitionDeleted = defineWorkflowEvent({
269
273
  name: "Editorial Workflows Definition Deleted",
270
274
  version: 1,
@@ -845,7 +849,7 @@ function toGroqDescribeContext(ctx) {
845
849
  };
846
850
  }
847
851
 
848
- const VAR_LABELS = new Map([ ...START_ALLOWED_VARS, ...CONDITION_VARS ].map(entry => [ entry.name, entry.label ]));
852
+ const VAR_LABELS = new Map([ ...START_REQUIREMENT_VARS, ...CONDITION_VARS ].map(entry => [ entry.name, entry.label ]));
849
853
 
850
854
  function renderWorkflowRead(read, ctx) {
851
855
  if (read.variable === "fields" && typeof read.path[0] == "string") {
@@ -918,13 +922,6 @@ function clauseVarRequirement(requirement) {
918
922
  params: {},
919
923
  text: "no activity in this stage may have failed"
920
924
  });
921
- if (target.variable === SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR) return kind === "truthy" ? phrase("requirement.subject-has-in-flight-instance", {
922
- params: {},
923
- text: "the subject must already have an in-flight workflow"
924
- }) : phrase("requirement.subject-has-no-in-flight-instance", {
925
- params: {},
926
- text: "the subject must not already have an in-flight workflow"
927
- });
928
925
  }
929
926
  }
930
927
 
@@ -1420,14 +1417,14 @@ function activitySites(activity, stage) {
1420
1417
  activity: activity.name
1421
1418
  },
1422
1419
  condition: activity.filter
1423
- } ] : [], ...Object.entries(activity.requirements ?? {}).map(([requirement, condition]) => ({
1420
+ } ] : [], ...(activity.requirements ?? []).map(requirement => ({
1424
1421
  stage: stage,
1425
1422
  address: {
1426
1423
  kind: "requirement",
1427
1424
  activity: activity.name,
1428
- requirement: requirement
1425
+ requirement: requirement.name
1429
1426
  },
1430
- condition: condition
1427
+ condition: requirement.query
1431
1428
  })), ...(activity.actions ?? []).flatMap(action => [ ...action.filter !== void 0 ? [ {
1432
1429
  stage: stage,
1433
1430
  address: {
@@ -1495,13 +1492,7 @@ async function evaluateStartFilter(args) {
1495
1492
  const {filter: filter, definition: definition, document: document, scope: scope} = args, parsed = parseStartFilter({
1496
1493
  filter: filter,
1497
1494
  definition: definition
1498
- });
1499
- assertFilterSubject({
1500
- readsSubject: parsed.readsSubject,
1501
- definition: definition,
1502
- scope: scope
1503
- });
1504
- const dataset = await filterDataset(parsed.needsDataset, scope);
1495
+ }), dataset = await filterDataset(parsed.needsDataset, scope);
1505
1496
  if (dataset === void 0) return !1;
1506
1497
  try {
1507
1498
  const result = await (await evaluate(parsed.tree, {
@@ -1511,8 +1502,7 @@ async function evaluateStartFilter(args) {
1511
1502
  dataset: dataset,
1512
1503
  params: startContextParams({
1513
1504
  definition: definition,
1514
- scope: scope,
1515
- dataset: dataset
1505
+ scope: scope
1516
1506
  })
1517
1507
  })).get();
1518
1508
  return isUnevaluable(result) ? !1 : !!result;
@@ -1528,11 +1518,9 @@ async function evaluateStartFilter(args) {
1528
1518
  function parseStartFilter(args) {
1529
1519
  const {filter: filter, definition: definition} = args;
1530
1520
  try {
1531
- const readsSubject = readsSubjectHasInFlightInstance(filter);
1532
1521
  return {
1533
1522
  tree: parse(filter),
1534
- needsDataset: analyzeCondition(filter).readsDataset || readsSubject,
1535
- readsSubject: readsSubject
1523
+ needsDataset: analyzeCondition(filter).readsDataset
1536
1524
  };
1537
1525
  } catch (err) {
1538
1526
  rethrowNamingDefinition({
@@ -1543,70 +1531,81 @@ function parseStartFilter(args) {
1543
1531
  }
1544
1532
  }
1545
1533
 
1546
- function assertFilterSubject(args) {
1547
- if (!(!args.readsSubject || args.scope?.subject !== void 0)) throw new ContractViolationError(`start.filter on definition "${args.definition.name ?? "<unnamed>"}" reads $${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.`);
1548
- }
1549
-
1550
1534
  async function filterDataset(needsDataset, scope) {
1551
1535
  if (!needsDataset) return [];
1552
1536
  if (scope?.fetchDataset !== void 0) return scope.fetchDataset();
1553
1537
  }
1554
1538
 
1555
- async function explainStartAllowed(args) {
1556
- const {allowed: allowed, definition: definition, fields: fields, scope: scope} = args;
1539
+ async function explainStartRequirement(args) {
1540
+ const {query: query, definition: definition, fields: fields, scope: scope} = args;
1557
1541
  let needsDataset;
1558
1542
  try {
1559
- needsDataset = analyzeCondition(allowed).readsDataset || readsSubjectHasInFlightInstance(allowed);
1543
+ needsDataset = analyzeCondition(query).readsDataset;
1560
1544
  } catch (err) {
1561
1545
  rethrowNamingDefinition({
1562
1546
  err: err,
1563
1547
  definition: definition,
1564
- key: "allowed"
1548
+ key: "requirement"
1565
1549
  });
1566
1550
  }
1567
1551
  let dataset = [];
1568
1552
  if (needsDataset) {
1569
- if (scope?.fetchDataset === void 0) throw new 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.`);
1553
+ if (scope?.fetchDataset === void 0) throw new 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.`);
1570
1554
  dataset = await scope.fetchDataset();
1571
1555
  }
1572
1556
  try {
1573
1557
  return await explainCondition({
1574
- condition: allowed,
1558
+ condition: query,
1575
1559
  dataset: dataset,
1576
1560
  params: startContextParams({
1577
1561
  definition: definition,
1578
1562
  scope: scope,
1579
- fields: fields,
1580
- dataset: dataset
1563
+ fields: fields
1581
1564
  })
1582
1565
  });
1583
1566
  } catch (err) {
1584
1567
  rethrowNamingDefinition({
1585
1568
  err: err,
1586
1569
  definition: definition,
1587
- key: "allowed"
1570
+ key: "requirement"
1588
1571
  });
1589
1572
  }
1590
1573
  }
1591
1574
 
1592
- function unboundAllowedReads(allowed, fields) {
1593
- return [ ...conditionFieldReadNames(allowed) ].filter(name => !Object.hasOwn(fields, name));
1575
+ function unboundRequirementReads(query, fields) {
1576
+ return [ ...conditionFieldReadNames(query) ].filter(name => !Object.hasOwn(fields, name));
1577
+ }
1578
+
1579
+ function hasSingleSubjectRequirement(definition) {
1580
+ return definition.start?.requirements?.some(requirement => requirement.type === "singleSubject") ?? !1;
1581
+ }
1582
+
1583
+ async function singleSubjectRequirementRefused(args) {
1584
+ const {definition: definition, fields: fields, scope: scope} = args, name = singleSubjectDefinitionName(definition), subject = (fields !== void 0 ? startSubject(definition, fields) : void 0) ?? scope?.subject;
1585
+ if (subject === void 0) return !1;
1586
+ if (scope?.fetchDataset === void 0) throw new 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.`);
1587
+ const dataset = await scope.fetchDataset();
1588
+ return sliceHasInFlightSubject({
1589
+ dataset: dataset,
1590
+ subject: subject,
1591
+ definition: name
1592
+ });
1594
1593
  }
1595
1594
 
1596
- function unboundAllowedReadsWithSubject(args) {
1597
- const {allowed: allowed, fields: fields, definition: definition} = args, subjectField = (definition.fields ?? []).find(isSubjectEntry)?.name, reads = unboundAllowedReads(allowed, fields);
1598
- return subjectField !== void 0 && readsSubjectHasInFlightInstance(allowed) && !Object.hasOwn(fields, subjectField) && !reads.includes(subjectField) && reads.push(subjectField),
1599
- reads;
1595
+ function singleSubjectDefinitionName(definition) {
1596
+ const label = definition.name ?? "<unnamed>";
1597
+ if (definition.start?.requirements?.find(requirement => requirement.type === "singleSubject") === void 0) throw new ContractViolationError(`singleSubjectRequirementRefused called for definition "${label}", which declares no \`singleSubject\` requirement — evaluate only what the definition declares.`);
1598
+ if (definition.name === void 0) throw new ContractViolationError("singleSubjectRequirementRefused needs the definition `name` — the rule scopes its in-flight scan to instances of the definition, which a nameless source cannot identify.");
1599
+ return definition.name;
1600
1600
  }
1601
1601
 
1602
1602
  function rethrowNamingDefinition({err: err, definition: definition, key: key}) {
1603
- rethrowWithContext(err, `start.${key} on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (check the predicate and its evaluation inputs)`);
1603
+ rethrowWithContext(err, `${key === "filter" ? "start.filter" : "start requirement"} on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (check the predicate and its evaluation inputs)`);
1604
1604
  }
1605
1605
 
1606
- function startContextParams({definition: definition, scope: scope, fields: fields, dataset: dataset}) {
1607
- const subject = fields === void 0 ? scope?.subject : startSubject(definition, fields) ?? scope?.subject, params = baseStartParams(definition, scope);
1608
- return fields !== void 0 && (params.fields = fields), subject !== void 0 && (params[SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR] = subjectHasInFlightInstance(dataset, subject)),
1609
- params;
1606
+ function startContextParams({definition: definition, scope: scope, fields: fields}) {
1607
+ const params = baseStartParams(definition, scope);
1608
+ return fields !== void 0 && (params.fields = fields), params;
1610
1609
  }
1611
1610
 
1612
1611
  function baseStartParams(definition, scope) {
@@ -1623,10 +1622,6 @@ function baseStartParams(definition, scope) {
1623
1622
  };
1624
1623
  }
1625
1624
 
1626
- function readsSubjectHasInFlightInstance(groq) {
1627
- return conditionParameterNames(groq).has(SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR);
1628
- }
1629
-
1630
1625
  function startSubject(definition, fields) {
1631
1626
  const subjectEntry = (definition.fields ?? []).find(isSubjectEntry);
1632
1627
  if (subjectEntry === void 0) return;
@@ -1634,15 +1629,12 @@ function startSubject(definition, fields) {
1634
1629
  return isGdr(value) ? value.id : void 0;
1635
1630
  }
1636
1631
 
1637
- function subjectHasInFlightInstance(dataset, subject) {
1632
+ function sliceHasInFlightSubject(args) {
1633
+ const {dataset: dataset, subject: subject, definition: definition} = args;
1638
1634
  return dataset.some(row => {
1639
1635
  if (typeof row != "object" || row === null) return !1;
1640
1636
  const instance = row;
1641
- return instance._type !== WORKFLOW_INSTANCE_TYPE || instance.completedAt !== void 0 || !Array.isArray(instance.fields) ? !1 : instance.fields.some(entry => {
1642
- if (typeof entry != "object" || entry === null) return !1;
1643
- const field = entry;
1644
- return field._type === "subject" && isGdr(field.value) && field.value.id === subject;
1645
- });
1637
+ return instance.completedAt != null || instance.definition !== definition ? !1 : instance.subject === subject;
1646
1638
  });
1647
1639
  }
1648
1640
 
@@ -1672,11 +1664,11 @@ function startFilterSyntaxIssues(groq) {
1672
1664
  });
1673
1665
  }
1674
1666
 
1675
- function startAllowedSyntaxIssues(groq) {
1667
+ function startRequirementSyntaxIssues(groq) {
1676
1668
  return startContextSyntaxIssues({
1677
1669
  groq: groq,
1678
- vars: START_ALLOWED_VARS,
1679
- context: "start-allowed"
1670
+ vars: START_REQUIREMENT_VARS,
1671
+ context: "start-requirement"
1680
1672
  });
1681
1673
  }
1682
1674
 
@@ -1684,7 +1676,7 @@ function startContextSyntaxIssues({groq: groq, vars: vars, context: context, ski
1684
1676
  try {
1685
1677
  parse(groq);
1686
1678
  } catch (err) {
1687
- return [ err instanceof Error ? err.message : String(err) ];
1679
+ return [ errorMessage(err) ];
1688
1680
  }
1689
1681
  const bound = new Set(vars.map(v2 => v2.name));
1690
1682
  return [ ...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(", "));
@@ -2523,24 +2515,24 @@ function applyFieldSet(op, ctx) {
2523
2515
  src: op.value,
2524
2516
  ctx: ctx,
2525
2517
  target: entrySlot(entry)
2526
- });
2527
- return validateFieldValue({
2518
+ }), validated = validateFieldValue({
2528
2519
  entryType: entry._type,
2529
2520
  entryName: entry.name,
2530
2521
  value: value,
2531
2522
  ...entryShape(entry)
2532
- }), assertRuntimeRefsWithinSurface({
2523
+ });
2524
+ return assertRuntimeRefsWithinSurface({
2533
2525
  entryType: entry._type,
2534
2526
  entryName: entry.name,
2535
- value: value,
2527
+ value: validated,
2536
2528
  ...entryShape(entry),
2537
2529
  ctx: ctx,
2538
2530
  src: op.value
2539
- }), setEntryValue(entry, value), {
2531
+ }), setEntryValue(entry, validated), {
2540
2532
  opType: op.type,
2541
2533
  target: op.target,
2542
2534
  resolved: {
2543
- value: value
2535
+ value: validated
2544
2536
  }
2545
2537
  };
2546
2538
  }
@@ -2601,27 +2593,27 @@ function applyFieldAppend(op, ctx) {
2601
2593
  ...slot !== void 0 ? {
2602
2594
  target: slot
2603
2595
  } : {}
2604
- });
2605
- validateFieldAppendItem({
2596
+ }), validated = validateFieldAppendItem({
2606
2597
  entryType: entry._type,
2607
2598
  entryName: entry.name,
2608
2599
  item: item,
2609
2600
  types: entry._type === "doc.refs" ? entry.types : void 0,
2610
2601
  of: entry._type === "array" ? entry.of : void 0
2611
- }), assertRuntimeRefsWithinSurface({
2602
+ });
2603
+ assertRuntimeRefsWithinSurface({
2612
2604
  entryType: entry._type === "doc.refs" ? "doc.ref" : "object",
2613
2605
  entryName: entry.name,
2614
- value: item,
2606
+ value: validated,
2615
2607
  fields: entry._type === "array" ? entry.of : void 0,
2616
2608
  ctx: ctx,
2617
2609
  src: op.value
2618
2610
  });
2619
2611
  const current = Array.isArray(entry.value) ? entry.value : [];
2620
- return setEntryValue(entry, [ ...current, withRowKey(item) ]), {
2612
+ return setEntryValue(entry, [ ...current, withRowKey(validated) ]), {
2621
2613
  opType: op.type,
2622
2614
  target: op.target,
2623
2615
  resolved: {
2624
- item: item
2616
+ item: validated
2625
2617
  }
2626
2618
  };
2627
2619
  }
@@ -2649,8 +2641,9 @@ async function applyFieldUpdateWhere(op, ctx) {
2649
2641
  mode: "value",
2650
2642
  issues: [ `field.updateWhere value must resolve to an object of fields to merge (target "${op.target.field}")` ]
2651
2643
  });
2644
+ const mergeRecord = merge;
2652
2645
  requireNoReservedMergeKeys({
2653
- merge: merge,
2646
+ merge: mergeRecord,
2654
2647
  entry: entry,
2655
2648
  op: op
2656
2649
  });
@@ -2660,25 +2653,27 @@ async function applyFieldUpdateWhere(op, ctx) {
2660
2653
  ctx: ctx
2661
2654
  }), next = rows.map((row, index) => matches[index] ? {
2662
2655
  ...row,
2663
- ...merge
2664
- } : row);
2665
- return validateFieldValue({
2656
+ ...mergeRecord
2657
+ } : row), validated = validateFieldValue({
2666
2658
  entryType: entry._type,
2667
2659
  entryName: entry.name,
2668
2660
  value: next,
2669
2661
  of: entry.of
2670
- }), assertRuntimeRefsWithinSurface({
2662
+ });
2663
+ assertRuntimeRefsWithinSurface({
2671
2664
  entryType: "object",
2672
2665
  entryName: entry.name,
2673
2666
  value: merge,
2674
2667
  fields: entry.of,
2675
2668
  ctx: ctx,
2676
2669
  src: op.value
2677
- }), setEntryValue(entry, next), {
2670
+ }), setEntryValue(entry, validated);
2671
+ const firstMatch = matches.indexOf(!0), persistedRows = validated, persistedMerge = firstMatch === -1 ? merge : Object.fromEntries(Object.keys(mergeRecord).map(key => [ key, persistedRows[firstMatch]?.[key] ]));
2672
+ return {
2678
2673
  opType: op.type,
2679
2674
  target: op.target,
2680
2675
  resolved: {
2681
- merge: merge
2676
+ merge: persistedMerge
2682
2677
  }
2683
2678
  };
2684
2679
  }
@@ -2870,8 +2865,8 @@ function validateDefinition(definition) {
2870
2865
  address: address
2871
2866
  }));
2872
2867
  }
2873
- definition.start?.filter !== void 0 && v2.report("start.filter", startFilterSyntaxIssues(definition.start.filter)),
2874
- definition.start?.allowed !== void 0 && v2.report("start.allowed", startAllowedSyntaxIssues(definition.start.allowed));
2868
+ definition.start?.filter !== void 0 && v2.report("start.filter", startFilterSyntaxIssues(definition.start.filter));
2869
+ for (const [index, requirement] of (definition.start?.requirements ?? []).entries()) requirement.type === "groq" && v2.report(`start.requirements[${index}].query`, startRequirementSyntaxIssues(requirement.query));
2875
2870
  for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
2876
2871
  for (const stage of definition.stages) validateStage(v2, stage);
2877
2872
  for (const issue of checkWorkflowInvariants(definition)) v2.errors.push(` · ${formatIssuePath(issue.path)}: ${issue.message}`);
@@ -3301,6 +3296,11 @@ function definitionsListGroq(versionOrder) {
3301
3296
  return `*[_type == "${WORKFLOW_DEFINITION_TYPE}" && ${tagScopeFilter()}] | order(name asc, version ${versionOrder})`;
3302
3297
  }
3303
3298
 
3299
+ function latestDefinitionsGroq() {
3300
+ const scoped = `_type == "${WORKFLOW_DEFINITION_TYPE}" && ${tagScopeFilter()}`;
3301
+ return `*[${scoped} && version == math::max(*[${scoped} && name == ^.name].version)] | order(name asc)`;
3302
+ }
3303
+
3304
3304
  function latestDeployedDefinitions(rows) {
3305
3305
  const byName = /* @__PURE__ */ new Map;
3306
3306
  for (const row of rows) {
@@ -3382,7 +3382,7 @@ function blockedState(input) {
3382
3382
  if (blocked !== void 0) return {
3383
3383
  state: "blocked",
3384
3384
  activity: blocked.activity.name,
3385
- requirements: blocked.unmetRequirements ?? [],
3385
+ requirements: (blocked.unmetRequirements ?? []).map(requirement => requirement.name),
3386
3386
  assignees: input.assignees[blocked.activity.name] ?? []
3387
3387
  };
3388
3388
  }
@@ -3425,7 +3425,7 @@ function diagnoseInstance(input) {
3425
3425
  };
3426
3426
  }
3427
3427
 
3428
- const RUNNABLE_VERBS = /* @__PURE__ */ new Set([ "set-stage", "abort", "drain-effects" ]);
3428
+ const RUNNABLE_VERBS = /* @__PURE__ */ new Set([ "set-stage", "abort", "drain-effects", "reset-activity" ]);
3429
3429
 
3430
3430
  function remediationsForCause(cause) {
3431
3431
  switch (cause.kind) {
@@ -3927,7 +3927,7 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3927
3927
  defaultValue: defaultValue
3928
3928
  });
3929
3929
  if (entry.initialValue?.type === "query") {
3930
- const issues = checkFieldValue({
3930
+ const check = parseFieldValue({
3931
3931
  entryType: entry.type,
3932
3932
  value: value,
3933
3933
  types: entry.types,
@@ -3936,9 +3936,9 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3936
3936
  options: entry.options,
3937
3937
  validation: entry.validation
3938
3938
  });
3939
- return issues !== void 0 ? (ctx.recordDiscard?.({
3939
+ return "issues" in check ? (ctx.recordDiscard?.({
3940
3940
  field: entry.name,
3941
- detail: issues.join("; ")
3941
+ detail: check.issues.join("; ")
3942
3942
  }), buildResolvedEntry({
3943
3943
  entry: entry,
3944
3944
  value: defaultValue,
@@ -3946,12 +3946,12 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3946
3946
  now: ctx.now
3947
3947
  })) : buildResolvedEntry({
3948
3948
  entry: entry,
3949
- value: value,
3949
+ value: check.output,
3950
3950
  _key: randomKey2(),
3951
3951
  now: ctx.now
3952
3952
  });
3953
3953
  }
3954
- return validateFieldValue({
3954
+ const validated = validateFieldValue({
3955
3955
  entryType: entry.type,
3956
3956
  entryName: entry.name,
3957
3957
  value: value,
@@ -3960,16 +3960,17 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
3960
3960
  of: entry.of,
3961
3961
  options: entry.options,
3962
3962
  validation: entry.validation
3963
- }), entry.initialValue?.type === "input" && ctx.inputProvenance !== "definition" && assertRefsWithinSurface({
3963
+ });
3964
+ return entry.initialValue?.type === "input" && ctx.inputProvenance !== "definition" && assertRefsWithinSurface({
3964
3965
  entryType: entry.type,
3965
3966
  entryName: entry.name,
3966
- value: value,
3967
+ value: validated,
3967
3968
  fields: entry.fields,
3968
3969
  of: entry.of,
3969
3970
  surface: ctx.refSurface
3970
3971
  }), buildResolvedEntry({
3971
3972
  entry: entry,
3972
- value: value,
3973
+ value: validated,
3973
3974
  _key: randomKey2(),
3974
3975
  now: ctx.now
3975
3976
  });
@@ -4001,12 +4002,27 @@ function assertInputValueShape(entry, value) {
4001
4002
  function assertGdrShape(value, context) {
4002
4003
  if (typeof value != "object" || value === null) throw new ContractViolationError(`Invalid GDR for ${context}: expected { id: "<scheme>:...", type: "<schema>" }, got ${typeof value}.`);
4003
4004
  const v2 = value;
4004
- if (typeof v2.id != "string" || !isGdrUri(v2.id)) throw new 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.`);
4005
+ if (typeof v2.id != "string") throw invalidGdrUriError(context, v2.id);
4006
+ try {
4007
+ parseGdr(v2.id);
4008
+ } catch (error) {
4009
+ throw error instanceof VersionSpecificDatasetGdrError ? new ContractViolationError(`Invalid GDR for ${context}: ${error.message}`) : invalidGdrUriError(context, v2.id);
4010
+ }
4005
4011
  if (typeof v2.type != "string" || v2.type.length === 0) throw new ContractViolationError(`Invalid GDR for ${context}: \`type\` (schema name) must be a non-empty string. Got ${JSON.stringify(v2.type)}.`);
4006
4012
  }
4007
4013
 
4014
+ function invalidGdrUriError(context, id) {
4015
+ return new 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.`);
4016
+ }
4017
+
4008
4018
  function normalizeQueryResult({entryType: entryType, raw: raw, workflowResource: workflowResource}) {
4009
- return raw == null ? raw : isSingleDocRefKind(entryType) ? coerceToGdr(raw, workflowResource) : entryType === "doc.refs" ? Array.isArray(raw) ? raw.map(item => coerceToGdr(item, workflowResource)).filter(v2 => v2 !== null) : [] : raw;
4019
+ if (raw == null) return raw;
4020
+ try {
4021
+ return isSingleDocRefKind(entryType) ? coerceToGdr(raw, workflowResource) : entryType === "doc.refs" ? Array.isArray(raw) ? raw.map(item => coerceToGdr(item, workflowResource)).filter(v2 => v2 !== null) : [] : raw;
4022
+ } catch (error) {
4023
+ if (error instanceof VersionSpecificDatasetGdrError) return raw;
4024
+ throw error;
4025
+ }
4010
4026
  }
4011
4027
 
4012
4028
  function coerceGdrShape(raw, workflowResource) {
@@ -4348,219 +4364,706 @@ function parseInstanceDocument(doc) {
4348
4364
  });
4349
4365
  }
4350
4366
 
4351
- async function getInstanceDocument(client, instanceId) {
4352
- const doc = await client.getDocument(instanceId);
4353
- return doc ? readInstanceDoc(doc) : void 0;
4354
- }
4367
+ const SYNC_COMMIT = {
4368
+ visibility: "sync"
4369
+ }, ENGINE_API_VERSION = "2026-04-29", REQUEST_TAG = {
4370
+ engine: "workflow",
4371
+ deploy: "workflow.deploy",
4372
+ deleteDefinition: "workflow.delete-definition",
4373
+ start: "workflow.start",
4374
+ fireAction: "workflow.fire-action",
4375
+ editField: "workflow.edit-field",
4376
+ completeEffect: "workflow.complete-effect",
4377
+ commitEffectOps: "workflow.commit-effect-ops",
4378
+ tick: "workflow.tick",
4379
+ evaluate: "workflow.evaluate",
4380
+ evaluateStart: "workflow.evaluate-start",
4381
+ diagnose: "workflow.diagnose",
4382
+ availableActions: "workflow.available-actions",
4383
+ setStage: "workflow.set-stage",
4384
+ abort: "workflow.abort",
4385
+ resetActivity: "workflow.reset-activity",
4386
+ getInstance: "workflow.get-instance",
4387
+ children: "workflow.children",
4388
+ instancesForDocument: "workflow.instances-for-document",
4389
+ definitionsForDocument: "workflow.definitions-for-document",
4390
+ query: "workflow.query",
4391
+ discover: "workflow.discover",
4392
+ guardQuery: "workflow.guard.query",
4393
+ guardDeploy: "workflow.guard.deploy",
4394
+ guardRefresh: "workflow.guard.refresh",
4395
+ guardRetract: "workflow.guard.retract",
4396
+ effectList: "workflow.effect.list",
4397
+ effectFind: "workflow.effect.find",
4398
+ drain: "workflow.drain",
4399
+ effect: "workflow.effect",
4400
+ verifyDefinitions: "workflow.verify-definitions",
4401
+ accessResolveActor: "workflow.access.resolve-actor",
4402
+ accessGrants: "workflow.access.grants"
4403
+ }, RAW_PATCH = /* @__PURE__ */ Symbol("workflow-engine.raw-patch");
4355
4404
 
4356
- function readInstanceDoc(doc) {
4357
- return parseInstanceDocument(assertReadableModel(doc));
4405
+ function unwrapPatch(patch) {
4406
+ return typeof patch == "object" && patch !== null && RAW_PATCH in patch ? patch[RAW_PATCH] : patch;
4358
4407
  }
4359
4408
 
4360
- async function reload({client: client, instanceId: instanceId, tag: tag}) {
4361
- const doc = await getInstanceDocument(client, instanceId);
4362
- if (!doc) throw new InstanceNotFoundError({
4363
- instanceId: instanceId
4364
- });
4365
- if (doc.tag !== tag) throw new InstanceNotFoundError({
4366
- instanceId: instanceId,
4367
- detail: "not visible to this engine: tag mismatch"
4368
- });
4369
- return doc;
4370
- }
4409
+ const wrapperCache = /* @__PURE__ */ new WeakMap, effectHandlerClientCache = /* @__PURE__ */ new WeakMap, RAW_CLIENT = /* @__PURE__ */ Symbol("workflow-engine.raw-client");
4371
4410
 
4372
- function contextEntry(name, value) {
4373
- const _key = randomKey();
4374
- return typeof value == "string" ? {
4375
- _key: _key,
4376
- _type: "context.string",
4377
- name: name,
4378
- value: value
4379
- } : typeof value == "number" ? {
4380
- _key: _key,
4381
- _type: "context.number",
4382
- name: name,
4383
- value: value
4384
- } : typeof value == "boolean" ? {
4385
- _key: _key,
4386
- _type: "context.boolean",
4387
- name: name,
4388
- value: value
4389
- } : {
4390
- _key: _key,
4391
- _type: "context.ref",
4392
- name: name,
4393
- value: value
4394
- };
4411
+ function unwrapRequestTag(client) {
4412
+ let current = client;
4413
+ for (;RAW_CLIENT in current; ) current = current[RAW_CLIENT];
4414
+ return current;
4395
4415
  }
4396
4416
 
4397
- function contextJsonEntry(name, value) {
4398
- return {
4399
- _key: randomKey(),
4400
- _type: "context.json",
4401
- name: name,
4402
- value: serializeContextValue(name, value)
4403
- };
4417
+ function withRequestTag(client, fallback) {
4418
+ let byTag = wrapperCache.get(client);
4419
+ byTag === void 0 && (byTag = /* @__PURE__ */ new Map, wrapperCache.set(client, byTag));
4420
+ const cached = byTag.get(fallback);
4421
+ if (cached !== void 0) return cached;
4422
+ const wrapped = buildTaggedClient(client, fallback);
4423
+ return byTag.set(fallback, wrapped), wrapped;
4404
4424
  }
4405
4425
 
4406
- function serializeContextValue(name, value) {
4407
- let json;
4408
- try {
4409
- json = JSON.stringify(value);
4410
- } catch (err) {
4411
- rethrowWithContext(err, `context entry "${name}" holds unserializable JSON`);
4426
+ function effectHandlerClient(client) {
4427
+ const cached = effectHandlerClientCache.get(client);
4428
+ if (cached !== void 0) return cached;
4429
+ let tagged;
4430
+ if (client.withConfig === void 0) tagged = withRequestTag(client, REQUEST_TAG.effect); else {
4431
+ const prefix = clientRequestTagPrefix(client), effectTag = composeRequestTag(prefix, REQUEST_TAG.effect);
4432
+ tagged = client.withConfig({
4433
+ requestTagPrefix: effectTag
4434
+ });
4412
4435
  }
4413
- if (json === void 0) throw new Error(`context entry "${name}" holds a non-JSON value (undefined, a function, or a symbol)`);
4414
- return json;
4436
+ return effectHandlerClientCache.set(client, tagged), tagged;
4415
4437
  }
4416
4438
 
4417
- function buildInstanceBase(args) {
4418
- const {id: id, now: now, actor: actor, perspective: perspective, executionContext: executionContext} = args, entries = [ {
4419
- _key: randomKey(),
4420
- _type: "stageEntered",
4421
- at: now,
4422
- stage: args.initialStage,
4423
- ...actor !== void 0 ? {
4424
- actor: actor
4425
- } : {}
4426
- }, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries, body = {
4427
- _id: id,
4428
- _type: WORKFLOW_INSTANCE_TYPE,
4429
- _rev: "",
4430
- _createdAt: now,
4431
- _updatedAt: now,
4432
- tag: args.tag,
4433
- workflowResource: args.workflowResource,
4434
- definition: args.definitionName,
4435
- pinnedVersion: args.pinnedVersion,
4436
- ...args.pinnedContentHash !== void 0 ? {
4437
- pinnedContentHash: args.pinnedContentHash
4438
- } : {},
4439
- definitionSnapshot: JSON.stringify(args.definition),
4440
- fields: args.fields,
4441
- context: args.context,
4442
- ancestors: args.ancestors,
4443
- ...perspective !== void 0 ? {
4444
- perspective: perspective
4445
- } : {},
4446
- currentStage: args.initialStage,
4447
- stages: [],
4448
- subworkflows: [],
4449
- pendingEffects: [],
4450
- effectHistory: [],
4451
- history: history,
4452
- startedAt: now,
4453
- lastChangedAt: now
4454
- };
4455
- return {
4456
- ...body,
4457
- ...modelStampFor({
4458
- documentType: "instance",
4459
- document: body
4460
- })
4461
- };
4439
+ function effectHandlerResolver(resolver) {
4440
+ return mapResourceClientResolver(resolver, effectHandlerClient);
4462
4441
  }
4463
4442
 
4464
- async function hydrateSnapshot(args) {
4465
- const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set, loadInto = async (uri, perspective) => {
4466
- if (visited.has(uri)) return;
4467
- const held = overlay?.get(uri);
4468
- if (held !== void 0) {
4469
- loaded.push(held), visited.add(uri);
4470
- return;
4471
- }
4472
- const fetched = await loadByGdr({
4473
- defaultClient: client,
4474
- clientForGdr: clientForGdr,
4475
- defaultResource: instance.workflowResource,
4476
- uri: uri,
4477
- perspective: perspective
4478
- });
4479
- fetched && (loaded.push(fetched), visited.add(uri));
4443
+ function mapResourceClientResolver(resolver, mapClient) {
4444
+ if (resolver !== void 0) return parsed => {
4445
+ const client = resolver(parsed);
4446
+ return client === void 0 ? void 0 : mapClient(client);
4480
4447
  };
4481
- loaded.push({
4482
- doc: instance,
4483
- resource: instance.workflowResource
4484
- }), visited.add(selfGdr(instance));
4485
- for (const ref of collectWatchRefs(instance)) await loadInto(ref.id, readsRaw(ref) ? "raw" : instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE);
4486
- return buildSnapshot({
4487
- docs: loaded
4488
- });
4489
- }
4490
-
4491
- async function loadByGdr({defaultClient: defaultClient, clientForGdr: clientForGdr, defaultResource: defaultResource, uri: uri, perspective: perspective}) {
4492
- const parsed = tryParseGdr(uri);
4493
- if (parsed === void 0) {
4494
- const doc2 = await readDoc({
4495
- client: defaultClient,
4496
- id: uri,
4497
- perspective: perspective
4498
- });
4499
- return doc2 ? {
4500
- doc: doc2,
4501
- resource: defaultResource
4502
- } : null;
4503
- }
4504
- const routed = clientForGdr(parsed), doc = await readDoc({
4505
- client: routed,
4506
- id: parsed.documentId,
4507
- perspective: perspective
4508
- });
4509
- return doc ? {
4510
- doc: doc,
4511
- resource: resourceFromParsed(parsed)
4512
- } : null;
4513
4448
  }
4514
4449
 
4515
- async function readDoc({client: client, id: id, perspective: perspective}) {
4516
- if (perspective === "raw") {
4517
- const doc2 = await client.getDocument(id) ?? null;
4518
- return doc2 ? doc2._type === WORKFLOW_INSTANCE_TYPE ? readInstanceDoc(doc2) : assertReadableModel(doc2) : null;
4519
- }
4520
- const {query: query, params: params} = contentDocQuery(id);
4521
- return await client.fetch(query, params, {
4522
- perspective: perspective
4523
- }) ?? null;
4450
+ function isWorkflowsFamilyPrefix(prefix) {
4451
+ return prefix === void 0 ? !1 : prefix === "sanity.workflows" || prefix.startsWith("sanity.workflows.") || prefix === "sanity.workflows-mcp";
4524
4452
  }
4525
4453
 
4526
- function collectEntryDocUris(resolvedFieldEntries) {
4527
- return entryDocRefs(resolvedFieldEntries).map(ref => ref.id);
4454
+ function clientRequestTagPrefix(client) {
4455
+ const probe = client;
4456
+ return typeof probe.config == "function" ? probe.config().requestTagPrefix : void 0;
4528
4457
  }
4529
4458
 
4530
- function isEngineContext(ctx) {
4531
- return "client" in ctx;
4459
+ function composeRequestTag(prefix, tag) {
4460
+ if (prefix === void 0) return tag;
4461
+ const relative = isWorkflowsFamilyPrefix(prefix) ? stripWorkflowRoot(tag) : tag;
4462
+ return `${prefix}.${relative}`;
4532
4463
  }
4533
4464
 
4534
- function loadCallContext({client: client, instanceId: instanceId, options: options}) {
4535
- return loadContext({
4536
- client: client,
4537
- instanceId: instanceId,
4538
- options: {
4539
- clientForGdr: options.clientForGdr,
4540
- refSurface: options.refSurface,
4541
- ...options.actor ? {
4542
- actor: options.actor
4543
- } : {},
4544
- ...options.clock ? {
4545
- clock: options.clock
4546
- } : {},
4547
- ...options.executionContext ? {
4548
- executionContext: options.executionContext
4549
- } : {},
4550
- ...options.telemetry ? {
4551
- telemetry: options.telemetry
4552
- } : {}
4553
- }
4554
- });
4465
+ function stripWorkflowRoot(tag) {
4466
+ return tag.startsWith("workflow.") ? tag.slice(9) : tag;
4555
4467
  }
4556
4468
 
4557
- async function retryOnRevisionConflict(args) {
4558
- const {client: client, instanceId: instanceId, options: options, commit: commit, onExhausted: onExhausted} = args;
4559
- for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
4560
- const ctx = await loadCallContext({
4561
- client: client,
4562
- instanceId: instanceId,
4563
- options: options
4469
+ function buildTaggedClient(client, fallback) {
4470
+ const strip = isWorkflowsFamilyPrefix(clientRequestTagPrefix(unwrapRequestTag(client))), normalize = tag => strip ? stripWorkflowRoot(tag) : tag, stamp = options => options === void 0 ? {
4471
+ tag: normalize(fallback)
4472
+ } : options.tag === void 0 ? {
4473
+ ...options,
4474
+ tag: normalize(fallback)
4475
+ } : typeof options.tag == "string" && options.tag !== normalize(options.tag) ? {
4476
+ ...options,
4477
+ tag: normalize(options.tag)
4478
+ } : options, wrapPatch = patch => {
4479
+ let target = patch;
4480
+ const wrapped = {
4481
+ get [RAW_PATCH]() {
4482
+ return target;
4483
+ },
4484
+ set: props => (target = target.set(props), wrapped),
4485
+ setIfMissing: props => (target = target.setIfMissing(props), wrapped),
4486
+ unset: paths => (target = target.unset(paths), wrapped),
4487
+ ifRevisionId: rev => (target = target.ifRevisionId(rev), wrapped),
4488
+ commit: options => target.commit(stamp(options))
4489
+ };
4490
+ return wrapped;
4491
+ }, wrapTransaction = tx => {
4492
+ let target = tx;
4493
+ const wrapped = {
4494
+ create: doc => (target = target.create(doc), wrapped),
4495
+ patch: patch => (target = target.patch(unwrapPatch(patch)), wrapped),
4496
+ delete: id => (target = target.delete(id), wrapped),
4497
+ commit: options => target.commit(stamp(options))
4498
+ };
4499
+ return wrapped;
4500
+ }, overrides = {
4501
+ [RAW_CLIENT]: unwrapRequestTag(client),
4502
+ fetch: (query, params, options) => client.fetch(query, params, stamp(options)),
4503
+ getDocument: (id, options) => client.getDocument(id, stamp(options)),
4504
+ patch: documentId => wrapPatch(client.patch(documentId)),
4505
+ create: (doc, options) => client.create(doc, stamp(options)),
4506
+ transaction: () => wrapTransaction(client.transaction()),
4507
+ ...client.withConfig !== void 0 ? {
4508
+ withConfig: config => withRequestTag(client.withConfig(config), fallback)
4509
+ } : {},
4510
+ ...client.action !== void 0 ? {
4511
+ action: (action, options) => client.action(action, stamp(options))
4512
+ } : {},
4513
+ ...client.request !== void 0 ? {
4514
+ request: opts => client.request(stamp(opts))
4515
+ } : {}
4516
+ };
4517
+ return new Proxy(client, {
4518
+ has: (target, property) => Object.hasOwn(overrides, property) || property in target,
4519
+ get: (target, property) => {
4520
+ if (Object.hasOwn(overrides, property)) return Reflect.get(overrides, property, overrides);
4521
+ const value = Reflect.get(target, property, target);
4522
+ return typeof value == "function" ? value.bind(target) : value;
4523
+ }
4524
+ });
4525
+ }
4526
+
4527
+ const pinCache = /* @__PURE__ */ new WeakMap, enginePinned = /* @__PURE__ */ new WeakSet;
4528
+
4529
+ function pinApiVersion(client) {
4530
+ const raw = unwrapRequestTag(client);
4531
+ if (enginePinned.has(raw) || raw.withConfig === void 0) return client;
4532
+ let sibling = pinCache.get(raw);
4533
+ return sibling === void 0 && (sibling = raw.withConfig({
4534
+ apiVersion: ENGINE_API_VERSION
4535
+ }), pinCache.set(raw, sibling), enginePinned.add(unwrapRequestTag(sibling))), sibling;
4536
+ }
4537
+
4538
+ function taggedResolver(resolver, fallback) {
4539
+ return mapResourceClientResolver(resolver, client => withRequestTag(pinApiVersion(client), fallback));
4540
+ }
4541
+
4542
+ function taggedScope(args, tag) {
4543
+ const resourceClients = taggedResolver(args.resourceClients, tag);
4544
+ return {
4545
+ ...args,
4546
+ client: withRequestTag(pinApiVersion(args.client), tag),
4547
+ ...resourceClients !== void 0 ? {
4548
+ resourceClients: resourceClients
4549
+ } : {}
4550
+ };
4551
+ }
4552
+
4553
+ const ACTOR_KIND_SET = new Set(ACTOR_KINDS), resolutionCache = /* @__PURE__ */ new WeakMap, warnedInstances = /* @__PURE__ */ new WeakMap;
4554
+
4555
+ async function normalizeInstanceIdentities(args) {
4556
+ const {client: client, instance: instance} = args, pending = collectLegacyIds(instance);
4557
+ if (pending.size === 0) return instance;
4558
+ const resolved = await resolveLegacyIds({
4559
+ client: client,
4560
+ instance: instance,
4561
+ pending: pending
4562
+ }), unresolved = [ ...pending ].filter(id => !resolved.has(id));
4563
+ return unresolved.length > 0 ? (warnUnresolvedOnce({
4564
+ client: client,
4565
+ instance: instance,
4566
+ unresolved: unresolved
4567
+ }), instance) : rewriteIdentitySlots(instance, id => resolved.get(id));
4568
+ }
4569
+
4570
+ async function resolveLegacyIds(args) {
4571
+ const resolved = /* @__PURE__ */ new Map, directoryIds = [];
4572
+ for (const id of args.pending) {
4573
+ const {globalId: globalId} = classifyPrincipalId(id);
4574
+ globalId !== void 0 ? resolved.set(id, globalId) : directoryIds.push(id);
4575
+ }
4576
+ const projectId = anchorProjectId(args.instance.workflowResource);
4577
+ if (directoryIds.length === 0 || projectId === void 0) return resolved;
4578
+ const fromDirectory = await resolveThroughDirectory({
4579
+ client: args.client,
4580
+ projectId: projectId,
4581
+ ids: directoryIds
4582
+ });
4583
+ for (const [id, globalId] of fromDirectory) resolved.set(id, globalId);
4584
+ return resolved;
4585
+ }
4586
+
4587
+ function warnUnresolvedOnce(args) {
4588
+ const key = unwrapRequestTag(args.client);
4589
+ let warned = warnedInstances.get(key);
4590
+ warned === void 0 && (warned = /* @__PURE__ */ new Set, warnedInstances.set(key, warned)),
4591
+ !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.`));
4592
+ }
4593
+
4594
+ function anchorProjectId(resource) {
4595
+ if (resource.type === "dataset") return datasetResourceParts(resource.id).projectId;
4596
+ }
4597
+
4598
+ function collectLegacyIds(instance) {
4599
+ const found = /* @__PURE__ */ new Set;
4600
+ return rewriteIdentitySlots(instance, id => {
4601
+ classifyPrincipalId(id).namespace === "project" && found.add(id);
4602
+ }), found;
4603
+ }
4604
+
4605
+ function rewriteIdentitySlots(instance, visit) {
4606
+ const record = instance, fields = walkEntries(record.fields, visit), stages = walkStages(record.stages, visit);
4607
+ return fields === record.fields && stages === record.stages ? instance : {
4608
+ ...record,
4609
+ fields: fields,
4610
+ stages: stages
4611
+ };
4612
+ }
4613
+
4614
+ function walkStages(stages, visit) {
4615
+ return Array.isArray(stages) ? shareMap(stages, stage => {
4616
+ const walked = walkEntryRow({
4617
+ row: stage,
4618
+ visit: visit
4619
+ });
4620
+ if (walked === null || typeof walked != "object") return walked;
4621
+ const record = walked, activities = walkActivities(record.activities, visit);
4622
+ return activities === record.activities ? walked === stage ? stage : walked : {
4623
+ ...record,
4624
+ activities: activities
4625
+ };
4626
+ }) : stages;
4627
+ }
4628
+
4629
+ function walkActivities(activities, visit) {
4630
+ return Array.isArray(activities) ? shareMap(activities, activity => walkEntryRow({
4631
+ row: activity,
4632
+ visit: visit
4633
+ })) : activities;
4634
+ }
4635
+
4636
+ function walkEntryRow(args) {
4637
+ const {row: row, visit: visit} = args;
4638
+ if (row === null || typeof row != "object") return row;
4639
+ const record = row, patch = {}, fields = walkEntries(record.fields, visit);
4640
+ if (fields !== record.fields && (patch.fields = fields), typeof record.completedBy == "string") {
4641
+ const stamped = visit(record.completedBy) ?? record.completedBy;
4642
+ stamped !== record.completedBy && (patch.completedBy = stamped);
4643
+ }
4644
+ return Object.keys(patch).length === 0 ? row : {
4645
+ ...record,
4646
+ ...patch
4647
+ };
4648
+ }
4649
+
4650
+ function walkEntries(entries, visit) {
4651
+ return Array.isArray(entries) ? shareMap(entries, entry => {
4652
+ if (entry === null || typeof entry != "object") return entry;
4653
+ const record = entry, value = walkDeclaredValue({
4654
+ type: record._type,
4655
+ fields: record.fields,
4656
+ of: record.of,
4657
+ value: record.value,
4658
+ visit: visit
4659
+ });
4660
+ return value === record.value ? entry : {
4661
+ ...record,
4662
+ value: value
4663
+ };
4664
+ }) : entries;
4665
+ }
4666
+
4667
+ function collectDeclaredGlobalIds(args) {
4668
+ const found = /* @__PURE__ */ new Set;
4669
+ return walkDeclaredValue({
4670
+ type: args.type,
4671
+ fields: args.fields,
4672
+ of: args.of,
4673
+ value: args.value,
4674
+ visit: id => {
4675
+ classifyPrincipalId(id).namespace === "global" && found.add(id);
4676
+ }
4677
+ }), found;
4678
+ }
4679
+
4680
+ function rewriteDeclaredPrincipalIds(args) {
4681
+ return walkDeclaredValue({
4682
+ type: args.type,
4683
+ fields: args.fields,
4684
+ of: args.of,
4685
+ value: args.value,
4686
+ visit: id => args.map.get(id)
4687
+ });
4688
+ }
4689
+
4690
+ function walkDeclaredValue(args) {
4691
+ const {type: type, value: value, visit: visit} = args;
4692
+ return type === "actor" || type === "assignee" ? visitPrincipalObject(value, visit) : type === "assignees" ? Array.isArray(value) ? shareMap(value, item => visitPrincipalObject(item, visit)) : value : type === "object" ? walkDeclaredObject({
4693
+ value: value,
4694
+ shapes: args.fields,
4695
+ visit: visit
4696
+ }) : type === "array" && Array.isArray(value) ? shareMap(value, row => walkDeclaredObject({
4697
+ value: row,
4698
+ shapes: args.of,
4699
+ visit: visit
4700
+ })) : value;
4701
+ }
4702
+
4703
+ function walkDeclaredObject(args) {
4704
+ const {value: value, shapes: shapes, visit: visit} = args;
4705
+ if (value === null || typeof value != "object" || Array.isArray(value) || !Array.isArray(shapes)) return value;
4706
+ const record = value, patch = {};
4707
+ for (const shape of shapes) {
4708
+ const step = walkDeclaredProperty({
4709
+ record: record,
4710
+ shape: shape,
4711
+ visit: visit
4712
+ });
4713
+ step !== void 0 && (patch[step.name] = step.value);
4714
+ }
4715
+ return Object.keys(patch).length === 0 ? value : {
4716
+ ...record,
4717
+ ...patch
4718
+ };
4719
+ }
4720
+
4721
+ function walkDeclaredProperty(args) {
4722
+ const {record: record, shape: shape, visit: visit} = args;
4723
+ if (shape === null || typeof shape != "object") return;
4724
+ const name = shape.name;
4725
+ if (typeof name != "string" || !Object.hasOwn(record, name)) return;
4726
+ const walked = walkDeclaredValue({
4727
+ type: shape.type,
4728
+ fields: shape.fields,
4729
+ of: shape.of,
4730
+ value: record[name],
4731
+ visit: visit
4732
+ });
4733
+ return walked === record[name] ? void 0 : {
4734
+ name: name,
4735
+ value: walked
4736
+ };
4737
+ }
4738
+
4739
+ function visitPrincipalObject(value, visit) {
4740
+ if (value === null || typeof value != "object" || Array.isArray(value)) return value;
4741
+ const record = value;
4742
+ if (typeof record.id != "string" || record.type !== "user" && !(typeof record.kind == "string" && ACTOR_KIND_SET.has(record.kind))) return value;
4743
+ const stamped = visit(record.id) ?? record.id;
4744
+ return stamped === record.id ? value : {
4745
+ ...record,
4746
+ id: stamped
4747
+ };
4748
+ }
4749
+
4750
+ function shareMap(items, fn) {
4751
+ let changed = !1;
4752
+ const next = items.map(item => {
4753
+ const out = fn(item);
4754
+ return out !== item && (changed = !0), out;
4755
+ });
4756
+ return changed ? next : items;
4757
+ }
4758
+
4759
+ const SAFE_ID = /^[A-Za-z0-9_-]+$/;
4760
+
4761
+ async function resolveThroughDirectory(args) {
4762
+ const {client: client, projectId: projectId, ids: ids} = args, cache = cacheFor(unwrapRequestTag(client)), out = /* @__PURE__ */ new Map, safeMisses = ids.filter(id => {
4763
+ const hit = cache.get(id);
4764
+ return typeof hit == "string" && out.set(id, hit), hit === void 0;
4765
+ }).filter(id => SAFE_ID.test(id) && SAFE_ID.test(projectId));
4766
+ if (safeMisses.length === 0 || client.request === void 0) return out;
4767
+ try {
4768
+ const response = await client.request({
4769
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${safeMisses.map(encodeURIComponent).join(",")}`,
4770
+ tag: REQUEST_TAG.accessResolveActor
4771
+ });
4772
+ ingestDirectoryRows({
4773
+ response: response,
4774
+ requested: new Set(safeMisses),
4775
+ cache: cache,
4776
+ out: out
4777
+ });
4778
+ for (const id of safeMisses) out.has(id) || cache.set(id, null);
4779
+ } catch (err) {
4780
+ console.warn(`workflow: project-users directory lookup failed for project "${projectId}" — legacy principal ids stay unresolved this pass. Original error: ${errorMessage(err)}`);
4781
+ }
4782
+ return out;
4783
+ }
4784
+
4785
+ function cacheFor(client) {
4786
+ let cache = resolutionCache.get(client);
4787
+ return cache === void 0 && (cache = /* @__PURE__ */ new Map, resolutionCache.set(client, cache)),
4788
+ cache;
4789
+ }
4790
+
4791
+ function ingestDirectoryRows(args) {
4792
+ const records = Array.isArray(args.response) ? args.response : [ args.response ];
4793
+ for (const row of records) typeof row?.id != "string" || typeof row.sanityUserId != "string" || args.requested.has(row.id) && classifyPrincipalId(row.sanityUserId).namespace === "global" && (args.cache.set(row.id, row.sanityUserId),
4794
+ args.out.set(row.id, row.sanityUserId));
4795
+ }
4796
+
4797
+ async function getInstanceDocument(client, instanceId) {
4798
+ const doc = await client.getDocument(instanceId);
4799
+ if (doc) return normalizeInstanceIdentities({
4800
+ client: client,
4801
+ instance: readInstanceDoc(doc)
4802
+ });
4803
+ }
4804
+
4805
+ function readInstanceDoc(doc) {
4806
+ return parseInstanceDocument(assertReadableModel(doc));
4807
+ }
4808
+
4809
+ async function reload({client: client, instanceId: instanceId, tag: tag}) {
4810
+ const doc = await getInstanceDocument(client, instanceId);
4811
+ if (!doc) throw new InstanceNotFoundError({
4812
+ instanceId: instanceId
4813
+ });
4814
+ if (doc.tag !== tag) throw new InstanceNotFoundError({
4815
+ instanceId: instanceId,
4816
+ detail: "not visible to this engine: tag mismatch"
4817
+ });
4818
+ return doc;
4819
+ }
4820
+
4821
+ function contextEntry(name, value) {
4822
+ const _key = randomKey();
4823
+ return typeof value == "string" ? {
4824
+ _key: _key,
4825
+ _type: "context.string",
4826
+ name: name,
4827
+ value: value
4828
+ } : typeof value == "number" ? {
4829
+ _key: _key,
4830
+ _type: "context.number",
4831
+ name: name,
4832
+ value: value
4833
+ } : typeof value == "boolean" ? {
4834
+ _key: _key,
4835
+ _type: "context.boolean",
4836
+ name: name,
4837
+ value: value
4838
+ } : {
4839
+ _key: _key,
4840
+ _type: "context.ref",
4841
+ name: name,
4842
+ value: value
4843
+ };
4844
+ }
4845
+
4846
+ function contextJsonEntry(name, value) {
4847
+ return {
4848
+ _key: randomKey(),
4849
+ _type: "context.json",
4850
+ name: name,
4851
+ value: serializeContextValue(name, value)
4852
+ };
4853
+ }
4854
+
4855
+ function serializeContextValue(name, value) {
4856
+ let json;
4857
+ try {
4858
+ json = JSON.stringify(value);
4859
+ } catch (err) {
4860
+ rethrowWithContext(err, `context entry "${name}" holds unserializable JSON`);
4861
+ }
4862
+ if (json === void 0) throw new Error(`context entry "${name}" holds a non-JSON value (undefined, a function, or a symbol)`);
4863
+ return json;
4864
+ }
4865
+
4866
+ function buildInstanceBase(args) {
4867
+ const {id: id, now: now, actor: actor, perspective: perspective, executionContext: executionContext} = args, entries = [ {
4868
+ _key: randomKey(),
4869
+ _type: "stageEntered",
4870
+ at: now,
4871
+ stage: args.initialStage,
4872
+ ...actor !== void 0 ? {
4873
+ actor: actor
4874
+ } : {}
4875
+ }, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries, body = {
4876
+ _id: id,
4877
+ _type: WORKFLOW_INSTANCE_TYPE,
4878
+ _rev: "",
4879
+ _createdAt: now,
4880
+ _updatedAt: now,
4881
+ tag: args.tag,
4882
+ workflowResource: args.workflowResource,
4883
+ definition: args.definitionName,
4884
+ pinnedVersion: args.pinnedVersion,
4885
+ ...args.pinnedContentHash !== void 0 ? {
4886
+ pinnedContentHash: args.pinnedContentHash
4887
+ } : {},
4888
+ definitionSnapshot: JSON.stringify(args.definition),
4889
+ fields: args.fields,
4890
+ context: args.context,
4891
+ ancestors: args.ancestors,
4892
+ ...perspective !== void 0 ? {
4893
+ perspective: perspective
4894
+ } : {},
4895
+ currentStage: args.initialStage,
4896
+ stages: [],
4897
+ subworkflows: [],
4898
+ pendingEffects: [],
4899
+ effectHistory: [],
4900
+ history: history,
4901
+ startedAt: now,
4902
+ lastChangedAt: now
4903
+ };
4904
+ return {
4905
+ ...body,
4906
+ ...modelStampFor({
4907
+ documentType: "instance",
4908
+ document: body
4909
+ })
4910
+ };
4911
+ }
4912
+
4913
+ async function hydrateSnapshot(args) {
4914
+ const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set;
4915
+ loaded.push({
4916
+ doc: instance,
4917
+ resource: instance.workflowResource
4918
+ }), visited.add(selfGdr(instance));
4919
+ const {pending: pending, ordered: ordered} = planReads({
4920
+ client: client,
4921
+ clientForGdr: clientForGdr,
4922
+ instance: instance,
4923
+ overlay: overlay,
4924
+ visited: visited
4925
+ }), fetched = await readPending(pending);
4926
+ for (const entry of ordered) if (!isPendingRead(entry)) loaded.push(entry); else {
4927
+ const doc = fetched.get(entry);
4928
+ doc !== void 0 && loaded.push({
4929
+ doc: doc,
4930
+ resource: entry.resource
4931
+ });
4932
+ }
4933
+ return buildSnapshot({
4934
+ docs: loaded
4935
+ });
4936
+ }
4937
+
4938
+ function planReads(args) {
4939
+ const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay, visited: visited} = args, pending = [], ordered = [];
4940
+ for (const ref of collectWatchRefs(instance)) {
4941
+ if (visited.has(ref.id)) continue;
4942
+ visited.add(ref.id);
4943
+ const held = overlay?.get(ref.id);
4944
+ if (held !== void 0) ordered.push(held); else {
4945
+ const read = routeRead({
4946
+ defaultClient: client,
4947
+ clientForGdr: clientForGdr,
4948
+ defaultResource: instance.workflowResource,
4949
+ uri: ref.id,
4950
+ perspective: readsRaw(ref) ? "raw" : instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE
4951
+ });
4952
+ pending.push(read), ordered.push(read);
4953
+ }
4954
+ }
4955
+ return {
4956
+ pending: pending,
4957
+ ordered: ordered
4958
+ };
4959
+ }
4960
+
4961
+ function isPendingRead(entry) {
4962
+ return "client" in entry;
4963
+ }
4964
+
4965
+ const SNAPSHOT_READ_BATCH_SIZE = 100, SNAPSHOT_READ_CONCURRENCY = 4, snapshotDocsQuery = "*[_id in $ids]";
4966
+
4967
+ function routeRead({defaultClient: defaultClient, clientForGdr: clientForGdr, defaultResource: defaultResource, uri: uri, perspective: perspective}) {
4968
+ const parsed = tryParseGdr(uri);
4969
+ return parsed === void 0 ? {
4970
+ client: defaultClient,
4971
+ id: uri,
4972
+ perspective: perspective,
4973
+ resource: defaultResource
4974
+ } : {
4975
+ client: clientForGdr(parsed),
4976
+ id: parsed.documentId,
4977
+ perspective: perspective,
4978
+ resource: resourceFromParsed(parsed)
4979
+ };
4980
+ }
4981
+
4982
+ async function readPending(reads) {
4983
+ const batches = groupReads(reads).flatMap(group => chunks(group.reads, SNAPSHOT_READ_BATCH_SIZE).map(batch => ({
4984
+ group: group,
4985
+ batch: batch
4986
+ }))), results = [];
4987
+ for (const wave of chunks(batches, SNAPSHOT_READ_CONCURRENCY)) results.push(...await Promise.all(wave.map(readBatch)));
4988
+ return new Map(results.flat());
4989
+ }
4990
+
4991
+ async function readBatch(args) {
4992
+ const {group: group, batch: batch} = args, ids = batch.map(read => read.id), docs = await group.client.fetch(snapshotDocsQuery, {
4993
+ ids: ids
4994
+ }, {
4995
+ perspective: group.perspective
4996
+ }), byId = new Map(docs.map(doc => [ doc._id, validateRawDoc(doc, group.perspective) ]));
4997
+ return batch.flatMap(read => {
4998
+ const doc = byId.get(read.id);
4999
+ return doc === void 0 ? [] : [ [ read, doc ] ];
5000
+ });
5001
+ }
5002
+
5003
+ function validateRawDoc(doc, perspective) {
5004
+ return perspective !== "raw" ? doc : doc._type === WORKFLOW_INSTANCE_TYPE ? readInstanceDoc(doc) : assertReadableModel(doc);
5005
+ }
5006
+
5007
+ function groupReads(reads) {
5008
+ const byClient = /* @__PURE__ */ new Map;
5009
+ for (const read of reads) {
5010
+ let clientGroups = byClient.get(read.client);
5011
+ clientGroups === void 0 && (clientGroups = /* @__PURE__ */ new Map, byClient.set(read.client, clientGroups));
5012
+ const key = JSON.stringify(read.perspective);
5013
+ let group = clientGroups.get(key);
5014
+ group === void 0 && (group = {
5015
+ client: read.client,
5016
+ perspective: read.perspective,
5017
+ reads: []
5018
+ }, clientGroups.set(key, group)), group.reads.push(read);
5019
+ }
5020
+ return [ ...byClient.values() ].flatMap(groups => [ ...groups.values() ]);
5021
+ }
5022
+
5023
+ function chunks(values, size) {
5024
+ return Array.from({
5025
+ length: Math.ceil(values.length / size)
5026
+ }, (_, index) => values.slice(index * size, (index + 1) * size));
5027
+ }
5028
+
5029
+ function collectEntryDocUris(resolvedFieldEntries) {
5030
+ return entryDocRefs(resolvedFieldEntries).map(ref => ref.id);
5031
+ }
5032
+
5033
+ function isEngineContext(ctx) {
5034
+ return "client" in ctx;
5035
+ }
5036
+
5037
+ function loadCallContext({client: client, instanceId: instanceId, options: options}) {
5038
+ return loadContext({
5039
+ client: client,
5040
+ instanceId: instanceId,
5041
+ options: {
5042
+ clientForGdr: options.clientForGdr,
5043
+ refSurface: options.refSurface,
5044
+ ...options.actor ? {
5045
+ actor: options.actor
5046
+ } : {},
5047
+ ...options.clock ? {
5048
+ clock: options.clock
5049
+ } : {},
5050
+ ...options.executionContext ? {
5051
+ executionContext: options.executionContext
5052
+ } : {},
5053
+ ...options.telemetry ? {
5054
+ telemetry: options.telemetry
5055
+ } : {}
5056
+ }
5057
+ });
5058
+ }
5059
+
5060
+ async function retryOnRevisionConflict(args) {
5061
+ const {client: client, instanceId: instanceId, options: options, commit: commit, onExhausted: onExhausted} = args;
5062
+ for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
5063
+ const ctx = await loadCallContext({
5064
+ client: client,
5065
+ instanceId: instanceId,
5066
+ options: options
4564
5067
  });
4565
5068
  try {
4566
5069
  return await commit(ctx);
@@ -4576,29 +5079,31 @@ async function loadContext({client: client, instanceId: instanceId, options: opt
4576
5079
  if (!instance) throw new InstanceNotFoundError({
4577
5080
  instanceId: instanceId
4578
5081
  });
4579
- const definition = parseDefinitionSnapshot(instance), {clientForGdr: clientForGdr, refSurface: refSurface} = options, clock = options.clock ?? wallClock, snapshot = await hydrateSnapshot({
5082
+ return buildEngineContext({
4580
5083
  client: client,
4581
- clientForGdr: clientForGdr,
5084
+ clientForGdr: options.clientForGdr,
5085
+ refSurface: options.refSurface,
4582
5086
  instance: instance,
5087
+ definition: parseDefinitionSnapshot(instance),
5088
+ ...options.actor !== void 0 ? {
5089
+ actor: options.actor
5090
+ } : {},
5091
+ ...options.clock !== void 0 ? {
5092
+ clock: options.clock
5093
+ } : {},
5094
+ ...options.executionContext !== void 0 ? {
5095
+ executionContext: options.executionContext
5096
+ } : {},
5097
+ ...options.telemetry !== void 0 ? {
5098
+ telemetry: options.telemetry
5099
+ } : {},
4583
5100
  ...options.overlay !== void 0 ? {
4584
5101
  overlay: options.overlay
5102
+ } : {},
5103
+ ...options.settlingCohorts !== void 0 ? {
5104
+ settlingCohorts: options.settlingCohorts
4585
5105
  } : {}
4586
5106
  });
4587
- return {
4588
- client: client,
4589
- clientForGdr: clientForGdr,
4590
- refSurface: refSurface,
4591
- clock: clock,
4592
- now: clock(),
4593
- executionContext: resolveExecutionContext(options?.executionContext),
4594
- telemetry: resolveTelemetry(options.telemetry),
4595
- ...options?.actor !== void 0 ? {
4596
- actor: options.actor
4597
- } : {},
4598
- instance: instance,
4599
- definition: definition,
4600
- snapshot: snapshot
4601
- };
4602
5107
  }
4603
5108
 
4604
5109
  async function ctxConditionParams(ctx, opts) {
@@ -4636,6 +5141,9 @@ async function buildEngineContext(args) {
4636
5141
  now: clock(),
4637
5142
  executionContext: resolveExecutionContext(args.executionContext),
4638
5143
  telemetry: resolveTelemetry(args.telemetry),
5144
+ ...args.settlingCohorts !== void 0 ? {
5145
+ settlingCohorts: args.settlingCohorts
5146
+ } : {},
4639
5147
  ...args.actor !== void 0 ? {
4640
5148
  actor: args.actor
4641
5149
  } : {},
@@ -4644,7 +5152,10 @@ async function buildEngineContext(args) {
4644
5152
  snapshot: await hydrateSnapshot({
4645
5153
  client: client,
4646
5154
  clientForGdr: clientForGdr,
4647
- instance: instance
5155
+ instance: instance,
5156
+ ...args.overlay !== void 0 ? {
5157
+ overlay: args.overlay
5158
+ } : {}
4648
5159
  })
4649
5160
  };
4650
5161
  }
@@ -4767,10 +5278,6 @@ function recordProcessedRequest({mutation: mutation, record: record, now: now})
4767
5278
  } ];
4768
5279
  }
4769
5280
 
4770
- const SYNC_COMMIT = {
4771
- visibility: "sync"
4772
- }, ENGINE_API_VERSION = "2026-04-29";
4773
-
4774
5281
  function resolveGuardRead(args) {
4775
5282
  const {expr: expr, ctx: ctx, deref: deref} = args;
4776
5283
  if (expr === "$self") return selfGdr(ctx.instance);
@@ -4793,63 +5300,342 @@ function resolveGuardRead(args) {
4793
5300
  throw new Error(`Guard read "${expr}" is not a supported deploy-time value — use "$self", "$now", "$fields.<name>[.path]", or "$effects['<name>'][.path]" (guards store resolved values; the lake cannot see $fields or $effects)`);
4794
5301
  }
4795
5302
 
4796
- function resolveIdRefTarget(expr, ctx) {
4797
- const value = resolveGuardRead({
4798
- expr: expr,
4799
- ctx: ctx,
4800
- deref: !1
4801
- });
4802
- if (typeof value == "string" && isGdrUri(value)) return {
4803
- parsed: parseGdr(value)
4804
- };
4805
- if (value && typeof value == "object") {
4806
- const v2 = value;
4807
- if (typeof v2.id == "string" && isGdrUri(v2.id)) return typeof v2.type == "string" ? {
4808
- parsed: parseGdr(v2.id),
4809
- type: v2.type
4810
- } : {
4811
- parsed: parseGdr(v2.id)
4812
- };
5303
+ function resolveIdRefTarget(expr, ctx) {
5304
+ const value = resolveGuardRead({
5305
+ expr: expr,
5306
+ ctx: ctx,
5307
+ deref: !1
5308
+ });
5309
+ if (typeof value == "string" && isGdrUri(value)) return {
5310
+ parsed: parseGdr(value)
5311
+ };
5312
+ if (value && typeof value == "object") {
5313
+ const v2 = value;
5314
+ if (typeof v2.id == "string" && isGdrUri(v2.id)) return typeof v2.type == "string" ? {
5315
+ parsed: parseGdr(v2.id),
5316
+ type: v2.type
5317
+ } : {
5318
+ parsed: parseGdr(v2.id)
5319
+ };
5320
+ }
5321
+ return null;
5322
+ }
5323
+
5324
+ function resolveIdRefTargets(idRefs, ctx) {
5325
+ const targets = [];
5326
+ for (const expr of idRefs ?? []) {
5327
+ const target = resolveIdRefTarget(expr, ctx);
5328
+ if (target === null) return null;
5329
+ targets.push(target);
5330
+ }
5331
+ return targets.length === 0 ? null : targets;
5332
+ }
5333
+
5334
+ function assertSingleResource(targets) {
5335
+ const resource = resourceFromParsed(targets[0].parsed);
5336
+ for (const g of targets) {
5337
+ const r = resourceFromParsed(g.parsed);
5338
+ 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.`);
5339
+ }
5340
+ return resource;
5341
+ }
5342
+
5343
+ function bareIdRefs(targets) {
5344
+ return targets.flatMap(g => [ g.parsed.documentId, `drafts.${g.parsed.documentId}` ]);
5345
+ }
5346
+
5347
+ function resolveMatchTypes(targets, authorTypes) {
5348
+ if (authorTypes !== void 0) return authorTypes;
5349
+ const inferred = [ ...new Set(targets.map(g => g.type).filter(t => !!t)) ];
5350
+ return inferred.length > 0 ? inferred : void 0;
5351
+ }
5352
+
5353
+ function resolveMetadata(metadata, ctx) {
5354
+ const out = {};
5355
+ for (const [k, expr] of Object.entries(metadata ?? {})) out[k] = resolveGuardRead({
5356
+ expr: expr,
5357
+ ctx: ctx,
5358
+ deref: !0
5359
+ });
5360
+ return out;
5361
+ }
5362
+
5363
+ function isRecord(value) {
5364
+ return typeof value == "object" && value !== null && !Array.isArray(value);
5365
+ }
5366
+
5367
+ function isClientProjectUser(value) {
5368
+ 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");
5369
+ }
5370
+
5371
+ function objectProperty(value, property) {
5372
+ if (!isRecord(value)) return;
5373
+ const propertyValue = value[property];
5374
+ return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
5375
+ }
5376
+
5377
+ function stringProperty(value, property) {
5378
+ const propertyValue = Reflect.get(value, property);
5379
+ return typeof propertyValue == "string" ? propertyValue : void 0;
5380
+ }
5381
+
5382
+ function apiErrorType(error) {
5383
+ const response = objectProperty(error, "response"), body = objectProperty(response, "body");
5384
+ if (!body) return;
5385
+ const nestedError = objectProperty(body, "error");
5386
+ return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
5387
+ }
5388
+
5389
+ function isProjectUserNotFoundError(error) {
5390
+ return apiErrorType(error) === "projectUserNotFoundError";
5391
+ }
5392
+
5393
+ const memberJoinCache = /* @__PURE__ */ new WeakMap;
5394
+
5395
+ function joinCacheFor(client) {
5396
+ let cache = memberJoinCache.get(client);
5397
+ return cache === void 0 && (cache = /* @__PURE__ */ new Map, memberJoinCache.set(client, cache)),
5398
+ cache;
5399
+ }
5400
+
5401
+ async function projectMemberIds(request, projectId) {
5402
+ const project = await request({
5403
+ uri: `/projects/${encodeURIComponent(projectId)}`
5404
+ }), members = isRecord(project) ? project.members : void 0;
5405
+ return Array.isArray(members) ? members.map(member => isRecord(member) ? member.id : void 0).filter(id => typeof id == "string") : [];
5406
+ }
5407
+
5408
+ async function resolveGlobalProjectUser(args) {
5409
+ const {client: client, request: request, projectId: projectId, id: id} = args, cache = joinCacheFor(client), key = `${projectId}:${id}`, hit = cache.get(key);
5410
+ if (hit !== void 0) return hit === null ? {
5411
+ status: "missing"
5412
+ } : {
5413
+ status: "resolved",
5414
+ user: hit
5415
+ };
5416
+ try {
5417
+ await ingestMemberJoin({
5418
+ request: request,
5419
+ projectId: projectId,
5420
+ cache: cache
5421
+ });
5422
+ const found = cache.get(key);
5423
+ return found != null ? {
5424
+ status: "resolved",
5425
+ user: found
5426
+ } : (cache.set(key, null), {
5427
+ status: "missing"
5428
+ });
5429
+ } catch (cause) {
5430
+ return {
5431
+ status: "inaccessible",
5432
+ cause: cause
5433
+ };
5434
+ }
5435
+ }
5436
+
5437
+ async function ingestMemberJoin(args) {
5438
+ const {request: request, projectId: projectId, cache: cache} = args, memberIds = await projectMemberIds(request, projectId);
5439
+ if (memberIds.length === 0) return;
5440
+ const response = await request({
5441
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${memberIds.map(encodeURIComponent).join(",")}`
5442
+ });
5443
+ for (const row of Array.isArray(response) ? response : [ response ]) isClientProjectUser(row) && typeof row.sanityUserId == "string" && cache.set(`${projectId}:${row.sanityUserId}`, row);
5444
+ }
5445
+
5446
+ function clientProjectUserDirectory(client, projectId) {
5447
+ return {
5448
+ findById: async id => {
5449
+ if (!client.request) return {
5450
+ status: "inaccessible",
5451
+ cause: new Error("Project-user resolution requires WorkflowClient.request")
5452
+ };
5453
+ if (classifyPrincipalId(id).namespace === "global") return resolveGlobalProjectUser({
5454
+ client: client,
5455
+ request: client.request,
5456
+ projectId: projectId,
5457
+ id: id
5458
+ });
5459
+ try {
5460
+ const response = await client.request({
5461
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
5462
+ }), candidate = Array.isArray(response) ? response[0] : response;
5463
+ return candidate == null ? {
5464
+ status: "missing"
5465
+ } : isClientProjectUser(candidate) ? {
5466
+ status: "resolved",
5467
+ user: candidate
5468
+ } : {
5469
+ status: "inaccessible",
5470
+ cause: new Error("Project-user response had an invalid shape")
5471
+ };
5472
+ } catch (cause) {
5473
+ return isProjectUserNotFoundError(cause) ? {
5474
+ status: "missing"
5475
+ } : {
5476
+ status: "inaccessible",
5477
+ cause: cause
5478
+ };
5479
+ }
5480
+ }
5481
+ };
5482
+ }
5483
+
5484
+ function resolveClientActor(client, args) {
5485
+ return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
5486
+ }
5487
+
5488
+ async function resolveActor(directory, actor) {
5489
+ if (actor.kind !== "person") return {
5490
+ status: "not-person",
5491
+ actor: actor
5492
+ };
5493
+ const personActor = {
5494
+ ...actor,
5495
+ kind: "person"
5496
+ }, lookup = await directory.findById(personActor.id);
5497
+ return lookup.status === "resolved" ? {
5498
+ status: "resolved",
5499
+ actor: personActor,
5500
+ user: lookup.user
5501
+ } : lookup.status === "inaccessible" ? {
5502
+ status: "inaccessible",
5503
+ actor: personActor,
5504
+ ...lookup.cause === void 0 ? {} : {
5505
+ cause: lookup.cause
5506
+ }
5507
+ } : {
5508
+ status: "missing",
5509
+ actor: personActor
5510
+ };
5511
+ }
5512
+
5513
+ const IDENTITY_KINDS = /* @__PURE__ */ new Set([ "actor", "assignee", "assignees" ]);
5514
+
5515
+ async function localizeGuardMetadata(args) {
5516
+ const {doc: doc, guard: guard, client: client, instance: instance, definition: definition} = args;
5517
+ if (doc.resourceType !== "dataset" || client.request === void 0) return doc;
5518
+ const request = client.request, {projectId: projectId} = datasetResourceParts(doc.resourceId), shapes = metadataShapes({
5519
+ guard: guard,
5520
+ instance: instance,
5521
+ definition: definition
5522
+ }), globals = collectMetadataGlobals({
5523
+ metadata: doc.metadata,
5524
+ shapes: shapes
5525
+ });
5526
+ if (globals.size === 0) return doc;
5527
+ const map = /* @__PURE__ */ new Map;
5528
+ for (const id of globals) {
5529
+ const lookup = await resolveGlobalProjectUser({
5530
+ client: client,
5531
+ request: request,
5532
+ projectId: projectId,
5533
+ id: id
5534
+ });
5535
+ lookup.status === "resolved" && map.set(id, lookup.user.id);
4813
5536
  }
4814
- return null;
5537
+ return map.size === 0 ? doc : {
5538
+ ...doc,
5539
+ metadata: rewriteMetadata({
5540
+ metadata: doc.metadata,
5541
+ shapes: shapes,
5542
+ map: map
5543
+ })
5544
+ };
4815
5545
  }
4816
5546
 
4817
- function resolveIdRefTargets(idRefs, ctx) {
4818
- const targets = [];
4819
- for (const expr of idRefs ?? []) {
4820
- const target = resolveIdRefTarget(expr, ctx);
4821
- if (target === null) return null;
4822
- targets.push(target);
5547
+ function metadataShapes(args) {
5548
+ const out = /* @__PURE__ */ new Map;
5549
+ for (const [key, expr] of Object.entries(args.guard.metadata ?? {})) {
5550
+ const shape = shapeOfRead({
5551
+ expr: expr,
5552
+ instance: args.instance,
5553
+ definition: args.definition
5554
+ });
5555
+ shape !== void 0 && out.set(key, shape);
4823
5556
  }
4824
- return targets.length === 0 ? null : targets;
5557
+ return out;
4825
5558
  }
4826
5559
 
4827
- function assertSingleResource(targets) {
4828
- const resource = resourceFromParsed(targets[0].parsed);
4829
- for (const g of targets) {
4830
- const r = resourceFromParsed(g.parsed);
4831
- 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.`);
4832
- }
4833
- return resource;
5560
+ function shapeOfRead(args) {
5561
+ const fieldRead = FIELD_READ.exec(args.expr);
5562
+ if (fieldRead !== null) return shapeOfFieldRead(args.instance, fieldRead);
5563
+ const effectsRead = EFFECTS_READ.exec(args.expr);
5564
+ if (effectsRead !== null) return shapeOfEffectsRead(args.definition, effectsRead);
4834
5565
  }
4835
5566
 
4836
- function bareIdRefs(targets) {
4837
- return targets.flatMap(g => [ g.parsed.documentId, `drafts.${g.parsed.documentId}` ]);
5567
+ function shapeOfFieldRead(instance, fieldRead) {
5568
+ const entry = (instance.fields ?? []).find(s => s.name === fieldRead[1]);
5569
+ if (entry !== void 0) return narrowByPath({
5570
+ type: entry._type,
5571
+ fields: Reflect.get(entry, "fields"),
5572
+ of: Reflect.get(entry, "of")
5573
+ }, fieldRead[2]);
4838
5574
  }
4839
5575
 
4840
- function resolveMatchTypes(targets, authorTypes) {
4841
- if (authorTypes !== void 0) return authorTypes;
4842
- const inferred = [ ...new Set(targets.map(g => g.type).filter(t => !!t)) ];
4843
- return inferred.length > 0 ? inferred : void 0;
5576
+ function shapeOfEffectsRead(definition, effectsRead) {
5577
+ const outputs = findEffect(definition, effectsRead[1])?.outputs ?? [], path = effectsRead[2];
5578
+ if (path === void 0) return;
5579
+ const [head, ...rest] = path.split("."), output = outputs.find(shape => shape.name === head);
5580
+ if (output !== void 0) return narrowByPath({
5581
+ type: output.type,
5582
+ fields: output.fields,
5583
+ of: output.of
5584
+ }, rest.length > 0 ? rest.join(".") : void 0);
4844
5585
  }
4845
5586
 
4846
- function resolveMetadata(metadata, ctx) {
4847
- const out = {};
4848
- for (const [k, expr] of Object.entries(metadata ?? {})) out[k] = resolveGuardRead({
4849
- expr: expr,
4850
- ctx: ctx,
4851
- deref: !0
4852
- });
5587
+ function narrowByPath(shape, path) {
5588
+ if (path === void 0) return shape;
5589
+ if (typeof shape.type != "string" || !IDENTITY_KINDS.has(shape.type)) return;
5590
+ const steps = identityPathSteps(shape.type, path);
5591
+ if (steps === void 0) return;
5592
+ const itemType = shape.type === "assignees" ? "assignee" : shape.type;
5593
+ if (steps.length === 0) return {
5594
+ type: itemType
5595
+ };
5596
+ if (steps.length === 1 && steps[0] === "id") return {
5597
+ type: itemType,
5598
+ idLeaf: !0
5599
+ };
5600
+ }
5601
+
5602
+ function identityPathSteps(kind, path) {
5603
+ const segments = path.split(".");
5604
+ return kind !== "assignees" ? segments : /^\d+$/.test(segments[0] ?? "") ? segments.slice(1) : void 0;
5605
+ }
5606
+
5607
+ function collectMetadataGlobals(args) {
5608
+ const found = /* @__PURE__ */ new Set;
5609
+ for (const [key, shape] of args.shapes) {
5610
+ const value = args.metadata[key];
5611
+ if (shape.idLeaf === !0) {
5612
+ typeof value == "string" && classifyPrincipalId(value).namespace === "global" && found.add(value);
5613
+ continue;
5614
+ }
5615
+ for (const id of collectDeclaredGlobalIds({
5616
+ ...shape,
5617
+ value: value
5618
+ })) found.add(id);
5619
+ }
5620
+ return found;
5621
+ }
5622
+
5623
+ function rewriteMetadata(args) {
5624
+ const out = {
5625
+ ...args.metadata
5626
+ };
5627
+ for (const [key, shape] of args.shapes) {
5628
+ const value = out[key];
5629
+ if (shape.idLeaf === !0) {
5630
+ typeof value == "string" && (out[key] = args.map.get(value) ?? value);
5631
+ continue;
5632
+ }
5633
+ out[key] = rewriteDeclaredPrincipalIds({
5634
+ ...shape,
5635
+ value: value,
5636
+ map: args.map
5637
+ });
5638
+ }
4853
5639
  return out;
4854
5640
  }
4855
5641
 
@@ -4885,151 +5671,6 @@ function parseGuardDocument(doc) {
4885
5671
  });
4886
5672
  }
4887
5673
 
4888
- const REQUEST_TAG = {
4889
- engine: "workflow",
4890
- deploy: "workflow.deploy",
4891
- deleteDefinition: "workflow.delete-definition",
4892
- start: "workflow.start",
4893
- fireAction: "workflow.fire-action",
4894
- editField: "workflow.edit-field",
4895
- completeEffect: "workflow.complete-effect",
4896
- commitEffectOps: "workflow.commit-effect-ops",
4897
- tick: "workflow.tick",
4898
- evaluate: "workflow.evaluate",
4899
- evaluateStart: "workflow.evaluate-start",
4900
- diagnose: "workflow.diagnose",
4901
- availableActions: "workflow.available-actions",
4902
- setStage: "workflow.set-stage",
4903
- abort: "workflow.abort",
4904
- getInstance: "workflow.get-instance",
4905
- children: "workflow.children",
4906
- instancesForDocument: "workflow.instances-for-document",
4907
- definitionsForDocument: "workflow.definitions-for-document",
4908
- query: "workflow.query",
4909
- discover: "workflow.discover",
4910
- guardQuery: "workflow.guard.query",
4911
- guardDeploy: "workflow.guard.deploy",
4912
- guardRefresh: "workflow.guard.refresh",
4913
- guardRetract: "workflow.guard.retract",
4914
- effectList: "workflow.effect.list",
4915
- effectFind: "workflow.effect.find",
4916
- drain: "workflow.drain",
4917
- effect: "workflow.effect",
4918
- verifyDefinitions: "workflow.verify-definitions",
4919
- accessResolveActor: "workflow.access.resolve-actor",
4920
- accessGrants: "workflow.access.grants"
4921
- }, RAW_PATCH = /* @__PURE__ */ Symbol("workflow-engine.raw-patch");
4922
-
4923
- function unwrapPatch(patch) {
4924
- return typeof patch == "object" && patch !== null && RAW_PATCH in patch ? patch[RAW_PATCH] : patch;
4925
- }
4926
-
4927
- const wrapperCache = /* @__PURE__ */ new WeakMap, RAW_CLIENT = /* @__PURE__ */ Symbol("workflow-engine.raw-client");
4928
-
4929
- function unwrapRequestTag(client) {
4930
- let current = client;
4931
- for (;RAW_CLIENT in current; ) current = current[RAW_CLIENT];
4932
- return current;
4933
- }
4934
-
4935
- function withRequestTag(client, fallback) {
4936
- let byTag = wrapperCache.get(client);
4937
- byTag === void 0 && (byTag = /* @__PURE__ */ new Map, wrapperCache.set(client, byTag));
4938
- const cached = byTag.get(fallback);
4939
- if (cached !== void 0) return cached;
4940
- const wrapped = buildTaggedClient(client, fallback);
4941
- return byTag.set(fallback, wrapped), wrapped;
4942
- }
4943
-
4944
- function isWorkflowsFamilyPrefix(prefix) {
4945
- return prefix === void 0 ? !1 : prefix === "sanity.workflows" || prefix.startsWith("sanity.workflows.") || prefix === "sanity.workflows-mcp";
4946
- }
4947
-
4948
- function clientRequestTagPrefix(client) {
4949
- const probe = client;
4950
- return typeof probe.config == "function" ? probe.config().requestTagPrefix : void 0;
4951
- }
4952
-
4953
- function buildTaggedClient(client, fallback) {
4954
- const strip = isWorkflowsFamilyPrefix(clientRequestTagPrefix(unwrapRequestTag(client))), normalize = tag => strip && tag.startsWith("workflow.") ? tag.slice(9) : tag, stamp = options => options === void 0 ? {
4955
- tag: normalize(fallback)
4956
- } : options.tag === void 0 ? {
4957
- ...options,
4958
- tag: normalize(fallback)
4959
- } : typeof options.tag == "string" && options.tag !== normalize(options.tag) ? {
4960
- ...options,
4961
- tag: normalize(options.tag)
4962
- } : options, wrapPatch = patch => {
4963
- let target = patch;
4964
- const wrapped = {
4965
- get [RAW_PATCH]() {
4966
- return target;
4967
- },
4968
- set: props => (target = target.set(props), wrapped),
4969
- setIfMissing: props => (target = target.setIfMissing(props), wrapped),
4970
- unset: paths => (target = target.unset(paths), wrapped),
4971
- ifRevisionId: rev => (target = target.ifRevisionId(rev), wrapped),
4972
- commit: options => target.commit(stamp(options))
4973
- };
4974
- return wrapped;
4975
- }, wrapTransaction = tx => {
4976
- let target = tx;
4977
- const wrapped = {
4978
- create: doc => (target = target.create(doc), wrapped),
4979
- patch: patch => (target = target.patch(unwrapPatch(patch)), wrapped),
4980
- delete: id => (target = target.delete(id), wrapped),
4981
- commit: options => target.commit(stamp(options))
4982
- };
4983
- return wrapped;
4984
- };
4985
- return {
4986
- [RAW_CLIENT]: unwrapRequestTag(client),
4987
- fetch: (query, params, options) => client.fetch(query, params, stamp(options)),
4988
- getDocument: (id, options) => client.getDocument(id, stamp(options)),
4989
- patch: documentId => wrapPatch(client.patch(documentId)),
4990
- create: (doc, options) => client.create(doc, stamp(options)),
4991
- transaction: () => wrapTransaction(client.transaction()),
4992
- ...client.withConfig !== void 0 ? {
4993
- withConfig: config => withRequestTag(client.withConfig(config), fallback)
4994
- } : {},
4995
- ...client.action !== void 0 ? {
4996
- action: (action, options) => client.action(action, stamp(options))
4997
- } : {},
4998
- ...client.request !== void 0 ? {
4999
- request: opts => client.request(stamp(opts))
5000
- } : {}
5001
- };
5002
- }
5003
-
5004
- const pinCache = /* @__PURE__ */ new WeakMap, enginePinned = /* @__PURE__ */ new WeakSet;
5005
-
5006
- function pinApiVersion(client) {
5007
- const raw = unwrapRequestTag(client);
5008
- if (enginePinned.has(raw) || raw.withConfig === void 0) return client;
5009
- let sibling = pinCache.get(raw);
5010
- return sibling === void 0 && (sibling = raw.withConfig({
5011
- apiVersion: ENGINE_API_VERSION
5012
- }), pinCache.set(raw, sibling), enginePinned.add(unwrapRequestTag(sibling))), sibling;
5013
- }
5014
-
5015
- function taggedResolver(resolver, fallback) {
5016
- if (resolver !== void 0) return parsed => {
5017
- const client = resolver(parsed);
5018
- return client === void 0 ? void 0 : withRequestTag(pinApiVersion(client), fallback);
5019
- };
5020
- }
5021
-
5022
- function taggedScope(args, tag) {
5023
- const resourceClients = taggedResolver(args.resourceClients, tag);
5024
- return {
5025
- ...args,
5026
- client: withRequestTag(pinApiVersion(args.client), tag),
5027
- ...resourceClients !== void 0 ? {
5028
- resourceClients: resourceClients
5029
- } : {}
5030
- };
5031
- }
5032
-
5033
5674
  async function fetchGuards(args) {
5034
5675
  return (await args.client.fetch(args.query, args.params, {
5035
5676
  tag: REQUEST_TAG.guardQuery
@@ -5046,16 +5687,20 @@ function guardsForResource(client) {
5046
5687
  });
5047
5688
  }
5048
5689
 
5049
- function instanceGuardQuery(instanceId) {
5690
+ function instancesGuardQuery(instanceIds) {
5050
5691
  return {
5051
- query: "*[_type == $guardType && sourceInstanceId == $instanceId]",
5692
+ query: "*[_type == $guardType && sourceInstanceId in $instanceIds] | order(_id asc)",
5052
5693
  params: {
5053
5694
  guardType: GUARD_DOC_TYPE,
5054
- instanceId: instanceId
5695
+ instanceIds: [ ...instanceIds ]
5055
5696
  }
5056
5697
  };
5057
5698
  }
5058
5699
 
5700
+ function instanceGuardQuery(instanceId) {
5701
+ return instancesGuardQuery([ instanceId ]);
5702
+ }
5703
+
5059
5704
  function verdictGuardsForInstance(client, instanceId) {
5060
5705
  const {query: query, params: params} = instanceGuardQuery(instanceId);
5061
5706
  return fetchGuards({
@@ -5157,7 +5802,7 @@ function dedupById(guards) {
5157
5802
  return [ ...new Map(guards.map(g => [ g._id, g ])).values() ].sort((a, b) => a._id.localeCompare(b._id));
5158
5803
  }
5159
5804
 
5160
- const GUARD_OWNER = "robot:workflow-engine";
5805
+ const GUARD_OWNER = "robot:workflow-engine", DELETE_CHUNK_SIZE = 200;
5161
5806
 
5162
5807
  function resolveGuardRoute(guard, ctx) {
5163
5808
  const targets = resolveIdRefTargets(guard.match.idRefs, ctx);
@@ -5210,10 +5855,9 @@ function resolveGuard({guard: guard, instance: instance, stageName: stageName, n
5210
5855
  };
5211
5856
  }
5212
5857
 
5213
- async function upsertGuard(client, doc) {
5214
- if (!await client.getDocument(doc._id, {
5215
- tag: REQUEST_TAG.guardDeploy
5216
- })) {
5858
+ async function upsertGuard(args) {
5859
+ const {client: client, doc: doc, exists: exists} = args;
5860
+ if (!exists) {
5217
5861
  await client.create(doc, {
5218
5862
  ...SYNC_COMMIT,
5219
5863
  tag: REQUEST_TAG.guardDeploy
@@ -5239,7 +5883,9 @@ function resolvedStageGuards(args) {
5239
5883
  });
5240
5884
  resolved !== null && out.push({
5241
5885
  client: args.clientForGdr(resolved.routeGdr),
5242
- doc: resolved.doc
5886
+ resourceKey: resourceGdr(resourceFromParsed(resolved.routeGdr)),
5887
+ value: resolved.doc,
5888
+ guard: guard
5243
5889
  });
5244
5890
  }
5245
5891
  return out;
@@ -5256,12 +5902,54 @@ function resolvedStageGuardRoutes(args) {
5256
5902
  const route = resolveGuardRoute(guard, ctx);
5257
5903
  route !== null && out.push({
5258
5904
  client: args.clientForGdr(route.routeGdr),
5259
- guardId: route.guardId
5905
+ resourceKey: resourceGdr(resourceFromParsed(route.routeGdr)),
5906
+ value: route.guardId
5260
5907
  });
5261
5908
  }
5262
5909
  return out;
5263
5910
  }
5264
5911
 
5912
+ function groupByResource(routed) {
5913
+ const groups = /* @__PURE__ */ new Map;
5914
+ for (const item of routed) {
5915
+ const existing = groups.get(item.resourceKey);
5916
+ if (existing !== void 0) {
5917
+ existing.values.push(item.value);
5918
+ continue;
5919
+ }
5920
+ groups.set(item.resourceKey, {
5921
+ client: item.client,
5922
+ resourceKey: item.resourceKey,
5923
+ values: [ item.value ]
5924
+ });
5925
+ }
5926
+ return [ ...groups.values() ];
5927
+ }
5928
+
5929
+ async function existingGuardIds(groups) {
5930
+ const entries = await Promise.all(groups.map(async ({client: client, resourceKey: resourceKey, values: values}) => {
5931
+ const ids = values.map(doc => doc._id), existing = await client.fetch("*[_id in $ids]._id", {
5932
+ ids: ids
5933
+ }, {
5934
+ tag: REQUEST_TAG.guardDeploy
5935
+ });
5936
+ return [ resourceKey, new Set(existing) ];
5937
+ }));
5938
+ return new Map(entries);
5939
+ }
5940
+
5941
+ async function observedGuards(groups) {
5942
+ const entries = await Promise.all(groups.map(async ({client: client, resourceKey: resourceKey, values: ids}) => {
5943
+ const guards = await client.fetch("*[_id in $ids]{_id, predicate, _rev}", {
5944
+ ids: ids
5945
+ }, {
5946
+ tag: REQUEST_TAG.guardRetract
5947
+ });
5948
+ return [ resourceKey, new Map(guards.map(guard => [ guard._id, guard ])) ];
5949
+ }));
5950
+ return new Map(entries);
5951
+ }
5952
+
5265
5953
  async function committedInstance(args) {
5266
5954
  return getInstanceDocument(args.client, args.instance._id);
5267
5955
  }
@@ -5269,10 +5957,22 @@ async function committedInstance(args) {
5269
5957
  async function deployStageGuards(args) {
5270
5958
  const live = await committedInstance(args);
5271
5959
  if (live === void 0 || live.currentStage !== args.stageName || live.abortedAt !== void 0) return;
5960
+ const routed = resolvedStageGuards(args), existingByResource = await existingGuardIds(groupByResource(routed));
5272
5961
  let deployed = 0;
5273
- for (const {client: client, doc: doc} of resolvedStageGuards(args)) {
5962
+ for (const {client: client, resourceKey: resourceKey, value: doc, guard: guard} of routed) {
5274
5963
  try {
5275
- await upsertGuard(client, doc);
5964
+ const localized = await localizeGuardMetadata({
5965
+ doc: doc,
5966
+ guard: guard,
5967
+ client: client,
5968
+ instance: args.instance,
5969
+ definition: args.definition
5970
+ });
5971
+ await upsertGuard({
5972
+ client: client,
5973
+ doc: localized,
5974
+ exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
5975
+ });
5276
5976
  } catch (cause) {
5277
5977
  throw deployed > 0 ? new PartialGuardDeployError({
5278
5978
  stageName: args.stageName,
@@ -5285,13 +5985,11 @@ async function deployStageGuards(args) {
5285
5985
  }
5286
5986
 
5287
5987
  async function retractStageGuards(args) {
5288
- const observed = [];
5289
- for (const {client: client, guardId: guardId} of resolvedStageGuardRoutes(args)) {
5290
- const guard = await client.getDocument(guardId, {
5291
- tag: REQUEST_TAG.guardRetract
5292
- });
5988
+ const observed = [], routed = resolvedStageGuardRoutes(args), guardsByResource = await observedGuards(groupByResource(routed));
5989
+ for (const {client: client, resourceKey: resourceKey, value: guardId} of routed) {
5990
+ const guard = guardsByResource.get(resourceKey)?.get(guardId);
5293
5991
  if (guard) {
5294
- if (guard._rev === void 0) throw new Error(`Cannot retract guard ${guardId}: persisted document has no revision`);
5992
+ if (guard._rev === void 0 || guard._rev === null) throw new Error(`Cannot retract guard ${guardId}: persisted document has no revision`);
5295
5993
  observed.push({
5296
5994
  client: client,
5297
5995
  guardId: guardId,
@@ -5322,10 +6020,11 @@ async function deleteOrphanedDefinitionGuards(args) {
5322
6020
  let count = 0;
5323
6021
  for (const {client: resourceClient, guards: guards} of perClient) {
5324
6022
  const own = guards.filter(guard => guard.sourceInstanceId.startsWith(ownPartitionPrefix));
5325
- if (own.length === 0) continue;
5326
- const tx = resourceClient.transaction();
5327
- for (const guard of own) tx.delete(guard._id);
5328
- await tx.commit(), count += own.length;
6023
+ if (own.length !== 0) for (let start = 0; start < own.length; start += DELETE_CHUNK_SIZE) {
6024
+ const chunk = own.slice(start, start + DELETE_CHUNK_SIZE), tx = resourceClient.transaction();
6025
+ for (const guard of chunk) tx.delete(guard._id);
6026
+ await tx.commit(), count += chunk.length;
6027
+ }
5329
6028
  }
5330
6029
  return count;
5331
6030
  }
@@ -5391,7 +6090,8 @@ async function commitAbort({ctx: ctx, reason: reason, requestRecord: requestReco
5391
6090
  ...actor !== void 0 ? {
5392
6091
  actor: actor
5393
6092
  } : {}
5394
- }), mutation.completedAt = at, mutation.abortedAt = at, await persist(ctx, mutation),
6093
+ }), mutation.completedAt = at, mutation.abortedAt = at;
6094
+ const instance = readInstanceDoc(await persist(ctx, mutation));
5395
6095
  await retractStageGuards({
5396
6096
  client: ctx.client,
5397
6097
  clientForGdr: ctx.clientForGdr,
@@ -5411,7 +6111,8 @@ async function commitAbort({ctx: ctx, reason: reason, requestRecord: requestReco
5411
6111
  });
5412
6112
  return {
5413
6113
  fired: !0,
5414
- stage: stage.name
6114
+ stage: stage.name,
6115
+ instance: instance
5415
6116
  };
5416
6117
  }
5417
6118
 
@@ -6162,18 +6863,42 @@ async function fetchGrants(args) {
6162
6863
  });
6163
6864
  }
6164
6865
 
6165
- async function advisoryCan({instance: instance, actor: actor, grants: grants}) {
6866
+ async function advisoryCanForCall(args) {
6867
+ const actor = args.options?.actor;
6868
+ if (!(args.options?.grants === void 0 || actor === void 0)) return advisoryCan({
6869
+ instance: args.instance,
6870
+ identity: lakePrincipalId({
6871
+ actor: actor,
6872
+ localPrincipalId: args.options.localPrincipalId
6873
+ }),
6874
+ grants: args.options.grants
6875
+ });
6876
+ }
6877
+
6878
+ async function advisoryCan({instance: instance, identity: identity, grants: grants}) {
6166
6879
  if (grants === void 0) return;
6167
6880
  const can = {};
6168
6881
  for (const permission of DOCUMENT_VALUE_PERMISSIONS) can[permission] = await grantsPermissionOn({
6169
6882
  document: instance,
6170
6883
  grants: grants,
6171
6884
  permission: permission,
6172
- userId: actor.id
6885
+ userId: identity
6173
6886
  });
6174
6887
  return can;
6175
6888
  }
6176
6889
 
6890
+ function requirementDescriptor(requirement) {
6891
+ return {
6892
+ name: requirement.name,
6893
+ ...requirement.title !== void 0 ? {
6894
+ title: requirement.title
6895
+ } : {},
6896
+ ...requirement.description !== void 0 ? {
6897
+ description: requirement.description
6898
+ } : {}
6899
+ };
6900
+ }
6901
+
6177
6902
  function subjectDenialLabels(denied) {
6178
6903
  return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
6179
6904
  }
@@ -6194,10 +6919,10 @@ class ActionDisabledError extends WorkflowError {
6194
6919
 
6195
6920
  class StartNotAllowedError extends WorkflowError {
6196
6921
  definition;
6197
- insight;
6922
+ unmetRequirements;
6198
6923
  constructor(args) {
6199
- 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."),
6200
- this.name = "StartNotAllowedError", this.definition = args.definition, this.insight = args.insight;
6924
+ super("start-not-allowed", `startInstance refused definition "${args.definition}": ${args.unmetRequirements.map(requirement => requirement.title ?? requirement.name).join(", ")}. Pre-flight the verdict with evaluateStart.`),
6925
+ this.name = "StartNotAllowedError", this.definition = args.definition, this.unmetRequirements = args.unmetRequirements;
6201
6926
  }
6202
6927
  }
6203
6928
 
@@ -6213,7 +6938,7 @@ const disabledReasonDetail = {
6213
6938
  "stage-terminal": r => `stage "${r.stage}" is terminal`,
6214
6939
  "instance-completed": r => `instance completed at ${r.completedAt}`,
6215
6940
  "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
6216
- "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`,
6941
+ "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
6217
6942
  "subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
6218
6943
  };
6219
6944
 
@@ -6282,11 +7007,10 @@ async function resolveActionCommit({ctx: ctx, activityName: activityName, action
6282
7007
  }
6283
7008
  });
6284
7009
  if (action.filter !== void 0) {
6285
- const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({
7010
+ const can = await advisoryCanForCall({
6286
7011
  instance: ctx.instance,
6287
- actor: actor,
6288
- grants: options.grants
6289
- }) : void 0;
7012
+ options: options
7013
+ });
6290
7014
  if (!await ctxEvaluateCondition({
6291
7015
  ctx: ctx,
6292
7016
  condition: action.filter,
@@ -6539,30 +7263,12 @@ async function fireTriggeredAction({ctx: ctx, mutation: mutation, activity: acti
6539
7263
  };
6540
7264
  }
6541
7265
 
6542
- async function primeInitialStage({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
6543
- const instance = await getInstanceDocument(client, instanceId);
7266
+ async function primeInitialStage(args) {
7267
+ const {client: client, instanceId: instanceId, refSurface: refSurface, clientForGdr: clientForGdr} = args, instance = await getInstanceDocument(client, instanceId);
6544
7268
  if (!instance || instance.stages.length > 0) return;
6545
- const definition = parseDefinitionSnapshot(instance), stage = definition.stages.find(s => s.name === instance.currentStage);
7269
+ const ctx = await buildCascadeStepContext(args, instance), {definition: definition} = ctx, stage = definition.stages.find(s => s.name === instance.currentStage);
6546
7270
  if (stage === void 0) return;
6547
- const ctx = await buildEngineContext({
6548
- client: client,
6549
- clientForGdr: clientForGdr,
6550
- refSurface: refSurface,
6551
- instance: instance,
6552
- definition: definition,
6553
- ...clock ? {
6554
- clock: clock
6555
- } : {},
6556
- ...actor ? {
6557
- actor: actor
6558
- } : {},
6559
- ...executionContext ? {
6560
- executionContext: executionContext
6561
- } : {},
6562
- ...telemetry ? {
6563
- telemetry: telemetry
6564
- } : {}
6565
- }), now = ctx.now, discards = [], initialStageEntry = {
7271
+ const now = ctx.now, discards = [], initialStageEntry = {
6566
7272
  _key: randomKey(),
6567
7273
  name: stage.name,
6568
7274
  enteredAt: now,
@@ -6631,29 +7337,37 @@ async function primeInitialStage({client: client, instanceId: instanceId, actor:
6631
7337
 
6632
7338
  const CASCADE_LIMIT = 100;
6633
7339
 
6634
- async function runCascadeHop({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
6635
- const ctx = await loadContext({
7340
+ 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}) {
7341
+ const contextOptions = {
7342
+ clientForGdr: clientForGdr,
7343
+ refSurface: refSurface,
7344
+ ...actor ? {
7345
+ actor: actor
7346
+ } : {},
7347
+ ...clock ? {
7348
+ clock: clock
7349
+ } : {},
7350
+ ...executionContext ? {
7351
+ executionContext: executionContext
7352
+ } : {},
7353
+ ...telemetry ? {
7354
+ telemetry: telemetry
7355
+ } : {},
7356
+ ...overlay ? {
7357
+ overlay: overlay
7358
+ } : {},
7359
+ ...settlingCohorts ? {
7360
+ settlingCohorts: settlingCohorts
7361
+ } : {}
7362
+ }, ctx = instance === void 0 ? await loadContext({
6636
7363
  client: client,
6637
7364
  instanceId: instanceId,
6638
- options: {
6639
- clientForGdr: clientForGdr,
6640
- refSurface: refSurface,
6641
- ...actor ? {
6642
- actor: actor
6643
- } : {},
6644
- ...clock ? {
6645
- clock: clock
6646
- } : {},
6647
- ...executionContext ? {
6648
- executionContext: executionContext
6649
- } : {},
6650
- ...telemetry ? {
6651
- telemetry: telemetry
6652
- } : {},
6653
- ...overlay ? {
6654
- overlay: overlay
6655
- } : {}
6656
- }
7365
+ options: contextOptions
7366
+ }) : await buildEngineContext({
7367
+ client: client,
7368
+ instance: instance,
7369
+ definition: parseDefinitionSnapshot(instance),
7370
+ ...contextOptions
6657
7371
  });
6658
7372
  if (isTerminal(ctx)) return {
6659
7373
  moved: !1
@@ -6683,10 +7397,10 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
6683
7397
  });
6684
7398
  }
6685
7399
 
6686
- async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
7400
+ async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts}) {
6687
7401
  let count = 0;
6688
7402
  for (;;) {
6689
- if (await drainCondemnedChildren({
7403
+ const drained = await drainCondemnedChildren({
6690
7404
  client: client,
6691
7405
  instanceId: instanceId,
6692
7406
  actor: actor,
@@ -6695,7 +7409,8 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6695
7409
  clock: clock,
6696
7410
  executionContext: executionContext,
6697
7411
  telemetry: telemetry
6698
- }), !(await runCascadeHop({
7412
+ });
7413
+ if (!(await runCascadeHop({
6699
7414
  client: client,
6700
7415
  instanceId: instanceId,
6701
7416
  actor: actor,
@@ -6704,7 +7419,11 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6704
7419
  clock: clock,
6705
7420
  executionContext: executionContext,
6706
7421
  telemetry: telemetry,
6707
- overlay: overlay
7422
+ overlay: overlay,
7423
+ settlingCohorts: settlingCohorts,
7424
+ ...drained.drained ? {} : {
7425
+ instance: drained.instance
7426
+ }
6708
7427
  })).moved) return count;
6709
7428
  if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
6710
7429
  instanceId: instanceId,
@@ -6713,39 +7432,50 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6713
7432
  }
6714
7433
  }
6715
7434
 
6716
- async function drainCondemnedChildren({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining = /* @__PURE__ */ new Set}) {
6717
- if (draining.has(instanceId)) return;
7435
+ 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}) {
7436
+ if (draining.has(instanceId)) return {
7437
+ instance: preloadedInstance,
7438
+ drained: !1
7439
+ };
6718
7440
  draining.add(instanceId);
6719
- const instance = await getInstanceDocument(client, instanceId);
6720
- if (!instance) return;
7441
+ const instance = preloadedInstance ?? await getInstanceDocument(client, instanceId);
7442
+ if (!instance) return {
7443
+ instance: void 0,
7444
+ drained: !1
7445
+ };
6721
7446
  const condemned = condemnedSubworkflows(instance);
6722
- if (condemned.length !== 0) {
6723
- for (const row of condemned) await settleCondemnedRow({
6724
- client: client,
6725
- ownerId: instanceId,
6726
- row: row,
6727
- actor: actor,
6728
- clientForGdr: clientForGdr,
6729
- refSurface: refSurface,
6730
- clock: clock,
6731
- executionContext: executionContext,
6732
- telemetry: telemetry,
6733
- draining: draining
6734
- });
6735
- await stampDrainedRows({
6736
- client: client,
6737
- instance: instance,
6738
- condemned: condemned,
6739
- clock: clock,
6740
- executionContext: executionContext
6741
- });
6742
- }
7447
+ if (condemned.length === 0) return {
7448
+ instance: instance,
7449
+ drained: !1
7450
+ };
7451
+ for (const row of condemned) await settleCondemnedRow({
7452
+ client: client,
7453
+ ownerId: instanceId,
7454
+ row: row,
7455
+ actor: actor,
7456
+ clientForGdr: clientForGdr,
7457
+ refSurface: refSurface,
7458
+ clock: clock,
7459
+ executionContext: executionContext,
7460
+ telemetry: telemetry,
7461
+ draining: draining
7462
+ });
7463
+ return await stampDrainedRows({
7464
+ client: client,
7465
+ instance: instance,
7466
+ condemned: condemned,
7467
+ clock: clock,
7468
+ executionContext: executionContext
7469
+ }), {
7470
+ instance: instance,
7471
+ drained: !0
7472
+ };
6743
7473
  }
6744
7474
 
6745
7475
  async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining}) {
6746
7476
  const childId = toBareId(row.ref.id);
6747
7477
  try {
6748
- await abortInstance({
7478
+ const result = await abortInstance({
6749
7479
  client: client,
6750
7480
  instanceId: childId,
6751
7481
  reason: row.abortPending?.reason ?? "condemned by parent",
@@ -6765,7 +7495,8 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6765
7495
  telemetry: telemetry
6766
7496
  } : {}
6767
7497
  }
6768
- }), await drainCondemnedChildren({
7498
+ });
7499
+ await drainCondemnedChildren({
6769
7500
  client: client,
6770
7501
  instanceId: childId,
6771
7502
  actor: actor,
@@ -6774,7 +7505,10 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6774
7505
  clock: clock,
6775
7506
  executionContext: executionContext,
6776
7507
  telemetry: telemetry,
6777
- draining: draining
7508
+ draining: draining,
7509
+ ...result.fired ? {
7510
+ instance: result.instance
7511
+ } : {}
6778
7512
  });
6779
7513
  } catch (cause) {
6780
7514
  if (cause instanceof InstanceNotFoundError) return;
@@ -6787,9 +7521,7 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6787
7521
  }
6788
7522
 
6789
7523
  async function stampDrainedRows({client: client, instance: instance, condemned: condemned, clock: clock, executionContext: executionContext}) {
6790
- const now = (clock ?? wallClock)(), stamp = resolveExecutionContext(executionContext), ids = condemned.map(row => toBareId(row.ref.id)), children = await client.fetch("*[_id in $ids]{_id, currentStage, completedAt, abortedAt, modelVersion, minReaderModel}", {
6791
- ids: ids
6792
- }), byId = new Map(children.map(c => [ assertReadableModel(c)._id, c ])), condemnedKeys = new Set(condemned.map(row => row._key)), history = [ ...instance.history ], subworkflows = (instance.subworkflows ?? []).map(row => {
7524
+ const now = (clock ?? wallClock)(), stamp = resolveExecutionContext(executionContext), ids = condemned.map(row => 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 => {
6793
7525
  if (row.resolved !== void 0 || !condemnedKeys.has(row._key)) return row;
6794
7526
  const child = byId.get(toBareId(row.ref.id)), resolved = child !== void 0 ? terminalResolution(child) : {
6795
7527
  at: now,
@@ -6811,6 +7543,16 @@ async function stampDrainedRows({client: client, instance: instance, condemned:
6811
7543
  }).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
6812
7544
  }
6813
7545
 
7546
+ async function loadInstancesById(client, ids) {
7547
+ const children = await client.fetch("*[_id in $ids]", {
7548
+ ids: ids
7549
+ });
7550
+ return new Map(children.filter(child => child._type === WORKFLOW_INSTANCE_TYPE).map(child => {
7551
+ const instance = readInstanceDoc(child);
7552
+ return [ instance._id, instance ];
7553
+ }));
7554
+ }
7555
+
6814
7556
  function terminalResolution(child) {
6815
7557
  const status = resolvedChildStatus(child);
6816
7558
  if (!(status === void 0 || child.completedAt === void 0 || child.completedAt === null)) return {
@@ -6833,28 +7575,18 @@ function subworkflowResolvedEntry({row: row, at: at, status: status}) {
6833
7575
  };
6834
7576
  }
6835
7577
 
6836
- async function propagateToAncestors({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
6837
- const loaded = await loadPropagationPair(client, instanceId);
6838
- if (loaded === void 0) return;
6839
- const {child: child, parent: parent} = loaded, definition = parseDefinitionSnapshot(parent), ctx = await buildEngineContext({
7578
+ async function propagateToAncestors(args) {
7579
+ const {instance: instance, ...stepArgs} = args, {client: client, instanceId: instanceId} = stepArgs, loaded = await loadPropagationPair({
6840
7580
  client: client,
6841
- clientForGdr: clientForGdr,
6842
- refSurface: refSurface,
6843
- instance: parent,
6844
- definition: definition,
6845
- ...clock ? {
6846
- clock: clock
6847
- } : {},
6848
- ...actor ? {
6849
- actor: actor
6850
- } : {},
6851
- ...executionContext ? {
6852
- executionContext: executionContext
6853
- } : {},
6854
- ...telemetry ? {
6855
- telemetry: telemetry
7581
+ instanceId: instanceId,
7582
+ ...instance !== void 0 ? {
7583
+ instance: instance
6856
7584
  } : {}
6857
- }), mutation = startMutation(parent), row = mutation.subworkflows.find(r => toBareId(r.ref.id) === child._id);
7585
+ });
7586
+ if (loaded === void 0) return;
7587
+ const {child: child, parent: parent} = loaded;
7588
+ if (stepArgs.settlingCohorts?.has(parent._id)) return;
7589
+ const ctx = await buildCascadeStepContext(stepArgs, parent), mutation = startMutation(parent), row = mutation.subworkflows.find(r => toBareId(r.ref.id) === child._id);
6858
7590
  if (row === void 0) {
6859
7591
  await recordOrphanedPropagation({
6860
7592
  ctx: ctx,
@@ -6869,29 +7601,11 @@ async function propagateToAncestors({client: client, instanceId: instanceId, act
6869
7601
  child: child,
6870
7602
  now: ctx.now
6871
7603
  }), parentTerminal = parent.completedAt !== void 0;
6872
- changed && await persist(ctx, mutation), !parentTerminal && (await cascadeAutoTransitions({
6873
- client: client,
6874
- instanceId: parent._id,
6875
- actor: actor,
6876
- clientForGdr: clientForGdr,
6877
- refSurface: refSurface,
6878
- clock: clock,
6879
- executionContext: executionContext,
6880
- telemetry: telemetry
6881
- }), await propagateToAncestors({
6882
- client: client,
6883
- instanceId: parent._id,
6884
- actor: actor,
6885
- clientForGdr: clientForGdr,
6886
- refSurface: refSurface,
6887
- clock: clock,
6888
- executionContext: executionContext,
6889
- telemetry: telemetry
6890
- }));
7604
+ changed && await persist(ctx, mutation), !parentTerminal && await cascadeAndPropagateParent(stepArgs, parent);
6891
7605
  }
6892
7606
 
6893
- async function loadPropagationPair(client, instanceId) {
6894
- const child = await getInstanceDocument(client, instanceId);
7607
+ async function loadPropagationPair({client: client, instanceId: instanceId, instance: instance}) {
7608
+ const child = instance ?? await getInstanceDocument(client, instanceId);
6895
7609
  if (!child) return;
6896
7610
  const parentGdr = parentRef(child);
6897
7611
  if (parentGdr === void 0) return;
@@ -6902,6 +7616,83 @@ async function loadPropagationPair(client, instanceId) {
6902
7616
  };
6903
7617
  }
6904
7618
 
7619
+ async function propagateSpawnBatch(args) {
7620
+ const {childIds: childIds, ...stepArgs} = args, {client: client, instanceId: instanceId} = stepArgs, parent = await getInstanceDocument(client, instanceId);
7621
+ if (parent === void 0) return;
7622
+ const children = await loadInstancesById(client, childIds);
7623
+ if (children.size !== childIds.length) throw new Error(`Spawn batch for ${instanceId} could not reload every child`);
7624
+ const ctx = await buildCascadeStepContext(stepArgs, parent), mutation = startMutation(parent);
7625
+ stampSpawnBatch({
7626
+ mutation: mutation,
7627
+ parent: parent,
7628
+ children: children,
7629
+ childIds: childIds,
7630
+ now: ctx.now
7631
+ }) && await persist(ctx, mutation), parent.completedAt === void 0 && await cascadeAndPropagateParent(stepArgs, parent);
7632
+ }
7633
+
7634
+ async function buildCascadeStepContext(args, instance) {
7635
+ return buildEngineContext({
7636
+ client: args.client,
7637
+ clientForGdr: args.clientForGdr,
7638
+ refSurface: args.refSurface,
7639
+ instance: instance,
7640
+ definition: parseDefinitionSnapshot(instance),
7641
+ ...args.clock ? {
7642
+ clock: args.clock
7643
+ } : {},
7644
+ ...args.actor ? {
7645
+ actor: args.actor
7646
+ } : {},
7647
+ ...args.executionContext ? {
7648
+ executionContext: args.executionContext
7649
+ } : {},
7650
+ ...args.telemetry ? {
7651
+ telemetry: args.telemetry
7652
+ } : {},
7653
+ ...args.settlingCohorts ? {
7654
+ settlingCohorts: args.settlingCohorts
7655
+ } : {}
7656
+ });
7657
+ }
7658
+
7659
+ async function cascadeAndPropagateParent(args, parent) {
7660
+ await cascadeAutoTransitions({
7661
+ ...args,
7662
+ instanceId: parent._id
7663
+ }), await propagateToAncestors({
7664
+ ...args,
7665
+ instanceId: parent._id
7666
+ });
7667
+ }
7668
+
7669
+ function stampSpawnBatch({mutation: mutation, parent: parent, children: children, childIds: childIds, now: now}) {
7670
+ let changed = !1;
7671
+ for (const childId of childIds) {
7672
+ const child = requireSpawnBatchChild({
7673
+ parent: parent,
7674
+ children: children,
7675
+ childId: childId
7676
+ }), row = mutation.subworkflows.find(entry => toBareId(entry.ref.id) === childId);
7677
+ if (row === void 0) throw new Error(`Spawn batch child ${childId} has no registry row on parent ${parent._id}`);
7678
+ changed = stampResolvedChild({
7679
+ mutation: mutation,
7680
+ row: row,
7681
+ child: child,
7682
+ now: now
7683
+ }) || changed;
7684
+ }
7685
+ return changed;
7686
+ }
7687
+
7688
+ function requireSpawnBatchChild({parent: parent, children: children, childId: childId}) {
7689
+ const child = children.get(childId);
7690
+ if (child === void 0) throw new Error(`Spawn batch child ${childId} disappeared`);
7691
+ const expectedParent = parentRef(child);
7692
+ if (expectedParent === void 0 || toBareId(expectedParent.id) !== parent._id) throw new Error(`Spawn batch child ${childId} does not belong to parent ${parent._id}`);
7693
+ return child;
7694
+ }
7695
+
6905
7696
  function stampResolvedChild({mutation: mutation, row: row, child: child, now: now}) {
6906
7697
  if (row.resolved !== void 0) return !1;
6907
7698
  const resolved = terminalResolution(child);
@@ -7037,36 +7828,14 @@ async function persist(ctx, mutation) {
7037
7828
  for (const {body: body} of pendingCreates) tx.create(body);
7038
7829
  tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)),
7039
7830
  await tx.commit(), mutation.pendingCreates = [];
7040
- const actorForPriming = ctx.actor;
7041
- for (const {body: body, started: started} of pendingCreates) try {
7042
- await primeInitialStage({
7043
- client: ctx.client,
7044
- instanceId: body._id,
7045
- actor: actorForPriming,
7046
- clientForGdr: ctx.clientForGdr,
7047
- refSurface: ctx.refSurface,
7048
- clock: ctx.clock,
7049
- executionContext: ctx.executionContext,
7050
- telemetry: ctx.telemetry
7051
- }), await cascadeAutoTransitions({
7052
- client: ctx.client,
7053
- instanceId: body._id,
7054
- actor: actorForPriming,
7055
- clientForGdr: ctx.clientForGdr,
7056
- refSurface: ctx.refSurface,
7057
- clock: ctx.clock,
7058
- executionContext: ctx.executionContext,
7059
- telemetry: ctx.telemetry
7060
- }), await propagateToAncestors({
7061
- client: ctx.client,
7831
+ const actorForPriming = ctx.actor, settlingCohorts = new Set(ctx.settlingCohorts).add(ctx.instance._id);
7832
+ for (const {body: body} of pendingCreates) try {
7833
+ const stepArgs = spawnStepArgs(ctx, {
7062
7834
  instanceId: body._id,
7063
7835
  actor: actorForPriming,
7064
- clientForGdr: ctx.clientForGdr,
7065
- refSurface: ctx.refSurface,
7066
- clock: ctx.clock,
7067
- executionContext: ctx.executionContext,
7068
- telemetry: ctx.telemetry
7069
- }), ctx.telemetry.log(WorkflowInstanceStarted, started);
7836
+ settlingCohorts: settlingCohorts
7837
+ });
7838
+ await primeInitialStage(stepArgs), await cascadeAutoTransitions(stepArgs);
7070
7839
  } catch (cause) {
7071
7840
  throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
7072
7841
  instanceId: ctx.instance._id,
@@ -7074,11 +7843,50 @@ async function persist(ctx, mutation) {
7074
7843
  reason: `spawned child "${body._id}" failed to settle after the spawn transaction committed`
7075
7844
  });
7076
7845
  }
7846
+ await settleSpawnBatch(ctx, {
7847
+ pendingCreates: pendingCreates,
7848
+ actor: actorForPriming,
7849
+ settlingCohorts: settlingCohorts
7850
+ });
7851
+ for (const {started: started} of pendingCreates) ctx.telemetry.log(WorkflowInstanceStarted, started);
7077
7852
  const reloaded = await getInstanceDocument(ctx.client, ctx.instance._id);
7078
7853
  if (!reloaded) throw new Error(`Instance ${ctx.instance._id} disappeared after transaction commit`);
7079
7854
  return reloaded;
7080
7855
  }
7081
7856
 
7857
+ async function settleSpawnBatch(ctx, args) {
7858
+ try {
7859
+ await propagateSpawnBatch({
7860
+ ...spawnStepArgs(ctx, {
7861
+ instanceId: ctx.instance._id,
7862
+ actor: args.actor,
7863
+ settlingCohorts: args.settlingCohorts
7864
+ }),
7865
+ childIds: args.pendingCreates.map(({body: body}) => body._id)
7866
+ });
7867
+ } catch (cause) {
7868
+ throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
7869
+ instanceId: ctx.instance._id,
7870
+ guardError: cause,
7871
+ reason: "spawned children failed to propagate after the spawn transaction committed"
7872
+ });
7873
+ }
7874
+ }
7875
+
7876
+ function spawnStepArgs(ctx, args) {
7877
+ return {
7878
+ client: ctx.client,
7879
+ instanceId: args.instanceId,
7880
+ actor: args.actor,
7881
+ clientForGdr: ctx.clientForGdr,
7882
+ refSurface: ctx.refSurface,
7883
+ clock: ctx.clock,
7884
+ executionContext: ctx.executionContext,
7885
+ telemetry: ctx.telemetry,
7886
+ settlingCohorts: args.settlingCohorts ?? ctx.settlingCohorts
7887
+ };
7888
+ }
7889
+
7082
7890
  function stampExecutionContext(ctx, mutation) {
7083
7891
  const stamp = ctx.executionContext, appended = mutation.history.slice(ctx.instance.history.length);
7084
7892
  mutation.history = [ ...mutation.history.slice(0, ctx.instance.history.length), ...stampHistoryEntries(appended, stamp) ];
@@ -7206,24 +8014,25 @@ function buildEffectSettlement(pending, outcome) {
7206
8014
  }
7207
8015
 
7208
8016
  function validateEffectOutputs(args) {
7209
- const {outputs: outputs, declared: declared, effectName: effectName} = args, byName = new Map(declared.map(shape => [ shape.name, shape ])), issues = [];
8017
+ const {outputs: outputs, declared: declared, effectName: effectName} = args, byName = new Map(declared.map(shape => [ shape.name, shape ])), issues = [], normalized = {};
7210
8018
  for (const [key, value] of Object.entries(outputs)) {
7211
8019
  const shape = byName.get(key);
7212
8020
  if (shape === void 0) {
7213
8021
  issues.push(`"${key}" is not a declared output`);
7214
8022
  continue;
7215
8023
  }
7216
- const shapeIssues = effectOutputIssues(shape, value);
7217
- shapeIssues !== void 0 && issues.push(...shapeIssues.map(i => `"${key}": ${i}`));
8024
+ const check = effectOutputCheck(shape, value);
8025
+ "issues" in check ? issues.push(...check.issues.map(i => `"${key}": ${i}`)) : normalized[key] = check.output;
7218
8026
  }
7219
8027
  if (issues.length > 0) throw new EffectOutputsInvalidError({
7220
8028
  effect: effectName,
7221
8029
  issues: issues
7222
8030
  });
8031
+ return normalized;
7223
8032
  }
7224
8033
 
7225
- function effectOutputIssues(shape, value) {
7226
- return checkFieldValue({
8034
+ function effectOutputCheck(shape, value) {
8035
+ return parseFieldValue({
7227
8036
  entryType: shape.type,
7228
8037
  value: value,
7229
8038
  ...shape.fields !== void 0 ? {
@@ -7269,15 +8078,15 @@ function validateCompletionInput({pending: pending, definition: definition, stat
7269
8078
  effect: pending.name,
7270
8079
  issues: [ "outputs cannot accompany a failed completion — report outputs on a done completion instead" ]
7271
8080
  });
7272
- if (outputs !== void 0) {
7273
- const declared = findEffect(definition, pending.name)?.outputs ?? [];
7274
- validateEffectOutputs({
7275
- outputs: outputs,
7276
- declared: declared,
7277
- effectName: pending.name
7278
- });
7279
- }
7280
- return ops !== void 0 ? validateEffectOps(ops, pending.name) : [];
8081
+ const normalizedOutputs = outputs !== void 0 ? validateEffectOutputs({
8082
+ outputs: outputs,
8083
+ declared: findEffect(definition, pending.name)?.outputs ?? [],
8084
+ effectName: pending.name
8085
+ }) : void 0;
8086
+ return {
8087
+ ops: ops !== void 0 ? validateEffectOps(ops, pending.name) : [],
8088
+ outputs: normalizedOutputs
8089
+ };
7281
8090
  }
7282
8091
 
7283
8092
  async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, requestRecord: requestRecord, actor: actor}) {
@@ -7286,7 +8095,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
7286
8095
  record: requestRecord,
7287
8096
  now: ctx.now
7288
8097
  });
7289
- const pending = requirePendingEffect(ctx.instance, effectKey), validatedOps = validateCompletionInput({
8098
+ const pending = requirePendingEffect(ctx.instance, effectKey), {ops: validatedOps, outputs: validatedOutputs} = validateCompletionInput({
7290
8099
  pending: pending,
7291
8100
  definition: ctx.definition,
7292
8101
  status: status,
@@ -7305,7 +8114,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
7305
8114
  detail: detail,
7306
8115
  error: error,
7307
8116
  durationMs: durationMs,
7308
- outputs: outputs
8117
+ outputs: validatedOutputs
7309
8118
  });
7310
8119
  mutation.effectHistory.push(settlement.run);
7311
8120
  const wroteEffectOutputs = status === "done" && outputs !== void 0;
@@ -7503,6 +8312,64 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
7503
8312
  };
7504
8313
  }
7505
8314
 
8315
+ const RESET_ACTIVITY_TARGETS = [ "active", "skipped" ];
8316
+
8317
+ function isResetActivityTarget(value) {
8318
+ return RESET_ACTIVITY_TARGETS.includes(value);
8319
+ }
8320
+
8321
+ async function resetActivity(args) {
8322
+ const {client: client, instanceId: instanceId, activity: activity, to: to, requestRecord: requestRecord, options: options} = args, ctx = await loadCallContext({
8323
+ client: client,
8324
+ instanceId: instanceId,
8325
+ options: options
8326
+ });
8327
+ return commitResetActivity({
8328
+ ctx: ctx,
8329
+ activity: activity,
8330
+ to: to,
8331
+ requestRecord: requestRecord,
8332
+ actor: options?.actor
8333
+ });
8334
+ }
8335
+
8336
+ async function commitResetActivity({ctx: ctx, activity: activity, to: to, requestRecord: requestRecord, actor: actor}) {
8337
+ if (assertRequestUnprocessed({
8338
+ instance: ctx.instance,
8339
+ record: requestRecord,
8340
+ now: ctx.now
8341
+ }), isTerminal(ctx)) return {
8342
+ fired: !1
8343
+ };
8344
+ const mutation = startMutation(ctx.instance), openStage2 = findOpenStageEntry(mutation), entry = findCurrentActivityEntry(mutation, activity);
8345
+ if (openStage2 === void 0 || entry === void 0) throw new ContractViolationError(`resetActivity: activity "${activity}" is not in the current stage of instance "${ctx.instance._id}"`);
8346
+ const from = entry.status;
8347
+ if (from === to) return {
8348
+ fired: !1
8349
+ };
8350
+ if (!isTerminalActivityStatus(from)) throw new 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.`);
8351
+ return recordProcessedRequest({
8352
+ mutation: mutation,
8353
+ record: requestRecord,
8354
+ now: ctx.now
8355
+ }), applyActivityStatusChange({
8356
+ entry: entry,
8357
+ history: mutation.history,
8358
+ stage: openStage2.name,
8359
+ to: to,
8360
+ at: ctx.now,
8361
+ ...actor !== void 0 ? {
8362
+ actor: actor
8363
+ } : {}
8364
+ }), await persist(ctx, mutation), {
8365
+ fired: !0,
8366
+ stage: openStage2.name,
8367
+ activity: activity,
8368
+ from: from,
8369
+ to: to
8370
+ };
8371
+ }
8372
+
7506
8373
  const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ new WeakMap;
7507
8374
 
7508
8375
  async function resolveAccess(taggedClient, args = {}) {
@@ -7512,10 +8379,13 @@ async function resolveAccess(taggedClient, args = {}) {
7512
8379
  client: client,
7513
8380
  requestFn: requestFn,
7514
8381
  resourcePath: args.grantsFromPath
7515
- }) : Promise.resolve(void 0), [actor, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
7516
- if (actor === void 0) throw new ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
8382
+ }) : Promise.resolve(void 0), [identity, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
8383
+ if (identity === void 0) throw new ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
7517
8384
  return {
7518
- actor: actor,
8385
+ actor: identity.actor,
8386
+ ...identity.localPrincipalId !== void 0 ? {
8387
+ localPrincipalId: identity.localPrincipalId
8388
+ } : {},
7519
8389
  ...grants !== void 0 ? {
7520
8390
  grants: grants
7521
8391
  } : {}
@@ -7525,7 +8395,7 @@ async function resolveAccess(taggedClient, args = {}) {
7525
8395
  function cachedActor(client, requestFn) {
7526
8396
  const cached = actorCache.get(client);
7527
8397
  if (cached !== void 0) return cached;
7528
- const pending = fetchActor(requestFn).catch(err => {
8398
+ const pending = fetchActor(client, requestFn).catch(err => {
7529
8399
  throw actorCache.get(client) === pending && actorCache.delete(client), err;
7530
8400
  });
7531
8401
  return actorCache.set(client, pending), pending;
@@ -7552,27 +8422,90 @@ function cachedGrants({client: client, requestFn: requestFn, resourcePath: resou
7552
8422
  byPath.set(resourcePath, cached)), cached;
7553
8423
  }
7554
8424
 
7555
- async function fetchActor(requestFn) {
7556
- let user;
8425
+ async function fetchActor(client, requestFn) {
8426
+ const [resourceUser, globalUser] = await Promise.all([ fetchCurrentUser(requestFn, "the workflow resource host"), fetchGlobalUser(client) ]), resourceId = usableId(resourceUser), globalId = usableId(globalUser.user), id = globalId ?? resourceId;
8427
+ if (id === void 0) return;
8428
+ refuseProjectPrincipalWithoutGlobal({
8429
+ resourceId: resourceId,
8430
+ globalId: globalId,
8431
+ globalUser: globalUser
8432
+ });
8433
+ const roleNames = (resourceUser?.roles?.length ? resourceUser : globalUser.user)?.roles?.map(r => r.name).filter(n => !!n) ?? [];
8434
+ return {
8435
+ actor: {
8436
+ kind: "person",
8437
+ id: id,
8438
+ ...roleNames.length > 0 ? {
8439
+ roles: roleNames
8440
+ } : {}
8441
+ },
8442
+ ...resourceId !== void 0 && resourceId !== id ? {
8443
+ localPrincipalId: resourceId
8444
+ } : {}
8445
+ };
8446
+ }
8447
+
8448
+ function refuseProjectPrincipalWithoutGlobal(args) {
8449
+ const {resourceId: resourceId, globalId: globalId, globalUser: globalUser} = args;
8450
+ if (globalId !== void 0 || resourceId === void 0 || classifyPrincipalId(resourceId).namespace !== "project") return;
8451
+ const reason = "reason" in globalUser ? globalUser.reason : "no record";
8452
+ 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 ? {
8453
+ cause: globalUser.cause
8454
+ } : void 0);
8455
+ }
8456
+
8457
+ function usableId(user) {
8458
+ if (!(!user || typeof user.id != "string" || user.id.length === 0)) return user.id;
8459
+ }
8460
+
8461
+ async function fetchCurrentUser(requestFn, hostDescription) {
7557
8462
  try {
7558
- user = await requestFn({
8463
+ return await requestFn({
7559
8464
  uri: "/users/me",
7560
8465
  tag: REQUEST_TAG.accessResolveActor
7561
8466
  });
7562
8467
  } catch (err) {
7563
- 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.', {
8468
+ 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.`, {
7564
8469
  cause: err
7565
8470
  });
7566
8471
  }
7567
- if (!user || typeof user.id != "string" || user.id.length === 0) return;
7568
- const roleNames = user.roles?.map(r => r.name).filter(n => !!n) ?? [];
7569
- return {
7570
- kind: "person",
7571
- id: user.id,
7572
- ...roleNames.length > 0 ? {
7573
- roles: roleNames
7574
- } : {}
8472
+ }
8473
+
8474
+ async function fetchGlobalUser(client) {
8475
+ if (typeof client.withConfig != "function") return {
8476
+ user: void 0,
8477
+ reason: "the client cannot reach the global API host (no withConfig)"
8478
+ };
8479
+ let globalRequest, globalClient;
8480
+ try {
8481
+ globalClient = client.withConfig({
8482
+ useProjectHostname: !1
8483
+ }), globalRequest = lazyRequest(globalClient);
8484
+ } catch (err) {
8485
+ return {
8486
+ user: void 0,
8487
+ reason: `building the global-host sibling client failed: ${errorMessage(err)}`,
8488
+ cause: err
8489
+ };
8490
+ }
8491
+ if (globalRequest === void 0) return {
8492
+ user: void 0,
8493
+ reason: "the global-host sibling client cannot issue requests"
7575
8494
  };
8495
+ try {
8496
+ return {
8497
+ user: await globalRequest({
8498
+ uri: "/users/me",
8499
+ tag: REQUEST_TAG.accessResolveActor
8500
+ })
8501
+ };
8502
+ } catch (err) {
8503
+ return {
8504
+ user: void 0,
8505
+ reason: `global-host /users/me failed: ${errorMessage(err)}`,
8506
+ cause: err
8507
+ };
8508
+ }
7576
8509
  }
7577
8510
 
7578
8511
  async function fetchGrantsCached(requestFn, resourcePath) {
@@ -7748,35 +8681,48 @@ async function subjectResourceGrants(args) {
7748
8681
  const resolved = await Promise.all([ ...clients.entries() ].map(async ([key, entry]) => {
7749
8682
  const path = aclPathForResource(entry.resource);
7750
8683
  if (path === void 0) return;
7751
- const grants = await grantsForClientPath(entry.client, path);
7752
- return grants === void 0 ? void 0 : [ key, grants ];
8684
+ const [grants, actorId] = await Promise.all([ grantsForClientPath(entry.client, path), resourceActorId(entry.client) ]);
8685
+ if (!(grants === void 0 || actorId === void 0)) return [ key, {
8686
+ grants: grants,
8687
+ actorId: actorId
8688
+ } ];
7753
8689
  }));
7754
8690
  return new Map(resolved.filter(entry => entry !== void 0));
7755
8691
  }
7756
8692
 
8693
+ async function resourceActorId(client) {
8694
+ try {
8695
+ const access = await resolveAccess(client);
8696
+ return lakePrincipalId(access);
8697
+ } catch (err) {
8698
+ 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: ${errorMessage(err)}`);
8699
+ return;
8700
+ }
8701
+ }
8702
+
7757
8703
  async function evaluateInstance(args) {
7758
8704
  const {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, resourceClients: resourceClients} = args, now = (args.clock ?? wallClock)();
7759
8705
  validateTag(tag);
7760
- const {actor: actor, grants: grants} = await resolveAccess(client, {
8706
+ const [access, instance] = await Promise.all([ resolveAccess(client, {
7761
8707
  ...args.grantsFromPath !== void 0 ? {
7762
8708
  grantsFromPath: args.grantsFromPath
7763
8709
  } : {}
7764
- }), instance = await reload({
8710
+ }), reload({
7765
8711
  client: client,
7766
8712
  instanceId: instanceId,
7767
8713
  tag: tag
7768
- }), definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
8714
+ }) ]), {actor: actor, grants: grants, localPrincipalId: localPrincipalId} = access, definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
7769
8715
  client: client,
7770
8716
  workflowResource: workflowResource,
7771
8717
  resourceClients: resourceClients
7772
- }), snapshot = await hydrateSnapshot({
8718
+ }), [snapshot, guards, resourceGrants] = await Promise.all([ hydrateSnapshot({
7773
8719
  client: client,
7774
8720
  clientForGdr: clientForGdr,
7775
8721
  instance: instance
7776
- }), guards = await verdictGuardsForInstance(client, instance._id), resourceGrants = await subjectResourceGrants({
8722
+ }), verdictGuardsForInstance(client, instance._id), subjectResourceGrants({
7777
8723
  clientForGdr: clientForGdr,
7778
8724
  instance: instance
7779
- });
8725
+ }) ]);
7780
8726
  return evaluateFromSnapshot({
7781
8727
  instance: instance,
7782
8728
  definition: definition,
@@ -7785,6 +8731,9 @@ async function evaluateInstance(args) {
7785
8731
  guards: guards,
7786
8732
  now: now,
7787
8733
  resourceGrants: resourceGrants,
8734
+ ...localPrincipalId !== void 0 ? {
8735
+ localPrincipalId: localPrincipalId
8736
+ } : {},
7788
8737
  ...grants !== void 0 ? {
7789
8738
  grants: grants
7790
8739
  } : {}
@@ -7840,9 +8789,12 @@ async function evaluateFromSnapshot(args) {
7840
8789
  definition: definition,
7841
8790
  snapshot: snapshot,
7842
8791
  now: now
7843
- }, can = await advisoryCan({
7844
- instance: instance,
8792
+ }, anchorIdentity = lakePrincipalId({
7845
8793
  actor: actor,
8794
+ localPrincipalId: args.localPrincipalId
8795
+ }), can = await advisoryCan({
8796
+ instance: instance,
8797
+ identity: anchorIdentity,
7846
8798
  grants: grants
7847
8799
  }), scope = await renderConditionScope(scopeSource, {
7848
8800
  actor: actor,
@@ -7859,11 +8811,10 @@ async function evaluateFromSnapshot(args) {
7859
8811
  activityName: activityName
7860
8812
  })), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
7861
8813
  instance: instance,
7862
- actor: actor,
8814
+ identity: anchorIdentity,
7863
8815
  guards: args.guards
7864
8816
  }), subjectDenials = await forecastSubjectDenials({
7865
8817
  instance: instance,
7866
- actor: actor,
7867
8818
  snapshot: snapshot,
7868
8819
  resourceGrants: args.resourceGrants
7869
8820
  }), subjectPermissionReason = subjectDenials.length > 0 ? {
@@ -8007,20 +8958,20 @@ async function editPredicateInsight(args) {
8007
8958
  }
8008
8959
 
8009
8960
  async function forecastSubjectDenials(args) {
8010
- const {instance: instance, actor: actor, snapshot: snapshot, resourceGrants: resourceGrants} = args;
8961
+ const {instance: instance, snapshot: snapshot, resourceGrants: resourceGrants} = args;
8011
8962
  if (resourceGrants === void 0 || resourceGrants.size === 0) return [];
8012
8963
  const denials = [], seen = /* @__PURE__ */ new Set;
8013
8964
  for (const {ref: ref, parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
8014
8965
  if (seen.has(ref.id)) continue;
8015
8966
  seen.add(ref.id);
8016
- const grants = resourceGrants.get(resourceGdr(resource));
8017
- if (grants === void 0) continue;
8967
+ const access = resourceGrants.get(resourceGdr(resource));
8968
+ if (access === void 0) continue;
8018
8969
  const doc = snapshot.docs.find(d => d._id === ref.id);
8019
8970
  doc !== void 0 && await subjectUpdateAllowed({
8020
8971
  doc: doc,
8021
8972
  parsed: parsed,
8022
- grants: grants,
8023
- actorId: actor.id
8973
+ grants: access.grants,
8974
+ actorId: access.actorId
8024
8975
  }) === !1 && denials.push({
8025
8976
  subject: ref.id,
8026
8977
  resource: resourceGdr(resource),
@@ -8101,12 +9052,12 @@ async function explainActivityConditions({activity: activity, activityScope: act
8101
9052
  snapshot: snapshot,
8102
9053
  sites: sites
8103
9054
  }), requirementEntries = [];
8104
- for (const [name, condition] of Object.entries(activity.requirements ?? {})) requirementEntries.push([ name, await explainAt({
9055
+ for (const requirement of activity.requirements ?? []) requirementEntries.push([ requirement, await explainAt({
8105
9056
  kind: "requirement",
8106
9057
  activity: activity.name,
8107
- requirement: name
8108
- }, condition) ]);
8109
- const requirementInsights = Object.fromEntries(requirementEntries), unmetRequirements = requirementEntries.filter(([, insight]) => insight.outcome !== "satisfied").map(([name]) => name), filterInsight = activity.filter !== void 0 ? await explainSite({
9058
+ requirement: requirement.name
9059
+ }, requirement.query) ]);
9060
+ 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({
8110
9061
  site: {
8111
9062
  kind: "activity-filter",
8112
9063
  activity: activity.name
@@ -8252,12 +9203,12 @@ function actionEvaluationIdentity(action) {
8252
9203
  };
8253
9204
  }
8254
9205
 
8255
- async function instanceGuardReason({instance: instance, actor: actor, guards: guards}) {
9206
+ async function instanceGuardReason({instance: instance, identity: identity, guards: guards}) {
8256
9207
  if (guards === void 0 || guards.length === 0) return;
8257
9208
  const denied = await instanceWriteDenials({
8258
9209
  instance: instance,
8259
9210
  guards: guards,
8260
- identity: actor.id
9211
+ identity: identity
8261
9212
  });
8262
9213
  if (denied.length !== 0) return {
8263
9214
  kind: "mutation-guard-denied",
@@ -8296,24 +9247,24 @@ function inFlightFilter() {
8296
9247
  return "!defined(completedAt)";
8297
9248
  }
8298
9249
 
8299
- 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`;
9250
+ 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";
8300
9251
 
8301
9252
  function documentPrefilter(documents, params) {
8302
9253
  for (const document of documents) if (!isGdrUri(document)) throw new ContractViolationError(`every document must be a resource-qualified GDR URI (e.g. "dataset:project:dataset:doc-id"); got: ${JSON.stringify(document)}`);
8303
9254
  return params.documents = [ ...documents ], params.bareIds = documents.map(extractDocumentId),
8304
- `_id in $bareIds || count(ancestors[id in $documents]) > 0 || ${FIELD_REFS_DOC} || count(stages[${FIELD_REFS_DOC}]) > 0 || ${ACTIVITY_REFS_DOC}`;
9255
+ `_id in $bareIds || count(ancestors[id in $documents]) > 0 || ${LIVE_SUBWORKFLOW_REFS_DOC} || ${FIELD_REFS_DOC} || ${OPEN_STAGE_REFS_DOC}`;
8305
9256
  }
8306
9257
 
8307
9258
  function documentArm(filter, params) {
8308
9259
  if (filter.documents === void 0 && filter.document === void 0) return;
8309
- const documents = [ ...filter.documents ?? [], ...filter.document !== void 0 ? [ filter.document ] : [] ];
9260
+ const documents = [ .../* @__PURE__ */ new Set([ ...filter.documents ?? [], ...filter.document !== void 0 ? [ filter.document ] : [] ]) ].sort();
8310
9261
  return documentPrefilter(documents, params);
8311
9262
  }
8312
9263
 
8313
9264
  function idsArm(filter, params) {
8314
9265
  if (filter.ids !== void 0) {
8315
9266
  for (const id of filter.ids) if (isGdrUri(id)) throw new 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)}`);
8316
- return params.ids = [ ...filter.ids ], "_id in $ids";
9267
+ return params.ids = [ ...new Set(filter.ids) ].sort(), "_id in $ids";
8317
9268
  }
8318
9269
  }
8319
9270
 
@@ -8727,11 +9678,10 @@ async function commitEdit({ctx: ctx, target: target, mode: mode, value: value, r
8727
9678
  }
8728
9679
 
8729
9680
  async function assertFieldEditable({ctx: ctx, site: site, options: options}) {
8730
- const actor = options?.actor, window = fieldWindowOpen(ctx.instance, site), can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({
9681
+ const actor = options?.actor, window = fieldWindowOpen(ctx.instance, site), can = await advisoryCanForCall({
8731
9682
  instance: ctx.instance,
8732
- actor: actor,
8733
- grants: options.grants
8734
- }) : void 0, predicateSatisfied = window.open && site.effective !== !0 && site.effective !== void 0 ? await ctxEvaluateCondition({
9683
+ options: options
9684
+ }), predicateSatisfied = window.open && site.effective !== !0 && site.effective !== void 0 ? await ctxEvaluateCondition({
8735
9685
  ctx: ctx,
8736
9686
  condition: site.effective,
8737
9687
  opts: {
@@ -9089,8 +10039,7 @@ async function abortAndPropagate(args) {
9089
10039
  executionContext: executionContext,
9090
10040
  telemetry: telemetry
9091
10041
  })
9092
- });
9093
- return await drainCondemnedChildren({
10042
+ }), drained = await drainCondemnedChildren({
9094
10043
  client: client,
9095
10044
  instanceId: instanceId,
9096
10045
  actor: actor,
@@ -9102,8 +10051,12 @@ async function abortAndPropagate(args) {
9102
10051
  } : {},
9103
10052
  ...telemetry !== void 0 ? {
9104
10053
  telemetry: telemetry
10054
+ } : {},
10055
+ ...result.fired ? {
10056
+ instance: result.instance
9105
10057
  } : {}
9106
- }), await propagateToAncestors({
10058
+ });
10059
+ return await propagateToAncestors({
9107
10060
  client: client,
9108
10061
  instanceId: instanceId,
9109
10062
  actor: actor,
@@ -9115,6 +10068,9 @@ async function abortAndPropagate(args) {
9115
10068
  } : {},
9116
10069
  ...telemetry !== void 0 ? {
9117
10070
  telemetry: telemetry
10071
+ } : {},
10072
+ ...result.fired && !drained.drained ? {
10073
+ instance: drained.instance
9118
10074
  } : {}
9119
10075
  }), result.fired;
9120
10076
  }
@@ -9222,6 +10178,9 @@ function buildEngineCallOptions(args) {
9222
10178
  actor: args.actor,
9223
10179
  clientForGdr: args.clientForGdr,
9224
10180
  refSurface: args.refSurface,
10181
+ ...args.localPrincipalId !== void 0 ? {
10182
+ localPrincipalId: args.localPrincipalId
10183
+ } : {},
9225
10184
  ...args.grants !== void 0 ? {
9226
10185
  grants: args.grants
9227
10186
  } : {},
@@ -9363,6 +10322,15 @@ async function abortInstances(args) {
9363
10322
  return aborted;
9364
10323
  }
9365
10324
 
10325
+ function projectStartSliceRow(instance) {
10326
+ const subject = (instance.fields ?? []).find(field => field._type === "subject")?.value;
10327
+ return {
10328
+ definition: instance.definition,
10329
+ subject: typeof subject == "object" && subject !== null && "id" in subject && isGdrUri(subject.id) ? subject.id : null,
10330
+ completedAt: instance.completedAt ?? null
10331
+ };
10332
+ }
10333
+
9366
10334
  async function fetchStartSlice(args) {
9367
10335
  const {client: client, tag: tag} = args, {query: query, params: params} = instancesQuery({
9368
10336
  tag: tag,
@@ -9370,7 +10338,7 @@ async function fetchStartSlice(args) {
9370
10338
  includeCompleted: !0
9371
10339
  }
9372
10340
  });
9373
- return (await client.fetch(query, params)).map(assertReadableModel);
10341
+ return (await client.fetch(query, params)).map(assertReadableModel).map(projectStartSliceRow);
9374
10342
  }
9375
10343
 
9376
10344
  const workflow = {
@@ -9519,6 +10487,9 @@ const workflow = {
9519
10487
  ...access.grants !== void 0 ? {
9520
10488
  grants: access.grants
9521
10489
  } : {},
10490
+ ...access.localPrincipalId !== void 0 ? {
10491
+ localPrincipalId: access.localPrincipalId
10492
+ } : {},
9522
10493
  clock: clock,
9523
10494
  clientForGdr: clientForGdr,
9524
10495
  refSurface: refSurface,
@@ -9587,6 +10558,9 @@ const workflow = {
9587
10558
  ...access.grants !== void 0 ? {
9588
10559
  grants: access.grants
9589
10560
  } : {},
10561
+ ...access.localPrincipalId !== void 0 ? {
10562
+ localPrincipalId: access.localPrincipalId
10563
+ } : {},
9590
10564
  clock: clock,
9591
10565
  clientForGdr: clientForGdr,
9592
10566
  refSurface: refSurface,
@@ -9746,7 +10720,7 @@ const workflow = {
9746
10720
  });
9747
10721
  },
9748
10722
  tick: async rawArgs => {
9749
- 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({
10723
+ 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({
9750
10724
  client: client,
9751
10725
  instanceId: instanceId,
9752
10726
  tag: tag
@@ -9754,7 +10728,7 @@ const workflow = {
9754
10728
  await assertInstanceWriteAllowed({
9755
10729
  instance: current,
9756
10730
  guards: guards,
9757
- identity: actor.id
10731
+ identity: lakePrincipalId(access)
9758
10732
  });
9759
10733
  const {cascaded: cascaded, instance: instance} = await cascadeAndReload({
9760
10734
  client: client,
@@ -9871,6 +10845,59 @@ const workflow = {
9871
10845
  }
9872
10846
  });
9873
10847
  },
10848
+ resetActivity: async rawArgs => {
10849
+ const args = taggedScope(rawArgs, REQUEST_TAG.resetActivity), {client: client, tag: tag, instanceId: instanceId, activity: activity, executionContext: executionContext} = args, to = args.to ?? "active";
10850
+ if (!isResetActivityTarget(to)) throw new ContractViolationError(`resetActivity: "to" must be "active" or "skipped"; got ${JSON.stringify(args.to)}`);
10851
+ return runCommitVerb({
10852
+ args: args,
10853
+ op: "resetActivity",
10854
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, record: record, before: before}) => {
10855
+ const result = await resetActivity({
10856
+ client: client,
10857
+ instanceId: instanceId,
10858
+ activity: activity,
10859
+ to: to,
10860
+ ...record !== void 0 ? {
10861
+ requestRecord: record
10862
+ } : {},
10863
+ options: engineOptionsForActor({
10864
+ actor: actor,
10865
+ clock: clock,
10866
+ clientForGdr: clientForGdr,
10867
+ refSurface: refSurface,
10868
+ executionContext: executionContext,
10869
+ telemetry: args.telemetry
10870
+ })
10871
+ }), cascaded = result.fired ? await cascade({
10872
+ client: client,
10873
+ instanceId: instanceId,
10874
+ actor: actor,
10875
+ clientForGdr: clientForGdr,
10876
+ refSurface: refSurface,
10877
+ clock: clock,
10878
+ ...executionContext !== void 0 ? {
10879
+ executionContext: executionContext
10880
+ } : {},
10881
+ ...args.telemetry !== void 0 ? {
10882
+ telemetry: args.telemetry
10883
+ } : {}
10884
+ }) : 0;
10885
+ return resolveTelemetry(args.telemetry).log(WorkflowActivityReset, {
10886
+ ...definitionHashFragment(before.pinnedContentHash),
10887
+ instanceId: instanceId,
10888
+ changed: result.fired
10889
+ }), {
10890
+ instance: await reload({
10891
+ client: client,
10892
+ instanceId: instanceId,
10893
+ tag: tag
10894
+ }),
10895
+ cascaded: cascaded,
10896
+ changed: result.fired
10897
+ };
10898
+ }
10899
+ });
10900
+ },
9874
10901
  getInstance: async rawArgs => {
9875
10902
  const args = taggedScope(rawArgs, REQUEST_TAG.getInstance), {client: client, tag: tag, instanceId: instanceId} = args;
9876
10903
  return validateTag(tag), reload({
@@ -10017,21 +11044,18 @@ const workflow = {
10017
11044
  return (await client.fetch(query, params)).map(readInstanceDoc).filter(instance => instanceWatchesDocument(instance, document));
10018
11045
  },
10019
11046
  definitionsForDocument: async rawArgs => {
10020
- const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document, subject: subject} = args;
11047
+ const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document} = args;
10021
11048
  validateTag(tag);
10022
- const clock = args.clock ?? wallClock, deployed = (await client.fetch(definitionsListGroq("desc"), {
11049
+ const clock = args.clock ?? wallClock, deployed = (await client.fetch(latestDefinitionsGroq(), {
10023
11050
  tag: tag
10024
- })).map(assertReadableModel), latest = latestDeployedDefinitions(deployed);
11051
+ })).map(assertReadableModel);
10025
11052
  let slice;
10026
11053
  return applicableDefinitions({
10027
- definitions: latest,
11054
+ definitions: deployed,
10028
11055
  document: document,
10029
11056
  scope: {
10030
11057
  tag: tag,
10031
11058
  now: clock(),
10032
- ...subject !== void 0 ? {
10033
- subject: subject
10034
- } : {},
10035
11059
  fetchDataset: () => slice ??= fetchStartSlice({
10036
11060
  client: client,
10037
11061
  tag: tag
@@ -10054,34 +11078,17 @@ const workflow = {
10054
11078
  }), missingRequired = missingRequiredInputs({
10055
11079
  entryDefs: definition.fields ?? [],
10056
11080
  initialFields: initialFields ?? []
10057
- }), allowed = definition.start?.allowed;
10058
- if (allowed === void 0) return {
10059
- allowed: invalidInitialFields.length === 0,
10060
- outcome: "satisfied",
10061
- unboundReads: [],
10062
- missingRequired: missingRequired,
10063
- invalidInitialFields: invalidInitialFields
10064
- };
10065
- const unboundReads = unboundAllowedReadsWithSubject({
10066
- allowed: allowed,
10067
- fields: startFieldsParam({
10068
- entryDefs: definition.fields ?? [],
10069
- initialFields: initialFields ?? []
10070
- }),
10071
- definition: definition
10072
- }), insight = await startAllowedInsight({
11081
+ }), verdict = await startRequirementVerdict({
10073
11082
  client: client,
10074
11083
  tag: tag,
10075
11084
  definition: definition,
10076
- allowed: allowed,
10077
11085
  initialFields: initialFields ?? [],
10078
- now: clock()
10079
- }), provisional = unboundReads.length > 0;
11086
+ now: clock(),
11087
+ preflight: !0
11088
+ });
10080
11089
  return {
10081
- allowed: invalidInitialFields.length === 0 && !provisional && insight.outcome === "satisfied",
10082
- outcome: provisional ? "unevaluable" : insight.outcome,
10083
- insight: insight,
10084
- unboundReads: unboundReads,
11090
+ ...verdict,
11091
+ allowed: invalidInitialFields.length === 0 && verdict.allowed,
10085
11092
  missingRequired: missingRequired,
10086
11093
  invalidInitialFields: invalidInitialFields
10087
11094
  };
@@ -10093,11 +11100,10 @@ const workflow = {
10093
11100
  }
10094
11101
  };
10095
11102
 
10096
- async function startAllowedInsight(args) {
10097
- const {client: client, tag: tag, definition: definition, allowed: allowed, initialFields: initialFields, now: now} = args;
10098
- return explainStartAllowed({
10099
- allowed: allowed,
10100
- definition: definition,
11103
+ function startPredicateContext(args) {
11104
+ const {client: client, tag: tag, definition: definition, initialFields: initialFields, now: now} = args;
11105
+ let slice;
11106
+ return {
10101
11107
  fields: startFieldsParam({
10102
11108
  entryDefs: definition.fields ?? [],
10103
11109
  initialFields: initialFields
@@ -10105,12 +11111,71 @@ async function startAllowedInsight(args) {
10105
11111
  scope: {
10106
11112
  tag: tag,
10107
11113
  now: now,
10108
- fetchDataset: () => fetchStartSlice({
11114
+ fetchDataset: () => slice ??= fetchStartSlice({
10109
11115
  client: client,
10110
11116
  tag: tag
10111
11117
  })
10112
11118
  }
10113
- });
11119
+ };
11120
+ }
11121
+
11122
+ async function startRequirementVerdict(args) {
11123
+ const {client: client, tag: tag, definition: definition, initialFields: initialFields, now: now, preflight: preflight = !1} = args, declared = definition.start?.requirements ?? [];
11124
+ if (declared.length === 0) return {
11125
+ allowed: !0,
11126
+ outcome: "satisfied",
11127
+ requirements: [],
11128
+ unboundReads: []
11129
+ };
11130
+ const {fields: fields, scope: scope} = startPredicateContext({
11131
+ client: client,
11132
+ tag: tag,
11133
+ definition: definition,
11134
+ initialFields: initialFields,
11135
+ now: now
11136
+ }), subjectEntry = (definition.fields ?? []).find(isSubjectEntry)?.name, requirements = [], unbound = /* @__PURE__ */ new Set;
11137
+ for (const requirement of declared) {
11138
+ const descriptor = requirementDescriptor(requirement);
11139
+ if (requirement.type === "singleSubject") {
11140
+ if (preflight && subjectEntry !== void 0 && !Object.hasOwn(fields, subjectEntry)) unbound.add(subjectEntry),
11141
+ requirements.push({
11142
+ ...descriptor,
11143
+ outcome: "unevaluable"
11144
+ }); else {
11145
+ const refused = await singleSubjectRequirementRefused({
11146
+ definition: definition,
11147
+ fields: fields,
11148
+ scope: scope
11149
+ });
11150
+ requirements.push({
11151
+ ...descriptor,
11152
+ outcome: refused ? "unsatisfied" : "satisfied"
11153
+ });
11154
+ }
11155
+ continue;
11156
+ }
11157
+ const missing = unboundRequirementReads(requirement.query, fields);
11158
+ missing.forEach(name => unbound.add(name));
11159
+ const insight = await explainStartRequirement({
11160
+ query: requirement.query,
11161
+ definition: definition,
11162
+ fields: fields,
11163
+ scope: scope
11164
+ });
11165
+ requirements.push({
11166
+ ...descriptor,
11167
+ outcome: preflight && missing.length > 0 ? "unevaluable" : insight.outcome,
11168
+ insight: insight
11169
+ });
11170
+ }
11171
+ let outcome = "satisfied";
11172
+ return requirements.some(requirement => requirement.outcome === "unsatisfied") ? outcome = "unsatisfied" : requirements.some(requirement => requirement.outcome === "unevaluable") && (outcome = "unevaluable"),
11173
+ {
11174
+ allowed: outcome === "satisfied",
11175
+ outcome: outcome,
11176
+ requirements: requirements,
11177
+ unboundReads: [ ...unbound ]
11178
+ };
10114
11179
  }
10115
11180
 
10116
11181
  async function startFreshInstance(args) {
@@ -10210,19 +11275,16 @@ async function assertStartInputs(args) {
10210
11275
  initialFields: initialFields,
10211
11276
  definitionName: definition.name
10212
11277
  });
10213
- const allowed = definition.start?.allowed;
10214
- if (allowed === void 0) return;
10215
- const insight = await startAllowedInsight({
11278
+ const unmetRequirements = (await startRequirementVerdict({
10216
11279
  client: client,
10217
11280
  tag: tag,
10218
11281
  definition: definition,
10219
- allowed: allowed,
10220
11282
  initialFields: initialFields,
10221
11283
  now: now
10222
- });
10223
- if (insight.outcome !== "satisfied") throw new StartNotAllowedError({
11284
+ })).requirements.filter(requirement => requirement.outcome !== "satisfied").map(requirementDescriptor);
11285
+ if (unmetRequirements.length > 0) throw new StartNotAllowedError({
10224
11286
  definition: definition.name,
10225
- insight: insight
11287
+ unmetRequirements: unmetRequirements
10226
11288
  });
10227
11289
  }
10228
11290
 
@@ -10255,97 +11317,6 @@ async function resolveStartFields(args) {
10255
11317
  };
10256
11318
  }
10257
11319
 
10258
- function isRecord(value) {
10259
- return typeof value == "object" && value !== null && !Array.isArray(value);
10260
- }
10261
-
10262
- function isClientProjectUser(value) {
10263
- 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");
10264
- }
10265
-
10266
- function objectProperty(value, property) {
10267
- if (!isRecord(value)) return;
10268
- const propertyValue = value[property];
10269
- return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
10270
- }
10271
-
10272
- function stringProperty(value, property) {
10273
- const propertyValue = Reflect.get(value, property);
10274
- return typeof propertyValue == "string" ? propertyValue : void 0;
10275
- }
10276
-
10277
- function apiErrorType(error) {
10278
- const response = objectProperty(error, "response"), body = objectProperty(response, "body");
10279
- if (!body) return;
10280
- const nestedError = objectProperty(body, "error");
10281
- return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
10282
- }
10283
-
10284
- function isProjectUserNotFoundError(error) {
10285
- return apiErrorType(error) === "projectUserNotFoundError";
10286
- }
10287
-
10288
- function clientProjectUserDirectory(client, projectId) {
10289
- return {
10290
- findById: async id => {
10291
- if (!client.request) return {
10292
- status: "inaccessible",
10293
- cause: new Error("Project-user resolution requires WorkflowClient.request")
10294
- };
10295
- try {
10296
- const response = await client.request({
10297
- uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
10298
- }), candidate = Array.isArray(response) ? response[0] : response;
10299
- return candidate == null ? {
10300
- status: "missing"
10301
- } : isClientProjectUser(candidate) ? {
10302
- status: "resolved",
10303
- user: candidate
10304
- } : {
10305
- status: "inaccessible",
10306
- cause: new Error("Project-user response had an invalid shape")
10307
- };
10308
- } catch (cause) {
10309
- return isProjectUserNotFoundError(cause) ? {
10310
- status: "missing"
10311
- } : {
10312
- status: "inaccessible",
10313
- cause: cause
10314
- };
10315
- }
10316
- }
10317
- };
10318
- }
10319
-
10320
- function resolveClientActor(client, args) {
10321
- return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
10322
- }
10323
-
10324
- async function resolveActor(directory, actor) {
10325
- if (actor.kind !== "person") return {
10326
- status: "not-person",
10327
- actor: actor
10328
- };
10329
- const personActor = {
10330
- ...actor,
10331
- kind: "person"
10332
- }, lookup = await directory.findById(personActor.id);
10333
- return lookup.status === "resolved" ? {
10334
- status: "resolved",
10335
- actor: personActor,
10336
- user: lookup.user
10337
- } : lookup.status === "inaccessible" ? {
10338
- status: "inaccessible",
10339
- actor: personActor,
10340
- ...lookup.cause === void 0 ? {} : {
10341
- cause: lookup.cause
10342
- }
10343
- } : {
10344
- status: "missing",
10345
- actor: personActor
10346
- };
10347
- }
10348
-
10349
11320
  const EFFECT_COMMIT_QUEUE_DEPTH = 32, EFFECT_COMMIT_DISPATCH_CAP = 200;
10350
11321
 
10351
11322
  class EffectCommitQueueOverflowError extends WorkflowError {
@@ -10524,7 +11495,7 @@ function isCancelledCompletion(data) {
10524
11495
  }
10525
11496
 
10526
11497
  async function drainEffectsInternal(args) {
10527
- 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({
11498
+ 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({
10528
11499
  client: handlerClient,
10529
11500
  workflowResource: workflowResource,
10530
11501
  resourceClients: handlerResourceClients
@@ -10890,22 +11861,30 @@ function createInstanceSession(args) {
10890
11861
  }, tickScope = opScope(REQUEST_TAG.tick), fireScope = opScope(REQUEST_TAG.fireAction), editScope = opScope(REQUEST_TAG.editField), evalScope = opScope(REQUEST_TAG.evaluate);
10891
11862
  let overlay = /* @__PURE__ */ new Map;
10892
11863
  const previews = /* @__PURE__ */ new Map;
10893
- let heldGuards = [], committing = !1, buffered, deferredUpdateError;
10894
- const selfUri = () => gdrFromResource(instance.workflowResource, instance._id), applyUpdate = docs => {
10895
- const next = /* @__PURE__ */ new Map;
10896
- for (const ld of docs) {
10897
- const owned = {
10898
- doc: structuredClone(ld.doc),
10899
- resource: ld.resource
10900
- }, uri = gdrFromResource(owned.resource, owned.doc._id);
10901
- if (uri === selfUri()) {
10902
- const rawStamp = owned.doc._updatedAt;
10903
- !isParseableInstant(rawStamp) || owned.doc._type !== WORKFLOW_INSTANCE_TYPE ? asHeldInstance(owned.doc) : Date.parse(rawStamp) > Date.parse(instance._updatedAt) && (instance = asHeldInstance(owned.doc));
10904
- continue;
10905
- }
10906
- next.set(uri, owned);
11864
+ let heldGuards = [], committing = !1, buffered;
11865
+ const bufferedDocuments = /* @__PURE__ */ new Map;
11866
+ let deferredUpdateError;
11867
+ const flushDeferredError = () => {
11868
+ if (deferredUpdateError === void 0) return;
11869
+ const failure = deferredUpdateError;
11870
+ throw deferredUpdateError = void 0, failure;
11871
+ }, selfUri = () => gdrFromResource(instance.workflowResource, instance._id), applyDocument = (ld, target) => {
11872
+ const owned = {
11873
+ doc: structuredClone(ld.doc),
11874
+ resource: ld.resource
11875
+ }, uri = gdrFromResource(owned.resource, owned.doc._id);
11876
+ if (uri === selfUri()) {
11877
+ const rawStamp = owned.doc._updatedAt;
11878
+ !isParseableInstant(rawStamp) || owned.doc._type !== WORKFLOW_INSTANCE_TYPE ? asHeldInstance(owned.doc) : Date.parse(rawStamp) > Date.parse(instance._updatedAt) && (instance = asHeldInstance(owned.doc));
11879
+ return;
10907
11880
  }
11881
+ target.set(uri, owned);
11882
+ }, applyUpdate = docs => {
11883
+ const next = /* @__PURE__ */ new Map;
11884
+ for (const ld of docs) applyDocument(ld, next);
10908
11885
  overlay = next;
11886
+ }, applyDocumentUpdate = doc => {
11887
+ applyDocument(doc, overlay);
10909
11888
  }, access = () => resolveAccess(client, {
10910
11889
  ...args.grantsFromPath !== void 0 ? {
10911
11890
  grantsFromPath: args.grantsFromPath
@@ -10963,17 +11942,23 @@ function createInstanceSession(args) {
10963
11942
  refSurface: evalScope.refSurface
10964
11943
  });
10965
11944
  }, evaluateWith = async ({held: held, guards: guards, self: self}) => {
10966
- const {actor: actor, grants: grants} = await access(), resourceGrants = await subjectResourceGrants({
11945
+ const {actor: actor, localPrincipalId: localPrincipalId, grants: grants} = await access(), resourceGrants = await subjectResourceGrants({
10967
11946
  clientForGdr: evalScope.clientForGdr,
10968
11947
  instance: instance
11948
+ }), normalizedSelf = await normalizeInstanceIdentities({
11949
+ client: evalScope.client,
11950
+ instance: self
10969
11951
  });
10970
11952
  return evaluateFromSnapshot({
10971
- instance: self,
11953
+ instance: normalizedSelf,
10972
11954
  definition: definitionOf(),
10973
11955
  actor: actor,
10974
- snapshot: snapshotFrom(held, self),
11956
+ snapshot: snapshotFrom(held, normalizedSelf),
10975
11957
  guards: guards,
10976
11958
  resourceGrants: resourceGrants,
11959
+ ...localPrincipalId !== void 0 ? {
11960
+ localPrincipalId: localPrincipalId
11961
+ } : {},
10977
11962
  ...clock !== void 0 ? {
10978
11963
  now: clock()
10979
11964
  } : {},
@@ -11019,8 +12004,13 @@ function createInstanceSession(args) {
11019
12004
  committing = !0;
11020
12005
  const held = new Map(overlay);
11021
12006
  try {
11022
- const {actor: actor} = await access();
11023
- return await run(actor, held);
12007
+ const {actor: actor, localPrincipalId: localPrincipalId} = await access();
12008
+ return await run({
12009
+ actor: actor,
12010
+ ...localPrincipalId !== void 0 ? {
12011
+ localPrincipalId: localPrincipalId
12012
+ } : {}
12013
+ }, held);
11024
12014
  } finally {
11025
12015
  if (committing = !1, buffered !== void 0) {
11026
12016
  const next = buffered;
@@ -11031,6 +12021,12 @@ function createInstanceSession(args) {
11031
12021
  deferredUpdateError = err;
11032
12022
  }
11033
12023
  }
12024
+ for (const next of bufferedDocuments.values()) try {
12025
+ applyDocumentUpdate(next);
12026
+ } catch (err) {
12027
+ deferredUpdateError = err;
12028
+ }
12029
+ bufferedDocuments.clear();
11034
12030
  }
11035
12031
  };
11036
12032
  return {
@@ -11038,15 +12034,26 @@ function createInstanceSession(args) {
11038
12034
  return subscriptionDocumentsForInstance(instance);
11039
12035
  },
11040
12036
  update(docs) {
11041
- if (committing) buffered = docs; else try {
12037
+ if (committing) buffered = docs, bufferedDocuments.clear(); else try {
11042
12038
  applyUpdate(docs);
11043
12039
  } catch (err) {
11044
12040
  throw deferredUpdateError = void 0, err;
11045
12041
  }
11046
- if (deferredUpdateError !== void 0) {
11047
- const failure = deferredUpdateError;
11048
- throw deferredUpdateError = void 0, failure;
12042
+ flushDeferredError();
12043
+ },
12044
+ updateDocument(doc) {
12045
+ if (committing) {
12046
+ const uri = gdrFromResource(doc.resource, doc.doc._id);
12047
+ bufferedDocuments.set(uri, {
12048
+ doc: structuredClone(doc.doc),
12049
+ resource: doc.resource
12050
+ });
12051
+ } else try {
12052
+ applyDocumentUpdate(doc);
12053
+ } catch (err) {
12054
+ throw deferredUpdateError = void 0, err;
11049
12055
  }
12056
+ flushDeferredError();
11050
12057
  },
11051
12058
  updateGuards(guards) {
11052
12059
  heldGuards = guards.map(guard => parseGuardDocument(structuredClone(guard)));
@@ -11078,11 +12085,14 @@ function createInstanceSession(args) {
11078
12085
  site !== void 0 && previews.delete(siteKey(site));
11079
12086
  },
11080
12087
  tick() {
11081
- return commit(async (actor, held) => {
12088
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11082
12089
  await assertInstanceWriteAllowed({
11083
12090
  instance: instance,
11084
12091
  guards: heldGuards,
11085
- identity: actor.id
12092
+ identity: lakePrincipalId({
12093
+ actor: actor,
12094
+ localPrincipalId: localPrincipalId
12095
+ })
11086
12096
  });
11087
12097
  const before = await reload({
11088
12098
  client: tickScope.client,
@@ -11109,7 +12119,7 @@ function createInstanceSession(args) {
11109
12119
  });
11110
12120
  },
11111
12121
  fireAction({activity: activity, action: action, params: params}) {
11112
- return commit(async (actor, held) => {
12122
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11113
12123
  assertActionAllowed({
11114
12124
  evaluation: await evaluateWith({
11115
12125
  held: held,
@@ -11125,6 +12135,9 @@ function createInstanceSession(args) {
11125
12135
  activity: activity,
11126
12136
  action: action,
11127
12137
  actor: actor,
12138
+ ...localPrincipalId !== void 0 ? {
12139
+ localPrincipalId: localPrincipalId
12140
+ } : {},
11128
12141
  clientForGdr: fireScope.clientForGdr,
11129
12142
  refSurface: fireScope.refSurface,
11130
12143
  ...grants !== void 0 ? {
@@ -11167,7 +12180,7 @@ function createInstanceSession(args) {
11167
12180
  } catch (err) {
11168
12181
  return Promise.reject(err);
11169
12182
  }
11170
- return commit(async (actor, held) => {
12183
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11171
12184
  assertEditAllowed(await evaluateWith({
11172
12185
  held: held,
11173
12186
  guards: heldGuards,
@@ -11184,6 +12197,9 @@ function createInstanceSession(args) {
11184
12197
  value: value
11185
12198
  } : {},
11186
12199
  actor: actor,
12200
+ ...localPrincipalId !== void 0 ? {
12201
+ localPrincipalId: localPrincipalId
12202
+ } : {},
11187
12203
  clientForGdr: editScope.clientForGdr,
11188
12204
  refSurface: editScope.refSurface,
11189
12205
  ...grants !== void 0 ? {
@@ -11294,6 +12310,7 @@ function createEngine(args) {
11294
12310
  availableActions: rest => workflow.availableActions(withScope(rest)),
11295
12311
  setStage: rest => workflow.setStage(withScope(rest)),
11296
12312
  abortInstance: rest => workflow.abortInstance(withScope(rest)),
12313
+ resetActivity: rest => workflow.resetActivity(withScope(rest)),
11297
12314
  deleteDefinition: rest => workflow.deleteDefinition(withScope(rest)),
11298
12315
  getInstance: rest => workflow.getInstance(withScope(rest)),
11299
12316
  subscriptionDocumentsForInstance: rest => workflow.getInstance(withScope(rest)).then(subscriptionDocumentsForInstance),
@@ -11860,4 +12877,4 @@ function displayDescription(typeKey) {
11860
12877
  if (typeKey) return DISPLAY[typeKey]?.description;
11861
12878
  }
11862
12879
 
11863
- export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCommitEffectOpsError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, EFFECT_COMMIT_DISPATCH_CAP, EFFECT_COMMIT_QUEUE_DEPTH, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectCommitQueueOverflowError, EffectNotFoundError, EffectOpsInvalidError, EffectOutputsInvalidError, FIELD_KIND_DISPLAY, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GROUP_KIND_DISPLAY, GUARD_DOC_TYPE, GUARD_OWNER, GUARD_PREDICATE_VARS, HISTORY_DISPLAY, InitialFieldsInvalidError, InstanceNotFoundError, MAX_COUNTERFACTUAL_INDEX2 as MAX_COUNTERFACTUAL_INDEX, MissingHandlerError, ModelVersionAheadError, MutationGuardDeniedError, MutationGuardDocSchema, OP_DISPLAY, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, START_ALLOWED_VARS, START_FILTER_VARS, SpawnContractsInvalidError, StaleEffectClaimError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectStateReported, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activityAutonomyOf, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, clientConfigFromResource, clientProjectUserDirectory, compileGuard, computeDiffEntries, conditionFieldReadNames, conditionSitesOf, contentDocQuery, contentDraftFallback, contentReleaseName, contextMap, createEngine, createTelemetryIntake, datasetResourceParts, defaultLoggerFactory, definitionDeployedData, definitionLookupGroq, definitionsListGroq, deniedGuardLabels, deniedGuardRefs, denyingGuards, deployStageGuards, deriveActivityKind, deriveExecutorClassification, deriveWorkflowAutonomy, describeAtom, describeAutonomyWait, describeCondition, describeDefinition, describeFieldInsight, describeNode, describeSite, describeSiteHeading, diagnoseInputFromEvaluation, diagnoseInstance, diffEntry, displayDescription, displayTitle, documentActionDenials, documentPrefilter, driverKind, effectOutputsMap, entryDocRefs, errorMessage, evaluateFromSnapshot, evaluateMutationGuard, evaluateStartFilter, expandResourceAliases, explainCondition2 as explainCondition, explainStartAllowed, extractDocumentId, fieldTreeShape, findCurrentActivityEntry, findOpenStageEntry, formatRead, gdrFromResource, gdrRef, gdrUri, groupMembershipNames, groupSitesOf, guardMatches, guardsForDefinition, guardsForInstance, guardsForResource, guillemets2 as guillemets, hashDefinitionContent, humanize2 as humanize, inFlightFilter, initialFieldIssues, instanceDocId, instanceGuardQuery, instanceWatchesDocument, instancesQuery, isCascadeFired, isClaimExpired, isComparisonOp, isDefinitionApplicable, isFilterScopedOut, isGdr, isInputSourced, isNotesEntry, isProjectUserNotFoundError, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTelemetryEnvDenied, isTerminalActivityStatus, isTerminalStage, isTodoListEntry, isTodoListItem, isUnprimed, lakeGuardId, latestDeployedDefinitions, lintEffectOutputs, minReaderModelOf, missingRequiredInputs, modelVersionOf, narrateAutonomyWaits, noopTelemetry, parentRef, parseDefinitionInput, parseDefinitionSnapshot, parseGdr, parseGuardDocument, parseInstanceDocument, parseResourceGdr, processShellUserProperties, projectToWatchRef, quoted2 as quoted, readInstanceDoc, readsRaw, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refsOf, rejectedRefTypes, releaseDocId, releaseRef, remediationsFor, requiredModelFeatures, requiredReaderModel, resolveAccess, resolveActor, resolveClientActor, resolveFieldEntry$1 as resolveFieldEntry, resourceAliasesToMap, resourceFromParsed, resourceGdr, retractStageGuards, sameResource, scalarValidationIssues, schemaTreeShape, sentenceCase, silentLogger, stageAutonomyOf, startFieldsParam, startKindOf, startRefusal, stripSystemFields, subjectDenialLabels, subscriptionDocument, subscriptionDocumentsForInstance, sweepStaleClaims, tagScopeFilter, terminalState, toBareId, tryParseGdr, unboundAllowedReads, unsatisfiedTransitionSummaries, validateDefinition, validateTag, verdictGuardsForInstance, wallClock, whatIfCondition2 as whatIfCondition, withAssignment, workflow };
12880
+ export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, ANONYMOUS_IDENTITY, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCommitEffectOpsError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, EFFECT_COMMIT_DISPATCH_CAP, EFFECT_COMMIT_QUEUE_DEPTH, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectCommitQueueOverflowError, EffectNotFoundError, EffectOpsInvalidError, EffectOutputsInvalidError, FIELD_KIND_DISPLAY, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GROUP_KIND_DISPLAY, GUARD_DOC_TYPE, GUARD_OWNER, GUARD_PREDICATE_VARS, HISTORY_DISPLAY, InitialFieldsInvalidError, InstanceNotFoundError, MAX_COUNTERFACTUAL_INDEX2 as MAX_COUNTERFACTUAL_INDEX, MissingHandlerError, ModelVersionAheadError, MutationGuardDeniedError, MutationGuardDocSchema, OP_DISPLAY, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, START_FILTER_VARS, START_REQUIREMENT_VARS, SYSTEM_IDENTITY, SpawnContractsInvalidError, StaleEffectClaimError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowActivityReset, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectStateReported, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activityAutonomyOf, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, classifyPrincipalId, clientConfigFromResource, clientProjectUserDirectory, compileGuard, computeDiffEntries, conditionFieldReadNames, conditionSitesOf, contentDocQuery, contentDraftFallback, contentReleaseName, contextMap, createEngine, createTelemetryIntake, datasetResourceParts, defaultLoggerFactory, definitionDeployedData, definitionLookupGroq, definitionsListGroq, deniedGuardLabels, deniedGuardRefs, denyingGuards, deployStageGuards, deriveActivityKind, deriveExecutorClassification, deriveWorkflowAutonomy, describeAtom, describeAutonomyWait, describeCondition, describeDefinition, describeFieldInsight, describeNode, describeSite, describeSiteHeading, diagnoseInputFromEvaluation, diagnoseInstance, diffEntry, displayDescription, displayTitle, documentActionDenials, documentPrefilter, driverKind, effectOutputsMap, entryDocRefs, errorMessage, evaluateFromSnapshot, evaluateMutationGuard, evaluateStartFilter, expandResourceAliases, explainCondition2 as explainCondition, explainStartRequirement, extractDocumentId, fieldTreeShape, findCurrentActivityEntry, findOpenStageEntry, formatRead, gdrFromResource, gdrRef, gdrUri, groupMembershipNames, groupSitesOf, guardMatches, guardsForDefinition, guardsForInstance, guardsForResource, guillemets2 as guillemets, hasSingleSubjectRequirement, hashDefinitionContent, humanize2 as humanize, inFlightFilter, initialFieldIssues, instanceDocId, instanceGuardQuery, instanceWatchesDocument, instancesGuardQuery, instancesQuery, isCascadeFired, isClaimExpired, isClientProjectUser, isComparisonOp, isDefinitionApplicable, isFilterScopedOut, isGdr, isInputSourced, isNotesEntry, isProjectUserNotFoundError, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTelemetryEnvDenied, isTerminalActivityStatus, isTerminalStage, isTodoListEntry, isTodoListItem, isUnprimed, lakeGuardId, lakePrincipalId, latestDefinitionsGroq, latestDeployedDefinitions, lintEffectOutputs, minReaderModelOf, missingRequiredInputs, modelVersionOf, narrateAutonomyWaits, noopTelemetry, parentRef, parseDefinitionInput, parseDefinitionSnapshot, parseGdr, parseGuardDocument, parseInstanceDocument, parseResourceGdr, processShellUserProperties, projectStartSliceRow, projectToWatchRef, quoted2 as quoted, readInstanceDoc, readsRaw, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refsOf, rejectedRefTypes, releaseDocId, releaseRef, remediationsFor, requiredModelFeatures, requiredReaderModel, resolveAccess, resolveActor, resolveClientActor, resolveFieldEntry$1 as resolveFieldEntry, resourceAliasesToMap, resourceFromParsed, resourceGdr, retractStageGuards, sameResource, scalarValidationIssues, schemaTreeShape, sentenceCase, silentLogger, singleSubjectRequirementRefused, stageAutonomyOf, startFieldsParam, startKindOf, startRefusal, stripSystemFields, subjectDenialLabels, subscriptionDocument, subscriptionDocumentsForInstance, sweepStaleClaims, tagScopeFilter, terminalState, toBareId, tryParseGdr, unboundRequirementReads, unsatisfiedTransitionSummaries, validateDefinition, validateTag, verdictGuardsForInstance, wallClock, whatIfCondition2 as whatIfCondition, withAssignment, workflow };