@sanity/workflow-engine 0.20.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, VersionSpecificDatasetGdrError, 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
  });
@@ -4363,172 +4364,605 @@ function parseInstanceDocument(doc) {
4363
4364
  });
4364
4365
  }
4365
4366
 
4366
- async function getInstanceDocument(client, instanceId) {
4367
- const doc = await client.getDocument(instanceId);
4368
- return doc ? readInstanceDoc(doc) : void 0;
4369
- }
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");
4370
4404
 
4371
- function readInstanceDoc(doc) {
4372
- return parseInstanceDocument(assertReadableModel(doc));
4405
+ function unwrapPatch(patch) {
4406
+ return typeof patch == "object" && patch !== null && RAW_PATCH in patch ? patch[RAW_PATCH] : patch;
4373
4407
  }
4374
4408
 
4375
- async function reload({client: client, instanceId: instanceId, tag: tag}) {
4376
- const doc = await getInstanceDocument(client, instanceId);
4377
- if (!doc) throw new InstanceNotFoundError({
4378
- instanceId: instanceId
4379
- });
4380
- if (doc.tag !== tag) throw new InstanceNotFoundError({
4381
- instanceId: instanceId,
4382
- detail: "not visible to this engine: tag mismatch"
4383
- });
4384
- return doc;
4385
- }
4409
+ const wrapperCache = /* @__PURE__ */ new WeakMap, effectHandlerClientCache = /* @__PURE__ */ new WeakMap, RAW_CLIENT = /* @__PURE__ */ Symbol("workflow-engine.raw-client");
4386
4410
 
4387
- function contextEntry(name, value) {
4388
- const _key = randomKey();
4389
- return typeof value == "string" ? {
4390
- _key: _key,
4391
- _type: "context.string",
4392
- name: name,
4393
- value: value
4394
- } : typeof value == "number" ? {
4395
- _key: _key,
4396
- _type: "context.number",
4397
- name: name,
4398
- value: value
4399
- } : typeof value == "boolean" ? {
4400
- _key: _key,
4401
- _type: "context.boolean",
4402
- name: name,
4403
- value: value
4404
- } : {
4405
- _key: _key,
4406
- _type: "context.ref",
4407
- name: name,
4408
- value: value
4409
- };
4411
+ function unwrapRequestTag(client) {
4412
+ let current = client;
4413
+ for (;RAW_CLIENT in current; ) current = current[RAW_CLIENT];
4414
+ return current;
4410
4415
  }
4411
4416
 
4412
- function contextJsonEntry(name, value) {
4413
- return {
4414
- _key: randomKey(),
4415
- _type: "context.json",
4416
- name: name,
4417
- value: serializeContextValue(name, value)
4418
- };
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;
4419
4424
  }
4420
4425
 
4421
- function serializeContextValue(name, value) {
4422
- let json;
4423
- try {
4424
- json = JSON.stringify(value);
4425
- } catch (err) {
4426
- 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
+ });
4427
4435
  }
4428
- if (json === void 0) throw new Error(`context entry "${name}" holds a non-JSON value (undefined, a function, or a symbol)`);
4429
- return json;
4436
+ return effectHandlerClientCache.set(client, tagged), tagged;
4430
4437
  }
4431
4438
 
4432
- function buildInstanceBase(args) {
4433
- const {id: id, now: now, actor: actor, perspective: perspective, executionContext: executionContext} = args, entries = [ {
4434
- _key: randomKey(),
4435
- _type: "stageEntered",
4436
- at: now,
4437
- stage: args.initialStage,
4438
- ...actor !== void 0 ? {
4439
- actor: actor
4440
- } : {}
4441
- }, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries, body = {
4442
- _id: id,
4443
- _type: WORKFLOW_INSTANCE_TYPE,
4444
- _rev: "",
4445
- _createdAt: now,
4446
- _updatedAt: now,
4447
- tag: args.tag,
4448
- workflowResource: args.workflowResource,
4449
- definition: args.definitionName,
4450
- pinnedVersion: args.pinnedVersion,
4451
- ...args.pinnedContentHash !== void 0 ? {
4452
- pinnedContentHash: args.pinnedContentHash
4453
- } : {},
4454
- definitionSnapshot: JSON.stringify(args.definition),
4455
- fields: args.fields,
4456
- context: args.context,
4457
- ancestors: args.ancestors,
4458
- ...perspective !== void 0 ? {
4459
- perspective: perspective
4460
- } : {},
4461
- currentStage: args.initialStage,
4462
- stages: [],
4463
- subworkflows: [],
4464
- pendingEffects: [],
4465
- effectHistory: [],
4466
- history: history,
4467
- startedAt: now,
4468
- lastChangedAt: now
4469
- };
4470
- return {
4471
- ...body,
4472
- ...modelStampFor({
4473
- documentType: "instance",
4474
- document: body
4475
- })
4476
- };
4439
+ function effectHandlerResolver(resolver) {
4440
+ return mapResourceClientResolver(resolver, effectHandlerClient);
4477
4441
  }
4478
4442
 
4479
- async function hydrateSnapshot(args) {
4480
- const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set;
4481
- loaded.push({
4482
- doc: instance,
4483
- resource: instance.workflowResource
4484
- }), visited.add(selfGdr(instance));
4485
- const {pending: pending, ordered: ordered} = planReads({
4486
- client: client,
4487
- clientForGdr: clientForGdr,
4488
- instance: instance,
4489
- overlay: overlay,
4490
- visited: visited
4491
- }), fetched = await readPending(pending);
4492
- for (const entry of ordered) if (!isPendingRead(entry)) loaded.push(entry); else {
4493
- const doc = fetched.get(entry);
4494
- doc !== void 0 && loaded.push({
4495
- doc: doc,
4496
- resource: entry.resource
4497
- });
4498
- }
4499
- return buildSnapshot({
4500
- docs: loaded
4501
- });
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);
4447
+ };
4502
4448
  }
4503
4449
 
4504
- function planReads(args) {
4505
- const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay, visited: visited} = args, pending = [], ordered = [];
4506
- for (const ref of collectWatchRefs(instance)) {
4507
- if (visited.has(ref.id)) continue;
4508
- visited.add(ref.id);
4509
- const held = overlay?.get(ref.id);
4510
- if (held !== void 0) ordered.push(held); else {
4511
- const read = routeRead({
4512
- defaultClient: client,
4513
- clientForGdr: clientForGdr,
4514
- defaultResource: instance.workflowResource,
4515
- uri: ref.id,
4516
- perspective: readsRaw(ref) ? "raw" : instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE
4517
- });
4518
- pending.push(read), ordered.push(read);
4519
- }
4520
- }
4521
- return {
4522
- pending: pending,
4523
- ordered: ordered
4524
- };
4450
+ function isWorkflowsFamilyPrefix(prefix) {
4451
+ return prefix === void 0 ? !1 : prefix === "sanity.workflows" || prefix.startsWith("sanity.workflows.") || prefix === "sanity.workflows-mcp";
4525
4452
  }
4526
4453
 
4527
- function isPendingRead(entry) {
4528
- return "client" in entry;
4454
+ function clientRequestTagPrefix(client) {
4455
+ const probe = client;
4456
+ return typeof probe.config == "function" ? probe.config().requestTagPrefix : void 0;
4529
4457
  }
4530
4458
 
4531
- const SNAPSHOT_READ_BATCH_SIZE = 100, SNAPSHOT_READ_CONCURRENCY = 4, snapshotDocsQuery = "*[_id in $ids]";
4459
+ function composeRequestTag(prefix, tag) {
4460
+ if (prefix === void 0) return tag;
4461
+ const relative = isWorkflowsFamilyPrefix(prefix) ? stripWorkflowRoot(tag) : tag;
4462
+ return `${prefix}.${relative}`;
4463
+ }
4464
+
4465
+ function stripWorkflowRoot(tag) {
4466
+ return tag.startsWith("workflow.") ? tag.slice(9) : tag;
4467
+ }
4468
+
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]";
4532
4966
 
4533
4967
  function routeRead({defaultClient: defaultClient, clientForGdr: clientForGdr, defaultResource: defaultResource, uri: uri, perspective: perspective}) {
4534
4968
  const parsed = tryParseGdr(uri);
@@ -4645,29 +5079,31 @@ async function loadContext({client: client, instanceId: instanceId, options: opt
4645
5079
  if (!instance) throw new InstanceNotFoundError({
4646
5080
  instanceId: instanceId
4647
5081
  });
4648
- const definition = parseDefinitionSnapshot(instance), {clientForGdr: clientForGdr, refSurface: refSurface} = options, clock = options.clock ?? wallClock, snapshot = await hydrateSnapshot({
5082
+ return buildEngineContext({
4649
5083
  client: client,
4650
- clientForGdr: clientForGdr,
5084
+ clientForGdr: options.clientForGdr,
5085
+ refSurface: options.refSurface,
4651
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
+ } : {},
4652
5100
  ...options.overlay !== void 0 ? {
4653
5101
  overlay: options.overlay
5102
+ } : {},
5103
+ ...options.settlingCohorts !== void 0 ? {
5104
+ settlingCohorts: options.settlingCohorts
4654
5105
  } : {}
4655
5106
  });
4656
- return {
4657
- client: client,
4658
- clientForGdr: clientForGdr,
4659
- refSurface: refSurface,
4660
- clock: clock,
4661
- now: clock(),
4662
- executionContext: resolveExecutionContext(options?.executionContext),
4663
- telemetry: resolveTelemetry(options.telemetry),
4664
- ...options?.actor !== void 0 ? {
4665
- actor: options.actor
4666
- } : {},
4667
- instance: instance,
4668
- definition: definition,
4669
- snapshot: snapshot
4670
- };
4671
5107
  }
4672
5108
 
4673
5109
  async function ctxConditionParams(ctx, opts) {
@@ -4705,6 +5141,9 @@ async function buildEngineContext(args) {
4705
5141
  now: clock(),
4706
5142
  executionContext: resolveExecutionContext(args.executionContext),
4707
5143
  telemetry: resolveTelemetry(args.telemetry),
5144
+ ...args.settlingCohorts !== void 0 ? {
5145
+ settlingCohorts: args.settlingCohorts
5146
+ } : {},
4708
5147
  ...args.actor !== void 0 ? {
4709
5148
  actor: args.actor
4710
5149
  } : {},
@@ -4713,7 +5152,10 @@ async function buildEngineContext(args) {
4713
5152
  snapshot: await hydrateSnapshot({
4714
5153
  client: client,
4715
5154
  clientForGdr: clientForGdr,
4716
- instance: instance
5155
+ instance: instance,
5156
+ ...args.overlay !== void 0 ? {
5157
+ overlay: args.overlay
5158
+ } : {}
4717
5159
  })
4718
5160
  };
4719
5161
  }
@@ -4836,10 +5278,6 @@ function recordProcessedRequest({mutation: mutation, record: record, now: now})
4836
5278
  } ];
4837
5279
  }
4838
5280
 
4839
- const SYNC_COMMIT = {
4840
- visibility: "sync"
4841
- }, ENGINE_API_VERSION = "2026-04-29";
4842
-
4843
5281
  function resolveGuardRead(args) {
4844
5282
  const {expr: expr, ctx: ctx, deref: deref} = args;
4845
5283
  if (expr === "$self") return selfGdr(ctx.instance);
@@ -4859,66 +5297,345 @@ function resolveGuardRead(args) {
4859
5297
  const outputs = effectOutputsMap(ctx.instance)[effectsRead[1]];
4860
5298
  return effectsRead[2] !== void 0 ? getPath(outputs, effectsRead[2]) : outputs;
4861
5299
  }
4862
- 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)`);
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)`);
5301
+ }
5302
+
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);
5536
+ }
5537
+ return map.size === 0 ? doc : {
5538
+ ...doc,
5539
+ metadata: rewriteMetadata({
5540
+ metadata: doc.metadata,
5541
+ shapes: shapes,
5542
+ map: map
5543
+ })
5544
+ };
4863
5545
  }
4864
5546
 
4865
- function resolveIdRefTarget(expr, ctx) {
4866
- const value = resolveGuardRead({
4867
- expr: expr,
4868
- ctx: ctx,
4869
- deref: !1
4870
- });
4871
- if (typeof value == "string" && isGdrUri(value)) return {
4872
- parsed: parseGdr(value)
4873
- };
4874
- if (value && typeof value == "object") {
4875
- const v2 = value;
4876
- if (typeof v2.id == "string" && isGdrUri(v2.id)) return typeof v2.type == "string" ? {
4877
- parsed: parseGdr(v2.id),
4878
- type: v2.type
4879
- } : {
4880
- parsed: parseGdr(v2.id)
4881
- };
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);
4882
5556
  }
4883
- return null;
5557
+ return out;
4884
5558
  }
4885
5559
 
4886
- function resolveIdRefTargets(idRefs, ctx) {
4887
- const targets = [];
4888
- for (const expr of idRefs ?? []) {
4889
- const target = resolveIdRefTarget(expr, ctx);
4890
- if (target === null) return null;
4891
- targets.push(target);
4892
- }
4893
- return targets.length === 0 ? null : targets;
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);
4894
5565
  }
4895
5566
 
4896
- function assertSingleResource(targets) {
4897
- const resource = resourceFromParsed(targets[0].parsed);
4898
- for (const g of targets) {
4899
- const r = resourceFromParsed(g.parsed);
4900
- 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.`);
4901
- }
4902
- return resource;
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]);
4903
5574
  }
4904
5575
 
4905
- function bareIdRefs(targets) {
4906
- return targets.flatMap(g => [ g.parsed.documentId, `drafts.${g.parsed.documentId}` ]);
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);
4907
5585
  }
4908
5586
 
4909
- function resolveMatchTypes(targets, authorTypes) {
4910
- if (authorTypes !== void 0) return authorTypes;
4911
- const inferred = [ ...new Set(targets.map(g => g.type).filter(t => !!t)) ];
4912
- return inferred.length > 0 ? inferred : void 0;
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
+ };
4913
5600
  }
4914
5601
 
4915
- function resolveMetadata(metadata, ctx) {
4916
- const out = {};
4917
- for (const [k, expr] of Object.entries(metadata ?? {})) out[k] = resolveGuardRead({
4918
- expr: expr,
4919
- ctx: ctx,
4920
- deref: !0
4921
- });
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
+ }
4922
5639
  return out;
4923
5640
  }
4924
5641
 
@@ -4951,190 +5668,7 @@ function parseGuardDocument(doc) {
4951
5668
  schema: MutationGuardDocSchema,
4952
5669
  doc: doc,
4953
5670
  docType: GUARD_DOC_TYPE
4954
- });
4955
- }
4956
-
4957
- const REQUEST_TAG = {
4958
- engine: "workflow",
4959
- deploy: "workflow.deploy",
4960
- deleteDefinition: "workflow.delete-definition",
4961
- start: "workflow.start",
4962
- fireAction: "workflow.fire-action",
4963
- editField: "workflow.edit-field",
4964
- completeEffect: "workflow.complete-effect",
4965
- commitEffectOps: "workflow.commit-effect-ops",
4966
- tick: "workflow.tick",
4967
- evaluate: "workflow.evaluate",
4968
- evaluateStart: "workflow.evaluate-start",
4969
- diagnose: "workflow.diagnose",
4970
- availableActions: "workflow.available-actions",
4971
- setStage: "workflow.set-stage",
4972
- abort: "workflow.abort",
4973
- getInstance: "workflow.get-instance",
4974
- children: "workflow.children",
4975
- instancesForDocument: "workflow.instances-for-document",
4976
- definitionsForDocument: "workflow.definitions-for-document",
4977
- query: "workflow.query",
4978
- discover: "workflow.discover",
4979
- guardQuery: "workflow.guard.query",
4980
- guardDeploy: "workflow.guard.deploy",
4981
- guardRefresh: "workflow.guard.refresh",
4982
- guardRetract: "workflow.guard.retract",
4983
- effectList: "workflow.effect.list",
4984
- effectFind: "workflow.effect.find",
4985
- drain: "workflow.drain",
4986
- effect: "workflow.effect",
4987
- verifyDefinitions: "workflow.verify-definitions",
4988
- accessResolveActor: "workflow.access.resolve-actor",
4989
- accessGrants: "workflow.access.grants"
4990
- }, RAW_PATCH = /* @__PURE__ */ Symbol("workflow-engine.raw-patch");
4991
-
4992
- function unwrapPatch(patch) {
4993
- return typeof patch == "object" && patch !== null && RAW_PATCH in patch ? patch[RAW_PATCH] : patch;
4994
- }
4995
-
4996
- const wrapperCache = /* @__PURE__ */ new WeakMap, effectHandlerClientCache = /* @__PURE__ */ new WeakMap, RAW_CLIENT = /* @__PURE__ */ Symbol("workflow-engine.raw-client");
4997
-
4998
- function unwrapRequestTag(client) {
4999
- let current = client;
5000
- for (;RAW_CLIENT in current; ) current = current[RAW_CLIENT];
5001
- return current;
5002
- }
5003
-
5004
- function withRequestTag(client, fallback) {
5005
- let byTag = wrapperCache.get(client);
5006
- byTag === void 0 && (byTag = /* @__PURE__ */ new Map, wrapperCache.set(client, byTag));
5007
- const cached = byTag.get(fallback);
5008
- if (cached !== void 0) return cached;
5009
- const wrapped = buildTaggedClient(client, fallback);
5010
- return byTag.set(fallback, wrapped), wrapped;
5011
- }
5012
-
5013
- function effectHandlerClient(client) {
5014
- const cached = effectHandlerClientCache.get(client);
5015
- if (cached !== void 0) return cached;
5016
- let tagged;
5017
- if (client.withConfig === void 0) tagged = withRequestTag(client, REQUEST_TAG.effect); else {
5018
- const prefix = clientRequestTagPrefix(client), effectTag = composeRequestTag(prefix, REQUEST_TAG.effect);
5019
- tagged = client.withConfig({
5020
- requestTagPrefix: effectTag
5021
- });
5022
- }
5023
- return effectHandlerClientCache.set(client, tagged), tagged;
5024
- }
5025
-
5026
- function effectHandlerResolver(resolver) {
5027
- return mapResourceClientResolver(resolver, effectHandlerClient);
5028
- }
5029
-
5030
- function mapResourceClientResolver(resolver, mapClient) {
5031
- if (resolver !== void 0) return parsed => {
5032
- const client = resolver(parsed);
5033
- return client === void 0 ? void 0 : mapClient(client);
5034
- };
5035
- }
5036
-
5037
- function isWorkflowsFamilyPrefix(prefix) {
5038
- return prefix === void 0 ? !1 : prefix === "sanity.workflows" || prefix.startsWith("sanity.workflows.") || prefix === "sanity.workflows-mcp";
5039
- }
5040
-
5041
- function clientRequestTagPrefix(client) {
5042
- const probe = client;
5043
- return typeof probe.config == "function" ? probe.config().requestTagPrefix : void 0;
5044
- }
5045
-
5046
- function composeRequestTag(prefix, tag) {
5047
- if (prefix === void 0) return tag;
5048
- const relative = isWorkflowsFamilyPrefix(prefix) ? stripWorkflowRoot(tag) : tag;
5049
- return `${prefix}.${relative}`;
5050
- }
5051
-
5052
- function stripWorkflowRoot(tag) {
5053
- return tag.startsWith("workflow.") ? tag.slice(9) : tag;
5054
- }
5055
-
5056
- function buildTaggedClient(client, fallback) {
5057
- const strip = isWorkflowsFamilyPrefix(clientRequestTagPrefix(unwrapRequestTag(client))), normalize = tag => strip ? stripWorkflowRoot(tag) : tag, stamp = options => options === void 0 ? {
5058
- tag: normalize(fallback)
5059
- } : options.tag === void 0 ? {
5060
- ...options,
5061
- tag: normalize(fallback)
5062
- } : typeof options.tag == "string" && options.tag !== normalize(options.tag) ? {
5063
- ...options,
5064
- tag: normalize(options.tag)
5065
- } : options, wrapPatch = patch => {
5066
- let target = patch;
5067
- const wrapped = {
5068
- get [RAW_PATCH]() {
5069
- return target;
5070
- },
5071
- set: props => (target = target.set(props), wrapped),
5072
- setIfMissing: props => (target = target.setIfMissing(props), wrapped),
5073
- unset: paths => (target = target.unset(paths), wrapped),
5074
- ifRevisionId: rev => (target = target.ifRevisionId(rev), wrapped),
5075
- commit: options => target.commit(stamp(options))
5076
- };
5077
- return wrapped;
5078
- }, wrapTransaction = tx => {
5079
- let target = tx;
5080
- const wrapped = {
5081
- create: doc => (target = target.create(doc), wrapped),
5082
- patch: patch => (target = target.patch(unwrapPatch(patch)), wrapped),
5083
- delete: id => (target = target.delete(id), wrapped),
5084
- commit: options => target.commit(stamp(options))
5085
- };
5086
- return wrapped;
5087
- }, overrides = {
5088
- [RAW_CLIENT]: unwrapRequestTag(client),
5089
- fetch: (query, params, options) => client.fetch(query, params, stamp(options)),
5090
- getDocument: (id, options) => client.getDocument(id, stamp(options)),
5091
- patch: documentId => wrapPatch(client.patch(documentId)),
5092
- create: (doc, options) => client.create(doc, stamp(options)),
5093
- transaction: () => wrapTransaction(client.transaction()),
5094
- ...client.withConfig !== void 0 ? {
5095
- withConfig: config => withRequestTag(client.withConfig(config), fallback)
5096
- } : {},
5097
- ...client.action !== void 0 ? {
5098
- action: (action, options) => client.action(action, stamp(options))
5099
- } : {},
5100
- ...client.request !== void 0 ? {
5101
- request: opts => client.request(stamp(opts))
5102
- } : {}
5103
- };
5104
- return new Proxy(client, {
5105
- has: (target, property) => Object.hasOwn(overrides, property) || property in target,
5106
- get: (target, property) => {
5107
- if (Object.hasOwn(overrides, property)) return Reflect.get(overrides, property, overrides);
5108
- const value = Reflect.get(target, property, target);
5109
- return typeof value == "function" ? value.bind(target) : value;
5110
- }
5111
- });
5112
- }
5113
-
5114
- const pinCache = /* @__PURE__ */ new WeakMap, enginePinned = /* @__PURE__ */ new WeakSet;
5115
-
5116
- function pinApiVersion(client) {
5117
- const raw = unwrapRequestTag(client);
5118
- if (enginePinned.has(raw) || raw.withConfig === void 0) return client;
5119
- let sibling = pinCache.get(raw);
5120
- return sibling === void 0 && (sibling = raw.withConfig({
5121
- apiVersion: ENGINE_API_VERSION
5122
- }), pinCache.set(raw, sibling), enginePinned.add(unwrapRequestTag(sibling))), sibling;
5123
- }
5124
-
5125
- function taggedResolver(resolver, fallback) {
5126
- return mapResourceClientResolver(resolver, client => withRequestTag(pinApiVersion(client), fallback));
5127
- }
5128
-
5129
- function taggedScope(args, tag) {
5130
- const resourceClients = taggedResolver(args.resourceClients, tag);
5131
- return {
5132
- ...args,
5133
- client: withRequestTag(pinApiVersion(args.client), tag),
5134
- ...resourceClients !== void 0 ? {
5135
- resourceClients: resourceClients
5136
- } : {}
5137
- };
5671
+ });
5138
5672
  }
5139
5673
 
5140
5674
  async function fetchGuards(args) {
@@ -5350,7 +5884,8 @@ function resolvedStageGuards(args) {
5350
5884
  resolved !== null && out.push({
5351
5885
  client: args.clientForGdr(resolved.routeGdr),
5352
5886
  resourceKey: resourceGdr(resourceFromParsed(resolved.routeGdr)),
5353
- value: resolved.doc
5887
+ value: resolved.doc,
5888
+ guard: guard
5354
5889
  });
5355
5890
  }
5356
5891
  return out;
@@ -5424,11 +5959,18 @@ async function deployStageGuards(args) {
5424
5959
  if (live === void 0 || live.currentStage !== args.stageName || live.abortedAt !== void 0) return;
5425
5960
  const routed = resolvedStageGuards(args), existingByResource = await existingGuardIds(groupByResource(routed));
5426
5961
  let deployed = 0;
5427
- for (const {client: client, resourceKey: resourceKey, value: doc} of routed) {
5962
+ for (const {client: client, resourceKey: resourceKey, value: doc, guard: guard} of routed) {
5428
5963
  try {
5964
+ const localized = await localizeGuardMetadata({
5965
+ doc: doc,
5966
+ guard: guard,
5967
+ client: client,
5968
+ instance: args.instance,
5969
+ definition: args.definition
5970
+ });
5429
5971
  await upsertGuard({
5430
5972
  client: client,
5431
- doc: doc,
5973
+ doc: localized,
5432
5974
  exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
5433
5975
  });
5434
5976
  } catch (cause) {
@@ -5548,7 +6090,8 @@ async function commitAbort({ctx: ctx, reason: reason, requestRecord: requestReco
5548
6090
  ...actor !== void 0 ? {
5549
6091
  actor: actor
5550
6092
  } : {}
5551
- }), 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));
5552
6095
  await retractStageGuards({
5553
6096
  client: ctx.client,
5554
6097
  clientForGdr: ctx.clientForGdr,
@@ -5568,7 +6111,8 @@ async function commitAbort({ctx: ctx, reason: reason, requestRecord: requestReco
5568
6111
  });
5569
6112
  return {
5570
6113
  fired: !0,
5571
- stage: stage.name
6114
+ stage: stage.name,
6115
+ instance: instance
5572
6116
  };
5573
6117
  }
5574
6118
 
@@ -6319,18 +6863,42 @@ async function fetchGrants(args) {
6319
6863
  });
6320
6864
  }
6321
6865
 
6322
- 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}) {
6323
6879
  if (grants === void 0) return;
6324
6880
  const can = {};
6325
6881
  for (const permission of DOCUMENT_VALUE_PERMISSIONS) can[permission] = await grantsPermissionOn({
6326
6882
  document: instance,
6327
6883
  grants: grants,
6328
6884
  permission: permission,
6329
- userId: actor.id
6885
+ userId: identity
6330
6886
  });
6331
6887
  return can;
6332
6888
  }
6333
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
+
6334
6902
  function subjectDenialLabels(denied) {
6335
6903
  return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
6336
6904
  }
@@ -6351,10 +6919,10 @@ class ActionDisabledError extends WorkflowError {
6351
6919
 
6352
6920
  class StartNotAllowedError extends WorkflowError {
6353
6921
  definition;
6354
- insight;
6922
+ unmetRequirements;
6355
6923
  constructor(args) {
6356
- 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."),
6357
- 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;
6358
6926
  }
6359
6927
  }
6360
6928
 
@@ -6370,7 +6938,7 @@ const disabledReasonDetail = {
6370
6938
  "stage-terminal": r => `stage "${r.stage}" is terminal`,
6371
6939
  "instance-completed": r => `instance completed at ${r.completedAt}`,
6372
6940
  "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
6373
- "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`,
6941
+ "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
6374
6942
  "subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
6375
6943
  };
6376
6944
 
@@ -6439,11 +7007,10 @@ async function resolveActionCommit({ctx: ctx, activityName: activityName, action
6439
7007
  }
6440
7008
  });
6441
7009
  if (action.filter !== void 0) {
6442
- const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({
7010
+ const can = await advisoryCanForCall({
6443
7011
  instance: ctx.instance,
6444
- actor: actor,
6445
- grants: options.grants
6446
- }) : void 0;
7012
+ options: options
7013
+ });
6447
7014
  if (!await ctxEvaluateCondition({
6448
7015
  ctx: ctx,
6449
7016
  condition: action.filter,
@@ -6696,30 +7263,12 @@ async function fireTriggeredAction({ctx: ctx, mutation: mutation, activity: acti
6696
7263
  };
6697
7264
  }
6698
7265
 
6699
- async function primeInitialStage({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
6700
- 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);
6701
7268
  if (!instance || instance.stages.length > 0) return;
6702
- 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);
6703
7270
  if (stage === void 0) return;
6704
- const ctx = await buildEngineContext({
6705
- client: client,
6706
- clientForGdr: clientForGdr,
6707
- refSurface: refSurface,
6708
- instance: instance,
6709
- definition: definition,
6710
- ...clock ? {
6711
- clock: clock
6712
- } : {},
6713
- ...actor ? {
6714
- actor: actor
6715
- } : {},
6716
- ...executionContext ? {
6717
- executionContext: executionContext
6718
- } : {},
6719
- ...telemetry ? {
6720
- telemetry: telemetry
6721
- } : {}
6722
- }), now = ctx.now, discards = [], initialStageEntry = {
7271
+ const now = ctx.now, discards = [], initialStageEntry = {
6723
7272
  _key: randomKey(),
6724
7273
  name: stage.name,
6725
7274
  enteredAt: now,
@@ -6788,29 +7337,37 @@ async function primeInitialStage({client: client, instanceId: instanceId, actor:
6788
7337
 
6789
7338
  const CASCADE_LIMIT = 100;
6790
7339
 
6791
- async function runCascadeHop({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
6792
- 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({
6793
7363
  client: client,
6794
7364
  instanceId: instanceId,
6795
- options: {
6796
- clientForGdr: clientForGdr,
6797
- refSurface: refSurface,
6798
- ...actor ? {
6799
- actor: actor
6800
- } : {},
6801
- ...clock ? {
6802
- clock: clock
6803
- } : {},
6804
- ...executionContext ? {
6805
- executionContext: executionContext
6806
- } : {},
6807
- ...telemetry ? {
6808
- telemetry: telemetry
6809
- } : {},
6810
- ...overlay ? {
6811
- overlay: overlay
6812
- } : {}
6813
- }
7365
+ options: contextOptions
7366
+ }) : await buildEngineContext({
7367
+ client: client,
7368
+ instance: instance,
7369
+ definition: parseDefinitionSnapshot(instance),
7370
+ ...contextOptions
6814
7371
  });
6815
7372
  if (isTerminal(ctx)) return {
6816
7373
  moved: !1
@@ -6840,10 +7397,10 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
6840
7397
  });
6841
7398
  }
6842
7399
 
6843
- 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}) {
6844
7401
  let count = 0;
6845
7402
  for (;;) {
6846
- if (await drainCondemnedChildren({
7403
+ const drained = await drainCondemnedChildren({
6847
7404
  client: client,
6848
7405
  instanceId: instanceId,
6849
7406
  actor: actor,
@@ -6852,7 +7409,8 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6852
7409
  clock: clock,
6853
7410
  executionContext: executionContext,
6854
7411
  telemetry: telemetry
6855
- }), !(await runCascadeHop({
7412
+ });
7413
+ if (!(await runCascadeHop({
6856
7414
  client: client,
6857
7415
  instanceId: instanceId,
6858
7416
  actor: actor,
@@ -6861,7 +7419,11 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6861
7419
  clock: clock,
6862
7420
  executionContext: executionContext,
6863
7421
  telemetry: telemetry,
6864
- overlay: overlay
7422
+ overlay: overlay,
7423
+ settlingCohorts: settlingCohorts,
7424
+ ...drained.drained ? {} : {
7425
+ instance: drained.instance
7426
+ }
6865
7427
  })).moved) return count;
6866
7428
  if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
6867
7429
  instanceId: instanceId,
@@ -6870,39 +7432,50 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
6870
7432
  }
6871
7433
  }
6872
7434
 
6873
- async function drainCondemnedChildren({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining = /* @__PURE__ */ new Set}) {
6874
- 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
+ };
6875
7440
  draining.add(instanceId);
6876
- const instance = await getInstanceDocument(client, instanceId);
6877
- if (!instance) return;
7441
+ const instance = preloadedInstance ?? await getInstanceDocument(client, instanceId);
7442
+ if (!instance) return {
7443
+ instance: void 0,
7444
+ drained: !1
7445
+ };
6878
7446
  const condemned = condemnedSubworkflows(instance);
6879
- if (condemned.length !== 0) {
6880
- for (const row of condemned) await settleCondemnedRow({
6881
- client: client,
6882
- ownerId: instanceId,
6883
- row: row,
6884
- actor: actor,
6885
- clientForGdr: clientForGdr,
6886
- refSurface: refSurface,
6887
- clock: clock,
6888
- executionContext: executionContext,
6889
- telemetry: telemetry,
6890
- draining: draining
6891
- });
6892
- await stampDrainedRows({
6893
- client: client,
6894
- instance: instance,
6895
- condemned: condemned,
6896
- clock: clock,
6897
- executionContext: executionContext
6898
- });
6899
- }
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
+ };
6900
7473
  }
6901
7474
 
6902
7475
  async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining}) {
6903
7476
  const childId = toBareId(row.ref.id);
6904
7477
  try {
6905
- await abortInstance({
7478
+ const result = await abortInstance({
6906
7479
  client: client,
6907
7480
  instanceId: childId,
6908
7481
  reason: row.abortPending?.reason ?? "condemned by parent",
@@ -6922,7 +7495,8 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6922
7495
  telemetry: telemetry
6923
7496
  } : {}
6924
7497
  }
6925
- }), await drainCondemnedChildren({
7498
+ });
7499
+ await drainCondemnedChildren({
6926
7500
  client: client,
6927
7501
  instanceId: childId,
6928
7502
  actor: actor,
@@ -6931,7 +7505,10 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6931
7505
  clock: clock,
6932
7506
  executionContext: executionContext,
6933
7507
  telemetry: telemetry,
6934
- draining: draining
7508
+ draining: draining,
7509
+ ...result.fired ? {
7510
+ instance: result.instance
7511
+ } : {}
6935
7512
  });
6936
7513
  } catch (cause) {
6937
7514
  if (cause instanceof InstanceNotFoundError) return;
@@ -6944,9 +7521,7 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
6944
7521
  }
6945
7522
 
6946
7523
  async function stampDrainedRows({client: client, instance: instance, condemned: condemned, clock: clock, executionContext: executionContext}) {
6947
- 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}", {
6948
- ids: ids
6949
- }), 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 => {
6950
7525
  if (row.resolved !== void 0 || !condemnedKeys.has(row._key)) return row;
6951
7526
  const child = byId.get(toBareId(row.ref.id)), resolved = child !== void 0 ? terminalResolution(child) : {
6952
7527
  at: now,
@@ -6968,6 +7543,16 @@ async function stampDrainedRows({client: client, instance: instance, condemned:
6968
7543
  }).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
6969
7544
  }
6970
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
+
6971
7556
  function terminalResolution(child) {
6972
7557
  const status = resolvedChildStatus(child);
6973
7558
  if (!(status === void 0 || child.completedAt === void 0 || child.completedAt === null)) return {
@@ -6990,28 +7575,18 @@ function subworkflowResolvedEntry({row: row, at: at, status: status}) {
6990
7575
  };
6991
7576
  }
6992
7577
 
6993
- async function propagateToAncestors({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
6994
- const loaded = await loadPropagationPair(client, instanceId);
6995
- if (loaded === void 0) return;
6996
- 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({
6997
7580
  client: client,
6998
- clientForGdr: clientForGdr,
6999
- refSurface: refSurface,
7000
- instance: parent,
7001
- definition: definition,
7002
- ...clock ? {
7003
- clock: clock
7004
- } : {},
7005
- ...actor ? {
7006
- actor: actor
7007
- } : {},
7008
- ...executionContext ? {
7009
- executionContext: executionContext
7010
- } : {},
7011
- ...telemetry ? {
7012
- telemetry: telemetry
7581
+ instanceId: instanceId,
7582
+ ...instance !== void 0 ? {
7583
+ instance: instance
7013
7584
  } : {}
7014
- }), 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);
7015
7590
  if (row === void 0) {
7016
7591
  await recordOrphanedPropagation({
7017
7592
  ctx: ctx,
@@ -7026,29 +7601,11 @@ async function propagateToAncestors({client: client, instanceId: instanceId, act
7026
7601
  child: child,
7027
7602
  now: ctx.now
7028
7603
  }), parentTerminal = parent.completedAt !== void 0;
7029
- changed && await persist(ctx, mutation), !parentTerminal && (await cascadeAutoTransitions({
7030
- client: client,
7031
- instanceId: parent._id,
7032
- actor: actor,
7033
- clientForGdr: clientForGdr,
7034
- refSurface: refSurface,
7035
- clock: clock,
7036
- executionContext: executionContext,
7037
- telemetry: telemetry
7038
- }), await propagateToAncestors({
7039
- client: client,
7040
- instanceId: parent._id,
7041
- actor: actor,
7042
- clientForGdr: clientForGdr,
7043
- refSurface: refSurface,
7044
- clock: clock,
7045
- executionContext: executionContext,
7046
- telemetry: telemetry
7047
- }));
7604
+ changed && await persist(ctx, mutation), !parentTerminal && await cascadeAndPropagateParent(stepArgs, parent);
7048
7605
  }
7049
7606
 
7050
- async function loadPropagationPair(client, instanceId) {
7051
- const child = await getInstanceDocument(client, instanceId);
7607
+ async function loadPropagationPair({client: client, instanceId: instanceId, instance: instance}) {
7608
+ const child = instance ?? await getInstanceDocument(client, instanceId);
7052
7609
  if (!child) return;
7053
7610
  const parentGdr = parentRef(child);
7054
7611
  if (parentGdr === void 0) return;
@@ -7059,6 +7616,83 @@ async function loadPropagationPair(client, instanceId) {
7059
7616
  };
7060
7617
  }
7061
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
+
7062
7696
  function stampResolvedChild({mutation: mutation, row: row, child: child, now: now}) {
7063
7697
  if (row.resolved !== void 0) return !1;
7064
7698
  const resolved = terminalResolution(child);
@@ -7194,36 +7828,14 @@ async function persist(ctx, mutation) {
7194
7828
  for (const {body: body} of pendingCreates) tx.create(body);
7195
7829
  tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)),
7196
7830
  await tx.commit(), mutation.pendingCreates = [];
7197
- const actorForPriming = ctx.actor;
7198
- for (const {body: body, started: started} of pendingCreates) try {
7199
- await primeInitialStage({
7200
- client: ctx.client,
7201
- instanceId: body._id,
7202
- actor: actorForPriming,
7203
- clientForGdr: ctx.clientForGdr,
7204
- refSurface: ctx.refSurface,
7205
- clock: ctx.clock,
7206
- executionContext: ctx.executionContext,
7207
- telemetry: ctx.telemetry
7208
- }), await cascadeAutoTransitions({
7209
- client: ctx.client,
7210
- instanceId: body._id,
7211
- actor: actorForPriming,
7212
- clientForGdr: ctx.clientForGdr,
7213
- refSurface: ctx.refSurface,
7214
- clock: ctx.clock,
7215
- executionContext: ctx.executionContext,
7216
- telemetry: ctx.telemetry
7217
- }), await propagateToAncestors({
7218
- 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, {
7219
7834
  instanceId: body._id,
7220
7835
  actor: actorForPriming,
7221
- clientForGdr: ctx.clientForGdr,
7222
- refSurface: ctx.refSurface,
7223
- clock: ctx.clock,
7224
- executionContext: ctx.executionContext,
7225
- telemetry: ctx.telemetry
7226
- }), ctx.telemetry.log(WorkflowInstanceStarted, started);
7836
+ settlingCohorts: settlingCohorts
7837
+ });
7838
+ await primeInitialStage(stepArgs), await cascadeAutoTransitions(stepArgs);
7227
7839
  } catch (cause) {
7228
7840
  throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
7229
7841
  instanceId: ctx.instance._id,
@@ -7231,11 +7843,50 @@ async function persist(ctx, mutation) {
7231
7843
  reason: `spawned child "${body._id}" failed to settle after the spawn transaction committed`
7232
7844
  });
7233
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);
7234
7852
  const reloaded = await getInstanceDocument(ctx.client, ctx.instance._id);
7235
7853
  if (!reloaded) throw new Error(`Instance ${ctx.instance._id} disappeared after transaction commit`);
7236
7854
  return reloaded;
7237
7855
  }
7238
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
+
7239
7890
  function stampExecutionContext(ctx, mutation) {
7240
7891
  const stamp = ctx.executionContext, appended = mutation.history.slice(ctx.instance.history.length);
7241
7892
  mutation.history = [ ...mutation.history.slice(0, ctx.instance.history.length), ...stampHistoryEntries(appended, stamp) ];
@@ -7363,24 +8014,25 @@ function buildEffectSettlement(pending, outcome) {
7363
8014
  }
7364
8015
 
7365
8016
  function validateEffectOutputs(args) {
7366
- 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 = {};
7367
8018
  for (const [key, value] of Object.entries(outputs)) {
7368
8019
  const shape = byName.get(key);
7369
8020
  if (shape === void 0) {
7370
8021
  issues.push(`"${key}" is not a declared output`);
7371
8022
  continue;
7372
8023
  }
7373
- const shapeIssues = effectOutputIssues(shape, value);
7374
- 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;
7375
8026
  }
7376
8027
  if (issues.length > 0) throw new EffectOutputsInvalidError({
7377
8028
  effect: effectName,
7378
8029
  issues: issues
7379
8030
  });
8031
+ return normalized;
7380
8032
  }
7381
8033
 
7382
- function effectOutputIssues(shape, value) {
7383
- return checkFieldValue({
8034
+ function effectOutputCheck(shape, value) {
8035
+ return parseFieldValue({
7384
8036
  entryType: shape.type,
7385
8037
  value: value,
7386
8038
  ...shape.fields !== void 0 ? {
@@ -7426,15 +8078,15 @@ function validateCompletionInput({pending: pending, definition: definition, stat
7426
8078
  effect: pending.name,
7427
8079
  issues: [ "outputs cannot accompany a failed completion — report outputs on a done completion instead" ]
7428
8080
  });
7429
- if (outputs !== void 0) {
7430
- const declared = findEffect(definition, pending.name)?.outputs ?? [];
7431
- validateEffectOutputs({
7432
- outputs: outputs,
7433
- declared: declared,
7434
- effectName: pending.name
7435
- });
7436
- }
7437
- 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
+ };
7438
8090
  }
7439
8091
 
7440
8092
  async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, requestRecord: requestRecord, actor: actor}) {
@@ -7443,7 +8095,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
7443
8095
  record: requestRecord,
7444
8096
  now: ctx.now
7445
8097
  });
7446
- const pending = requirePendingEffect(ctx.instance, effectKey), validatedOps = validateCompletionInput({
8098
+ const pending = requirePendingEffect(ctx.instance, effectKey), {ops: validatedOps, outputs: validatedOutputs} = validateCompletionInput({
7447
8099
  pending: pending,
7448
8100
  definition: ctx.definition,
7449
8101
  status: status,
@@ -7462,7 +8114,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
7462
8114
  detail: detail,
7463
8115
  error: error,
7464
8116
  durationMs: durationMs,
7465
- outputs: outputs
8117
+ outputs: validatedOutputs
7466
8118
  });
7467
8119
  mutation.effectHistory.push(settlement.run);
7468
8120
  const wroteEffectOutputs = status === "done" && outputs !== void 0;
@@ -7660,6 +8312,64 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
7660
8312
  };
7661
8313
  }
7662
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
+
7663
8373
  const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ new WeakMap;
7664
8374
 
7665
8375
  async function resolveAccess(taggedClient, args = {}) {
@@ -7669,10 +8379,13 @@ async function resolveAccess(taggedClient, args = {}) {
7669
8379
  client: client,
7670
8380
  requestFn: requestFn,
7671
8381
  resourcePath: args.grantsFromPath
7672
- }) : Promise.resolve(void 0), [actor, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
7673
- 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.");
7674
8384
  return {
7675
- actor: actor,
8385
+ actor: identity.actor,
8386
+ ...identity.localPrincipalId !== void 0 ? {
8387
+ localPrincipalId: identity.localPrincipalId
8388
+ } : {},
7676
8389
  ...grants !== void 0 ? {
7677
8390
  grants: grants
7678
8391
  } : {}
@@ -7682,7 +8395,7 @@ async function resolveAccess(taggedClient, args = {}) {
7682
8395
  function cachedActor(client, requestFn) {
7683
8396
  const cached = actorCache.get(client);
7684
8397
  if (cached !== void 0) return cached;
7685
- const pending = fetchActor(requestFn).catch(err => {
8398
+ const pending = fetchActor(client, requestFn).catch(err => {
7686
8399
  throw actorCache.get(client) === pending && actorCache.delete(client), err;
7687
8400
  });
7688
8401
  return actorCache.set(client, pending), pending;
@@ -7709,27 +8422,90 @@ function cachedGrants({client: client, requestFn: requestFn, resourcePath: resou
7709
8422
  byPath.set(resourcePath, cached)), cached;
7710
8423
  }
7711
8424
 
7712
- async function fetchActor(requestFn) {
7713
- 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) {
7714
8462
  try {
7715
- user = await requestFn({
8463
+ return await requestFn({
7716
8464
  uri: "/users/me",
7717
8465
  tag: REQUEST_TAG.accessResolveActor
7718
8466
  });
7719
8467
  } catch (err) {
7720
- 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.`, {
7721
8469
  cause: err
7722
8470
  });
7723
8471
  }
7724
- if (!user || typeof user.id != "string" || user.id.length === 0) return;
7725
- const roleNames = user.roles?.map(r => r.name).filter(n => !!n) ?? [];
7726
- return {
7727
- kind: "person",
7728
- id: user.id,
7729
- ...roleNames.length > 0 ? {
7730
- roles: roleNames
7731
- } : {}
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)"
7732
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"
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
+ }
7733
8509
  }
7734
8510
 
7735
8511
  async function fetchGrantsCached(requestFn, resourcePath) {
@@ -7905,12 +8681,25 @@ async function subjectResourceGrants(args) {
7905
8681
  const resolved = await Promise.all([ ...clients.entries() ].map(async ([key, entry]) => {
7906
8682
  const path = aclPathForResource(entry.resource);
7907
8683
  if (path === void 0) return;
7908
- const grants = await grantsForClientPath(entry.client, path);
7909
- 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
+ } ];
7910
8689
  }));
7911
8690
  return new Map(resolved.filter(entry => entry !== void 0));
7912
8691
  }
7913
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
+
7914
8703
  async function evaluateInstance(args) {
7915
8704
  const {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, resourceClients: resourceClients} = args, now = (args.clock ?? wallClock)();
7916
8705
  validateTag(tag);
@@ -7922,7 +8711,7 @@ async function evaluateInstance(args) {
7922
8711
  client: client,
7923
8712
  instanceId: instanceId,
7924
8713
  tag: tag
7925
- }) ]), {actor: actor, grants: grants} = access, definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
8714
+ }) ]), {actor: actor, grants: grants, localPrincipalId: localPrincipalId} = access, definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
7926
8715
  client: client,
7927
8716
  workflowResource: workflowResource,
7928
8717
  resourceClients: resourceClients
@@ -7942,6 +8731,9 @@ async function evaluateInstance(args) {
7942
8731
  guards: guards,
7943
8732
  now: now,
7944
8733
  resourceGrants: resourceGrants,
8734
+ ...localPrincipalId !== void 0 ? {
8735
+ localPrincipalId: localPrincipalId
8736
+ } : {},
7945
8737
  ...grants !== void 0 ? {
7946
8738
  grants: grants
7947
8739
  } : {}
@@ -7997,9 +8789,12 @@ async function evaluateFromSnapshot(args) {
7997
8789
  definition: definition,
7998
8790
  snapshot: snapshot,
7999
8791
  now: now
8000
- }, can = await advisoryCan({
8001
- instance: instance,
8792
+ }, anchorIdentity = lakePrincipalId({
8002
8793
  actor: actor,
8794
+ localPrincipalId: args.localPrincipalId
8795
+ }), can = await advisoryCan({
8796
+ instance: instance,
8797
+ identity: anchorIdentity,
8003
8798
  grants: grants
8004
8799
  }), scope = await renderConditionScope(scopeSource, {
8005
8800
  actor: actor,
@@ -8016,11 +8811,10 @@ async function evaluateFromSnapshot(args) {
8016
8811
  activityName: activityName
8017
8812
  })), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
8018
8813
  instance: instance,
8019
- actor: actor,
8814
+ identity: anchorIdentity,
8020
8815
  guards: args.guards
8021
8816
  }), subjectDenials = await forecastSubjectDenials({
8022
8817
  instance: instance,
8023
- actor: actor,
8024
8818
  snapshot: snapshot,
8025
8819
  resourceGrants: args.resourceGrants
8026
8820
  }), subjectPermissionReason = subjectDenials.length > 0 ? {
@@ -8164,20 +8958,20 @@ async function editPredicateInsight(args) {
8164
8958
  }
8165
8959
 
8166
8960
  async function forecastSubjectDenials(args) {
8167
- const {instance: instance, actor: actor, snapshot: snapshot, resourceGrants: resourceGrants} = args;
8961
+ const {instance: instance, snapshot: snapshot, resourceGrants: resourceGrants} = args;
8168
8962
  if (resourceGrants === void 0 || resourceGrants.size === 0) return [];
8169
8963
  const denials = [], seen = /* @__PURE__ */ new Set;
8170
8964
  for (const {ref: ref, parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
8171
8965
  if (seen.has(ref.id)) continue;
8172
8966
  seen.add(ref.id);
8173
- const grants = resourceGrants.get(resourceGdr(resource));
8174
- if (grants === void 0) continue;
8967
+ const access = resourceGrants.get(resourceGdr(resource));
8968
+ if (access === void 0) continue;
8175
8969
  const doc = snapshot.docs.find(d => d._id === ref.id);
8176
8970
  doc !== void 0 && await subjectUpdateAllowed({
8177
8971
  doc: doc,
8178
8972
  parsed: parsed,
8179
- grants: grants,
8180
- actorId: actor.id
8973
+ grants: access.grants,
8974
+ actorId: access.actorId
8181
8975
  }) === !1 && denials.push({
8182
8976
  subject: ref.id,
8183
8977
  resource: resourceGdr(resource),
@@ -8258,12 +9052,12 @@ async function explainActivityConditions({activity: activity, activityScope: act
8258
9052
  snapshot: snapshot,
8259
9053
  sites: sites
8260
9054
  }), requirementEntries = [];
8261
- 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({
8262
9056
  kind: "requirement",
8263
9057
  activity: activity.name,
8264
- requirement: name
8265
- }, condition) ]);
8266
- 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({
8267
9061
  site: {
8268
9062
  kind: "activity-filter",
8269
9063
  activity: activity.name
@@ -8409,12 +9203,12 @@ function actionEvaluationIdentity(action) {
8409
9203
  };
8410
9204
  }
8411
9205
 
8412
- async function instanceGuardReason({instance: instance, actor: actor, guards: guards}) {
9206
+ async function instanceGuardReason({instance: instance, identity: identity, guards: guards}) {
8413
9207
  if (guards === void 0 || guards.length === 0) return;
8414
9208
  const denied = await instanceWriteDenials({
8415
9209
  instance: instance,
8416
9210
  guards: guards,
8417
- identity: actor.id
9211
+ identity: identity
8418
9212
  });
8419
9213
  if (denied.length !== 0) return {
8420
9214
  kind: "mutation-guard-denied",
@@ -8453,12 +9247,12 @@ function inFlightFilter() {
8453
9247
  return "!defined(completedAt)";
8454
9248
  }
8455
9249
 
8456
- 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";
8457
9251
 
8458
9252
  function documentPrefilter(documents, params) {
8459
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)}`);
8460
9254
  return params.documents = [ ...documents ], params.bareIds = documents.map(extractDocumentId),
8461
- `_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}`;
8462
9256
  }
8463
9257
 
8464
9258
  function documentArm(filter, params) {
@@ -8884,11 +9678,10 @@ async function commitEdit({ctx: ctx, target: target, mode: mode, value: value, r
8884
9678
  }
8885
9679
 
8886
9680
  async function assertFieldEditable({ctx: ctx, site: site, options: options}) {
8887
- 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({
8888
9682
  instance: ctx.instance,
8889
- actor: actor,
8890
- grants: options.grants
8891
- }) : 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({
8892
9685
  ctx: ctx,
8893
9686
  condition: site.effective,
8894
9687
  opts: {
@@ -9246,8 +10039,7 @@ async function abortAndPropagate(args) {
9246
10039
  executionContext: executionContext,
9247
10040
  telemetry: telemetry
9248
10041
  })
9249
- });
9250
- return await drainCondemnedChildren({
10042
+ }), drained = await drainCondemnedChildren({
9251
10043
  client: client,
9252
10044
  instanceId: instanceId,
9253
10045
  actor: actor,
@@ -9259,8 +10051,12 @@ async function abortAndPropagate(args) {
9259
10051
  } : {},
9260
10052
  ...telemetry !== void 0 ? {
9261
10053
  telemetry: telemetry
10054
+ } : {},
10055
+ ...result.fired ? {
10056
+ instance: result.instance
9262
10057
  } : {}
9263
- }), await propagateToAncestors({
10058
+ });
10059
+ return await propagateToAncestors({
9264
10060
  client: client,
9265
10061
  instanceId: instanceId,
9266
10062
  actor: actor,
@@ -9272,6 +10068,9 @@ async function abortAndPropagate(args) {
9272
10068
  } : {},
9273
10069
  ...telemetry !== void 0 ? {
9274
10070
  telemetry: telemetry
10071
+ } : {},
10072
+ ...result.fired && !drained.drained ? {
10073
+ instance: drained.instance
9275
10074
  } : {}
9276
10075
  }), result.fired;
9277
10076
  }
@@ -9379,6 +10178,9 @@ function buildEngineCallOptions(args) {
9379
10178
  actor: args.actor,
9380
10179
  clientForGdr: args.clientForGdr,
9381
10180
  refSurface: args.refSurface,
10181
+ ...args.localPrincipalId !== void 0 ? {
10182
+ localPrincipalId: args.localPrincipalId
10183
+ } : {},
9382
10184
  ...args.grants !== void 0 ? {
9383
10185
  grants: args.grants
9384
10186
  } : {},
@@ -9520,6 +10322,15 @@ async function abortInstances(args) {
9520
10322
  return aborted;
9521
10323
  }
9522
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
+
9523
10334
  async function fetchStartSlice(args) {
9524
10335
  const {client: client, tag: tag} = args, {query: query, params: params} = instancesQuery({
9525
10336
  tag: tag,
@@ -9527,7 +10338,7 @@ async function fetchStartSlice(args) {
9527
10338
  includeCompleted: !0
9528
10339
  }
9529
10340
  });
9530
- return (await client.fetch(query, params)).map(assertReadableModel);
10341
+ return (await client.fetch(query, params)).map(assertReadableModel).map(projectStartSliceRow);
9531
10342
  }
9532
10343
 
9533
10344
  const workflow = {
@@ -9676,6 +10487,9 @@ const workflow = {
9676
10487
  ...access.grants !== void 0 ? {
9677
10488
  grants: access.grants
9678
10489
  } : {},
10490
+ ...access.localPrincipalId !== void 0 ? {
10491
+ localPrincipalId: access.localPrincipalId
10492
+ } : {},
9679
10493
  clock: clock,
9680
10494
  clientForGdr: clientForGdr,
9681
10495
  refSurface: refSurface,
@@ -9744,6 +10558,9 @@ const workflow = {
9744
10558
  ...access.grants !== void 0 ? {
9745
10559
  grants: access.grants
9746
10560
  } : {},
10561
+ ...access.localPrincipalId !== void 0 ? {
10562
+ localPrincipalId: access.localPrincipalId
10563
+ } : {},
9747
10564
  clock: clock,
9748
10565
  clientForGdr: clientForGdr,
9749
10566
  refSurface: refSurface,
@@ -9903,7 +10720,7 @@ const workflow = {
9903
10720
  });
9904
10721
  },
9905
10722
  tick: async rawArgs => {
9906
- 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({
9907
10724
  client: client,
9908
10725
  instanceId: instanceId,
9909
10726
  tag: tag
@@ -9911,7 +10728,7 @@ const workflow = {
9911
10728
  await assertInstanceWriteAllowed({
9912
10729
  instance: current,
9913
10730
  guards: guards,
9914
- identity: actor.id
10731
+ identity: lakePrincipalId(access)
9915
10732
  });
9916
10733
  const {cascaded: cascaded, instance: instance} = await cascadeAndReload({
9917
10734
  client: client,
@@ -10028,6 +10845,59 @@ const workflow = {
10028
10845
  }
10029
10846
  });
10030
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
+ },
10031
10901
  getInstance: async rawArgs => {
10032
10902
  const args = taggedScope(rawArgs, REQUEST_TAG.getInstance), {client: client, tag: tag, instanceId: instanceId} = args;
10033
10903
  return validateTag(tag), reload({
@@ -10174,21 +11044,18 @@ const workflow = {
10174
11044
  return (await client.fetch(query, params)).map(readInstanceDoc).filter(instance => instanceWatchesDocument(instance, document));
10175
11045
  },
10176
11046
  definitionsForDocument: async rawArgs => {
10177
- 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;
10178
11048
  validateTag(tag);
10179
- const clock = args.clock ?? wallClock, deployed = (await client.fetch(definitionsListGroq("desc"), {
11049
+ const clock = args.clock ?? wallClock, deployed = (await client.fetch(latestDefinitionsGroq(), {
10180
11050
  tag: tag
10181
- })).map(assertReadableModel), latest = latestDeployedDefinitions(deployed);
11051
+ })).map(assertReadableModel);
10182
11052
  let slice;
10183
11053
  return applicableDefinitions({
10184
- definitions: latest,
11054
+ definitions: deployed,
10185
11055
  document: document,
10186
11056
  scope: {
10187
11057
  tag: tag,
10188
11058
  now: clock(),
10189
- ...subject !== void 0 ? {
10190
- subject: subject
10191
- } : {},
10192
11059
  fetchDataset: () => slice ??= fetchStartSlice({
10193
11060
  client: client,
10194
11061
  tag: tag
@@ -10211,34 +11078,17 @@ const workflow = {
10211
11078
  }), missingRequired = missingRequiredInputs({
10212
11079
  entryDefs: definition.fields ?? [],
10213
11080
  initialFields: initialFields ?? []
10214
- }), allowed = definition.start?.allowed;
10215
- if (allowed === void 0) return {
10216
- allowed: invalidInitialFields.length === 0,
10217
- outcome: "satisfied",
10218
- unboundReads: [],
10219
- missingRequired: missingRequired,
10220
- invalidInitialFields: invalidInitialFields
10221
- };
10222
- const unboundReads = unboundAllowedReadsWithSubject({
10223
- allowed: allowed,
10224
- fields: startFieldsParam({
10225
- entryDefs: definition.fields ?? [],
10226
- initialFields: initialFields ?? []
10227
- }),
10228
- definition: definition
10229
- }), insight = await startAllowedInsight({
11081
+ }), verdict = await startRequirementVerdict({
10230
11082
  client: client,
10231
11083
  tag: tag,
10232
11084
  definition: definition,
10233
- allowed: allowed,
10234
11085
  initialFields: initialFields ?? [],
10235
- now: clock()
10236
- }), provisional = unboundReads.length > 0;
11086
+ now: clock(),
11087
+ preflight: !0
11088
+ });
10237
11089
  return {
10238
- allowed: invalidInitialFields.length === 0 && !provisional && insight.outcome === "satisfied",
10239
- outcome: provisional ? "unevaluable" : insight.outcome,
10240
- insight: insight,
10241
- unboundReads: unboundReads,
11090
+ ...verdict,
11091
+ allowed: invalidInitialFields.length === 0 && verdict.allowed,
10242
11092
  missingRequired: missingRequired,
10243
11093
  invalidInitialFields: invalidInitialFields
10244
11094
  };
@@ -10250,11 +11100,10 @@ const workflow = {
10250
11100
  }
10251
11101
  };
10252
11102
 
10253
- async function startAllowedInsight(args) {
10254
- const {client: client, tag: tag, definition: definition, allowed: allowed, initialFields: initialFields, now: now} = args;
10255
- return explainStartAllowed({
10256
- allowed: allowed,
10257
- definition: definition,
11103
+ function startPredicateContext(args) {
11104
+ const {client: client, tag: tag, definition: definition, initialFields: initialFields, now: now} = args;
11105
+ let slice;
11106
+ return {
10258
11107
  fields: startFieldsParam({
10259
11108
  entryDefs: definition.fields ?? [],
10260
11109
  initialFields: initialFields
@@ -10262,12 +11111,71 @@ async function startAllowedInsight(args) {
10262
11111
  scope: {
10263
11112
  tag: tag,
10264
11113
  now: now,
10265
- fetchDataset: () => fetchStartSlice({
11114
+ fetchDataset: () => slice ??= fetchStartSlice({
10266
11115
  client: client,
10267
11116
  tag: tag
10268
11117
  })
10269
11118
  }
10270
- });
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
+ };
10271
11179
  }
10272
11180
 
10273
11181
  async function startFreshInstance(args) {
@@ -10367,19 +11275,16 @@ async function assertStartInputs(args) {
10367
11275
  initialFields: initialFields,
10368
11276
  definitionName: definition.name
10369
11277
  });
10370
- const allowed = definition.start?.allowed;
10371
- if (allowed === void 0) return;
10372
- const insight = await startAllowedInsight({
11278
+ const unmetRequirements = (await startRequirementVerdict({
10373
11279
  client: client,
10374
11280
  tag: tag,
10375
11281
  definition: definition,
10376
- allowed: allowed,
10377
11282
  initialFields: initialFields,
10378
11283
  now: now
10379
- });
10380
- if (insight.outcome !== "satisfied") throw new StartNotAllowedError({
11284
+ })).requirements.filter(requirement => requirement.outcome !== "satisfied").map(requirementDescriptor);
11285
+ if (unmetRequirements.length > 0) throw new StartNotAllowedError({
10381
11286
  definition: definition.name,
10382
- insight: insight
11287
+ unmetRequirements: unmetRequirements
10383
11288
  });
10384
11289
  }
10385
11290
 
@@ -10412,97 +11317,6 @@ async function resolveStartFields(args) {
10412
11317
  };
10413
11318
  }
10414
11319
 
10415
- function isRecord(value) {
10416
- return typeof value == "object" && value !== null && !Array.isArray(value);
10417
- }
10418
-
10419
- function isClientProjectUser(value) {
10420
- 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");
10421
- }
10422
-
10423
- function objectProperty(value, property) {
10424
- if (!isRecord(value)) return;
10425
- const propertyValue = value[property];
10426
- return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
10427
- }
10428
-
10429
- function stringProperty(value, property) {
10430
- const propertyValue = Reflect.get(value, property);
10431
- return typeof propertyValue == "string" ? propertyValue : void 0;
10432
- }
10433
-
10434
- function apiErrorType(error) {
10435
- const response = objectProperty(error, "response"), body = objectProperty(response, "body");
10436
- if (!body) return;
10437
- const nestedError = objectProperty(body, "error");
10438
- return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
10439
- }
10440
-
10441
- function isProjectUserNotFoundError(error) {
10442
- return apiErrorType(error) === "projectUserNotFoundError";
10443
- }
10444
-
10445
- function clientProjectUserDirectory(client, projectId) {
10446
- return {
10447
- findById: async id => {
10448
- if (!client.request) return {
10449
- status: "inaccessible",
10450
- cause: new Error("Project-user resolution requires WorkflowClient.request")
10451
- };
10452
- try {
10453
- const response = await client.request({
10454
- uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
10455
- }), candidate = Array.isArray(response) ? response[0] : response;
10456
- return candidate == null ? {
10457
- status: "missing"
10458
- } : isClientProjectUser(candidate) ? {
10459
- status: "resolved",
10460
- user: candidate
10461
- } : {
10462
- status: "inaccessible",
10463
- cause: new Error("Project-user response had an invalid shape")
10464
- };
10465
- } catch (cause) {
10466
- return isProjectUserNotFoundError(cause) ? {
10467
- status: "missing"
10468
- } : {
10469
- status: "inaccessible",
10470
- cause: cause
10471
- };
10472
- }
10473
- }
10474
- };
10475
- }
10476
-
10477
- function resolveClientActor(client, args) {
10478
- return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
10479
- }
10480
-
10481
- async function resolveActor(directory, actor) {
10482
- if (actor.kind !== "person") return {
10483
- status: "not-person",
10484
- actor: actor
10485
- };
10486
- const personActor = {
10487
- ...actor,
10488
- kind: "person"
10489
- }, lookup = await directory.findById(personActor.id);
10490
- return lookup.status === "resolved" ? {
10491
- status: "resolved",
10492
- actor: personActor,
10493
- user: lookup.user
10494
- } : lookup.status === "inaccessible" ? {
10495
- status: "inaccessible",
10496
- actor: personActor,
10497
- ...lookup.cause === void 0 ? {} : {
10498
- cause: lookup.cause
10499
- }
10500
- } : {
10501
- status: "missing",
10502
- actor: personActor
10503
- };
10504
- }
10505
-
10506
11320
  const EFFECT_COMMIT_QUEUE_DEPTH = 32, EFFECT_COMMIT_DISPATCH_CAP = 200;
10507
11321
 
10508
11322
  class EffectCommitQueueOverflowError extends WorkflowError {
@@ -11128,17 +11942,23 @@ function createInstanceSession(args) {
11128
11942
  refSurface: evalScope.refSurface
11129
11943
  });
11130
11944
  }, evaluateWith = async ({held: held, guards: guards, self: self}) => {
11131
- const {actor: actor, grants: grants} = await access(), resourceGrants = await subjectResourceGrants({
11945
+ const {actor: actor, localPrincipalId: localPrincipalId, grants: grants} = await access(), resourceGrants = await subjectResourceGrants({
11132
11946
  clientForGdr: evalScope.clientForGdr,
11133
11947
  instance: instance
11948
+ }), normalizedSelf = await normalizeInstanceIdentities({
11949
+ client: evalScope.client,
11950
+ instance: self
11134
11951
  });
11135
11952
  return evaluateFromSnapshot({
11136
- instance: self,
11953
+ instance: normalizedSelf,
11137
11954
  definition: definitionOf(),
11138
11955
  actor: actor,
11139
- snapshot: snapshotFrom(held, self),
11956
+ snapshot: snapshotFrom(held, normalizedSelf),
11140
11957
  guards: guards,
11141
11958
  resourceGrants: resourceGrants,
11959
+ ...localPrincipalId !== void 0 ? {
11960
+ localPrincipalId: localPrincipalId
11961
+ } : {},
11142
11962
  ...clock !== void 0 ? {
11143
11963
  now: clock()
11144
11964
  } : {},
@@ -11184,8 +12004,13 @@ function createInstanceSession(args) {
11184
12004
  committing = !0;
11185
12005
  const held = new Map(overlay);
11186
12006
  try {
11187
- const {actor: actor} = await access();
11188
- 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);
11189
12014
  } finally {
11190
12015
  if (committing = !1, buffered !== void 0) {
11191
12016
  const next = buffered;
@@ -11260,11 +12085,14 @@ function createInstanceSession(args) {
11260
12085
  site !== void 0 && previews.delete(siteKey(site));
11261
12086
  },
11262
12087
  tick() {
11263
- return commit(async (actor, held) => {
12088
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11264
12089
  await assertInstanceWriteAllowed({
11265
12090
  instance: instance,
11266
12091
  guards: heldGuards,
11267
- identity: actor.id
12092
+ identity: lakePrincipalId({
12093
+ actor: actor,
12094
+ localPrincipalId: localPrincipalId
12095
+ })
11268
12096
  });
11269
12097
  const before = await reload({
11270
12098
  client: tickScope.client,
@@ -11291,7 +12119,7 @@ function createInstanceSession(args) {
11291
12119
  });
11292
12120
  },
11293
12121
  fireAction({activity: activity, action: action, params: params}) {
11294
- return commit(async (actor, held) => {
12122
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11295
12123
  assertActionAllowed({
11296
12124
  evaluation: await evaluateWith({
11297
12125
  held: held,
@@ -11307,6 +12135,9 @@ function createInstanceSession(args) {
11307
12135
  activity: activity,
11308
12136
  action: action,
11309
12137
  actor: actor,
12138
+ ...localPrincipalId !== void 0 ? {
12139
+ localPrincipalId: localPrincipalId
12140
+ } : {},
11310
12141
  clientForGdr: fireScope.clientForGdr,
11311
12142
  refSurface: fireScope.refSurface,
11312
12143
  ...grants !== void 0 ? {
@@ -11349,7 +12180,7 @@ function createInstanceSession(args) {
11349
12180
  } catch (err) {
11350
12181
  return Promise.reject(err);
11351
12182
  }
11352
- return commit(async (actor, held) => {
12183
+ return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
11353
12184
  assertEditAllowed(await evaluateWith({
11354
12185
  held: held,
11355
12186
  guards: heldGuards,
@@ -11366,6 +12197,9 @@ function createInstanceSession(args) {
11366
12197
  value: value
11367
12198
  } : {},
11368
12199
  actor: actor,
12200
+ ...localPrincipalId !== void 0 ? {
12201
+ localPrincipalId: localPrincipalId
12202
+ } : {},
11369
12203
  clientForGdr: editScope.clientForGdr,
11370
12204
  refSurface: editScope.refSurface,
11371
12205
  ...grants !== void 0 ? {
@@ -11476,6 +12310,7 @@ function createEngine(args) {
11476
12310
  availableActions: rest => workflow.availableActions(withScope(rest)),
11477
12311
  setStage: rest => workflow.setStage(withScope(rest)),
11478
12312
  abortInstance: rest => workflow.abortInstance(withScope(rest)),
12313
+ resetActivity: rest => workflow.resetActivity(withScope(rest)),
11479
12314
  deleteDefinition: rest => workflow.deleteDefinition(withScope(rest)),
11480
12315
  getInstance: rest => workflow.getInstance(withScope(rest)),
11481
12316
  subscriptionDocumentsForInstance: rest => workflow.getInstance(withScope(rest)).then(subscriptionDocumentsForInstance),
@@ -12042,4 +12877,4 @@ function displayDescription(typeKey) {
12042
12877
  if (typeKey) return DISPLAY[typeKey]?.description;
12043
12878
  }
12044
12879
 
12045
- 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, instancesGuardQuery, instancesQuery, isCascadeFired, isClaimExpired, isClientProjectUser, 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 };