@sanity/workflow-engine 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -123,7 +123,7 @@ function isGdr(value) {
123
123
  return typeof value == "object" && value !== null && typeof value.id == "string" && typeof value.type == "string";
124
124
  }
125
125
  function buildParams(args) {
126
- const { instance, now, snapshot, extra } = args, currentTasks2 = findOpenStageEntry(instance)?.tasks ?? [];
126
+ const { instance, now, snapshot, extra } = args, currentActivities2 = findOpenStageEntry(instance)?.activities ?? [];
127
127
  return {
128
128
  self: selfGdr(instance),
129
129
  fields: renderedFields(instance.fields ?? [], snapshot),
@@ -134,10 +134,12 @@ function buildParams(args) {
134
134
  /** `$effects` — parent context handoff by entry name, completed-effect
135
135
  * outputs namespaced under the effect's name (`$effects['x.y'].out`). */
136
136
  effects: effectsContextMap(instance),
137
- /** `$tasks` — the current stage's task rows, statuses included. */
138
- tasks: currentTasks2,
139
- allTasksDone: currentTasks2.every((t) => t.status === "done" || t.status === "skipped"),
140
- anyTaskFailed: currentTasks2.some((t) => t.status === "failed"),
137
+ /** `$activities` — the current stage's activity rows, statuses included. */
138
+ activities: currentActivities2,
139
+ allActivitiesDone: currentActivities2.every(
140
+ (activity) => activity.status === "done" || activity.status === "skipped"
141
+ ),
142
+ anyActivityFailed: currentActivities2.some((activity) => activity.status === "failed"),
141
143
  ...extra
142
144
  };
143
145
  }
@@ -149,15 +151,15 @@ function renderedFields(entries, snapshot) {
149
151
  function renderedValue(entry, snapshot) {
150
152
  return entry._type !== "doc.ref" || entry.value === null || snapshot === void 0 ? entry.value : snapshot.docs.find((d) => d._id === entry.value.id) ?? entry.value;
151
153
  }
152
- function scopedFieldOverlay(instance, snapshot, taskName) {
154
+ function scopedFieldOverlay(instance, snapshot, activityName) {
153
155
  const stageEntry = findOpenStageEntry(instance);
154
156
  if (stageEntry === void 0) return {};
155
- const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), task = taskName ? stageEntry.tasks.find((t) => t.name === taskName) : void 0;
156
- return { ...stageFields, ...renderedFields(task?.fields ?? [], snapshot) };
157
+ const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), activity = activityName ? stageEntry.activities.find((t) => t.name === activityName) : void 0;
158
+ return { ...stageFields, ...renderedFields(activity?.fields ?? [], snapshot) };
157
159
  }
158
- function assignedFor(instance, taskName, actor, roleAliases) {
160
+ function assignedFor(instance, activityName, actor, roleAliases) {
159
161
  if (actor === void 0) return !1;
160
- const entry = findOpenStageEntry(instance)?.tasks.find((t) => t.name === taskName)?.fields?.find((s) => s._type === "assignees");
162
+ const entry = findOpenStageEntry(instance)?.activities.find((t) => t.name === activityName)?.fields?.find((s) => s._type === "assignees");
161
163
  return entry === void 0 ? !1 : entry.value.some(
162
164
  (a) => a.type === "user" ? a.id === actor.id : schema.actorFulfillsRole(actor.roles, a.role, roleAliases)
163
165
  );
@@ -289,7 +291,7 @@ function validateDefinition(definition) {
289
291
  validateStage(v2, stage);
290
292
  if (v2.errors.length > 0)
291
293
  throw new Error(
292
- `defineWorkflow("${definition.name}", v${definition.version}): ${v2.errors.length} validation error${v2.errors.length === 1 ? "" : "s"}:
294
+ `defineWorkflow("${definition.name}"): ${v2.errors.length} validation error${v2.errors.length === 1 ? "" : "s"}:
293
295
  ` + v2.errors.join(`
294
296
  `)
295
297
  );
@@ -310,7 +312,7 @@ function createDefinitionValidator() {
310
312
  return { errors, tryParse, checkCondition: (groq, where) => {
311
313
  tryParse(groq, where), rejectTypeScan(groq, where);
312
314
  }, checkEntry: (entry, label) => {
313
- entry.source.type === "query" && tryParse(entry.source.query, `${label}.query`);
315
+ entry.initialValue?.type === "query" && tryParse(entry.initialValue.query, `${label}.query`);
314
316
  }, checkGuardRead: (expr, where) => {
315
317
  isGuardReadExpr(expr) || errors.push(
316
318
  ` \xB7 ${where}: guard read "${expr}" is not a supported deploy-time value \u2014 use "$self", "$now", "$fields.<name>[.path]", or "$effects['<name>'][.path]" (guards store resolved values; the lake cannot see $fields or $effects)`
@@ -327,8 +329,8 @@ function validateStage(v2, stage) {
327
329
  for (const [key, groq] of effectBindings(t.effects))
328
330
  v2.tryParse(groq, `transition "${t.name}" effect binding "${key}"`);
329
331
  }
330
- for (const task of stage.tasks ?? [])
331
- validateTask(v2, stage.name, task);
332
+ for (const activity of stage.activities ?? [])
333
+ validateActivity(v2, stage.name, activity);
332
334
  }
333
335
  function validateGuardReads(v2, stageName, guard) {
334
336
  const where = `stage "${stageName}" guard "${guard.name}"`;
@@ -337,22 +339,22 @@ function validateGuardReads(v2, stageName, guard) {
337
339
  for (const [key, expr] of Object.entries(guard.metadata ?? {}))
338
340
  v2.checkGuardRead(expr, `${where} metadata "${key}"`);
339
341
  }
340
- function validateTask(v2, stageName, task) {
341
- const where = `stage "${stageName}" task "${task.name}"`;
342
- for (const entry of task.fields ?? [])
342
+ function validateActivity(v2, stageName, activity) {
343
+ const where = `stage "${stageName}" activity "${activity.name}"`;
344
+ for (const entry of activity.fields ?? [])
343
345
  v2.checkEntry(entry, `${where}.fields "${entry.name}"`);
344
- task.filter !== void 0 && v2.checkCondition(task.filter, `${where}.filter`), task.completeWhen !== void 0 && v2.checkCondition(task.completeWhen, `${where}.completeWhen`), task.failWhen !== void 0 && v2.checkCondition(task.failWhen, `${where}.failWhen`);
345
- for (const [name, groq] of Object.entries(task.requirements ?? {}))
346
+ activity.filter !== void 0 && v2.checkCondition(activity.filter, `${where}.filter`), activity.completeWhen !== void 0 && v2.checkCondition(activity.completeWhen, `${where}.completeWhen`), activity.failWhen !== void 0 && v2.checkCondition(activity.failWhen, `${where}.failWhen`);
347
+ for (const [name, groq] of Object.entries(activity.requirements ?? {}))
346
348
  v2.checkCondition(groq, `${where}.requirements "${name}"`);
347
- for (const [key, groq] of effectBindings(task.effects))
349
+ for (const [key, groq] of effectBindings(activity.effects))
348
350
  v2.tryParse(groq, `${where} effect binding "${key}"`);
349
- validateTaskActions(v2, task), task.subworkflows !== void 0 && validateSubworkflows(v2, where, task.subworkflows);
351
+ validateActivityActions(v2, activity), activity.subworkflows !== void 0 && validateSubworkflows(v2, where, activity.subworkflows);
350
352
  }
351
- function validateTaskActions(v2, task) {
352
- for (const a of task.actions ?? []) {
353
- a.filter !== void 0 && v2.checkCondition(a.filter, `task "${task.name}" action "${a.name}".filter`);
353
+ function validateActivityActions(v2, activity) {
354
+ for (const a of activity.actions ?? []) {
355
+ a.filter !== void 0 && v2.checkCondition(a.filter, `activity "${activity.name}" action "${a.name}".filter`);
354
356
  for (const [key, groq] of effectBindings(a.effects))
355
- v2.tryParse(groq, `task "${task.name}" action "${a.name}" effect binding "${key}"`);
357
+ v2.tryParse(groq, `activity "${activity.name}" action "${a.name}" effect binding "${key}"`);
356
358
  }
357
359
  }
358
360
  function validateSubworkflows(v2, where, sub) {
@@ -367,10 +369,10 @@ function effectBindings(effects) {
367
369
  (e) => Object.entries(e.bindings ?? {}).map(([k, groq]) => [`${e.name}.${k}`, groq])
368
370
  );
369
371
  }
370
- function actionVerdict(task, action) {
372
+ function actionVerdict(activity, action) {
371
373
  return {
372
- task: task.task.name,
373
- taskStatus: task.status,
374
+ activity: activity.activity.name,
375
+ activityStatus: activity.status,
374
376
  action: action.action.name,
375
377
  title: action.action.title,
376
378
  allowed: action.allowed,
@@ -378,8 +380,8 @@ function actionVerdict(task, action) {
378
380
  params: action.action.params ?? []
379
381
  };
380
382
  }
381
- function availableActions(tasks) {
382
- return tasks.flatMap((t) => t.actions.map((a) => actionVerdict(t, a)));
383
+ function availableActions(activities) {
384
+ return activities.flatMap((t) => t.actions.map((a) => actionVerdict(t, a)));
383
385
  }
384
386
  const wallClock = () => (/* @__PURE__ */ new Date()).toISOString(), GUARD_DOC_TYPE = "temp.system.guard";
385
387
  class MutationGuardDeniedError extends Error {
@@ -501,60 +503,67 @@ function diagnoseInputFromEvaluation(evaluation) {
501
503
  const stage = openStage(evaluation.instance);
502
504
  return {
503
505
  instance: evaluation.instance,
504
- tasks: evaluation.currentStage.tasks,
506
+ activities: evaluation.currentStage.activities,
505
507
  transitions: evaluation.currentStage.transitions,
506
508
  assignees: Object.fromEntries(
507
- evaluation.currentStage.tasks.map((t) => [t.task.name, assigneesOf(stage, t.task.name)])
509
+ evaluation.currentStage.activities.map((t) => [
510
+ t.activity.name,
511
+ assigneesOf(stage, t.activity.name)
512
+ ])
508
513
  )
509
514
  };
510
515
  }
511
516
  function openStage(instance) {
512
517
  return findOpenStageEntry(instance);
513
518
  }
514
- function assigneesOf(stage, taskName) {
515
- return (stage?.tasks.find((t) => t.name === taskName)?.fields ?? []).filter((s) => s._type === "assignees").flatMap((s) => s.value);
519
+ function assigneesOf(stage, activityName) {
520
+ return (stage?.activities.find((t) => t.name === activityName)?.fields ?? []).filter((s) => s._type === "assignees").flatMap((s) => s.value);
516
521
  }
517
522
  function isResolved(status) {
518
523
  return status === "done" || status === "skipped";
519
524
  }
520
525
  function failedEffectCause(input) {
521
- const stalled = new Set(input.tasks.filter((t) => !isResolved(t.status)).map((t) => t.task.name)), effect = [...input.instance.effectHistory].reverse().find((e) => e.status === "failed" && e.origin.kind === "task" && stalled.has(e.origin.name));
526
+ const stalled = new Set(
527
+ input.activities.filter((t) => !isResolved(t.status)).map((t) => t.activity.name)
528
+ ), effect = [...input.instance.effectHistory].reverse().find(
529
+ (e) => e.status === "failed" && e.origin.kind === "activity" && stalled.has(e.origin.name)
530
+ );
522
531
  return effect ? { kind: "failed-effect", effect } : void 0;
523
532
  }
524
533
  function hungEffectCause(input) {
525
534
  const effect = input.instance.pendingEffects.find((e) => e.claim !== void 0);
526
535
  return effect ? { kind: "hung-effect", effect } : void 0;
527
536
  }
528
- function failedTaskCause(input) {
529
- const failed = input.tasks.find((t) => t.status === "failed");
530
- return failed ? { kind: "failed-task", task: failed.task.name } : void 0;
537
+ function failedActivityCause(input) {
538
+ const failed = input.activities.find((t) => t.status === "failed");
539
+ return failed ? { kind: "failed-activity", activity: failed.activity.name } : void 0;
531
540
  }
532
541
  function waitingState(input) {
533
- const active = input.tasks.find(
534
- (t) => t.status === "active" && (t.task.actions ?? []).length > 0 && (t.unmetRequirements ?? []).length === 0
542
+ const active = input.activities.find(
543
+ (t) => t.status === "active" && (t.activity.actions ?? []).length > 0 && (t.unmetRequirements ?? []).length === 0
535
544
  );
536
545
  if (active !== void 0)
537
546
  return {
538
547
  state: "waiting",
539
- task: active.task.name,
540
- assignees: input.assignees[active.task.name] ?? [],
541
- actions: (active.task.actions ?? []).map((a) => a.name)
548
+ activity: active.activity.name,
549
+ assignees: input.assignees[active.activity.name] ?? [],
550
+ actions: (active.activity.actions ?? []).map((a) => a.name)
542
551
  };
543
552
  }
544
553
  function blockedState(input) {
545
- const blocked = input.tasks.find(
546
- (t) => t.status === "active" && (t.task.actions ?? []).length > 0 && (t.unmetRequirements ?? []).length > 0
554
+ const blocked = input.activities.find(
555
+ (t) => t.status === "active" && (t.activity.actions ?? []).length > 0 && (t.unmetRequirements ?? []).length > 0
547
556
  );
548
557
  if (blocked !== void 0)
549
558
  return {
550
559
  state: "blocked",
551
- task: blocked.task.name,
560
+ activity: blocked.activity.name,
552
561
  requirements: blocked.unmetRequirements ?? [],
553
- assignees: input.assignees[blocked.task.name] ?? []
562
+ assignees: input.assignees[blocked.activity.name] ?? []
554
563
  };
555
564
  }
556
565
  function noTransitionFiresCause(input) {
557
- if (input.transitions.length === 0 || input.transitions.some((t) => t.filterSatisfied) || !input.tasks.every((t) => isResolved(t.status)))
566
+ if (input.transitions.length === 0 || input.transitions.some((t) => t.filterSatisfied) || !input.activities.every((t) => isResolved(t.status)))
558
567
  return;
559
568
  const undecidable = input.transitions.filter((t) => t.unevaluable);
560
569
  return undecidable.length > 0 ? { kind: "transition-unevaluable", transitions: undecidable.map((t) => t.transition.name) } : { kind: "no-transition-fires" };
@@ -567,7 +576,7 @@ function diagnoseInstance(input) {
567
576
  }
568
577
  if (instance.completedAt !== void 0)
569
578
  return { state: "completed", at: instance.completedAt };
570
- const cause = failedEffectCause(input) ?? failedTaskCause(input) ?? hungEffectCause(input) ?? noTransitionFiresCause(input);
579
+ const cause = failedEffectCause(input) ?? failedActivityCause(input) ?? hungEffectCause(input) ?? noTransitionFiresCause(input);
571
580
  return cause !== void 0 ? { state: "stuck", cause } : waitingState(input) ?? blockedState(input) ?? { state: "progressing" };
572
581
  }
573
582
  const RUNNABLE_VERBS = /* @__PURE__ */ new Set(["set-stage", "abort"]);
@@ -589,12 +598,12 @@ function remediationsForCause(cause) {
589
598
  },
590
599
  { verb: "abort", rationale: "Abort the instance if the effect never drains." }
591
600
  ];
592
- case "failed-task":
601
+ case "failed-activity":
593
602
  return [
594
- { verb: "reset-task", rationale: "Reset or skip the failed task." },
603
+ { verb: "reset-activity", rationale: "Reset or skip the failed activity." },
595
604
  {
596
605
  verb: "set-stage",
597
- rationale: "Move the instance past the failed task to the intended next stage."
606
+ rationale: "Move the instance past the failed activity to the intended next stage."
598
607
  }
599
608
  ];
600
609
  case "no-transition-fires":
@@ -616,13 +625,15 @@ function remediationsFor(diagnosis) {
616
625
  }
617
626
  class ActionParamsInvalidError extends Error {
618
627
  action;
619
- task;
628
+ activity;
620
629
  issues;
621
630
  constructor(args) {
622
631
  const lines = args.issues.map((i) => ` - ${i.param}: ${i.reason}`).join(`
623
632
  `);
624
- super(`Action "${args.action}" on task "${args.task}" rejected: invalid params
625
- ${lines}`), this.name = "ActionParamsInvalidError", this.action = args.action, this.task = args.task, this.issues = args.issues;
633
+ super(
634
+ `Action "${args.action}" on activity "${args.activity}" rejected: invalid params
635
+ ${lines}`
636
+ ), this.name = "ActionParamsInvalidError", this.action = args.action, this.activity = args.activity, this.issues = args.issues;
626
637
  }
627
638
  }
628
639
  class RequiredFieldNotProvidedError extends Error {
@@ -632,7 +643,7 @@ class RequiredFieldNotProvidedError extends Error {
632
643
  const lines = args.missing.map((m) => ` - ${m.name} (${m.type})`).join(`
633
644
  `), where = args.definition !== void 0 ? ` for workflow "${args.definition}"` : "";
634
645
  super(
635
- `Required init fields${where} was not provided:
646
+ `Required input fields${where} were not provided:
636
647
  ${lines}
637
648
  Provide each via \`initialFields\` when starting standalone, or via the parent's \`subworkflows.with\` when spawned.`
638
649
  ), this.name = "RequiredFieldNotProvidedError", args.definition !== void 0 && (this.definition = args.definition), this.missing = args.missing;
@@ -664,13 +675,13 @@ class PartialGuardDeployError extends Error {
664
675
  const CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS = 3;
665
676
  class ConcurrentFireActionError extends Error {
666
677
  instanceId;
667
- task;
678
+ activity;
668
679
  action;
669
680
  attempts;
670
681
  constructor(args) {
671
682
  super(
672
- `Action "${args.action}" on task "${args.task}" (instance ${args.instanceId}) lost the optimistic-locking race ${args.attempts} attempts running \u2014 a concurrent writer kept committing first. Re-read and retry, or investigate a write storm on this instance.`
673
- ), this.name = "ConcurrentFireActionError", this.instanceId = args.instanceId, this.task = args.task, this.action = args.action, this.attempts = args.attempts;
683
+ `Action "${args.action}" on activity "${args.activity}" (instance ${args.instanceId}) lost the optimistic-locking race ${args.attempts} attempts running \u2014 a concurrent writer kept committing first. Re-read and retry, or investigate a write storm on this instance.`
684
+ ), this.name = "ConcurrentFireActionError", this.instanceId = args.instanceId, this.activity = args.activity, this.action = args.action, this.attempts = args.attempts;
674
685
  }
675
686
  }
676
687
  function isRevisionConflict(error) {
@@ -683,7 +694,7 @@ class ConcurrentEditFieldError extends Error {
683
694
  target;
684
695
  attempts;
685
696
  constructor(args) {
686
- const where = args.target.task !== void 0 ? `${args.target.task}.${args.target.field}` : args.target.field;
697
+ const where = args.target.activity !== void 0 ? `${args.target.activity}.${args.target.field}` : args.target.field;
687
698
  super(
688
699
  `Edit of "${args.target.scope}:${where}" (instance ${args.instanceId}) lost the optimistic-locking race ${args.attempts} attempts running \u2014 a concurrent writer kept committing first. Re-read and retry, or investigate a write storm on this instance.`
689
700
  ), this.name = "ConcurrentEditFieldError", this.instanceId = args.instanceId, this.target = args.target, this.attempts = args.attempts;
@@ -761,6 +772,9 @@ function subscriptionDocumentsForInstance(instance) {
761
772
  ...instance.perspective !== void 0 ? { perspective: instance.perspective } : {}
762
773
  };
763
774
  }
775
+ function instanceWatchesDocument(instance, document) {
776
+ return collectWatchRefs(instance).some((ref) => ref.id === document);
777
+ }
764
778
  function entryDocRefs(entries) {
765
779
  return fieldEntries(entries).flatMap((entry) => entry._type === "doc.ref" ? isGdr(entry.value) ? [entry.value] : [] : entry._type === "doc.refs" ? Array.isArray(entry.value) ? entry.value.filter(isGdr) : [] : []);
766
780
  }
@@ -875,14 +889,14 @@ function guardIdRefs(definition) {
875
889
  function staticIdRefGdr(idRef, stage, definition) {
876
890
  const read = /^\$fields\.([\w-]+)/.exec(idRef);
877
891
  if (read === null) return;
878
- const source = entriesInScope(stage, definition).find((e) => e.name === read[1])?.source;
879
- return source?.type === "literal" ? gdrFromValue(source.value) : void 0;
892
+ const seed = entriesInScope(stage, definition).find((e) => e.name === read[1])?.initialValue;
893
+ return seed?.type === "literal" ? gdrFromValue(seed.value) : void 0;
880
894
  }
881
895
  function entriesInScope(stage, definition) {
882
896
  return [
883
897
  ...definition.fields ?? [],
884
898
  ...stage.fields ?? [],
885
- ...(stage.tasks ?? []).flatMap((task) => task.fields ?? [])
899
+ ...(stage.activities ?? []).flatMap((activity) => activity.fields ?? [])
886
900
  ];
887
901
  }
888
902
  function gdrFromValue(value) {
@@ -1064,76 +1078,73 @@ const GdrShape = v__namespace.looseObject({
1064
1078
  }), AssigneeShape = v__namespace.union([
1065
1079
  v__namespace.looseObject({ type: v__namespace.literal("user"), id: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)) }),
1066
1080
  v__namespace.looseObject({ type: v__namespace.literal("role"), role: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)) })
1067
- ]), ChecklistItemShape = v__namespace.looseObject({
1068
- label: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)),
1069
- done: v__namespace.boolean(),
1070
- doneBy: v__namespace.optional(v__namespace.string()),
1071
- doneAt: v__namespace.optional(v__namespace.string()),
1072
- _key: v__namespace.optional(v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)))
1073
- }), NoteItemShape = v__namespace.pipe(
1074
- v__namespace.looseObject({
1075
- _key: v__namespace.optional(v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)))
1076
- }),
1077
- v__namespace.check(
1078
- (val) => typeof val == "object" && val !== null && !Array.isArray(val),
1079
- "must be an object"
1080
- )
1081
- ), NullableString = v__namespace.union([v__namespace.null(), v__namespace.string()]), NullableNumber = v__namespace.union([v__namespace.null(), v__namespace.number()]), NullableBoolean = v__namespace.union([v__namespace.null(), v__namespace.boolean()]), NullableDateTime = v__namespace.union([
1081
+ ]), NullableString = v__namespace.union([v__namespace.null(), v__namespace.string()]), NullableNumber = v__namespace.union([v__namespace.null(), v__namespace.number()]), NullableBoolean = v__namespace.union([v__namespace.null(), v__namespace.boolean()]), NullableDateTime = v__namespace.union([
1082
1082
  v__namespace.null(),
1083
1083
  v__namespace.pipe(
1084
1084
  v__namespace.string(),
1085
1085
  v__namespace.check((s) => !Number.isNaN(Date.parse(s)), "must be an ISO-8601 datetime string")
1086
1086
  )
1087
+ ]), NullableDate = v__namespace.union([
1088
+ v__namespace.null(),
1089
+ v__namespace.pipe(v__namespace.string(), v__namespace.regex(/^\d{4}-\d{2}-\d{2}$/, "must be a `YYYY-MM-DD` date"))
1087
1090
  ]), NullableUrl = NullableString, valueSchemas = {
1088
1091
  "doc.ref": v__namespace.union([v__namespace.null(), GdrShape]),
1089
1092
  "doc.refs": v__namespace.array(GdrShape),
1090
1093
  "release.ref": v__namespace.union([v__namespace.null(), ReleaseRefShape]),
1091
1094
  query: v__namespace.any(),
1092
- "value.string": NullableString,
1093
- "value.url": NullableUrl,
1094
- "value.number": NullableNumber,
1095
- "value.boolean": NullableBoolean,
1096
- "value.dateTime": NullableDateTime,
1097
- "value.actor": v__namespace.union([v__namespace.null(), ActorShape]),
1098
- checklist: v__namespace.array(ChecklistItemShape),
1099
- notes: v__namespace.array(NoteItemShape),
1095
+ string: NullableString,
1096
+ text: NullableString,
1097
+ number: NullableNumber,
1098
+ boolean: NullableBoolean,
1099
+ date: NullableDate,
1100
+ datetime: NullableDateTime,
1101
+ url: NullableUrl,
1102
+ actor: v__namespace.union([v__namespace.null(), ActorShape]),
1103
+ assignee: v__namespace.union([v__namespace.null(), AssigneeShape]),
1100
1104
  assignees: v__namespace.array(AssigneeShape)
1101
- }, itemSchemas = {
1102
- "doc.refs": GdrShape,
1103
- checklist: ChecklistItemShape,
1104
- notes: NoteItemShape,
1105
- assignees: AssigneeShape
1106
1105
  };
1107
- function isAppendable(entryType) {
1108
- return entryType in itemSchemas;
1106
+ function shapeValueSchema(shape) {
1107
+ return shape.type === "object" ? objectSchema(shape.fields ?? []) : shape.type === "array" ? v__namespace.array(objectSchema(shape.of ?? [])) : valueSchemas[shape.type] ?? v__namespace.any();
1109
1108
  }
1110
- function validateFieldValue(args) {
1111
- const schema2 = valueSchemas[args.entryType];
1112
- if (schema2 === void 0)
1113
- throw new FieldValueShapeError({
1114
- entryType: args.entryType,
1115
- entryName: args.entryName,
1116
- issues: [`unknown field entry type ${args.entryType}`],
1117
- mode: "value"
1118
- });
1109
+ function objectSchema(fields) {
1110
+ const entries = /* @__PURE__ */ Object.create(null);
1111
+ for (const f of fields) entries[f.name] = v__namespace.optional(shapeValueSchema(f));
1112
+ return v__namespace.looseObject(entries);
1113
+ }
1114
+ function wholeValueSchema(entryType, shape) {
1115
+ return entryType === "object" ? v__namespace.union([v__namespace.null(), objectSchema(shape.fields ?? [])]) : entryType === "array" ? v__namespace.array(objectSchema(shape.of ?? [])) : valueSchemas[entryType];
1116
+ }
1117
+ function appendItemSchema(entryType, shape) {
1118
+ if (entryType === "array") return objectSchema(shape.of ?? []);
1119
+ if (entryType === "doc.refs") return GdrShape;
1120
+ if (entryType === "assignees") return AssigneeShape;
1121
+ }
1122
+ function checkFieldValue(args) {
1123
+ const schema2 = wholeValueSchema(args.entryType, args);
1124
+ if (schema2 === void 0) return [`unknown field entry type ${args.entryType}`];
1119
1125
  const result = v__namespace.safeParse(schema2, args.value);
1120
- if (!result.success)
1126
+ return result.success ? void 0 : formatIssues(result.issues);
1127
+ }
1128
+ function validateFieldValue(args) {
1129
+ const issues = checkFieldValue(args);
1130
+ if (issues !== void 0)
1121
1131
  throw new FieldValueShapeError({
1122
1132
  entryType: args.entryType,
1123
1133
  entryName: args.entryName,
1124
- issues: formatIssues(result.issues),
1134
+ issues,
1125
1135
  mode: "value"
1126
1136
  });
1127
1137
  }
1128
1138
  function validateFieldAppendItem(args) {
1129
- if (!isAppendable(args.entryType))
1139
+ const schema2 = appendItemSchema(args.entryType, args);
1140
+ if (schema2 === void 0)
1130
1141
  throw new FieldValueShapeError({
1131
1142
  entryType: args.entryType,
1132
1143
  entryName: args.entryName,
1133
1144
  issues: [`field entry type ${args.entryType} does not support append`],
1134
1145
  mode: "item"
1135
1146
  });
1136
- const schema2 = itemSchemas[args.entryType], result = v__namespace.safeParse(schema2, args.item);
1147
+ const result = v__namespace.safeParse(schema2, args.item);
1137
1148
  if (!result.success)
1138
1149
  throw new FieldValueShapeError({
1139
1150
  entryType: args.entryType,
@@ -1161,7 +1172,7 @@ function derivePerspectiveFromFields(entries) {
1161
1172
  async function resolveDeclaredFields(args) {
1162
1173
  const { entryDefs, initialFields, ctx, randomKey: randomKey2 } = args;
1163
1174
  if (entryDefs === void 0 || entryDefs.length === 0) return [];
1164
- assertRequiredInitProvided(entryDefs, initialFields, ctx.definitionName);
1175
+ assertRequiredInputProvided(entryDefs, initialFields, ctx.definitionName);
1165
1176
  const out = [];
1166
1177
  for (const entry of entryDefs) {
1167
1178
  const scopedCtx = { ...ctx, resolvedFields: out };
@@ -1169,8 +1180,8 @@ async function resolveDeclaredFields(args) {
1169
1180
  }
1170
1181
  return out;
1171
1182
  }
1172
- function assertRequiredInitProvided(entryDefs, initialFields, definitionName) {
1173
- const missing = entryDefs.filter((entry) => entry.required === !0 && entry.source.type === "init").filter((entry) => {
1183
+ function assertRequiredInputProvided(entryDefs, initialFields, definitionName) {
1184
+ const missing = entryDefs.filter((entry) => entry.required === !0 && entry.initialValue?.type === "input").filter((entry) => {
1174
1185
  const match = initialFields.find((s) => s.name === entry.name && s.type === entry.type);
1175
1186
  return match === void 0 || match.value === void 0 || match.value === null;
1176
1187
  }).map((entry) => ({ name: entry.name, type: entry.type }));
@@ -1180,18 +1191,13 @@ function assertRequiredInitProvided(entryDefs, initialFields, definitionName) {
1180
1191
  missing
1181
1192
  });
1182
1193
  }
1183
- const ARRAY_SLOT_TYPES = /* @__PURE__ */ new Set([
1184
- "doc.refs",
1185
- "checklist",
1186
- "notes",
1187
- "assignees"
1188
- ]);
1194
+ const ARRAY_SLOT_TYPES = /* @__PURE__ */ new Set(["doc.refs", "array", "assignees"]);
1189
1195
  function defaultEntryValue(entryType) {
1190
1196
  return ARRAY_SLOT_TYPES.has(entryType) ? [] : null;
1191
1197
  }
1192
- function resolveInitValue(entry, initialFields, defaultValue) {
1198
+ function resolveInputValue(entry, initialFields, defaultValue) {
1193
1199
  const initMatch = initialFields.find((s) => s.name === entry.name && s.type === entry.type);
1194
- return initMatch === void 0 ? defaultValue : (assertInitValueShape(entry, initMatch.value), initMatch.value);
1200
+ return initMatch === void 0 ? defaultValue : (assertInputValueShape(entry, initMatch.value), initMatch.value);
1195
1201
  }
1196
1202
  function resolveFieldReadValue(source, ctx, defaultValue) {
1197
1203
  const target = (source.scope === "workflow" ? ctx.workflowFields ?? [] : ctx.resolvedFields ?? []).find((s) => s.name === source.field), targetValue = target === void 0 ? void 0 : target.value;
@@ -1202,7 +1208,7 @@ async function resolveQueryValue(entry, source, ctx, client, defaultValue) {
1202
1208
  self: ctx.selfId ?? null,
1203
1209
  fields: fieldMapFromResolved(ctx.resolvedFields ?? []),
1204
1210
  stage: ctx.stageName ?? null,
1205
- task: ctx.taskName ?? null,
1211
+ activity: ctx.activityName ?? null,
1206
1212
  now: ctx.now,
1207
1213
  tag: ctx.tag
1208
1214
  });
@@ -1217,38 +1223,47 @@ async function resolveQueryValue(entry, source, ctx, client, defaultValue) {
1217
1223
  }
1218
1224
  }
1219
1225
  async function resolveEntryValue(entry, initialFields, ctx, defaultValue) {
1220
- const source = entry.source;
1221
- return source.type === "init" ? resolveInitValue(entry, initialFields, defaultValue) : source.type === "literal" ? source.value ?? defaultValue : source.type === "fieldRead" ? resolveFieldReadValue(source, ctx, defaultValue) : source.type === "query" && ctx.client !== void 0 ? resolveQueryValue(entry, source, ctx, ctx.client, defaultValue) : defaultValue;
1226
+ const source = entry.initialValue;
1227
+ return source === void 0 ? defaultValue : source.type === "input" ? resolveInputValue(entry, initialFields, defaultValue) : source.type === "literal" ? source.value ?? defaultValue : source.type === "fieldRead" ? resolveFieldReadValue(source, ctx, defaultValue) : source.type === "query" && ctx.client !== void 0 ? resolveQueryValue(entry, source, ctx, ctx.client, defaultValue) : defaultValue;
1222
1228
  }
1223
1229
  function buildResolvedEntry(entry, value, _key, now) {
1224
- const titleProp = entry.title !== void 0 ? { title: entry.title } : {}, descriptionProp = entry.description !== void 0 ? { description: entry.description } : {};
1225
- return entry.type === "query" ? {
1226
- _key,
1227
- _type: "query",
1228
- name: entry.name,
1229
- ...titleProp,
1230
- ...descriptionProp,
1231
- value,
1232
- resolvedAt: now
1233
- } : {
1230
+ return {
1234
1231
  _key,
1235
1232
  _type: entry.type,
1236
1233
  name: entry.name,
1237
- ...titleProp,
1238
- ...descriptionProp,
1239
- value
1234
+ ...entry.title !== void 0 ? { title: entry.title } : {},
1235
+ ...entry.description !== void 0 ? { description: entry.description } : {},
1236
+ value,
1237
+ ...entry.initialValue?.type === "query" ? { resolvedAt: now } : {},
1238
+ ...entry.type === "object" ? { fields: entry.fields ?? [] } : {},
1239
+ ...entry.type === "array" ? { of: entry.of ?? [] } : {}
1240
1240
  };
1241
1241
  }
1242
1242
  async function resolveOneEntry(entry, initialFields, ctx, randomKey2) {
1243
1243
  const defaultValue = defaultEntryValue(entry.type), value = await resolveEntryValue(entry, initialFields, ctx, defaultValue);
1244
- return validateFieldValue({ entryType: entry.type, entryName: entry.name, value }), buildResolvedEntry(entry, value, randomKey2(), ctx.now);
1244
+ if (entry.initialValue?.type === "query") {
1245
+ const issues = checkFieldValue({
1246
+ entryType: entry.type,
1247
+ value,
1248
+ fields: entry.fields,
1249
+ of: entry.of
1250
+ });
1251
+ return issues !== void 0 ? (ctx.recordDiscard?.({ field: entry.name, detail: issues.join("; ") }), buildResolvedEntry(entry, defaultValue, randomKey2(), ctx.now)) : buildResolvedEntry(entry, value, randomKey2(), ctx.now);
1252
+ }
1253
+ return validateFieldValue({
1254
+ entryType: entry.type,
1255
+ entryName: entry.name,
1256
+ value,
1257
+ fields: entry.fields,
1258
+ of: entry.of
1259
+ }), buildResolvedEntry(entry, value, randomKey2(), ctx.now);
1245
1260
  }
1246
1261
  function fieldMapFromResolved(entries) {
1247
1262
  const out = {};
1248
1263
  for (const s of entries) out[s.name] = s.value;
1249
1264
  return out;
1250
1265
  }
1251
- function assertInitValueShape(entry, value) {
1266
+ function assertInputValueShape(entry, value) {
1252
1267
  if (entry.type === "doc.ref") {
1253
1268
  assertGdrShape(value, `field entry "${entry.name}" (doc.ref)`);
1254
1269
  return;
@@ -1258,14 +1273,14 @@ function assertInitValueShape(entry, value) {
1258
1273
  const v2 = value;
1259
1274
  if (typeof v2.releaseName != "string" || v2.releaseName.length === 0)
1260
1275
  throw new Error(
1261
- `Invalid init value for field entry "${entry.name}" (release.ref): \`releaseName\` must be a non-empty string. Got ${JSON.stringify(v2.releaseName)}.`
1276
+ `Invalid input value for field entry "${entry.name}" (release.ref): \`releaseName\` must be a non-empty string. Got ${JSON.stringify(v2.releaseName)}.`
1262
1277
  );
1263
1278
  return;
1264
1279
  }
1265
1280
  if (entry.type === "doc.refs") {
1266
1281
  if (!Array.isArray(value))
1267
1282
  throw new Error(
1268
- `Invalid init value for field entry "${entry.name}" (doc.refs): expected an array of GDRs, got ${typeof value}.`
1283
+ `Invalid input value for field entry "${entry.name}" (doc.refs): expected an array of GDRs, got ${typeof value}.`
1269
1284
  );
1270
1285
  for (const [i, item] of value.entries())
1271
1286
  assertGdrShape(item, `field entry "${entry.name}" (doc.refs) item [${i}]`);
@@ -1329,12 +1344,12 @@ async function loadContext(client, instanceId, options) {
1329
1344
  async function ctxConditionParams(ctx, opts) {
1330
1345
  const base = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), fields = {
1331
1346
  ...base.fields,
1332
- ...scopedFieldOverlay(ctx.instance, ctx.snapshot, opts?.taskName)
1347
+ ...scopedFieldOverlay(ctx.instance, ctx.snapshot, opts?.activityName)
1333
1348
  }, params = {
1334
1349
  ...base,
1335
1350
  fields,
1336
1351
  actor: opts?.actor,
1337
- assigned: opts?.taskName !== void 0 ? assignedFor(ctx.instance, opts.taskName, opts?.actor, ctx.definition.roleAliases) : !1,
1352
+ assigned: opts?.activityName !== void 0 ? assignedFor(ctx.instance, opts.activityName, opts?.actor, ctx.definition.roleAliases) : !1,
1338
1353
  ...opts?.vars
1339
1354
  };
1340
1355
  return { ...await evaluatePredicates({
@@ -1372,7 +1387,7 @@ function findStage(definition, stageName) {
1372
1387
  return stage;
1373
1388
  }
1374
1389
  async function resolveStageFieldEntries(args) {
1375
- const { client, instance, stage, now, initialFields } = args;
1390
+ const { client, instance, stage, now, initialFields, recordDiscard } = args;
1376
1391
  return resolveDeclaredFields({
1377
1392
  entryDefs: stage.fields,
1378
1393
  initialFields: initialFields ?? [],
@@ -1383,18 +1398,19 @@ async function resolveStageFieldEntries(args) {
1383
1398
  tag: instance.tag,
1384
1399
  stageName: stage.name,
1385
1400
  workflowResource: instance.workflowResource,
1386
- // Allow stage-scope entries' `source: { type: "fieldRead", scope:
1401
+ // Allow stage-scope entries' `initialValue: { type: "fieldRead", scope:
1387
1402
  // "workflow", ... }` to see the already-resolved workflow-scope fields.
1388
1403
  workflowFields: instance.fields ?? [],
1389
- ...instance.perspective !== void 0 ? { perspective: instance.perspective } : {}
1404
+ ...instance.perspective !== void 0 ? { perspective: instance.perspective } : {},
1405
+ ...recordDiscard !== void 0 ? { recordDiscard } : {}
1390
1406
  },
1391
1407
  randomKey
1392
1408
  });
1393
1409
  }
1394
- async function resolveTaskFieldEntries(args) {
1395
- const { client, instance, stage, task, now } = args;
1410
+ async function resolveActivityFieldEntries(args) {
1411
+ const { client, instance, stage, activity, now, recordDiscard } = args;
1396
1412
  return resolveDeclaredFields({
1397
- entryDefs: task.fields,
1413
+ entryDefs: activity.fields,
1398
1414
  initialFields: [],
1399
1415
  ctx: {
1400
1416
  client,
@@ -1403,85 +1419,70 @@ async function resolveTaskFieldEntries(args) {
1403
1419
  tag: instance.tag,
1404
1420
  stageName: stage.name,
1405
1421
  workflowResource: instance.workflowResource,
1406
- taskName: task.name,
1422
+ activityName: activity.name,
1407
1423
  workflowFields: instance.fields ?? [],
1408
- ...instance.perspective !== void 0 ? { perspective: instance.perspective } : {}
1424
+ ...instance.perspective !== void 0 ? { perspective: instance.perspective } : {},
1425
+ ...recordDiscard !== void 0 ? { recordDiscard } : {}
1409
1426
  },
1410
1427
  randomKey
1411
1428
  });
1412
1429
  }
1413
- async function deployOrRollback(args) {
1414
- try {
1415
- await args.deploy();
1416
- } catch (guardError) {
1417
- if (!args.reversible)
1418
- throw new WorkflowStateDivergedError({
1419
- instanceId: args.instanceId,
1420
- guardError,
1421
- reason: "the move created child instances a parent-only rollback cannot undo"
1422
- });
1423
- try {
1424
- let patch = args.client.patch(args.instanceId).set(args.restore);
1425
- args.unset && args.unset.length > 0 && (patch = patch.unset(args.unset)), args.committedRev !== void 0 && (patch = patch.ifRevisionId(args.committedRev)), await patch.commit(SYNC_COMMIT);
1426
- } catch (rollbackError) {
1427
- throw new WorkflowStateDivergedError({
1428
- instanceId: args.instanceId,
1429
- guardError,
1430
- rollbackError,
1431
- reason: "rollback of the committed move failed"
1432
- });
1433
- }
1434
- throw guardError instanceof PartialGuardDeployError ? new WorkflowStateDivergedError({
1435
- instanceId: args.instanceId,
1436
- guardError,
1437
- reason: "a multi-guard deploy partially applied; the rolled-back move left orphaned guard locks"
1438
- }) : guardError;
1439
- }
1430
+ async function resolveBindings(args) {
1431
+ const resolved = {};
1432
+ for (const [key, groq] of Object.entries(args.bindings ?? {}))
1433
+ resolved[key] = await runGroq(groq, args.params, args.snapshot);
1434
+ return { ...resolved, ...args.staticInput };
1440
1435
  }
1441
- function applyTaskStatusChange(args) {
1442
- const { entry, history, stage, to, at, actor } = args, from = entry.status;
1443
- entry.status = to, schema.isTerminalTaskStatus(to) && (entry.completedAt = at, actor !== void 0 && (entry.completedBy = actor.id)), history.push({
1444
- _key: randomKey(),
1445
- _type: "taskStatusChanged",
1446
- at,
1447
- stage,
1448
- task: entry.name,
1449
- from,
1450
- to,
1451
- ...actor !== void 0 ? { actor } : {}
1452
- });
1436
+ async function reload(client, instanceId, tag) {
1437
+ const doc = await client.getDocument(instanceId);
1438
+ if (!doc)
1439
+ throw new Error(`Workflow instance ${instanceId} not found`);
1440
+ if (doc.tag !== tag)
1441
+ throw new Error(`Workflow instance ${instanceId} not visible to this engine (tag mismatch)`);
1442
+ return doc;
1453
1443
  }
1454
- function stageTransitionHistory(args) {
1455
- const { fromStage, toStage, at, transition, actor, via, reason } = args, shared = {
1456
- at,
1457
- ...transition !== void 0 ? { transition } : {},
1458
- ...actor !== void 0 ? { actor } : {},
1459
- ...via !== void 0 ? { via } : {},
1460
- ...reason !== void 0 ? { reason } : {}
1444
+ function effectsContextEntry(name, value) {
1445
+ const _key = randomKey();
1446
+ return typeof value == "string" ? { _key, _type: "effectsContext.string", name, value } : typeof value == "number" ? { _key, _type: "effectsContext.number", name, value } : typeof value == "boolean" ? { _key, _type: "effectsContext.boolean", name, value } : { _key, _type: "effectsContext.ref", name, value };
1447
+ }
1448
+ function effectsContextJsonEntry(name, value) {
1449
+ return { _key: randomKey(), _type: "effectsContext.json", name, value: JSON.stringify(value) };
1450
+ }
1451
+ function buildInstanceBase(args) {
1452
+ const { id, now, actor, perspective } = args;
1453
+ return {
1454
+ _id: id,
1455
+ _type: WORKFLOW_INSTANCE_TYPE,
1456
+ _rev: "",
1457
+ _createdAt: now,
1458
+ _updatedAt: now,
1459
+ tag: args.tag,
1460
+ workflowResource: args.workflowResource,
1461
+ definition: args.definitionName,
1462
+ pinnedVersion: args.pinnedVersion,
1463
+ ...args.pinnedContentHash !== void 0 ? { pinnedContentHash: args.pinnedContentHash } : {},
1464
+ definitionSnapshot: JSON.stringify(args.definition),
1465
+ fields: args.fields,
1466
+ effectsContext: args.effectsContext,
1467
+ ancestors: args.ancestors,
1468
+ ...perspective !== void 0 ? { perspective } : {},
1469
+ currentStage: args.initialStage,
1470
+ stages: [],
1471
+ pendingEffects: [],
1472
+ effectHistory: [],
1473
+ history: [
1474
+ {
1475
+ _key: randomKey(),
1476
+ _type: "stageEntered",
1477
+ at: now,
1478
+ stage: args.initialStage,
1479
+ ...actor !== void 0 ? { actor } : {}
1480
+ },
1481
+ ...args.extraHistory ?? []
1482
+ ],
1483
+ startedAt: now,
1484
+ lastChangedAt: now
1461
1485
  };
1462
- return [
1463
- {
1464
- _key: randomKey(),
1465
- _type: "stageExited",
1466
- stage: fromStage,
1467
- toStage,
1468
- ...shared
1469
- },
1470
- {
1471
- _key: randomKey(),
1472
- _type: "transitionFired",
1473
- fromStage,
1474
- toStage,
1475
- ...shared
1476
- },
1477
- {
1478
- _key: randomKey(),
1479
- _type: "stageEntered",
1480
- stage: toStage,
1481
- fromStage,
1482
- ...shared
1483
- }
1484
- ];
1485
1486
  }
1486
1487
  function startMutation(instance) {
1487
1488
  return {
@@ -1492,7 +1493,7 @@ function startMutation(instance) {
1492
1493
  stages: instance.stages.map((s) => ({
1493
1494
  ...s,
1494
1495
  fields: (s.fields ?? []).map((entry) => ({ ...entry })),
1495
- tasks: s.tasks.map((t) => ({
1496
+ activities: s.activities.map((t) => ({
1496
1497
  ...t,
1497
1498
  ...t.fields !== void 0 ? { fields: t.fields.map((entry) => ({ ...entry })) } : {}
1498
1499
  }))
@@ -1568,348 +1569,97 @@ function currentStageEntry(mutation) {
1568
1569
  );
1569
1570
  return entry;
1570
1571
  }
1571
- function currentTasks(mutation) {
1572
- return currentStageEntry(mutation).tasks;
1572
+ function currentActivities(mutation) {
1573
+ return currentStageEntry(mutation).activities;
1573
1574
  }
1574
- function findTaskInCurrentStage(ctx, taskName) {
1575
- const stage = findStage(ctx.definition, ctx.instance.currentStage), task = (stage.tasks ?? []).find((t) => t.name === taskName);
1576
- if (task === void 0)
1575
+ function findActivityInCurrentStage(ctx, activityName) {
1576
+ const stage = findStage(ctx.definition, ctx.instance.currentStage), activity = (stage.activities ?? []).find((t) => t.name === activityName);
1577
+ if (activity === void 0)
1577
1578
  throw new Error(
1578
- `Task "${taskName}" not found in current stage "${stage.name}" of ${ctx.definition.name}`
1579
+ `Activity "${activityName}" not found in current stage "${stage.name}" of ${ctx.definition.name}`
1579
1580
  );
1580
- return { stage, task };
1581
+ return { stage, activity };
1581
1582
  }
1582
- function requireMutationTaskEntry(mutation, task) {
1583
- const mutEntry = currentTasks(mutation).find((t) => t.name === task);
1583
+ function requireMutationActivityEntry(mutation, activity) {
1584
+ const mutEntry = currentActivities(mutation).find((t) => t.name === activity);
1584
1585
  if (mutEntry === void 0)
1585
- throw new Error(`Task "${task}" disappeared from mutation copy \u2014 invariant broken`);
1586
+ throw new Error(`Activity "${activity}" disappeared from mutation copy \u2014 invariant broken`);
1586
1587
  return mutEntry;
1587
1588
  }
1588
1589
  function findCurrentStageEntry(instance) {
1589
1590
  return findOpenStageEntry(instance);
1590
1591
  }
1591
- function findCurrentTasks(instance) {
1592
- return findCurrentStageEntry(instance)?.tasks ?? [];
1593
- }
1594
- const TAG_RE = /^[a-z0-9][a-z0-9-]*$/;
1595
- function validateTag(tag) {
1596
- if (!TAG_RE.test(tag))
1597
- throw new Error(
1598
- `tag: invalid tag "${tag}" \u2014 must match ${TAG_RE.source} (ASCII lowercase + digits + dashes, no leading dash, no dots)`
1599
- );
1600
- }
1601
- function tagScopeFilter() {
1602
- return "tag == $tag";
1603
- }
1604
- function definitionLookupGroq(explicit) {
1605
- const scoped = `_type == "${schema.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${tagScopeFilter()}`;
1606
- return explicit ? `*[${scoped} && version == $version][0]` : `*[${scoped}] | order(version desc)[0]`;
1607
- }
1608
- async function discoverItems(args) {
1609
- const { client, groq, params, perspective } = args, fetchOptions = perspective !== void 0 ? { perspective } : void 0;
1610
- return client.fetch(groq, paramsForLake(params), fetchOptions);
1592
+ function findCurrentActivities(instance) {
1593
+ return findCurrentStageEntry(instance)?.activities ?? [];
1611
1594
  }
1612
- function clientForDiscovery(defaultClient, clientForGdr, subjectGdrUri) {
1613
- if (!subjectGdrUri || !subjectGdrUri.includes(":")) return defaultClient;
1595
+ async function deployOrRollback(args) {
1614
1596
  try {
1615
- return clientForGdr(parseGdr(subjectGdrUri));
1616
- } catch {
1617
- return defaultClient;
1597
+ await args.deploy();
1598
+ } catch (guardError) {
1599
+ if (!args.reversible)
1600
+ throw new WorkflowStateDivergedError({
1601
+ instanceId: args.instanceId,
1602
+ guardError,
1603
+ reason: "the move created child instances a parent-only rollback cannot undo"
1604
+ });
1605
+ try {
1606
+ let patch = args.client.patch(args.instanceId).set(args.restore);
1607
+ args.unset && args.unset.length > 0 && (patch = patch.unset(args.unset)), args.committedRev !== void 0 && (patch = patch.ifRevisionId(args.committedRev)), await patch.commit(SYNC_COMMIT);
1608
+ } catch (rollbackError) {
1609
+ throw new WorkflowStateDivergedError({
1610
+ instanceId: args.instanceId,
1611
+ guardError,
1612
+ rollbackError,
1613
+ reason: "rollback of the committed move failed"
1614
+ });
1615
+ }
1616
+ throw guardError instanceof PartialGuardDeployError ? new WorkflowStateDivergedError({
1617
+ instanceId: args.instanceId,
1618
+ guardError,
1619
+ reason: "a multi-guard deploy partially applied; the rolled-back move left orphaned guard locks"
1620
+ }) : guardError;
1618
1621
  }
1619
1622
  }
1620
- async function reload(client, instanceId, tag) {
1621
- const doc = await client.getDocument(instanceId);
1622
- if (!doc)
1623
- throw new Error(`Workflow instance ${instanceId} not found`);
1624
- if (doc.tag !== tag)
1625
- throw new Error(`Workflow instance ${instanceId} not visible to this engine (tag mismatch)`);
1626
- return doc;
1623
+ async function persistThenDeploy(ctx, mutation, deploy) {
1624
+ const spawned = mutation.pendingCreates.length > 0, committed = await persist(ctx, mutation);
1625
+ await deployOrRollback({
1626
+ client: ctx.client,
1627
+ instanceId: ctx.instance._id,
1628
+ committedRev: committed._rev,
1629
+ restore: instanceStateFields(ctx.instance),
1630
+ unset: ctx.instance.completedAt === void 0 ? ["completedAt"] : [],
1631
+ reversible: !spawned,
1632
+ deploy
1633
+ });
1627
1634
  }
1628
- function effectsContextEntry(name, value) {
1629
- const _key = randomKey();
1630
- return typeof value == "string" ? { _key, _type: "effectsContext.string", name, value } : typeof value == "number" ? { _key, _type: "effectsContext.number", name, value } : typeof value == "boolean" ? { _key, _type: "effectsContext.boolean", name, value } : { _key, _type: "effectsContext.ref", name, value };
1635
+ async function refreshStageGuards(ctx, mutation, stageName) {
1636
+ await deployStageGuards({
1637
+ client: ctx.client,
1638
+ clientForGdr: ctx.clientForGdr,
1639
+ instance: materializeInstance(ctx.instance, mutation),
1640
+ definition: ctx.definition,
1641
+ stageName,
1642
+ now: ctx.now
1643
+ });
1631
1644
  }
1632
- function effectsContextJsonEntry(name, value) {
1633
- return { _key: randomKey(), _type: "effectsContext.json", name, value: JSON.stringify(value) };
1645
+ async function completeEffect(args) {
1646
+ const { client, instanceId, effectKey, status, outputs, detail, error, durationMs, options } = args, ctx = await loadContext(client, instanceId, {
1647
+ ...options?.clock ? { clock: options.clock } : {},
1648
+ ...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
1649
+ });
1650
+ return commitCompleteEffect(
1651
+ ctx,
1652
+ effectKey,
1653
+ status,
1654
+ outputs,
1655
+ detail,
1656
+ error,
1657
+ durationMs,
1658
+ options?.actor
1659
+ );
1634
1660
  }
1635
- function buildInstanceBase(args) {
1636
- const { id, now, actor, perspective } = args;
1637
- return {
1638
- _id: id,
1639
- _type: WORKFLOW_INSTANCE_TYPE,
1640
- _rev: "",
1641
- _createdAt: now,
1642
- _updatedAt: now,
1643
- tag: args.tag,
1644
- workflowResource: args.workflowResource,
1645
- definition: args.definitionName,
1646
- pinnedVersion: args.pinnedVersion,
1647
- definitionSnapshot: JSON.stringify(args.definition),
1648
- fields: args.fields,
1649
- effectsContext: args.effectsContext,
1650
- ancestors: args.ancestors,
1651
- ...perspective !== void 0 ? { perspective } : {},
1652
- currentStage: args.initialStage,
1653
- stages: [],
1654
- pendingEffects: [],
1655
- effectHistory: [],
1656
- history: [
1657
- {
1658
- _key: randomKey(),
1659
- _type: "stageEntered",
1660
- at: now,
1661
- stage: args.initialStage,
1662
- ...actor !== void 0 ? { actor } : {}
1663
- }
1664
- ],
1665
- startedAt: now,
1666
- lastChangedAt: now
1667
- };
1668
- }
1669
- const ENGINE_ACTOR_ID_PREFIX = "engine.";
1670
- function engineSystemActor(name) {
1671
- return { kind: "system", id: `${ENGINE_ACTOR_ID_PREFIX}${name}` };
1672
- }
1673
- function isEngineActor(actor) {
1674
- return actor.kind === "system" && actor.id.startsWith(ENGINE_ACTOR_ID_PREFIX);
1675
- }
1676
- const MAX_SPAWN_DEPTH = 6, SUBWORKFLOWS_ALL_DONE = "count($subworkflows[status != 'done']) == 0";
1677
- function gateActor(outcome) {
1678
- return engineSystemActor(outcome === "failed" ? "failWhen" : "completeWhen");
1679
- }
1680
- function effectiveCompleteWhen(task) {
1681
- return task.completeWhen ?? (task.subworkflows !== void 0 ? SUBWORKFLOWS_ALL_DONE : void 0);
1682
- }
1683
- async function evaluateResolutionGate(ctx, task, vars) {
1684
- const scope = { taskName: task.name, vars };
1685
- if (task.failWhen !== void 0 && await ctxEvaluateCondition(ctx, task.failWhen, scope))
1686
- return "failed";
1687
- const completeWhen = effectiveCompleteWhen(task);
1688
- if (completeWhen !== void 0 && await ctxEvaluateCondition(ctx, completeWhen, scope))
1689
- return "done";
1690
- }
1691
- async function spawnSubworkflows(ctx, mutation, task, sub, actor) {
1692
- if (ctx.instance.ancestors.length + 1 > MAX_SPAWN_DEPTH) {
1693
- const chain = [...ctx.instance.ancestors.map((a) => a.id), selfGdr(ctx.instance)].join(" \u2192 ");
1694
- throw new Error(
1695
- `Subworkflow depth limit (${MAX_SPAWN_DEPTH}) exceeded on task "${task.name}" of ${ctx.instance._id}. Ancestor chain: ${chain}`
1696
- );
1697
- }
1698
- const now = ctx.now, definition = await resolveDefinitionRef(ctx.client, sub.definition, ctx.instance.tag);
1699
- if (definition === null) {
1700
- const versionLabel = sub.definition.version === void 0 || sub.definition.version === "latest" ? "latest" : `v${sub.definition.version}`;
1701
- throw new Error(
1702
- `Subworkflow definition "${sub.definition.name}" ${versionLabel} not deployed (parent ${ctx.instance._id}, task ${task.name})`
1703
- );
1704
- }
1705
- const parentScope = await ctxConditionParams(ctx, {
1706
- taskName: task.name,
1707
- ...actor ? { actor } : {}
1708
- }), { rows, discoveryResource } = await resolveSpawnRows(ctx, sub), effectsContext = await resolveContextHandoff(ctx, sub, parentScope), refs = [];
1709
- for (const row of rows) {
1710
- const initialFields = await projectRowFields(
1711
- ctx,
1712
- sub,
1713
- definition,
1714
- parentScope,
1715
- row,
1716
- discoveryResource
1717
- ), { ref, body } = await prepareChildInstance({
1718
- client: ctx.client,
1719
- parent: ctx.instance,
1720
- definition,
1721
- initialFields,
1722
- effectsContext,
1723
- actor,
1724
- now
1725
- });
1726
- refs.push(ref), mutation.pendingCreates.push(body);
1727
- }
1728
- return refs;
1729
- }
1730
- async function resolveSpawnRows(ctx, sub) {
1731
- const params = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), groq = sub.forEach;
1732
- let value, discoveryResource;
1733
- if (groq.includes("*")) {
1734
- const routingUri = entrySubjectRefs(ctx.instance.fields)[0]?.id, client = clientForDiscovery(ctx.client, ctx.clientForGdr, routingUri);
1735
- client !== ctx.client && routingUri !== void 0 && (discoveryResource = resourceFromGdrUri(routingUri)), value = await discoverItems({
1736
- client,
1737
- groq,
1738
- params,
1739
- // Draft-aware by default (drafts overlay published) when there's no
1740
- // release, so a `forEach` discovers in-flight items the same way the
1741
- // engine reads any other content.
1742
- perspective: ctx.instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE
1743
- });
1744
- } else {
1745
- const tree = groqJs.parse(groq, { params });
1746
- value = await (await groqJs.evaluate(tree, { dataset: [ctx.instance], params, root: ctx.instance })).get();
1747
- }
1748
- return Array.isArray(value) ? { rows: value, discoveryResource } : value == null ? { rows: [], discoveryResource } : { rows: [value], discoveryResource };
1749
- }
1750
- async function projectRowFields(ctx, sub, childDefinition, parentScope, row, discoveryResource) {
1751
- const out = [];
1752
- for (const [name, groq] of Object.entries(sub.with ?? {})) {
1753
- const declared = (childDefinition.fields ?? []).find((e) => e.name === name);
1754
- if (declared === void 0)
1755
- throw new Error(
1756
- `subworkflows.with["${name}"]: subworkflow "${childDefinition.name}" declares no field entry "${name}"`
1757
- );
1758
- const raw = await runGroq(groq, { ...parentScope, row }, ctx.snapshot), value = canonicaliseSpawnValue(declared.type, raw, {
1759
- parentResource: ctx.instance.workflowResource,
1760
- discoveryResource,
1761
- entryName: name,
1762
- childName: childDefinition.name
1763
- });
1764
- value != null && out.push({
1765
- type: declared.type,
1766
- name,
1767
- value
1768
- });
1769
- }
1770
- return out;
1771
- }
1772
- async function resolveContextHandoff(ctx, sub, parentScope) {
1773
- const out = {};
1774
- for (const [name, groq] of Object.entries(sub.context ?? {})) {
1775
- const v2 = await runGroq(groq, parentScope, ctx.snapshot);
1776
- v2 != null && (typeof v2 == "string" || typeof v2 == "number" || typeof v2 == "boolean" || typeof v2 == "object" && "id" in v2 && "type" in v2) && (out[name] = v2);
1777
- }
1778
- return out;
1779
- }
1780
- function canonicaliseSpawnValue(kind, value, cx) {
1781
- return kind === "doc.ref" ? coerceSpawnGdr(value, cx) : kind === "doc.refs" && Array.isArray(value) ? value.map((v2) => coerceSpawnGdr(v2, cx)).filter((v2) => v2 !== null) : value;
1782
- }
1783
- function assertBareIdRootsCorrectly(id, cx) {
1784
- if (!(cx.discoveryResource === void 0 || sameResource(cx.discoveryResource, cx.parentResource)))
1785
- throw new Error(
1786
- `subworkflows.with["${cx.entryName}"]: subworkflow "${cx.childName}" projected the bare id "${id}", which roots at the parent resource "${cx.parentResource.id}", but spawn discovery ran against "${cx.discoveryResource.id}". The child would reference a document that doesn't exist in the parent resource. Project an explicit GDR URI in the with-GROQ (e.g. "dataset:<projectId>:<dataset>:" + _id) so the child's subject points at the resource the discovered rows came from.`
1787
- );
1788
- }
1789
- function coerceSpawnGdr(raw, cx) {
1790
- const toUri = (id) => isGdrUri(id) ? id : (assertBareIdRootsCorrectly(id, cx), gdrFromResource(cx.parentResource, id));
1791
- if (raw == null) return null;
1792
- if (typeof raw == "object" && "id" in raw && "type" in raw) {
1793
- const r = raw;
1794
- if (typeof r.id == "string" && typeof r.type == "string")
1795
- return { id: toUri(r.id), type: r.type };
1796
- }
1797
- if (typeof raw == "object" && "_ref" in raw) {
1798
- const r = raw;
1799
- if (typeof r._ref == "string") return { id: toUri(r._ref), type: "document" };
1800
- }
1801
- return typeof raw == "string" && raw.length > 0 ? { id: toUri(raw), type: "document" } : null;
1802
- }
1803
- async function resolveDefinitionRef(client, ref, tag) {
1804
- const wantsExplicit = typeof ref.version == "number", params = { definition: ref.name, tag };
1805
- return wantsExplicit && (params.version = ref.version), await client.fetch(
1806
- definitionLookupGroq(wantsExplicit),
1807
- params
1808
- ) ?? null;
1809
- }
1810
- async function prepareChildInstance(args) {
1811
- const { client, parent, definition, initialFields, effectsContext, actor, now } = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId = `${childTag}.wf-instance.${randomKey()}`, childRef = { id: gdrFromResource(workflowResource, childDocId), type: WORKFLOW_INSTANCE_TYPE }, ancestors = [
1812
- ...parent.ancestors,
1813
- {
1814
- id: selfGdr(parent),
1815
- type: WORKFLOW_INSTANCE_TYPE
1816
- }
1817
- ], inheritedPerspective = parent.perspective, childFields = await resolveDeclaredFields({
1818
- entryDefs: definition.fields,
1819
- initialFields,
1820
- ctx: {
1821
- client,
1822
- now,
1823
- selfId: childDocId,
1824
- tag: childTag,
1825
- workflowResource,
1826
- definitionName: definition.name,
1827
- ...inheritedPerspective !== void 0 ? { perspective: inheritedPerspective } : {}
1828
- },
1829
- randomKey
1830
- }), childPerspective = derivePerspectiveFromFields(childFields) ?? inheritedPerspective, effectsContextEntries = Object.entries(effectsContext).map(
1831
- ([key, value]) => {
1832
- if (typeof value == "object" && value !== null && "id" in value && "type" in value) {
1833
- if (!isGdrUri(value.id))
1834
- throw new Error(
1835
- `subworkflows.context["${key}"]: GDR id must be a "<scheme>:..." URI, got ${JSON.stringify(value.id)}.`
1836
- );
1837
- return effectsContextEntry(key, { id: value.id, type: value.type });
1838
- }
1839
- return effectsContextEntry(key, value);
1840
- }
1841
- ), base = buildInstanceBase({
1842
- id: childDocId,
1843
- now,
1844
- tag: childTag,
1845
- workflowResource,
1846
- definitionName: definition.name,
1847
- pinnedVersion: definition.version,
1848
- definition,
1849
- fields: childFields,
1850
- effectsContext: effectsContextEntries,
1851
- ancestors,
1852
- perspective: childPerspective,
1853
- initialStage: definition.initialStage,
1854
- actor
1855
- });
1856
- return { ref: childRef, body: base };
1857
- }
1858
- async function subworkflowRows(ctx, refs) {
1859
- if (refs.length === 0) return [];
1860
- const ids = refs.map((r) => gdrToBareId(r.id));
1861
- return (await ctx.client.fetch("*[_id in $ids]{_id, currentStage, completedAt}", { ids })).map((c) => ({
1862
- _id: c._id,
1863
- stage: c.currentStage,
1864
- status: c.completedAt !== void 0 && c.completedAt !== null ? "done" : "active"
1865
- }));
1866
- }
1867
- async function resolveBindings(args) {
1868
- const resolved = {};
1869
- for (const [key, groq] of Object.entries(args.bindings ?? {}))
1870
- resolved[key] = await runGroq(groq, args.params, args.snapshot);
1871
- return { ...resolved, ...args.staticInput };
1872
- }
1873
- async function persistThenDeploy(ctx, mutation, deploy) {
1874
- const spawned = mutation.pendingCreates.length > 0, committed = await persist(ctx, mutation);
1875
- await deployOrRollback({
1876
- client: ctx.client,
1877
- instanceId: ctx.instance._id,
1878
- committedRev: committed._rev,
1879
- restore: instanceStateFields(ctx.instance),
1880
- unset: ctx.instance.completedAt === void 0 ? ["completedAt"] : [],
1881
- reversible: !spawned,
1882
- deploy
1883
- });
1884
- }
1885
- async function refreshStageGuards(ctx, mutation, stageName) {
1886
- await deployStageGuards({
1887
- client: ctx.client,
1888
- clientForGdr: ctx.clientForGdr,
1889
- instance: materializeInstance(ctx.instance, mutation),
1890
- definition: ctx.definition,
1891
- stageName,
1892
- now: ctx.now
1893
- });
1894
- }
1895
- async function completeEffect(args) {
1896
- const { client, instanceId, effectKey, status, outputs, detail, error, durationMs, options } = args, ctx = await loadContext(client, instanceId, {
1897
- ...options?.clock ? { clock: options.clock } : {},
1898
- ...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
1899
- });
1900
- return commitCompleteEffect(
1901
- ctx,
1902
- effectKey,
1903
- status,
1904
- outputs,
1905
- detail,
1906
- error,
1907
- durationMs,
1908
- options?.actor
1909
- );
1910
- }
1911
- function buildEffectHistoryEntry(pending, outcome) {
1912
- const { status, ranAt, actor, detail, error, durationMs, outputs } = outcome, resolvedActor = actor ?? pending.actor;
1661
+ function buildEffectHistoryEntry(pending, outcome) {
1662
+ const { status, ranAt, actor, detail, error, durationMs, outputs } = outcome, resolvedActor = actor ?? pending.actor;
1913
1663
  return {
1914
1664
  _key: pending._key,
1915
1665
  name: pending.name,
@@ -1982,7 +1732,7 @@ function buildQueuedEffect(effect, origin, params, actor, now) {
1982
1732
  async function queueEffects(ctx, mutation, effects, origin, actor, opts) {
1983
1733
  if (!effects || effects.length === 0) return;
1984
1734
  const now = ctx.now, liveCtx = { ...ctx, instance: materializeInstance(ctx.instance, mutation) }, params = await ctxConditionParams(liveCtx, {
1985
- ...opts?.taskName !== void 0 ? { taskName: opts.taskName } : {},
1735
+ ...opts?.activityName !== void 0 ? { activityName: opts.activityName } : {},
1986
1736
  ...actor !== void 0 ? { actor } : {},
1987
1737
  // Caller-supplied action params, readable in bindings as `$params.<name>`.
1988
1738
  vars: { params: opts?.callerParams ?? {} }
@@ -1997,18 +1747,74 @@ async function queueEffects(ctx, mutation, effects, origin, actor, opts) {
1997
1747
  mutation.pendingEffects.push(pending), mutation.history.push(history);
1998
1748
  }
1999
1749
  }
2000
- function resolveStaticSource(src, ctx) {
1750
+ function fieldQueryDiscardedEntry(args) {
1751
+ return {
1752
+ _key: randomKey(),
1753
+ _type: "fieldQueryDiscarded",
1754
+ at: args.at,
1755
+ scope: args.scope,
1756
+ field: args.field,
1757
+ detail: args.detail
1758
+ };
1759
+ }
1760
+ function recordFieldDiscards(target, scope, at) {
1761
+ return (discard) => target.push(fieldQueryDiscardedEntry({ scope, field: discard.field, detail: discard.detail, at }));
1762
+ }
1763
+ function applyActivityStatusChange(args) {
1764
+ const { entry, history, stage, to, at, actor } = args, from = entry.status;
1765
+ entry.status = to, schema.isTerminalActivityStatus(to) && (entry.completedAt = at, actor !== void 0 && (entry.completedBy = actor.id)), history.push({
1766
+ _key: randomKey(),
1767
+ _type: "activityStatusChanged",
1768
+ at,
1769
+ stage,
1770
+ activity: entry.name,
1771
+ from,
1772
+ to,
1773
+ ...actor !== void 0 ? { actor } : {}
1774
+ });
1775
+ }
1776
+ function stageTransitionHistory(args) {
1777
+ const { fromStage, toStage, at, transition, actor, via, reason } = args, shared = {
1778
+ at,
1779
+ ...transition !== void 0 ? { transition } : {},
1780
+ ...actor !== void 0 ? { actor } : {},
1781
+ ...via !== void 0 ? { via } : {},
1782
+ ...reason !== void 0 ? { reason } : {}
1783
+ };
1784
+ return [
1785
+ {
1786
+ _key: randomKey(),
1787
+ _type: "stageExited",
1788
+ stage: fromStage,
1789
+ toStage,
1790
+ ...shared
1791
+ },
1792
+ {
1793
+ _key: randomKey(),
1794
+ _type: "transitionFired",
1795
+ fromStage,
1796
+ toStage,
1797
+ ...shared
1798
+ },
1799
+ {
1800
+ _key: randomKey(),
1801
+ _type: "stageEntered",
1802
+ stage: toStage,
1803
+ fromStage,
1804
+ ...shared
1805
+ }
1806
+ ];
1807
+ }
1808
+ function resolveStaticValueExpr(src, ctx) {
2001
1809
  switch (src.type) {
2002
1810
  case "literal":
2003
- return { handled: !0, value: src.value };
1811
+ return src.value;
2004
1812
  case "param":
2005
- return { handled: !0, value: ctx.params?.[src.param] };
1813
+ return ctx.params?.[src.param];
2006
1814
  case "actor":
2007
- return { handled: !0, value: ctx.actor };
1815
+ return ctx.actor;
2008
1816
  case "now":
2009
- return { handled: !0, value: ctx.now };
2010
- default:
2011
- return { handled: !1 };
1817
+ return ctx.now;
2012
1818
  }
2013
1819
  }
2014
1820
  const FIELD_OP_TYPES = [
@@ -2021,7 +1827,7 @@ const FIELD_OP_TYPES = [
2021
1827
  function isFieldOp(summary) {
2022
1828
  return FIELD_OP_TYPES.includes(summary.opType);
2023
1829
  }
2024
- function validateActionParams(action, taskName, callerParams) {
1830
+ function validateActionParams(action, activityName, callerParams) {
2025
1831
  const declared = action.params ?? [], params = callerParams ?? {}, issues = [];
2026
1832
  for (const decl of declared) {
2027
1833
  const value = params[decl.name], present = decl.name in params && value !== void 0 && value !== null;
@@ -2035,7 +1841,7 @@ function validateActionParams(action, taskName, callerParams) {
2035
1841
  }));
2036
1842
  }
2037
1843
  if (issues.length > 0)
2038
- throw new ActionParamsInvalidError({ action: action.name, task: taskName, issues });
1844
+ throw new ActionParamsInvalidError({ action: action.name, activity: activityName, issues });
2039
1845
  return params;
2040
1846
  }
2041
1847
  function hasStringField(value, field) {
@@ -2068,7 +1874,15 @@ function runOps(args) {
2068
1874
  if (ops === void 0 || ops.length === 0) return [];
2069
1875
  const summaries = [];
2070
1876
  for (const op of ops) {
2071
- const summary = applyOp(op, { mutation, stage, taskName: origin.task, params, actor, self, now });
1877
+ const summary = applyOp(op, {
1878
+ mutation,
1879
+ stage,
1880
+ activityName: origin.activity,
1881
+ params,
1882
+ actor,
1883
+ self,
1884
+ now
1885
+ });
2072
1886
  summaries.push(summary), mutation.history.push(opAppliedEntry({ origin, summary, stage, actor, now }));
2073
1887
  }
2074
1888
  return summaries;
@@ -2080,7 +1894,7 @@ function opAppliedEntry(args) {
2080
1894
  _type: "opApplied",
2081
1895
  at: now,
2082
1896
  stage,
2083
- ...origin.task !== void 0 ? { task: origin.task } : {},
1897
+ ...origin.activity !== void 0 ? { activity: origin.activity } : {},
2084
1898
  ...origin.action !== void 0 ? { action: origin.action } : {},
2085
1899
  ...origin.transition !== void 0 ? { transition: origin.transition } : {},
2086
1900
  ...origin.edit === !0 ? { edit: !0 } : {},
@@ -2107,13 +1921,15 @@ function applyOp(op, ctx) {
2107
1921
  }
2108
1922
  }
2109
1923
  function applyFieldSet(op, ctx) {
2110
- const value = resolveOpSource(op.value, ctx), entry = locateEntry(ctx, op.target);
2111
- return validateFieldValue({ entryType: entry._type, entryName: entry.name, value }), setEntryValue(entry, value), { opType: op.type, target: op.target, resolved: { value } };
1924
+ const value = resolveOpValue(op.value, ctx), entry = locateEntry(ctx, op.target);
1925
+ return validateFieldValue({ entryType: entry._type, entryName: entry.name, value, ...entryShape(entry) }), setEntryValue(entry, value), { opType: op.type, target: op.target, resolved: { value } };
1926
+ }
1927
+ function entryShape(entry) {
1928
+ return entry._type === "object" ? { fields: entry.fields } : entry._type === "array" ? { of: entry.of } : {};
2112
1929
  }
2113
1930
  const EMPTY_BY_KIND = {
2114
1931
  "doc.refs": [],
2115
- checklist: [],
2116
- notes: [],
1932
+ array: [],
2117
1933
  assignees: []
2118
1934
  };
2119
1935
  function applyFieldUnset(op, ctx) {
@@ -2121,8 +1937,13 @@ function applyFieldUnset(op, ctx) {
2121
1937
  return setEntryValue(entry, EMPTY_BY_KIND[entry._type] ?? null), { opType: op.type, target: op.target };
2122
1938
  }
2123
1939
  function applyFieldAppend(op, ctx) {
2124
- const item = resolveOpSource(op.value, ctx), entry = locateEntry(ctx, op.target);
2125
- validateFieldAppendItem({ entryType: entry._type, entryName: entry.name, item });
1940
+ const item = resolveOpValue(op.value, ctx), entry = locateEntry(ctx, op.target);
1941
+ validateFieldAppendItem({
1942
+ entryType: entry._type,
1943
+ entryName: entry.name,
1944
+ item,
1945
+ of: entry._type === "array" ? entry.of : void 0
1946
+ });
2126
1947
  const current = Array.isArray(entry.value) ? entry.value : [];
2127
1948
  return setEntryValue(entry, [...current, withRowKey(item)]), { opType: op.type, target: op.target, resolved: { item } };
2128
1949
  }
@@ -2130,7 +1951,7 @@ function withRowKey(item) {
2130
1951
  return typeof item == "object" && item !== null && !Array.isArray(item) && !("_key" in item) ? { _key: randomKey(), ...item } : item;
2131
1952
  }
2132
1953
  function applyFieldUpdateWhere(op, ctx) {
2133
- const entry = locateEntry(ctx, op.target), rows = requireArrayValue(entry, op), merge = resolveOpSource(op.value, ctx);
1954
+ const entry = locateEntry(ctx, op.target), rows = requireArrayValue(entry, op), merge = resolveOpValue(op.value, ctx);
2134
1955
  if (merge === null || typeof merge != "object" || Array.isArray(merge))
2135
1956
  throw new Error(
2136
1957
  `field.updateWhere value must resolve to an object of fields to merge (target "${op.target.field}")`
@@ -2156,145 +1977,385 @@ function requireArrayValue(entry, op) {
2156
1977
  );
2157
1978
  return entry.value;
2158
1979
  }
2159
- function applyStatusSet(op, ctx) {
2160
- const entry = findOpenStageEntry(ctx.mutation)?.tasks.find((t) => t.name === op.task);
2161
- if (entry === void 0)
2162
- throw new Error(`status.set targets task "${op.task}" which has no entry in the open stage`);
2163
- return applyTaskStatusChange({
2164
- entry,
2165
- history: ctx.mutation.history,
2166
- stage: ctx.stage,
2167
- to: op.status,
2168
- at: ctx.now,
2169
- ...ctx.actor !== void 0 ? { actor: ctx.actor } : {}
2170
- }), { opType: op.type, resolved: { task: op.task, status: op.status } };
1980
+ function applyStatusSet(op, ctx) {
1981
+ const entry = findOpenStageEntry(ctx.mutation)?.activities.find((t) => t.name === op.activity);
1982
+ if (entry === void 0)
1983
+ throw new Error(
1984
+ `status.set targets activity "${op.activity}" which has no entry in the open stage`
1985
+ );
1986
+ return applyActivityStatusChange({
1987
+ entry,
1988
+ history: ctx.mutation.history,
1989
+ stage: ctx.stage,
1990
+ to: op.status,
1991
+ at: ctx.now,
1992
+ ...ctx.actor !== void 0 ? { actor: ctx.actor } : {}
1993
+ }), { opType: op.type, resolved: { activity: op.activity, status: op.status } };
1994
+ }
1995
+ function setEntryValue(entry, value) {
1996
+ entry.value = value;
1997
+ }
1998
+ function locateEntry(ctx, target) {
1999
+ const entry = fieldHost(ctx, target.scope)?.find((s) => s.name === target.field);
2000
+ if (entry === void 0)
2001
+ throw new Error(
2002
+ `Op target ${target.scope}:"${target.field}" has no resolved field entry on this instance \u2014 the deployed definition and the instance state have diverged`
2003
+ );
2004
+ return entry;
2005
+ }
2006
+ function fieldHost(ctx, scope) {
2007
+ if (scope === "workflow") return ctx.mutation.fields;
2008
+ const stageEntry = findOpenStageEntry(ctx.mutation);
2009
+ if (scope === "stage") return stageEntry?.fields;
2010
+ const activity = stageEntry?.activities.find((t) => t.name === ctx.activityName);
2011
+ return activity !== void 0 && activity.fields === void 0 && (activity.fields = []), activity?.fields;
2012
+ }
2013
+ function resolveOpValue(src, ctx) {
2014
+ switch (src.type) {
2015
+ case "literal":
2016
+ case "param":
2017
+ case "actor":
2018
+ case "now":
2019
+ return resolveStaticValueExpr(src, ctx);
2020
+ case "self":
2021
+ return ctx.self;
2022
+ case "stage":
2023
+ return ctx.stage;
2024
+ case "fieldRead": {
2025
+ const value = readEntryFromMutation(ctx, src);
2026
+ return src.path !== void 0 ? getPath(value, src.path) : value;
2027
+ }
2028
+ case "object": {
2029
+ const out = {};
2030
+ for (const [field, fieldSrc] of Object.entries(src.fields))
2031
+ out[field] = resolveOpValue(fieldSrc, ctx);
2032
+ return out;
2033
+ }
2034
+ }
2035
+ }
2036
+ function entryValue(entries, name) {
2037
+ return entries?.find((s) => s.name === name)?.value;
2038
+ }
2039
+ function readEntryFromMutation(ctx, src) {
2040
+ const scopes = src.scope !== void 0 ? [src.scope] : ["stage", "workflow"], stageEntry = findOpenStageEntry(ctx.mutation);
2041
+ for (const scope of scopes) {
2042
+ const host = scope === "workflow" ? ctx.mutation.fields : stageEntry?.fields, value = entryValue(host, src.field);
2043
+ if (value !== void 0) return value;
2044
+ }
2045
+ }
2046
+ function evalOpPredicate(p, row, ctx) {
2047
+ switch (p.type) {
2048
+ case "all":
2049
+ return p.of.every((sub) => evalOpPredicate(sub, row, ctx));
2050
+ case "any":
2051
+ return p.of.some((sub) => evalOpPredicate(sub, row, ctx));
2052
+ case "field":
2053
+ return row[p.field] === resolveOpValue(p.equals, ctx);
2054
+ }
2055
+ }
2056
+ const TAG_RE = /^[a-z0-9][a-z0-9-]*$/;
2057
+ function validateTag(tag) {
2058
+ if (!TAG_RE.test(tag))
2059
+ throw new Error(
2060
+ `tag: invalid tag "${tag}" \u2014 must match ${TAG_RE.source} (ASCII lowercase + digits + dashes, no leading dash, no dots)`
2061
+ );
2062
+ }
2063
+ function tagScopeFilter() {
2064
+ return "tag == $tag";
2065
+ }
2066
+ function definitionLookupGroq(explicit) {
2067
+ const scoped = `_type == "${schema.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${tagScopeFilter()}`;
2068
+ return explicit ? `*[${scoped} && version == $version][0]` : `*[${scoped}] | order(version desc)[0]`;
2069
+ }
2070
+ async function discoverItems(args) {
2071
+ const { client, groq, params, perspective } = args, fetchOptions = perspective !== void 0 ? { perspective } : void 0;
2072
+ return client.fetch(groq, paramsForLake(params), fetchOptions);
2073
+ }
2074
+ function clientForDiscovery(defaultClient, clientForGdr, subjectGdrUri) {
2075
+ if (!subjectGdrUri || !subjectGdrUri.includes(":")) return defaultClient;
2076
+ try {
2077
+ return clientForGdr(parseGdr(subjectGdrUri));
2078
+ } catch {
2079
+ return defaultClient;
2080
+ }
2081
+ }
2082
+ const ENGINE_ACTOR_ID_PREFIX = "engine.";
2083
+ function engineSystemActor(name) {
2084
+ return { kind: "system", id: `${ENGINE_ACTOR_ID_PREFIX}${name}` };
2085
+ }
2086
+ function isEngineActor(actor) {
2087
+ return actor.kind === "system" && actor.id.startsWith(ENGINE_ACTOR_ID_PREFIX);
2088
+ }
2089
+ const MAX_SPAWN_DEPTH = 6, SUBWORKFLOWS_ALL_DONE = "count($subworkflows[status != 'done']) == 0";
2090
+ function gateActor(outcome) {
2091
+ return engineSystemActor(outcome === "failed" ? "failWhen" : "completeWhen");
2092
+ }
2093
+ function effectiveCompleteWhen(activity) {
2094
+ return activity.completeWhen ?? (activity.subworkflows !== void 0 ? SUBWORKFLOWS_ALL_DONE : void 0);
2095
+ }
2096
+ async function evaluateResolutionGate(ctx, activity, vars) {
2097
+ const scope = { activityName: activity.name, vars };
2098
+ if (activity.failWhen !== void 0 && await ctxEvaluateCondition(ctx, activity.failWhen, scope))
2099
+ return "failed";
2100
+ const completeWhen = effectiveCompleteWhen(activity);
2101
+ if (completeWhen !== void 0 && await ctxEvaluateCondition(ctx, completeWhen, scope))
2102
+ return "done";
2103
+ }
2104
+ async function spawnSubworkflows(ctx, mutation, activity, sub, actor) {
2105
+ if (ctx.instance.ancestors.length + 1 > MAX_SPAWN_DEPTH) {
2106
+ const chain = [...ctx.instance.ancestors.map((a) => a.id), selfGdr(ctx.instance)].join(" \u2192 ");
2107
+ throw new Error(
2108
+ `Subworkflow depth limit (${MAX_SPAWN_DEPTH}) exceeded on activity "${activity.name}" of ${ctx.instance._id}. Ancestor chain: ${chain}`
2109
+ );
2110
+ }
2111
+ const now = ctx.now, definition = await resolveDefinitionRef(ctx.client, sub.definition, ctx.instance.tag);
2112
+ if (definition === null) {
2113
+ const versionLabel = sub.definition.version === void 0 || sub.definition.version === "latest" ? "latest" : `v${sub.definition.version}`;
2114
+ throw new Error(
2115
+ `Subworkflow definition "${sub.definition.name}" ${versionLabel} not deployed (parent ${ctx.instance._id}, activity ${activity.name})`
2116
+ );
2117
+ }
2118
+ const parentScope = await ctxConditionParams(ctx, {
2119
+ activityName: activity.name,
2120
+ ...actor ? { actor } : {}
2121
+ }), { rows, discoveryResource } = await resolveSpawnRows(ctx, sub), effectsContext = await resolveContextHandoff(ctx, sub, parentScope), refs = [];
2122
+ for (const row of rows) {
2123
+ const initialFields = await projectRowFields(
2124
+ ctx,
2125
+ sub,
2126
+ definition,
2127
+ parentScope,
2128
+ row,
2129
+ discoveryResource
2130
+ ), { ref, body } = await prepareChildInstance({
2131
+ client: ctx.client,
2132
+ parent: ctx.instance,
2133
+ definition,
2134
+ initialFields,
2135
+ effectsContext,
2136
+ actor,
2137
+ now
2138
+ });
2139
+ refs.push(ref), mutation.pendingCreates.push(body);
2140
+ }
2141
+ return refs;
2142
+ }
2143
+ async function resolveSpawnRows(ctx, sub) {
2144
+ const params = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), groq = sub.forEach;
2145
+ let value, discoveryResource;
2146
+ if (groq.includes("*")) {
2147
+ const routingUri = entrySubjectRefs(ctx.instance.fields)[0]?.id, client = clientForDiscovery(ctx.client, ctx.clientForGdr, routingUri);
2148
+ client !== ctx.client && routingUri !== void 0 && (discoveryResource = resourceFromGdrUri(routingUri)), value = await discoverItems({
2149
+ client,
2150
+ groq,
2151
+ params,
2152
+ // Draft-aware by default (drafts overlay published) when there's no
2153
+ // release, so a `forEach` discovers in-flight items the same way the
2154
+ // engine reads any other content.
2155
+ perspective: ctx.instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE
2156
+ });
2157
+ } else {
2158
+ const tree = groqJs.parse(groq, { params });
2159
+ value = await (await groqJs.evaluate(tree, { dataset: [ctx.instance], params, root: ctx.instance })).get();
2160
+ }
2161
+ return Array.isArray(value) ? { rows: value, discoveryResource } : value == null ? { rows: [], discoveryResource } : { rows: [value], discoveryResource };
2162
+ }
2163
+ async function projectRowFields(ctx, sub, childDefinition, parentScope, row, discoveryResource) {
2164
+ const out = [];
2165
+ for (const [name, groq] of Object.entries(sub.with ?? {})) {
2166
+ const declared = (childDefinition.fields ?? []).find((e) => e.name === name);
2167
+ if (declared === void 0)
2168
+ throw new Error(
2169
+ `subworkflows.with["${name}"]: subworkflow "${childDefinition.name}" declares no field entry "${name}"`
2170
+ );
2171
+ const raw = await runGroq(groq, { ...parentScope, row }, ctx.snapshot), value = canonicaliseSpawnValue(declared.type, raw, {
2172
+ parentResource: ctx.instance.workflowResource,
2173
+ discoveryResource,
2174
+ entryName: name,
2175
+ childName: childDefinition.name
2176
+ });
2177
+ value != null && out.push({
2178
+ type: declared.type,
2179
+ name,
2180
+ value
2181
+ });
2182
+ }
2183
+ return out;
2184
+ }
2185
+ async function resolveContextHandoff(ctx, sub, parentScope) {
2186
+ const out = {};
2187
+ for (const [name, groq] of Object.entries(sub.context ?? {})) {
2188
+ const v2 = await runGroq(groq, parentScope, ctx.snapshot);
2189
+ v2 != null && (typeof v2 == "string" || typeof v2 == "number" || typeof v2 == "boolean" || typeof v2 == "object" && "id" in v2 && "type" in v2) && (out[name] = v2);
2190
+ }
2191
+ return out;
2171
2192
  }
2172
- function setEntryValue(entry, value) {
2173
- entry.value = value;
2193
+ function canonicaliseSpawnValue(kind, value, cx) {
2194
+ return kind === "doc.ref" ? coerceSpawnGdr(value, cx) : kind === "doc.refs" && Array.isArray(value) ? value.map((v2) => coerceSpawnGdr(v2, cx)).filter((v2) => v2 !== null) : value;
2174
2195
  }
2175
- function locateEntry(ctx, target) {
2176
- const entry = fieldHost(ctx, target.scope)?.find((s) => s.name === target.field);
2177
- if (entry === void 0)
2196
+ function assertBareIdRootsCorrectly(id, cx) {
2197
+ if (!(cx.discoveryResource === void 0 || sameResource(cx.discoveryResource, cx.parentResource)))
2178
2198
  throw new Error(
2179
- `Op target ${target.scope}:"${target.field}" has no resolved field entry on this instance \u2014 the deployed definition and the instance state have diverged`
2199
+ `subworkflows.with["${cx.entryName}"]: subworkflow "${cx.childName}" projected the bare id "${id}", which roots at the parent resource "${cx.parentResource.id}", but spawn discovery ran against "${cx.discoveryResource.id}". The child would reference a document that doesn't exist in the parent resource. Project an explicit GDR URI in the with-GROQ (e.g. "dataset:<projectId>:<dataset>:" + _id) so the child's subject points at the resource the discovered rows came from.`
2180
2200
  );
2181
- return entry;
2182
- }
2183
- function fieldHost(ctx, scope) {
2184
- if (scope === "workflow") return ctx.mutation.fields;
2185
- const stageEntry = findOpenStageEntry(ctx.mutation);
2186
- if (scope === "stage") return stageEntry?.fields;
2187
- const task = stageEntry?.tasks.find((t) => t.name === ctx.taskName);
2188
- return task !== void 0 && task.fields === void 0 && (task.fields = []), task?.fields;
2189
2201
  }
2190
- function resolveOpSource(src, ctx) {
2191
- const staticValue = resolveStaticSource(src, ctx);
2192
- if (staticValue.handled) return staticValue.value;
2193
- switch (src.type) {
2194
- case "self":
2195
- return ctx.self;
2196
- case "stage":
2197
- return ctx.stage;
2198
- case "fieldRead": {
2199
- const value = readEntryFromMutation(ctx, src);
2200
- return src.path !== void 0 ? getPath(value, src.path) : value;
2201
- }
2202
- case "object": {
2203
- const out = {};
2204
- for (const [field, fieldSrc] of Object.entries(src.fields))
2205
- out[field] = resolveOpSource(fieldSrc, ctx);
2206
- return out;
2207
- }
2208
- default:
2209
- throw new Error(`Source type "${src.type}" is a field-entry origin, not a value`);
2202
+ function coerceSpawnGdr(raw, cx) {
2203
+ const toUri = (id) => isGdrUri(id) ? id : (assertBareIdRootsCorrectly(id, cx), gdrFromResource(cx.parentResource, id));
2204
+ if (raw == null) return null;
2205
+ if (typeof raw == "object" && "id" in raw && "type" in raw) {
2206
+ const r = raw;
2207
+ if (typeof r.id == "string" && typeof r.type == "string")
2208
+ return { id: toUri(r.id), type: r.type };
2209
+ }
2210
+ if (typeof raw == "object" && "_ref" in raw) {
2211
+ const r = raw;
2212
+ if (typeof r._ref == "string") return { id: toUri(r._ref), type: "document" };
2210
2213
  }
2214
+ return typeof raw == "string" && raw.length > 0 ? { id: toUri(raw), type: "document" } : null;
2211
2215
  }
2212
- function entryValue(entries, name) {
2213
- return entries?.find((s) => s.name === name)?.value;
2216
+ async function resolveDefinitionRef(client, ref, tag) {
2217
+ const wantsExplicit = typeof ref.version == "number", params = { definition: ref.name, tag };
2218
+ return wantsExplicit && (params.version = ref.version), await client.fetch(
2219
+ definitionLookupGroq(wantsExplicit),
2220
+ params
2221
+ ) ?? null;
2214
2222
  }
2215
- function readEntryFromMutation(ctx, src) {
2216
- const scopes = src.scope !== void 0 ? [src.scope] : ["stage", "workflow"], stageEntry = findOpenStageEntry(ctx.mutation);
2217
- for (const scope of scopes) {
2218
- const host = scope === "workflow" ? ctx.mutation.fields : stageEntry?.fields, value = entryValue(host, src.field);
2219
- if (value !== void 0) return value;
2220
- }
2223
+ async function prepareChildInstance(args) {
2224
+ const { client, parent, definition, initialFields, effectsContext, actor, now } = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId = `${childTag}.wf-instance.${randomKey()}`, childRef = { id: gdrFromResource(workflowResource, childDocId), type: WORKFLOW_INSTANCE_TYPE }, ancestors = [
2225
+ ...parent.ancestors,
2226
+ {
2227
+ id: selfGdr(parent),
2228
+ type: WORKFLOW_INSTANCE_TYPE
2229
+ }
2230
+ ], inheritedPerspective = parent.perspective, fieldDiscards = [], childFields = await resolveDeclaredFields({
2231
+ entryDefs: definition.fields,
2232
+ initialFields,
2233
+ ctx: {
2234
+ client,
2235
+ now,
2236
+ selfId: childDocId,
2237
+ tag: childTag,
2238
+ workflowResource,
2239
+ definitionName: definition.name,
2240
+ ...inheritedPerspective !== void 0 ? { perspective: inheritedPerspective } : {},
2241
+ recordDiscard: recordFieldDiscards(fieldDiscards, "workflow", now)
2242
+ },
2243
+ randomKey
2244
+ }), childPerspective = derivePerspectiveFromFields(childFields) ?? inheritedPerspective, effectsContextEntries = Object.entries(effectsContext).map(
2245
+ ([key, value]) => {
2246
+ if (typeof value == "object" && value !== null && "id" in value && "type" in value) {
2247
+ if (!isGdrUri(value.id))
2248
+ throw new Error(
2249
+ `subworkflows.context["${key}"]: GDR id must be a "<scheme>:..." URI, got ${JSON.stringify(value.id)}.`
2250
+ );
2251
+ return effectsContextEntry(key, { id: value.id, type: value.type });
2252
+ }
2253
+ return effectsContextEntry(key, value);
2254
+ }
2255
+ ), base = buildInstanceBase({
2256
+ id: childDocId,
2257
+ now,
2258
+ tag: childTag,
2259
+ workflowResource,
2260
+ definitionName: definition.name,
2261
+ pinnedVersion: definition.version,
2262
+ pinnedContentHash: definition.contentHash,
2263
+ definition,
2264
+ fields: childFields,
2265
+ effectsContext: effectsContextEntries,
2266
+ ancestors,
2267
+ perspective: childPerspective,
2268
+ initialStage: definition.initialStage,
2269
+ actor,
2270
+ ...fieldDiscards.length > 0 ? { extraHistory: fieldDiscards } : {}
2271
+ });
2272
+ return { ref: childRef, body: base };
2221
2273
  }
2222
- function evalOpPredicate(p, row, ctx) {
2223
- switch (p.type) {
2224
- case "all":
2225
- return p.of.every((sub) => evalOpPredicate(sub, row, ctx));
2226
- case "any":
2227
- return p.of.some((sub) => evalOpPredicate(sub, row, ctx));
2228
- case "field":
2229
- return row[p.field] === resolveOpSource(p.equals, ctx);
2230
- }
2274
+ async function subworkflowRows(ctx, refs) {
2275
+ if (refs.length === 0) return [];
2276
+ const ids = refs.map((r) => gdrToBareId(r.id));
2277
+ return (await ctx.client.fetch("*[_id in $ids]{_id, currentStage, completedAt}", { ids })).map((c) => ({
2278
+ _id: c._id,
2279
+ stage: c.currentStage,
2280
+ status: c.completedAt !== void 0 && c.completedAt !== null ? "done" : "active"
2281
+ }));
2231
2282
  }
2232
- async function invokeTask(args) {
2233
- const { client, instanceId, task: taskName, options } = args, ctx = await loadContext(client, instanceId, {
2283
+ async function invokeActivity(args) {
2284
+ const { client, instanceId, activity: activityName, options } = args, ctx = await loadContext(client, instanceId, {
2234
2285
  ...options?.clock ? { clock: options.clock } : {},
2235
2286
  ...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
2236
2287
  });
2237
- return commitInvoke(ctx, taskName, options?.actor);
2288
+ return commitInvoke(ctx, activityName, options?.actor);
2238
2289
  }
2239
- async function commitInvoke(ctx, taskName, actor) {
2240
- const { task } = findTaskInCurrentStage(ctx, taskName), entry = findCurrentTasks(ctx.instance).find((t) => t.name === taskName);
2290
+ async function commitInvoke(ctx, activityName, actor) {
2291
+ const { activity } = findActivityInCurrentStage(ctx, activityName), entry = findCurrentActivities(ctx.instance).find((t) => t.name === activityName);
2241
2292
  if (entry === void 0)
2242
- throw new Error(`Task "${taskName}" has no status entry on instance ${ctx.instance._id}`);
2293
+ throw new Error(
2294
+ `Activity "${activityName}" has no status entry on instance ${ctx.instance._id}`
2295
+ );
2243
2296
  if (entry.status !== "pending")
2244
- return { invoked: !1, task: taskName };
2245
- const mutation = startMutation(ctx.instance), mutEntry = requireMutationTaskEntry(mutation, taskName);
2246
- return await activateTask(ctx, mutation, task, mutEntry, actor), await persist(ctx, mutation), { invoked: !0, task: taskName };
2297
+ return { invoked: !1, activity: activityName };
2298
+ const mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName);
2299
+ return await activateActivity(ctx, mutation, activity, mutEntry, actor), await persist(ctx, mutation), { invoked: !0, activity: activityName };
2247
2300
  }
2248
- async function activateTask(ctx, mutation, task, entry, actor) {
2301
+ async function activateActivity(ctx, mutation, activity, entry, actor) {
2249
2302
  const now = ctx.now;
2250
- if (entry.status = "active", entry.startedAt = now, task.fields !== void 0 && task.fields.length > 0) {
2303
+ if (entry.status = "active", entry.startedAt = now, activity.fields !== void 0 && activity.fields.length > 0) {
2251
2304
  const stage = findStage(ctx.definition, mutation.currentStage);
2252
- entry.fields = await resolveTaskFieldEntries({
2305
+ entry.fields = await resolveActivityFieldEntries({
2253
2306
  client: ctx.client,
2254
2307
  instance: ctx.instance,
2255
2308
  stage,
2256
- task,
2257
- now
2309
+ activity,
2310
+ now,
2311
+ recordDiscard: recordFieldDiscards(mutation.history, "activity", now)
2258
2312
  });
2259
2313
  }
2260
2314
  runOps({
2261
- ops: task.ops,
2315
+ ops: activity.ops,
2262
2316
  mutation,
2263
2317
  stage: mutation.currentStage,
2264
- origin: { task: task.name },
2318
+ origin: { activity: activity.name },
2265
2319
  params: {},
2266
2320
  actor,
2267
2321
  self: selfGdr(ctx.instance),
2268
2322
  now
2269
2323
  }), mutation.history.push({
2270
2324
  _key: randomKey(),
2271
- _type: "taskActivated",
2325
+ _type: "activityActivated",
2272
2326
  at: now,
2273
2327
  stage: mutation.currentStage,
2274
- task: task.name,
2328
+ activity: activity.name,
2275
2329
  ...actor !== void 0 ? { actor } : {}
2276
- }), await queueEffects(ctx, mutation, task.effects, { kind: "task", name: task.name }, actor, {
2277
- taskName: task.name
2278
- });
2330
+ }), await queueEffects(
2331
+ ctx,
2332
+ mutation,
2333
+ activity.effects,
2334
+ { kind: "activity", name: activity.name },
2335
+ actor,
2336
+ {
2337
+ activityName: activity.name
2338
+ }
2339
+ );
2279
2340
  let pendingChildren;
2280
- if (task.subworkflows !== void 0) {
2281
- const createsBefore = mutation.pendingCreates.length, refs = await spawnSubworkflows(ctx, mutation, task, task.subworkflows, actor);
2341
+ if (activity.subworkflows !== void 0) {
2342
+ const createsBefore = mutation.pendingCreates.length, refs = await spawnSubworkflows(ctx, mutation, activity, activity.subworkflows, actor);
2282
2343
  entry.spawnedInstances = refs, pendingChildren = mutation.pendingCreates.slice(createsBefore).map((c) => ({ _id: c._id, stage: c.currentStage, status: "active" }));
2283
2344
  for (const ref of refs)
2284
2345
  mutation.history.push({
2285
2346
  _key: randomKey(),
2286
2347
  _type: "spawned",
2287
2348
  at: now,
2288
- task: task.name,
2349
+ activity: activity.name,
2289
2350
  instanceRef: ref
2290
2351
  });
2291
2352
  }
2292
- await autoResolveOnActivate(ctx, mutation, task, entry, now, pendingChildren) || resolveMachineStep(mutation, task, entry, now);
2353
+ await autoResolveOnActivate(ctx, mutation, activity, entry, now, pendingChildren) || resolveMachineStep(mutation, activity, entry, now);
2293
2354
  }
2294
- async function autoResolveOnActivate(ctx, mutation, task, entry, now, pendingChildren) {
2295
- if (task.failWhen === void 0 && effectiveCompleteWhen(task) === void 0) return !1;
2296
- const vars = pendingChildren !== void 0 ? { subworkflows: pendingChildren } : await taskConditionVars(ctx, entry), outcome = await evaluateResolutionGate(ctx, task, vars);
2297
- return outcome === void 0 ? !1 : (applyTaskStatusChange({
2355
+ async function autoResolveOnActivate(ctx, mutation, activity, entry, now, pendingChildren) {
2356
+ if (activity.failWhen === void 0 && effectiveCompleteWhen(activity) === void 0) return !1;
2357
+ const vars = pendingChildren !== void 0 ? { subworkflows: pendingChildren } : await activityConditionVars(ctx, entry), outcome = await evaluateResolutionGate(ctx, activity, vars);
2358
+ return outcome === void 0 ? !1 : (applyActivityStatusChange({
2298
2359
  entry,
2299
2360
  history: mutation.history,
2300
2361
  stage: mutation.currentStage,
@@ -2303,12 +2364,12 @@ async function autoResolveOnActivate(ctx, mutation, task, entry, now, pendingChi
2303
2364
  actor: gateActor(outcome)
2304
2365
  }), !0);
2305
2366
  }
2306
- async function taskConditionVars(ctx, entry) {
2367
+ async function activityConditionVars(ctx, entry) {
2307
2368
  return entry.spawnedInstances === void 0 || entry.spawnedInstances.length === 0 ? { subworkflows: [] } : { subworkflows: await subworkflowRows(ctx, entry.spawnedInstances) };
2308
2369
  }
2309
- function resolveMachineStep(mutation, task, entry, now) {
2310
- const waitsForActor = task.actions !== void 0 && task.actions.length > 0, waitsForCondition = task.completeWhen !== void 0 || task.subworkflows !== void 0;
2311
- waitsForActor || waitsForCondition || applyTaskStatusChange({
2370
+ function resolveMachineStep(mutation, activity, entry, now) {
2371
+ const waitsForActor = activity.actions !== void 0 && activity.actions.length > 0, waitsForCondition = activity.completeWhen !== void 0 || activity.subworkflows !== void 0;
2372
+ waitsForActor || waitsForCondition || applyActivityStatusChange({
2312
2373
  entry,
2313
2374
  history: mutation.history,
2314
2375
  stage: mutation.currentStage,
@@ -2317,15 +2378,17 @@ function resolveMachineStep(mutation, task, entry, now) {
2317
2378
  actor: engineSystemActor("machineStep")
2318
2379
  });
2319
2380
  }
2320
- async function buildStageTasks(ctx, stage) {
2381
+ async function buildStageActivities(ctx, stage) {
2321
2382
  const entries = [];
2322
- for (const task of stage.tasks ?? []) {
2323
- const outcome = await ctxEvaluateConditionOutcome(ctx, task.filter, { taskName: task.name }), inScope = outcome !== "unsatisfied";
2383
+ for (const activity of stage.activities ?? []) {
2384
+ const outcome = await ctxEvaluateConditionOutcome(ctx, activity.filter, {
2385
+ activityName: activity.name
2386
+ }), inScope = outcome !== "unsatisfied";
2324
2387
  entries.push({
2325
2388
  _key: randomKey(),
2326
- name: task.name,
2389
+ name: activity.name,
2327
2390
  status: inScope ? "pending" : "skipped",
2328
- ...task.filter !== void 0 ? { filterEvaluation: buildFilterEvaluation(ctx.now, task.filter, outcome) } : {}
2391
+ ...activity.filter !== void 0 ? { filterEvaluation: buildFilterEvaluation(ctx.now, activity.filter, outcome) } : {}
2329
2392
  });
2330
2393
  }
2331
2394
  return entries;
@@ -2335,7 +2398,7 @@ function buildFilterEvaluation(at, filter, outcome) {
2335
2398
  at,
2336
2399
  truthy: !1,
2337
2400
  unevaluable: !0,
2338
- detail: `Filter "${filter}" could not be evaluated (GROQ null \u2014 a referenced operand was missing or incomparable). Task kept in scope so the gate is not silently skipped.`
2401
+ detail: `Filter "${filter}" could not be evaluated (GROQ null \u2014 a referenced operand was missing or incomparable). Activity kept in scope so the gate is not silently skipped.`
2339
2402
  } : { at, truthy: outcome === "satisfied" };
2340
2403
  }
2341
2404
  function isTerminalStage(stage) {
@@ -2358,15 +2421,16 @@ async function enterStage(ctx, mutation, nextStage, actor, at) {
2358
2421
  client: ctx.client,
2359
2422
  instance: ctx.instance,
2360
2423
  stage: nextStage,
2361
- now: at
2424
+ now: at,
2425
+ recordDiscard: recordFieldDiscards(mutation.history, "stage", at)
2362
2426
  }),
2363
- tasks: await buildStageTasks(ctx, nextStage)
2427
+ activities: await buildStageActivities(ctx, nextStage)
2364
2428
  };
2365
2429
  mutation.stages.push(nextStageEntry);
2366
- for (const task of nextStage.tasks ?? []) {
2367
- if (task.activation !== "auto") continue;
2368
- const entry = nextStageEntry.tasks.find((t) => t.name === task.name);
2369
- entry && entry.status === "pending" && await activateTask(ctx, mutation, task, entry, actor);
2430
+ for (const activity of nextStage.activities ?? []) {
2431
+ if (activity.activation !== "auto") continue;
2432
+ const entry = nextStageEntry.activities.find((t) => t.name === activity.name);
2433
+ entry && entry.status === "pending" && await activateActivity(ctx, mutation, activity, entry, actor);
2370
2434
  }
2371
2435
  isTerminalStage(nextStage) && (mutation.completedAt = at);
2372
2436
  }
@@ -2490,15 +2554,22 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
2490
2554
  instance,
2491
2555
  definition,
2492
2556
  ...clock ? { clock } : {}
2493
- }), now = ctx.now, initialStageEntry = {
2557
+ }), now = ctx.now, discards = [], initialStageEntry = {
2494
2558
  _key: randomKey(),
2495
2559
  name: stage.name,
2496
2560
  enteredAt: now,
2497
- fields: await resolveStageFieldEntries({ client, instance, stage, now }),
2498
- tasks: await buildStageTasks(ctx, stage)
2561
+ fields: await resolveStageFieldEntries({
2562
+ client,
2563
+ instance,
2564
+ stage,
2565
+ now,
2566
+ recordDiscard: recordFieldDiscards(discards, "stage", now)
2567
+ }),
2568
+ activities: await buildStageActivities(ctx, stage)
2499
2569
  }, committed = await client.patch(instance._id).set({
2500
2570
  stages: [initialStageEntry],
2501
- lastChangedAt: now
2571
+ lastChangedAt: now,
2572
+ ...discards.length > 0 ? { history: [...instance.history, ...discards] } : {}
2502
2573
  }).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
2503
2574
  await deployOrRollback({
2504
2575
  client,
@@ -2517,12 +2588,20 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
2517
2588
  stageName: stage.name,
2518
2589
  now
2519
2590
  })
2520
- }), await autoActivatePrimedTasks(client, instanceId, definition, stage, actor, clientForGdr, clock);
2591
+ }), await autoActivatePrimedActivities(
2592
+ client,
2593
+ instanceId,
2594
+ definition,
2595
+ stage,
2596
+ actor,
2597
+ clientForGdr,
2598
+ clock
2599
+ );
2521
2600
  }
2522
- async function autoActivatePrimedTasks(client, instanceId, definition, stage, actor, clientForGdr, clock) {
2601
+ async function autoActivatePrimedActivities(client, instanceId, definition, stage, actor, clientForGdr, clock) {
2523
2602
  const resolvedClientForGdr = clientForGdr ?? (() => client);
2524
- for (const task of stage.tasks ?? []) {
2525
- if (task.activation !== "auto") continue;
2603
+ for (const activity of stage.activities ?? []) {
2604
+ if (activity.activation !== "auto") continue;
2526
2605
  const updated = await client.getDocument(instanceId);
2527
2606
  if (!updated) continue;
2528
2607
  const updatedCtx = await buildEngineContext({
@@ -2531,18 +2610,22 @@ async function autoActivatePrimedTasks(client, instanceId, definition, stage, ac
2531
2610
  instance: updated,
2532
2611
  definition,
2533
2612
  ...clock ? { clock } : {}
2534
- }), mutation = startMutation(updated), mutEntry = currentTasks(mutation).find((t) => t.name === task.name);
2535
- mutEntry && mutEntry.status === "pending" && (await activateTask(updatedCtx, mutation, task, mutEntry, actor), await persist(updatedCtx, mutation));
2613
+ }), mutation = startMutation(updated), mutEntry = currentActivities(mutation).find((t) => t.name === activity.name);
2614
+ mutEntry && mutEntry.status === "pending" && (await activateActivity(updatedCtx, mutation, activity, mutEntry, actor), await persist(updatedCtx, mutation));
2536
2615
  }
2537
2616
  }
2538
2617
  const CASCADE_LIMIT = 100;
2539
- async function gatherAutoResolveCandidates(ctx, tasks) {
2540
- const currentTasksList = findCurrentTasks(ctx.instance), candidates = [];
2541
- for (const task of tasks) {
2542
- const entry = currentTasksList.find((e) => e.name === task.name);
2618
+ async function gatherAutoResolveCandidates(ctx, activities) {
2619
+ const currentActivitiesList = findCurrentActivities(ctx.instance), candidates = [];
2620
+ for (const activity of activities) {
2621
+ const entry = currentActivitiesList.find((e) => e.name === activity.name);
2543
2622
  if (entry?.status !== "active") continue;
2544
- const outcome = await evaluateResolutionGate(ctx, task, await taskConditionVars(ctx, entry));
2545
- outcome !== void 0 && candidates.push({ task, outcome });
2623
+ const outcome = await evaluateResolutionGate(
2624
+ ctx,
2625
+ activity,
2626
+ await activityConditionVars(ctx, entry)
2627
+ );
2628
+ outcome !== void 0 && candidates.push({ activity, outcome });
2546
2629
  }
2547
2630
  return candidates;
2548
2631
  }
@@ -2551,17 +2634,17 @@ async function resolveCompleteWhen(client, instanceId, clientForGdr, clock, over
2551
2634
  ...clientForGdr ? { clientForGdr } : {},
2552
2635
  ...clock ? { clock } : {},
2553
2636
  ...overlay ? { overlay } : {}
2554
- }), stage = findStage(ctx.definition, ctx.instance.currentStage), gatedTasks = (stage.tasks ?? []).filter(
2637
+ }), stage = findStage(ctx.definition, ctx.instance.currentStage), gatedActivities = (stage.activities ?? []).filter(
2555
2638
  (t) => effectiveCompleteWhen(t) !== void 0 || t.failWhen !== void 0
2556
2639
  );
2557
- if (gatedTasks.length === 0) return 0;
2558
- const candidates = await gatherAutoResolveCandidates(ctx, gatedTasks);
2640
+ if (gatedActivities.length === 0) return 0;
2641
+ const candidates = await gatherAutoResolveCandidates(ctx, gatedActivities);
2559
2642
  if (candidates.length === 0) return 0;
2560
2643
  const mutation = startMutation(ctx.instance);
2561
2644
  let count = 0;
2562
- for (const { task, outcome } of candidates) {
2563
- const mutEntry = currentTasks(mutation).find((t) => t.name === task.name);
2564
- mutEntry === void 0 || mutEntry.status !== "active" || (applyTaskStatusChange({
2645
+ for (const { activity, outcome } of candidates) {
2646
+ const mutEntry = currentActivities(mutation).find((t) => t.name === activity.name);
2647
+ mutEntry === void 0 || mutEntry.status !== "active" || (applyActivityStatusChange({
2565
2648
  entry: mutEntry,
2566
2649
  history: mutation.history,
2567
2650
  stage: stage.name,
@@ -2594,9 +2677,9 @@ async function findResolvedParentSpawn(client, child, clientForGdr, clock) {
2594
2677
  if (!parent || parent._type !== WORKFLOW_INSTANCE_TYPE || typeof parent.definitionSnapshot != "string") return;
2595
2678
  const definition = parseDefinitionSnapshot(parent), stage = definition.stages.find((s) => s.name === parent.currentStage);
2596
2679
  if (stage === void 0) return;
2597
- const parentCurrentTasks = findCurrentTasks(parent), task = (stage.tasks ?? []).find((t) => t.subworkflows === void 0 ? !1 : parentCurrentTasks.find((e) => e.name === t.name)?.spawnedInstances?.some((r) => gdrToBareId(r.id) === child._id) ?? !1);
2598
- if (task?.subworkflows === void 0) return;
2599
- const entry = parentCurrentTasks.find((e) => e.name === task.name);
2680
+ const parentCurrentActivities = findCurrentActivities(parent), activity = (stage.activities ?? []).find((t) => t.subworkflows === void 0 ? !1 : parentCurrentActivities.find((e) => e.name === t.name)?.spawnedInstances?.some((r) => gdrToBareId(r.id) === child._id) ?? !1);
2681
+ if (activity?.subworkflows === void 0) return;
2682
+ const entry = parentCurrentActivities.find((e) => e.name === activity.name);
2600
2683
  if (entry === void 0 || entry.status !== "active") return;
2601
2684
  const ctx = await buildEngineContext({
2602
2685
  client,
@@ -2604,23 +2687,27 @@ async function findResolvedParentSpawn(client, child, clientForGdr, clock) {
2604
2687
  instance: parent,
2605
2688
  definition,
2606
2689
  ...clock ? { clock } : {}
2607
- }), outcome = await evaluateResolutionGate(ctx, task, await taskConditionVars(ctx, entry));
2690
+ }), outcome = await evaluateResolutionGate(
2691
+ ctx,
2692
+ activity,
2693
+ await activityConditionVars(ctx, entry)
2694
+ );
2608
2695
  if (outcome !== void 0)
2609
- return { parent, definition, stage, task, outcome };
2696
+ return { parent, definition, stage, activity, outcome };
2610
2697
  }
2611
2698
  async function propagateToAncestors(client, instanceId, actor, clientForGdr, clock) {
2612
2699
  const instance = await client.getDocument(instanceId);
2613
2700
  if (!instance) return;
2614
2701
  const resolvedClientForGdr = clientForGdr ?? (() => client), found = await findResolvedParentSpawn(client, instance, resolvedClientForGdr, clock);
2615
2702
  if (found === void 0) return;
2616
- const { parent, definition, stage, task, outcome } = found, ctx = await buildEngineContext({
2703
+ const { parent, definition, stage, activity, outcome } = found, ctx = await buildEngineContext({
2617
2704
  client,
2618
2705
  clientForGdr: resolvedClientForGdr,
2619
2706
  instance: parent,
2620
2707
  definition,
2621
2708
  ...clock ? { clock } : {}
2622
- }), mutation = startMutation(parent), mutEntry = currentTasks(mutation).find((e) => e.name === task.name);
2623
- mutEntry !== void 0 && (applyTaskStatusChange({
2709
+ }), mutation = startMutation(parent), mutEntry = currentActivities(mutation).find((e) => e.name === activity.name);
2710
+ mutEntry !== void 0 && (applyActivityStatusChange({
2624
2711
  entry: mutEntry,
2625
2712
  history: mutation.history,
2626
2713
  stage: stage.name,
@@ -2723,6 +2810,18 @@ async function fetchGrantsCached(requestFn, resourcePath) {
2723
2810
  return;
2724
2811
  }
2725
2812
  }
2813
+ function hasItems(arr) {
2814
+ return arr !== void 0 && arr.length > 0;
2815
+ }
2816
+ function deriveActivityKind(activity) {
2817
+ return hasItems(activity.actions) ? "user" : hasItems(activity.effects) ? "service" : activity.completeWhen !== void 0 || activity.subworkflows !== void 0 ? "receive" : "script";
2818
+ }
2819
+ function activityKind(activity) {
2820
+ return activity.kind ?? deriveActivityKind(activity);
2821
+ }
2822
+ function driverKind(actor) {
2823
+ return actor.kind === "user" ? "person" : actor.kind === "ai" ? "agent" : isEngineActor(actor) ? "engine" : "service";
2824
+ }
2726
2825
  function effectiveEditable(baseline, override) {
2727
2826
  if (baseline === void 0) return;
2728
2827
  if (override === void 0) return baseline;
@@ -2734,14 +2833,15 @@ function slotSites(definition, stage) {
2734
2833
  const sites = [];
2735
2834
  for (const entry of definition.fields ?? []) sites.push({ scope: "workflow", entry });
2736
2835
  for (const entry of stage.fields ?? []) sites.push({ scope: "stage", entry });
2737
- for (const task of stage.tasks ?? [])
2738
- for (const entry of task.fields ?? []) sites.push({ scope: "task", task: task.name, entry });
2836
+ for (const activity of stage.activities ?? [])
2837
+ for (const entry of activity.fields ?? [])
2838
+ sites.push({ scope: "activity", activity: activity.name, entry });
2739
2839
  return sites;
2740
2840
  }
2741
2841
  function toResolvedSlot(site, stage) {
2742
2842
  return {
2743
2843
  scope: site.scope,
2744
- ...site.task !== void 0 ? { task: site.task } : {},
2844
+ ...site.activity !== void 0 ? { activity: site.activity } : {},
2745
2845
  name: site.entry.name,
2746
2846
  type: site.entry.type,
2747
2847
  ...site.entry.title !== void 0 ? { title: site.entry.title } : {},
@@ -2753,16 +2853,16 @@ function editableSlotsInStage(definition, stage) {
2753
2853
  return slotSites(definition, stage).filter((site) => site.entry.editable !== void 0).map((site) => toResolvedSlot(site, stage));
2754
2854
  }
2755
2855
  function editTargetMatches(slot, target) {
2756
- return slot.name !== target.field ? !1 : target.task !== void 0 ? slot.scope === "task" && slot.task === target.task : target.scope !== void 0 ? slot.scope === target.scope : !0;
2856
+ return slot.name !== target.field ? !1 : target.activity !== void 0 ? slot.scope === "activity" && slot.activity === target.activity : target.scope !== void 0 ? slot.scope === target.scope : !0;
2757
2857
  }
2758
- const SCOPE_PRECEDENCE = { task: 0, stage: 1, workflow: 2 };
2858
+ const SCOPE_PRECEDENCE = { activity: 0, stage: 1, workflow: 2 };
2759
2859
  function resolveEditTarget(args) {
2760
2860
  const { definition, stage, target } = args, found = slotSites(definition, stage).filter(
2761
2861
  (site) => editTargetMatches(
2762
2862
  {
2763
2863
  scope: site.scope,
2764
2864
  name: site.entry.name,
2765
- ...site.task !== void 0 ? { task: site.task } : {}
2865
+ ...site.activity !== void 0 ? { activity: site.activity } : {}
2766
2866
  },
2767
2867
  target
2768
2868
  )
@@ -2772,9 +2872,11 @@ function resolveEditTarget(args) {
2772
2872
  function slotWindowOpen(instance, slot) {
2773
2873
  if (instance.completedAt !== void 0) return { open: !1, detail: "instance completed" };
2774
2874
  if (instance.abortedAt !== void 0) return { open: !1, detail: "instance aborted" };
2775
- if (slot.scope !== "task") return { open: !0 };
2776
- const status = findOpenStageEntry(instance)?.tasks.find((t) => t.name === slot.task)?.status;
2777
- return status === "active" ? { open: !0 } : { open: !1, detail: `task "${slot.task}" is ${status ?? "not active"}` };
2875
+ if (slot.scope !== "activity") return { open: !0 };
2876
+ const status = findOpenStageEntry(instance)?.activities.find(
2877
+ (t) => t.name === slot.activity
2878
+ )?.status;
2879
+ return status === "active" ? { open: !0 } : { open: !1, detail: `activity "${slot.activity}" is ${status ?? "not active"}` };
2778
2880
  }
2779
2881
  function readSlotValue(instance, slot) {
2780
2882
  return slotFieldHost(instance, slot)?.find((e) => e.name === slot.name)?.value;
@@ -2782,7 +2884,7 @@ function readSlotValue(instance, slot) {
2782
2884
  function slotFieldHost(instance, slot) {
2783
2885
  if (slot.scope === "workflow") return instance.fields;
2784
2886
  const stageEntry = findOpenStageEntry(instance);
2785
- return slot.scope === "stage" ? stageEntry?.fields : stageEntry?.tasks.find((t) => t.name === slot.task)?.fields;
2887
+ return slot.scope === "stage" ? stageEntry?.fields : stageEntry?.activities.find((t) => t.name === slot.activity)?.fields;
2786
2888
  }
2787
2889
  function slotProvenance(instance, ref) {
2788
2890
  let latest;
@@ -2802,18 +2904,6 @@ function editDisabledReason(args) {
2802
2904
  if (!predicateSatisfied)
2803
2905
  return { kind: "editor-not-permitted", predicate: effective === !0 ? "true" : effective };
2804
2906
  }
2805
- function hasItems(arr) {
2806
- return arr !== void 0 && arr.length > 0;
2807
- }
2808
- function deriveTaskKind(task) {
2809
- return hasItems(task.actions) ? "user" : hasItems(task.effects) ? "service" : task.completeWhen !== void 0 || task.subworkflows !== void 0 ? "receive" : "script";
2810
- }
2811
- function taskKind(task) {
2812
- return task.kind ?? deriveTaskKind(task);
2813
- }
2814
- function driverKind(actor) {
2815
- return actor.kind === "user" ? "person" : actor.kind === "ai" ? "agent" : isEngineActor(actor) ? "engine" : "service";
2816
- }
2817
2907
  async function evaluateInstance(args) {
2818
2908
  const { client, tag, instanceId, resourceClients } = args, now = (args.clock ?? wallClock)();
2819
2909
  validateTag(tag);
@@ -2837,12 +2927,12 @@ async function evaluateFromSnapshot(args) {
2837
2927
  throw new Error(
2838
2928
  `Instance "${instance._id}" currentStage "${instance.currentStage}" not in definition`
2839
2929
  );
2840
- const scope = await renderScope({ instance, definition, actor, grants, snapshot, now }), currentTaskEntries = findOpenStageEntry(instance)?.tasks ?? [], guardDenial = await instanceGuardReason(instance, actor, args.guards), taskEvaluations = [];
2841
- for (const task of stage.tasks ?? [])
2842
- taskEvaluations.push(
2843
- await evaluateTask({
2844
- task,
2845
- statusEntry: currentTaskEntries.find((t) => t.name === task.name),
2930
+ const scope = await renderScope({ instance, definition, actor, grants, snapshot, now }), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason(instance, actor, args.guards), activityEvaluations = [];
2931
+ for (const activity of stage.activities ?? [])
2932
+ activityEvaluations.push(
2933
+ await evaluateActivity({
2934
+ activity,
2935
+ statusEntry: currentActivityEntries.find((t) => t.name === activity.name),
2846
2936
  instance,
2847
2937
  actor,
2848
2938
  snapshot,
@@ -2867,9 +2957,9 @@ async function evaluateFromSnapshot(args) {
2867
2957
  }
2868
2958
  const currentStage = {
2869
2959
  stage,
2870
- tasks: taskEvaluations,
2960
+ activities: activityEvaluations,
2871
2961
  transitions: transitionEvaluations
2872
- }, pendingOnYou = taskEvaluations.filter((t) => t.pendingOnActor), canInteract = taskEvaluations.some((t) => t.actions.some((a) => a.allowed)), editGuardDenial = guardDenial?.kind === "mutation-guard-denied" ? guardDenial : void 0, editableSlots = await evaluateEditableSlots({
2962
+ }, pendingOnYou = activityEvaluations.filter((t) => t.pendingOnActor), canInteract = activityEvaluations.some((t) => t.actions.some((a) => a.allowed)), editGuardDenial = guardDenial?.kind === "mutation-guard-denied" ? guardDenial : void 0, editableSlots = await evaluateEditableSlots({
2873
2963
  instance,
2874
2964
  definition,
2875
2965
  stage,
@@ -2909,7 +2999,7 @@ async function evaluateEditableSlots(args) {
2909
2999
  }), value = readSlotValue(instance, slot);
2910
3000
  slots.push({
2911
3001
  scope: slot.scope,
2912
- ...slot.task !== void 0 ? { task: slot.task } : {},
3002
+ ...slot.activity !== void 0 ? { activity: slot.activity } : {},
2913
3003
  name: slot.name,
2914
3004
  type: slot.type,
2915
3005
  ...slot.title !== void 0 ? { title: slot.title } : {},
@@ -2925,7 +3015,14 @@ async function editPredicateSatisfied(args) {
2925
3015
  const { slot, window, guardDenial, instance, snapshot, scope, actor, roleAliases } = args;
2926
3016
  if (!window.open || guardDenial !== void 0) return !1;
2927
3017
  if (slot.effective === !0) return !0;
2928
- const params = slot.scope === "task" && slot.task !== void 0 ? taskScopeFor({ scope, instance, snapshot, actor, taskName: slot.task, roleAliases }) : scope;
3018
+ const params = slot.scope === "activity" && slot.activity !== void 0 ? activityScopeFor({
3019
+ scope,
3020
+ instance,
3021
+ snapshot,
3022
+ actor,
3023
+ activityName: slot.activity,
3024
+ roleAliases
3025
+ }) : scope;
2929
3026
  return evaluateCondition({ condition: slot.effective, snapshot, params });
2930
3027
  }
2931
3028
  async function renderScope(args) {
@@ -2953,20 +3050,20 @@ async function advisoryCan(instance, actor, grants) {
2953
3050
  });
2954
3051
  return can;
2955
3052
  }
2956
- function taskScopeFor(args) {
2957
- const { scope, instance, snapshot, actor, taskName, roleAliases } = args;
3053
+ function activityScopeFor(args) {
3054
+ const { scope, instance, snapshot, actor, activityName, roleAliases } = args;
2958
3055
  return {
2959
3056
  ...scope,
2960
3057
  fields: {
2961
3058
  ...scope.fields,
2962
- ...scopedFieldOverlay(instance, snapshot, taskName)
3059
+ ...scopedFieldOverlay(instance, snapshot, activityName)
2963
3060
  },
2964
- assigned: assignedFor(instance, taskName, actor, roleAliases)
3061
+ assigned: assignedFor(instance, activityName, actor, roleAliases)
2965
3062
  };
2966
3063
  }
2967
- async function evaluateTask(args) {
3064
+ async function evaluateActivity(args) {
2968
3065
  const {
2969
- task,
3066
+ activity,
2970
3067
  statusEntry,
2971
3068
  instance,
2972
3069
  actor,
@@ -2975,37 +3072,37 @@ async function evaluateTask(args) {
2975
3072
  roleAliases,
2976
3073
  stageHasExits,
2977
3074
  guardDenial
2978
- } = args, status = statusEntry?.status ?? "pending", taskScope = taskScopeFor({
3075
+ } = args, status = statusEntry?.status ?? "pending", activityScope = activityScopeFor({
2979
3076
  scope,
2980
3077
  instance,
2981
3078
  snapshot,
2982
3079
  actor,
2983
- taskName: task.name,
3080
+ activityName: activity.name,
2984
3081
  roleAliases
2985
- }), assigned = taskScope.assigned === !0, unmetRequirements = await evaluateRequirements({
2986
- requirements: task.requirements,
3082
+ }), assigned = activityScope.assigned === !0, unmetRequirements = await evaluateRequirements({
3083
+ requirements: activity.requirements,
2987
3084
  snapshot,
2988
- params: taskScope
3085
+ params: activityScope
2989
3086
  }), requirementsReason = unmetRequirements.length > 0 ? { kind: "requirements-unmet", unmetRequirements } : void 0, actions = [];
2990
- for (const action of task.actions ?? [])
3087
+ for (const action of activity.actions ?? [])
2991
3088
  actions.push(
2992
3089
  await evaluateAction({
2993
3090
  action,
2994
3091
  status,
2995
3092
  instance,
2996
3093
  snapshot,
2997
- taskScope,
3094
+ activityScope,
2998
3095
  stageHasExits,
2999
3096
  guardDenial,
3000
3097
  requirementsReason
3001
3098
  })
3002
3099
  );
3003
3100
  return {
3004
- task,
3101
+ activity,
3005
3102
  status,
3006
- kind: taskKind(task),
3103
+ kind: activityKind(activity),
3007
3104
  // "pending on actor" treats both `pending` and `active` as waiting on
3008
- // the assignee — pending tasks just haven't been activated yet, but
3105
+ // the assignee — pending activities just haven't been activated yet, but
3009
3106
  // they're still inbox items. The inbox reads the assignees-kind field
3010
3107
  // entry BY KIND (who-data is fields).
3011
3108
  pendingOnActor: (status === "active" || status === "pending") && assigned,
@@ -3019,7 +3116,7 @@ async function evaluateAction(args) {
3019
3116
  status,
3020
3117
  instance,
3021
3118
  snapshot,
3022
- taskScope,
3119
+ activityScope,
3023
3120
  stageHasExits,
3024
3121
  guardDenial,
3025
3122
  requirementsReason
@@ -3027,7 +3124,7 @@ async function evaluateAction(args) {
3027
3124
  return lifecycle !== void 0 ? disabled(action, lifecycle) : guardDenial !== void 0 ? disabled(action, guardDenial) : requirementsReason !== void 0 ? disabled(action, requirementsReason) : action.filter !== void 0 && !await evaluateCondition({
3028
3125
  condition: action.filter,
3029
3126
  snapshot,
3030
- params: taskScope
3127
+ params: activityScope
3031
3128
  }) ? disabled(action, { kind: "filter-failed", filter: action.filter }) : { action, allowed: !0 };
3032
3129
  }
3033
3130
  async function instanceGuardReason(instance, actor, guards) {
@@ -3045,8 +3142,8 @@ function lifecycleReason(instance, status, stageHasExits) {
3045
3142
  return { kind: "instance-completed", completedAt: instance.completedAt };
3046
3143
  if (!stageHasExits)
3047
3144
  return { kind: "stage-terminal", stage: instance.currentStage };
3048
- if (schema.isTerminalTaskStatus(status))
3049
- return { kind: "task-not-active", status };
3145
+ if (schema.isTerminalActivityStatus(status))
3146
+ return { kind: "activity-not-active", status };
3050
3147
  }
3051
3148
  function disabled(action, reason) {
3052
3149
  return { action, allowed: !1, disabledReason: reason };
@@ -3054,37 +3151,30 @@ function disabled(action, reason) {
3054
3151
  function definitionDocId(_workflowResource, tag, definition, version) {
3055
3152
  return `${tag}.${definition}.v${version}`;
3056
3153
  }
3057
- async function sortByDependencies(client, definitions, tag, workflowResource) {
3154
+ async function sortByDependencies(client, definitions, tag) {
3058
3155
  const byName = /* @__PURE__ */ new Map();
3059
3156
  for (const def of definitions) {
3060
- const list = byName.get(def.name) ?? [];
3061
- list.push(def), byName.set(def.name, list);
3157
+ if (byName.has(def.name))
3158
+ throw new Error(
3159
+ `workflow.deployDefinitions: duplicate definition name "${def.name}" in batch \u2014 names identify a batch member (versions are deploy-assigned, not authored)`
3160
+ );
3161
+ byName.set(def.name, def);
3062
3162
  }
3063
3163
  const findInBatch = (ref) => findRefInBatch(byName, ref);
3064
- return await assertCrossBatchRefsDeployed(client, definitions, {
3065
- tag,
3066
- workflowResource,
3067
- findInBatch
3068
- }), topoSortDefinitions(definitions, { workflowResource, tag, findInBatch });
3164
+ return await assertCrossBatchRefsDeployed(client, definitions, { tag, findInBatch }), topoSortDefinitions(definitions, { findInBatch });
3069
3165
  }
3070
3166
  function findRefInBatch(byName, ref) {
3071
- const candidates = byName.get(ref.name);
3072
- if (!(candidates === void 0 || candidates.length === 0))
3073
- return typeof ref.version == "number" ? candidates.find((c) => c.version === ref.version) : candidates.reduce(
3074
- (best, c) => best === void 0 || c.version > best.version ? c : best,
3075
- void 0
3076
- );
3167
+ if (typeof ref.version != "number")
3168
+ return byName.get(ref.name);
3077
3169
  }
3078
3170
  async function assertCrossBatchRefsDeployed(client, definitions, ctx) {
3079
3171
  const missing = [];
3080
- for (const def of definitions) {
3081
- const fromId = definitionDocId(ctx.workflowResource, ctx.tag, def.name, def.version);
3172
+ for (const def of definitions)
3082
3173
  for (const ref of refsOf(def)) {
3083
3174
  if (ctx.findInBatch(ref) !== void 0) continue;
3084
3175
  const label = await resolveDeployedRefLabel(client, ref, ctx.tag);
3085
- label !== void 0 && missing.push({ from: fromId, ref: label });
3176
+ label !== void 0 && missing.push({ from: def.name, ref: label });
3086
3177
  }
3087
- }
3088
3178
  if (missing.length === 0) return;
3089
3179
  const lines = missing.map((m) => ` - ${m.from} \u2192 ${m.ref} (neither in batch nor deployed)`);
3090
3180
  throw new Error(
@@ -3103,16 +3193,15 @@ async function resolveDeployedRefLabel(client, ref, tag) {
3103
3193
  }
3104
3194
  function topoSortDefinitions(definitions, ctx) {
3105
3195
  const visited = /* @__PURE__ */ new Set(), visiting = /* @__PURE__ */ new Set(), ordered = [], visit = (def) => {
3106
- const id = definitionDocId(ctx.workflowResource, ctx.tag, def.name, def.version);
3107
- if (!visited.has(id)) {
3108
- if (visiting.has(id))
3109
- throw new Error(`workflow.deployDefinitions: dependency cycle involving "${id}"`);
3110
- visiting.add(id);
3196
+ if (!visited.has(def.name)) {
3197
+ if (visiting.has(def.name))
3198
+ throw new Error(`workflow.deployDefinitions: dependency cycle involving "${def.name}"`);
3199
+ visiting.add(def.name);
3111
3200
  for (const ref of refsOf(def)) {
3112
3201
  const child = ctx.findInBatch(ref);
3113
3202
  child !== void 0 && visit(child);
3114
3203
  }
3115
- visiting.delete(id), visited.add(id), ordered.push(def);
3204
+ visiting.delete(def.name), visited.add(def.name), ordered.push(def);
3116
3205
  }
3117
3206
  };
3118
3207
  for (const def of definitions) visit(def);
@@ -3121,8 +3210,8 @@ function topoSortDefinitions(definitions, ctx) {
3121
3210
  function refsOf(def) {
3122
3211
  const out = [];
3123
3212
  for (const stage of def.stages)
3124
- for (const task of stage.tasks ?? []) {
3125
- const ref = task.subworkflows?.definition;
3213
+ for (const activity of stage.activities ?? []) {
3214
+ const ref = activity.subworkflows?.definition;
3126
3215
  ref !== void 0 && out.push({
3127
3216
  name: ref.name,
3128
3217
  ...ref.version !== void 0 ? { version: ref.version } : {}
@@ -3130,8 +3219,23 @@ function refsOf(def) {
3130
3219
  }
3131
3220
  return out;
3132
3221
  }
3133
- function isDefinitionUnchanged(existing, expected) {
3134
- return stableStringify(stripSystemFields(existing)) === stableStringify(stripSystemFields(expected));
3222
+ function hashDefinitionContent(def) {
3223
+ const canonical = stableStringify(def);
3224
+ let hash = 0xcbf29ce484222325n;
3225
+ for (let i = 0; i < canonical.length; i++)
3226
+ hash ^= BigInt(canonical.charCodeAt(i)), hash = hash * 0x100000001b3n & 0xffffffffffffffffn;
3227
+ return hash.toString(16).padStart(16, "0");
3228
+ }
3229
+ function planDefinitionDeploy(def, latest, target) {
3230
+ const contentHash = hashDefinitionContent(def), unchanged = latest !== void 0 && latest.contentHash === contentHash, version = unchanged ? latest.version : (latest?.version ?? 0) + 1, docId = definitionDocId(target.workflowResource, target.tag, def.name, version), document = {
3231
+ ...def,
3232
+ _id: docId,
3233
+ _type: schema.WORKFLOW_DEFINITION_TYPE,
3234
+ tag: target.tag,
3235
+ version,
3236
+ contentHash
3237
+ };
3238
+ return { status: unchanged ? "unchanged" : "create", version, contentHash, docId, document };
3135
3239
  }
3136
3240
  function stripSystemFields(doc) {
3137
3241
  const out = {};
@@ -3146,22 +3250,26 @@ function stableStringify(value) {
3146
3250
  }
3147
3251
  async function loadDefinition(client, definition, version, tag) {
3148
3252
  if (version !== void 0) {
3149
- const doc = await client.fetch(
3150
- definitionLookupGroq(!0),
3151
- { definition, version, tag }
3152
- );
3253
+ const doc = await client.fetch(definitionLookupGroq(!0), {
3254
+ definition,
3255
+ version,
3256
+ tag
3257
+ });
3153
3258
  if (!doc)
3154
3259
  throw new Error(`Workflow definition ${definition} v${version} not deployed`);
3155
3260
  return doc;
3156
3261
  }
3157
- const latest = await client.fetch(definitionLookupGroq(!1), {
3158
- definition,
3159
- tag
3160
- });
3262
+ const latest = await loadLatestDeployed(client, definition, tag);
3161
3263
  if (!latest)
3162
3264
  throw new Error(`No deployed definition for workflow ${definition}`);
3163
3265
  return latest;
3164
3266
  }
3267
+ async function loadLatestDeployed(client, definition, tag) {
3268
+ return await client.fetch(definitionLookupGroq(!1), {
3269
+ definition,
3270
+ tag
3271
+ }) ?? void 0;
3272
+ }
3165
3273
  async function loadDefinitionVersions(client, definition, tag) {
3166
3274
  return client.fetch(
3167
3275
  `*[_type == "${schema.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${tagScopeFilter()}] | order(version desc)`,
@@ -3208,55 +3316,55 @@ async function commitAbort(ctx, reason, actor) {
3208
3316
  }), { fired: !0, stage: stage.name };
3209
3317
  }
3210
3318
  async function fireAction(args) {
3211
- const { client, instanceId, task, action, params, options } = args;
3319
+ const { client, instanceId, activity, action, params, options } = args;
3212
3320
  for (let attempt = 1; attempt <= CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS; attempt++) {
3213
3321
  const ctx = await loadCallContext(client, instanceId, options);
3214
3322
  try {
3215
- return await commitAction(ctx, task, action, params, options);
3323
+ return await commitAction(ctx, activity, action, params, options);
3216
3324
  } catch (error) {
3217
3325
  if (!isRevisionConflict(error)) throw error;
3218
3326
  }
3219
3327
  }
3220
3328
  throw new ConcurrentFireActionError({
3221
3329
  instanceId,
3222
- task,
3330
+ activity,
3223
3331
  action,
3224
3332
  attempts: CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS
3225
3333
  });
3226
3334
  }
3227
- async function resolveActionCommit(ctx, taskName, actionName, callerParams, options) {
3228
- const actor = options?.actor, { stage, task } = findTaskInCurrentStage(ctx, taskName), action = (task.actions ?? []).find((a) => a.name === actionName);
3335
+ async function resolveActionCommit(ctx, activityName, actionName, callerParams, options) {
3336
+ const actor = options?.actor, { stage, activity } = findActivityInCurrentStage(ctx, activityName), action = (activity.actions ?? []).find((a) => a.name === actionName);
3229
3337
  if (action === void 0)
3230
- throw new Error(`Action "${actionName}" not declared on task "${taskName}"`);
3338
+ throw new Error(`Action "${actionName}" not declared on activity "${activityName}"`);
3231
3339
  const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan(ctx.instance, actor, options.grants) : void 0;
3232
3340
  if (!await ctxEvaluateCondition(ctx, action.filter, {
3233
- taskName,
3341
+ activityName,
3234
3342
  ...actor !== void 0 ? { actor } : {},
3235
3343
  ...can !== void 0 ? { vars: { can } } : {}
3236
3344
  }))
3237
- throw new Error(`Action "${actionName}" filter rejected on task "${taskName}"`);
3238
- const entry = findCurrentTasks(ctx.instance).find((t) => t.name === taskName);
3345
+ throw new Error(`Action "${actionName}" filter rejected on activity "${activityName}"`);
3346
+ const entry = findCurrentActivities(ctx.instance).find((t) => t.name === activityName);
3239
3347
  if (entry === void 0 || entry.status !== "active")
3240
3348
  throw new Error(
3241
- `Task "${taskName}" must be active to fire action "${actionName}"; status is ${entry?.status ?? "missing"}`
3349
+ `Activity "${activityName}" must be active to fire action "${actionName}"; status is ${entry?.status ?? "missing"}`
3242
3350
  );
3243
- const params = validateActionParams(action, taskName, callerParams);
3244
- return { stage, task, action, params };
3351
+ const params = validateActionParams(action, activityName, callerParams);
3352
+ return { stage, activity, action, params };
3245
3353
  }
3246
- async function commitAction(ctx, taskName, actionName, callerParams, options) {
3354
+ async function commitAction(ctx, activityName, actionName, callerParams, options) {
3247
3355
  const actor = options?.actor, { stage, action, params } = await resolveActionCommit(
3248
3356
  ctx,
3249
- taskName,
3357
+ activityName,
3250
3358
  actionName,
3251
3359
  callerParams,
3252
3360
  options
3253
- ), mutation = startMutation(ctx.instance), mutEntry = requireMutationTaskEntry(mutation, taskName), now = ctx.now;
3361
+ ), mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName), now = ctx.now;
3254
3362
  mutation.history.push({
3255
3363
  _key: randomKey(),
3256
3364
  _type: "actionFired",
3257
3365
  at: now,
3258
3366
  stage: stage.name,
3259
- task: taskName,
3367
+ activity: activityName,
3260
3368
  action: actionName,
3261
3369
  ...actor !== void 0 ? { actor, driverKind: driverKind(actor) } : {}
3262
3370
  });
@@ -3264,7 +3372,7 @@ async function commitAction(ctx, taskName, actionName, callerParams, options) {
3264
3372
  ops: action.ops,
3265
3373
  mutation,
3266
3374
  stage: stage.name,
3267
- origin: { task: taskName, action: actionName },
3375
+ origin: { activity: activityName, action: actionName },
3268
3376
  params,
3269
3377
  actor,
3270
3378
  self: selfGdr(ctx.instance),
@@ -3272,14 +3380,14 @@ async function commitAction(ctx, taskName, actionName, callerParams, options) {
3272
3380
  }), newStatus = mutEntry.status !== statusBefore ? mutEntry.status : void 0;
3273
3381
  return await queueEffects(ctx, mutation, action.effects, { kind: "action", name: actionName }, actor, {
3274
3382
  callerParams: params,
3275
- taskName
3383
+ activityName
3276
3384
  }), await persistThenDeploy(
3277
3385
  ctx,
3278
3386
  mutation,
3279
3387
  () => ranOps.some(isFieldOp) ? refreshStageGuards(ctx, mutation, stage.name) : Promise.resolve()
3280
3388
  ), {
3281
3389
  fired: !0,
3282
- task: taskName,
3390
+ activity: activityName,
3283
3391
  action: actionName,
3284
3392
  ...newStatus !== void 0 ? { newStatus } : {},
3285
3393
  ...ranOps.length > 0 ? { ranOps } : {}
@@ -3287,23 +3395,23 @@ async function commitAction(ctx, taskName, actionName, callerParams, options) {
3287
3395
  }
3288
3396
  class ActionDisabledError extends Error {
3289
3397
  reason;
3290
- task;
3398
+ activity;
3291
3399
  action;
3292
3400
  constructor(args) {
3293
- super(formatDisabledReason(args.task, args.action, args.reason)), this.name = "ActionDisabledError", this.reason = args.reason, this.task = args.task, this.action = args.action;
3401
+ super(formatDisabledReason(args.activity, args.action, args.reason)), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity, this.action = args.action;
3294
3402
  }
3295
3403
  }
3296
3404
  const disabledReasonDetail = {
3297
3405
  "filter-failed": (r) => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
3298
- "task-not-active": (r) => `task status is "${r.status}"`,
3406
+ "activity-not-active": (r) => `activity status is "${r.status}"`,
3299
3407
  "stage-terminal": (r) => `stage "${r.stage}" is terminal`,
3300
3408
  "instance-completed": (r) => `instance completed at ${r.completedAt}`,
3301
3409
  "mutation-guard-denied": (r) => `blocked by mutation guard(s) [${r.guardIds.join(", ")}]${r.detail ? ` (${r.detail})` : ""}`,
3302
3410
  "requirements-unmet": (r) => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`
3303
3411
  };
3304
- function formatDisabledReason(task, action, reason) {
3412
+ function formatDisabledReason(activity, action, reason) {
3305
3413
  const detail = disabledReasonDetail[reason.kind](reason);
3306
- return `Action "${task}:${action}" is not allowed: ${detail}`;
3414
+ return `Action "${activity}:${action}" is not allowed: ${detail}`;
3307
3415
  }
3308
3416
  class EditFieldDeniedError extends Error {
3309
3417
  reason;
@@ -3322,7 +3430,7 @@ const editDisabledReasonDetail = {
3322
3430
  function formatEditDisabledReason(target, reason) {
3323
3431
  const detail = editDisabledReasonDetail[reason.kind](
3324
3432
  reason
3325
- ), where = target.task !== void 0 ? `${target.task}.${target.field}` : target.field;
3433
+ ), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
3326
3434
  return `Field "${target.scope}:${where}" is not editable: ${detail}`;
3327
3435
  }
3328
3436
  async function editField(args) {
@@ -3354,7 +3462,7 @@ async function commitEdit(ctx, target, mode, value, options) {
3354
3462
  stage: stage.name,
3355
3463
  origin: {
3356
3464
  edit: !0,
3357
- ...slot.scope === "task" && slot.task !== void 0 ? { task: slot.task } : {}
3465
+ ...slot.scope === "activity" && slot.activity !== void 0 ? { activity: slot.activity } : {}
3358
3466
  },
3359
3467
  params: {},
3360
3468
  actor,
@@ -3373,7 +3481,7 @@ async function commitEdit(ctx, target, mode, value, options) {
3373
3481
  }
3374
3482
  async function assertSlotEditable(ctx, slot, options) {
3375
3483
  const actor = options?.actor, window = slotWindowOpen(ctx.instance, slot), can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan(ctx.instance, actor, options.grants) : void 0, predicateSatisfied = window.open && slot.effective !== !0 && slot.effective !== void 0 ? await ctxEvaluateCondition(ctx, slot.effective, {
3376
- ...slot.task !== void 0 ? { taskName: slot.task } : {},
3484
+ ...slot.activity !== void 0 ? { activityName: slot.activity } : {},
3377
3485
  ...actor !== void 0 ? { actor } : {},
3378
3486
  ...can !== void 0 ? { vars: { can } } : {}
3379
3487
  }) : slot.effective === !0, reason = editDisabledReason({
@@ -3399,18 +3507,18 @@ function slotTarget(slot) {
3399
3507
  return {
3400
3508
  scope: slot.scope,
3401
3509
  field: slot.name,
3402
- ...slot.task !== void 0 ? { task: slot.task } : {}
3510
+ ...slot.activity !== void 0 ? { activity: slot.activity } : {}
3403
3511
  };
3404
3512
  }
3405
3513
  function labelTarget(target) {
3406
3514
  return {
3407
- scope: target.scope ?? (target.task !== void 0 ? "task" : "workflow"),
3515
+ scope: target.scope ?? (target.activity !== void 0 ? "activity" : "workflow"),
3408
3516
  field: target.field,
3409
- ...target.task !== void 0 ? { task: target.task } : {}
3517
+ ...target.activity !== void 0 ? { activity: target.activity } : {}
3410
3518
  };
3411
3519
  }
3412
3520
  function describeTarget(target) {
3413
- const where = target.task !== void 0 ? `${target.task}.${target.field}` : target.field;
3521
+ const where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
3414
3522
  return target.scope !== void 0 ? `${target.scope}:${where}` : where;
3415
3523
  }
3416
3524
  function bareIdFromSpawnRef(uri) {
@@ -3475,10 +3583,10 @@ async function dispatchGatedWrite(args) {
3475
3583
  ...ranOps !== void 0 ? { ranOps } : {}
3476
3584
  };
3477
3585
  }
3478
- function assertActionAllowed(evaluation, task, action) {
3479
- const actionEval = evaluation.currentStage.tasks.find((t) => t.task.name === task)?.actions.find((a) => a.action.name === action);
3586
+ function assertActionAllowed(evaluation, activity, action) {
3587
+ const actionEval = evaluation.currentStage.activities.find((t) => t.activity.name === activity)?.actions.find((a) => a.action.name === action);
3480
3588
  if (actionEval?.allowed === !1 && actionEval.disabledReason)
3481
- throw new ActionDisabledError({ task, action, reason: actionEval.disabledReason });
3589
+ throw new ActionDisabledError({ activity, action, reason: actionEval.disabledReason });
3482
3590
  }
3483
3591
  function assertEditAllowed(evaluation, target) {
3484
3592
  const slot = evaluation.editableSlots.filter((s) => editTargetMatches(s, target)).sort((a, b) => SCOPE_PRECEDENCE[a.scope] - SCOPE_PRECEDENCE[b.scope])[0];
@@ -3487,7 +3595,7 @@ function assertEditAllowed(evaluation, target) {
3487
3595
  target: {
3488
3596
  scope: slot.scope,
3489
3597
  field: slot.name,
3490
- ...slot.task !== void 0 ? { task: slot.task } : {}
3598
+ ...slot.activity !== void 0 ? { activity: slot.activity } : {}
3491
3599
  },
3492
3600
  reason: slot.disabledReason
3493
3601
  });
@@ -3512,11 +3620,11 @@ function buildEngineCallOptions(args) {
3512
3620
  };
3513
3621
  }
3514
3622
  async function applyAction(args) {
3515
- const { client, instance, task, action, params, actor, grants, clock, clientForGdr } = args, options = buildEngineCallOptions({ actor, grants, clock, clientForGdr });
3516
- return findOpenStageEntry(instance)?.tasks.find((t) => t.name === task)?.status === "pending" && await invokeTask({ client, instanceId: instance._id, task, options }), (await fireAction({
3623
+ const { client, instance, activity, action, params, actor, grants, clock, clientForGdr } = args, options = buildEngineCallOptions({ actor, grants, clock, clientForGdr });
3624
+ return findOpenStageEntry(instance)?.activities.find((t) => t.name === activity)?.status === "pending" && await invokeActivity({ client, instanceId: instance._id, activity, options }), (await fireAction({
3517
3625
  client,
3518
3626
  instanceId: instance._id,
3519
- task,
3627
+ activity,
3520
3628
  action,
3521
3629
  ...params !== void 0 ? { params } : {},
3522
3630
  options
@@ -3596,14 +3704,16 @@ function previewIds(ids) {
3596
3704
  }
3597
3705
  const workflow = {
3598
3706
  /**
3599
- * Deploy a set of workflow definitions as one call. Each lands as a
3600
- * {@link WORKFLOW_DEFINITION_TYPE} document; subsequent versions are
3601
- * stored alongside earlier ones `startInstance` picks the highest
3602
- * version by default. The SDK figures out the dependency order itself
3603
- * (children before parents that spawn them via
3604
- * `subworkflows.definition`), idempotently writes any definition
3605
- * whose content has changed, and reports a per-definition outcome
3606
- * (`created` / `updated` / `unchanged`).
3707
+ * Deploy a set of workflow definitions as one call. Definitions are
3708
+ * immutable and content-addressed: the author writes no version, and each
3709
+ * deploy compares a definition's content fingerprint to the latest version
3710
+ * already deployed under its name. Identical content is a no-op
3711
+ * (`unchanged`); any change mints the next version (`created`) — deploy
3712
+ * never patches a deployed version, so a definition can't change out from
3713
+ * under the instances pinned to it. `startInstance` picks the highest
3714
+ * version by default. The engine figures out the dependency order itself
3715
+ * (children before parents that spawn them via `subworkflows.definition`)
3716
+ * and reports a per-definition outcome (`created` / `unchanged`).
3607
3717
  *
3608
3718
  * Refs may point inside the batch OR at already-deployed definitions
3609
3719
  * in the lake — both are valid. A ref pointing at neither errors with
@@ -3616,31 +3726,15 @@ const workflow = {
3616
3726
  validateTag(tag);
3617
3727
  for (const def of definitions)
3618
3728
  validateDefinition(def);
3619
- const ordered = await sortByDependencies(client, definitions, tag, workflowResource), results = [], tx = client.transaction();
3729
+ const ordered = await sortByDependencies(client, definitions, tag), results = [], tx = client.transaction();
3620
3730
  let hasWrites = !1;
3621
3731
  for (const def of ordered) {
3622
- const id = definitionDocId(workflowResource, tag, def.name, def.version), expected = {
3623
- ...def,
3624
- _id: id,
3625
- _type: schema.WORKFLOW_DEFINITION_TYPE,
3626
- tag
3627
- }, existing = await client.getDocument(id);
3628
- if (existing && existing.tag === tag && isDefinitionUnchanged(existing, expected)) {
3629
- results.push({ definition: def.name, version: def.version, status: "unchanged" });
3732
+ const latest = await loadLatestDeployed(client, def.name, tag), plan = planDefinitionDeploy(def, latest, { tag, workflowResource });
3733
+ if (plan.status === "unchanged") {
3734
+ results.push({ definition: def.name, version: plan.version, status: "unchanged" });
3630
3735
  continue;
3631
3736
  }
3632
- if (existing) {
3633
- const expectedKeys = new Set(Object.keys(expected)), toUnset = Object.keys(existing).filter(
3634
- (k) => !k.startsWith("_") && !expectedKeys.has(k)
3635
- ), patch = client.patch(id).set(expected).ifRevisionId(existing._rev);
3636
- toUnset.length > 0 && patch.unset(toUnset), tx.patch(patch);
3637
- } else
3638
- tx.create(expected);
3639
- hasWrites = !0, results.push({
3640
- definition: def.name,
3641
- version: def.version,
3642
- status: existing ? "updated" : "created"
3643
- });
3737
+ tx.create(plan.document), hasWrites = !0, results.push({ definition: def.name, version: plan.version, status: "created" });
3644
3738
  }
3645
3739
  return hasWrites && await tx.commit(), { results };
3646
3740
  },
@@ -3656,8 +3750,8 @@ const workflow = {
3656
3750
  * Spawn a new workflow instance from a deployed definition.
3657
3751
  *
3658
3752
  * Pins the snapshot at start-time, seeds `effectsContext`, enters
3659
- * the initial stage (which builds taskStatus, queues onEnter
3660
- * effects, auto-activates tasks). Then cascades auto-transitions
3753
+ * the initial stage (which builds activityStatus, queues onEnter
3754
+ * effects, auto-activates activities). Then cascades auto-transitions
3661
3755
  * until stable. Returns the resulting instance.
3662
3756
  */
3663
3757
  startInstance: async (args) => {
@@ -3677,7 +3771,7 @@ const workflow = {
3677
3771
  throw new Error(
3678
3772
  `Initial stage "${definition.initialStage}" missing in ${definitionName} v${definition.version}`
3679
3773
  );
3680
- const now = clock(), effectsContextEntries = effectsContext ? toEffectsContextEntries(effectsContext) : [], resolvedFields = await resolveDeclaredFields({
3774
+ const now = clock(), effectsContextEntries = effectsContext ? toEffectsContextEntries(effectsContext) : [], fieldDiscards = [], resolvedFields = await resolveDeclaredFields({
3681
3775
  entryDefs: definition.fields ?? [],
3682
3776
  initialFields: initialFields ?? [],
3683
3777
  ctx: {
@@ -3687,7 +3781,8 @@ const workflow = {
3687
3781
  tag,
3688
3782
  workflowResource,
3689
3783
  definitionName: definition.name,
3690
- ...perspective !== void 0 ? { perspective } : {}
3784
+ ...perspective !== void 0 ? { perspective } : {},
3785
+ recordDiscard: recordFieldDiscards(fieldDiscards, "workflow", now)
3691
3786
  },
3692
3787
  randomKey
3693
3788
  }), effectivePerspective = perspective ?? derivePerspectiveFromFields(resolvedFields), base = buildInstanceBase({
@@ -3697,18 +3792,20 @@ const workflow = {
3697
3792
  workflowResource,
3698
3793
  definitionName: definition.name,
3699
3794
  pinnedVersion: definition.version,
3795
+ pinnedContentHash: definition.contentHash,
3700
3796
  definition,
3701
3797
  fields: resolvedFields,
3702
3798
  effectsContext: effectsContextEntries,
3703
3799
  ancestors: ancestors ?? [],
3704
3800
  perspective: effectivePerspective,
3705
3801
  initialStage: definition.initialStage,
3706
- actor
3802
+ actor,
3803
+ ...fieldDiscards.length > 0 ? { extraHistory: fieldDiscards } : {}
3707
3804
  });
3708
3805
  return await client.create(base, SYNC_COMMIT), await primeInitialStage(client, id, actor, clientForGdr, clock), await cascade(client, id, actor, clientForGdr, clock), reload(client, id, tag);
3709
3806
  },
3710
3807
  /**
3711
- * Fire an action against a task. If the task is pending, it is
3808
+ * Fire an action against an activity. If the activity is pending, it is
3712
3809
  * auto-invoked first (pending → active) so callers don't have to know
3713
3810
  * the lifecycle. Cascades auto-transitions and propagates to ancestors
3714
3811
  * after the action commits.
@@ -3722,13 +3819,13 @@ const workflow = {
3722
3819
  client,
3723
3820
  tag,
3724
3821
  instanceId,
3725
- task,
3822
+ activity,
3726
3823
  action,
3727
3824
  params,
3728
3825
  idempotent,
3729
3826
  resourceClients
3730
3827
  } = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload(client, instanceId, tag);
3731
- return findOpenStageEntry(before)?.tasks.find((t) => t.name === task) === void 0 && idempotent === !0 ? { instance: before, cascaded: 0, fired: !1 } : dispatchGatedWrite({
3828
+ return findOpenStageEntry(before)?.activities.find((t) => t.name === activity) === void 0 && idempotent === !0 ? { instance: before, cascaded: 0, fired: !1 } : dispatchGatedWrite({
3732
3829
  client,
3733
3830
  tag,
3734
3831
  instanceId,
@@ -3738,10 +3835,10 @@ const workflow = {
3738
3835
  clock,
3739
3836
  before,
3740
3837
  ...resourceClients !== void 0 ? { resourceClients } : {},
3741
- apply: (instance, evaluation) => (assertActionAllowed(evaluation, task, action), applyAction({
3838
+ apply: (instance, evaluation) => (assertActionAllowed(evaluation, activity, action), applyAction({
3742
3839
  client,
3743
3840
  instance,
3744
- task,
3841
+ activity,
3745
3842
  action,
3746
3843
  actor,
3747
3844
  ...access.grants !== void 0 ? { grants: access.grants } : {},
@@ -3822,7 +3919,7 @@ const workflow = {
3822
3919
  * Re-evaluate auto-transitions and resolve due waits until stable.
3823
3920
  *
3824
3921
  * Used by the runtime after any event that might affect the workflow
3825
- * but isn't itself a task action: a subject doc was patched, a sibling
3922
+ * but isn't itself an activity action: a subject doc was patched, a sibling
3826
3923
  * workflow completed, the clock crossed a `completeWhen` deadline, etc.
3827
3924
  * The runtime doesn't need to know what changed — it just nudges
3828
3925
  * affected instances and the engine re-evaluates.
@@ -3932,8 +4029,8 @@ const workflow = {
3932
4029
  * declared by a `doc.ref` / `doc.refs` entry in scope), then
3933
4030
  * evaluates the supplied GROQ in groq-js against that dataset. The
3934
4031
  * rendered scope is auto-bound: `$self`, `$fields`, `$parent`,
3935
- * `$ancestors`, `$stage`, `$now`, `$effects`, `$tasks`,
3936
- * `$allTasksDone`, `$anyTaskFailed` — ids in GDR URI form to match
4032
+ * `$ancestors`, `$stage`, `$now`, `$effects`, `$activities`,
4033
+ * `$allActivitiesDone`, `$anyActivityFailed` — ids in GDR URI form to match
3937
4034
  * the snapshot's keying.
3938
4035
  *
3939
4036
  * Use when an external consumer (a UI, a debug pane, a test) wants
@@ -3986,34 +4083,83 @@ const workflow = {
3986
4083
  * List the actions an actor could fire on an instance's current stage,
3987
4084
  * each flagged `allowed` (with a structured `disabledReason` when not).
3988
4085
  * Projects the instance from the actor's perspective and flattens its
3989
- * tasks' actions. Returns the evaluation alongside the actions so a
4086
+ * activities' actions. Returns the evaluation alongside the actions so a
3990
4087
  * consumer can read the instance/stage context. Pure read.
3991
4088
  */
3992
4089
  availableActions: async (args) => {
3993
4090
  const evaluation = await evaluateInstance(args);
3994
- return { evaluation, actions: availableActions(evaluation.currentStage.tasks) };
4091
+ return { evaluation, actions: availableActions(evaluation.currentStage.activities) };
3995
4092
  },
3996
4093
  /**
3997
4094
  * Materialised spawned children of a parent instance.
3998
4095
  *
3999
4096
  * Walks `history` for `spawned` events — the durable
4000
- * record. (The per-task `spawnedInstances` field on the active stage
4097
+ * record. (The per-activity `spawnedInstances` field on the active stage
4001
4098
  * only exists while that stage is current; history survives.) Strips
4002
4099
  * the GDR URI on each `instanceRef` to a bare `_id`, fetches the
4003
4100
  * instances, drops any that aren't visible to this engine's tag, and
4004
4101
  * returns them sorted by `startedAt` ascending.
4005
4102
  *
4006
- * Pass `task` to restrict to a single spawning task on the parent.
4103
+ * Pass `activity` to restrict to a single spawning activity on the parent.
4007
4104
  */
4008
4105
  children: async (args) => {
4009
- const { client, tag, instanceId, task } = args;
4106
+ const { client, tag, instanceId, activity } = args;
4010
4107
  validateTag(tag);
4011
- const parent = await reload(client, instanceId, tag), isSpawned = (h) => h._type === "spawned", ids = parent.history.filter(isSpawned).filter((h) => task === void 0 || h.task === task).flatMap((h) => bareIdFromSpawnRef(h.instanceRef.id));
4108
+ const parent = await reload(client, instanceId, tag), isSpawned = (h) => h._type === "spawned", ids = parent.history.filter(isSpawned).filter((h) => activity === void 0 || h.activity === activity).flatMap((h) => bareIdFromSpawnRef(h.instanceRef.id));
4012
4109
  return ids.length === 0 ? [] : client.fetch(
4013
4110
  `*[_id in $ids && ${tagScopeFilter()}] | order(startedAt asc)`,
4014
4111
  { ids, tag }
4015
4112
  );
4016
4113
  },
4114
+ /**
4115
+ * Every in-flight instance whose reactive watch-set includes `document` —
4116
+ * the reverse of {@link subscriptionDocumentsForInstance}.
4117
+ *
4118
+ * For a non-reactive, content-change-driven runtime (a Sanity Function, an
4119
+ * Inngest/durable worker, any server) that holds no instances in memory: a
4120
+ * document changed; which instances should it `tick`? The watch-set covers
4121
+ * the instance itself, its ancestors, and the docs named by
4122
+ * `doc.ref` / `doc.refs` / `release.ref` field entries on the workflow scope
4123
+ * **and the current stage** — so a hand-rolled GROQ over `fields[]` gets it
4124
+ * subtly wrong (misses stage-scope refs, `release.ref`, ancestors).
4125
+ *
4126
+ * A coarse GROQ prefilter narrows candidates server-side (in-flight,
4127
+ * tag-scoped, mentioning the doc anywhere in state); the authoritative match
4128
+ * is {@link instanceWatchesDocument}, derived from `collectWatchRefs`, so the
4129
+ * reverse stays in lockstep with the forward set and honours the
4130
+ * open-stage-only rule the prefilter deliberately over-approximates.
4131
+ *
4132
+ * Single-resource: instances always live in the engine's own resource, so
4133
+ * this reads one client (unlike {@link guardsForInstance}, whose guard docs
4134
+ * are scattered across the watched resources). "Routed by resource" applies
4135
+ * to the **subject**: a cross-dataset doc is matched by its full,
4136
+ * resource-qualified GDR URI, never its bare id, so an instance watching
4137
+ * `dataset:A:ds:doc` is not matched by a change to `dataset:B:ds:doc`.
4138
+ *
4139
+ * `document` must be a resource-qualified GDR URI; a bare id is rejected
4140
+ * (it can't be resource-routed and would silently mismatch). Sorted by
4141
+ * `startedAt` ascending.
4142
+ */
4143
+ instancesForDocument: async (args) => {
4144
+ const { client, tag, document } = args;
4145
+ if (validateTag(tag), !isGdrUri(document))
4146
+ throw new Error(
4147
+ `workflow.instancesForDocument: "document" must be a resource-qualified GDR URI (e.g. "dataset:project:dataset:doc-id"); got: ${JSON.stringify(document)}`
4148
+ );
4149
+ const fieldRefsDoc = "count(fields[value.id == $document]) > 0 || count(fields[$document in value[].id]) > 0", query = `*[
4150
+ _type == "${WORKFLOW_INSTANCE_TYPE}" && ${tagScopeFilter()} && !defined(completedAt) && (
4151
+ _id == $bareId ||
4152
+ $document in ancestors[].id ||
4153
+ ${fieldRefsDoc} ||
4154
+ count(stages[${fieldRefsDoc}]) > 0
4155
+ )
4156
+ ] | order(startedAt asc)`;
4157
+ return (await client.fetch(query, {
4158
+ tag,
4159
+ document,
4160
+ bareId: extractDocumentId(document)
4161
+ })).filter((instance) => instanceWatchesDocument(instance, document));
4162
+ },
4017
4163
  /**
4018
4164
  * Permission helpers — Sanity ACL grants evaluated against documents
4019
4165
  * via GROQ. Used by `workflow.evaluate` to soft-gate actions when the
@@ -4067,10 +4213,13 @@ function walkEffectNames(def, visit) {
4067
4213
  for (const e of effects ?? []) visit(e.name, location);
4068
4214
  };
4069
4215
  for (const stage of def.stages ?? []) {
4070
- for (const t of stage.tasks ?? []) {
4071
- visitEffects(t.effects, `stage[${stage.name}].task[${t.name}].effects`);
4216
+ for (const t of stage.activities ?? []) {
4217
+ visitEffects(t.effects, `stage[${stage.name}].activity[${t.name}].effects`);
4072
4218
  for (const a of t.actions ?? [])
4073
- visitEffects(a.effects, `stage[${stage.name}].task[${t.name}].action[${a.name}].effects`);
4219
+ visitEffects(
4220
+ a.effects,
4221
+ `stage[${stage.name}].activity[${t.name}].action[${a.name}].effects`
4222
+ );
4074
4223
  }
4075
4224
  for (const tr of stage.transitions ?? [])
4076
4225
  visitEffects(tr.effects, `stage[${stage.name}].transition[${tr.name}].effects`);
@@ -4086,7 +4235,7 @@ async function drainEffectsInternal(args) {
4086
4235
  effectHandlers,
4087
4236
  missingHandler,
4088
4237
  logger
4089
- } = args, drainerActor = args.access?.actor ?? engineSystemActor("drainEffects"), completionAccess = {
4238
+ } = args, routeGdr = buildClientForGdr(client, resourceClients), clientFor = (ref) => routeGdr(parseGdr(typeof ref == "string" ? ref : ref.id)), drainerActor = args.access?.actor ?? engineSystemActor("drainEffects"), completionAccess = {
4090
4239
  actor: drainerActor,
4091
4240
  ...args.access?.grants !== void 0 ? { grants: args.access.grants } : {}
4092
4241
  };
@@ -4105,6 +4254,7 @@ async function drainEffectsInternal(args) {
4105
4254
  if (!await claimPendingEffect(client, before, candidate._key, drainerActor)) continue;
4106
4255
  const { outputs, dispatchError } = await dispatchEffect(handler, candidate, {
4107
4256
  client,
4257
+ clientFor,
4108
4258
  instanceId,
4109
4259
  logger
4110
4260
  });
@@ -4159,6 +4309,7 @@ async function dispatchEffect(handler, candidate, ctx) {
4159
4309
  try {
4160
4310
  const result = await handler(candidate.params, {
4161
4311
  client: ctx.client,
4312
+ clientFor: ctx.clientFor,
4162
4313
  instanceId: ctx.instanceId,
4163
4314
  effectKey: candidate._key,
4164
4315
  log: (message, extra) => ctx.logger(`effect.${candidate.name}`).info(message, extra)
@@ -4247,13 +4398,13 @@ function createInstanceSession(args) {
4247
4398
  return instance = await reload(client, instance._id, tag), { instance, cascaded, fired: cascaded > 0 };
4248
4399
  });
4249
4400
  },
4250
- fireAction({ task, action, params }) {
4401
+ fireAction({ activity, action, params }) {
4251
4402
  return commit(async (actor, held) => {
4252
- assertActionAllowed(await evaluateWith(held, heldGuards), task, action);
4403
+ assertActionAllowed(await evaluateWith(held, heldGuards), activity, action);
4253
4404
  const { grants } = await access(), ranOps = await applyAction({
4254
4405
  client,
4255
4406
  instance,
4256
- task,
4407
+ activity,
4257
4408
  action,
4258
4409
  actor,
4259
4410
  clientForGdr,
@@ -4360,13 +4511,14 @@ function createEngine(args) {
4360
4511
  definition,
4361
4512
  ...resourceClients !== void 0 ? { resourceClients } : {}
4362
4513
  }),
4363
- children: ({ instanceId, task }) => workflow.children({
4514
+ children: ({ instanceId, activity }) => workflow.children({
4364
4515
  client,
4365
4516
  tag,
4366
4517
  workflowResource,
4367
4518
  instanceId,
4368
- ...task !== void 0 ? { task } : {}
4519
+ ...activity !== void 0 ? { activity } : {}
4369
4520
  }),
4521
+ instancesForDocument: ({ document }) => workflow.instancesForDocument({ client, tag, workflowResource, document }),
4370
4522
  query: ({ groq, params }) => workflow.query({
4371
4523
  client,
4372
4524
  tag,
@@ -4412,33 +4564,25 @@ function createEngine(args) {
4412
4564
  })
4413
4565
  };
4414
4566
  }
4415
- function docIdFor(def, target) {
4416
- return definitionDocId(target.workflowResource, target.tag, def.name, def.version);
4417
- }
4418
- function diffStatus(existing, expected) {
4419
- return existing === void 0 ? "create" : isDefinitionUnchanged(existing, expected) ? "unchanged" : "update";
4420
- }
4421
- function diffEntry(def, existingRaw, target) {
4422
- const docId = docIdFor(def, target), expected = {
4423
- ...def,
4424
- _id: docId,
4425
- _type: schema.WORKFLOW_DEFINITION_TYPE,
4426
- tag: target.tag
4427
- }, status = diffStatus(existingRaw, expected), existing = existingRaw ? stripSystemFields(existingRaw) : void 0;
4567
+ function diffEntry(def, latestRaw, target) {
4568
+ const plan = planDefinitionDeploy(def, asLatest(latestRaw), target), existing = latestRaw ? stripSystemFields(latestRaw) : void 0;
4428
4569
  return {
4429
4570
  name: def.name,
4430
- version: def.version,
4431
- status,
4432
- docId,
4433
- expected,
4571
+ version: plan.version,
4572
+ status: plan.status,
4573
+ docId: plan.docId,
4574
+ expected: plan.document,
4434
4575
  ...existing !== void 0 ? { existing } : {}
4435
4576
  };
4436
4577
  }
4578
+ function asLatest(raw) {
4579
+ return raw === void 0 ? void 0 : { version: typeof raw.version == "number" ? raw.version : 0, ...typeof raw.contentHash == "string" ? { contentHash: raw.contentHash } : {} };
4580
+ }
4437
4581
  async function computeDiffEntries(client, defs, target) {
4438
4582
  const entries = [];
4439
4583
  for (const def of defs) {
4440
- const existingRaw = await client.getDocument(docIdFor(def, target)) ?? void 0;
4441
- entries.push(diffEntry(def, existingRaw, target));
4584
+ const latest = await loadLatestDeployed(client, def.name, target.tag);
4585
+ entries.push(diffEntry(def, latest, target));
4442
4586
  }
4443
4587
  return entries;
4444
4588
  }
@@ -4451,17 +4595,17 @@ const HISTORY_DISPLAY = {
4451
4595
  title: "Stage exited",
4452
4596
  description: "The instance left a stage on the way to the next one."
4453
4597
  },
4454
- taskActivated: {
4455
- title: "Task activated",
4456
- description: "A task flipped from `pending` to `active` and its onEnter effects queued."
4598
+ activityActivated: {
4599
+ title: "Activity activated",
4600
+ description: "An activity flipped from `pending` to `active` and its onEnter effects queued."
4457
4601
  },
4458
- taskStatusChanged: {
4459
- title: "Task status changed",
4460
- description: "An action or op flipped a task's status (active \u2192 done / skipped / failed)."
4602
+ activityStatusChanged: {
4603
+ title: "Activity status changed",
4604
+ description: "An action or op flipped an activity's status (active \u2192 done / skipped / failed)."
4461
4605
  },
4462
4606
  actionFired: {
4463
4607
  title: "Action fired",
4464
- description: "An action was invoked against a task \u2014 ops ran, status may have flipped, effects queued."
4608
+ description: "An action was invoked against an activity \u2014 ops ran, status may have flipped, effects queued."
4465
4609
  },
4466
4610
  transitionFired: {
4467
4611
  title: "Transition fired",
@@ -4477,7 +4621,7 @@ const HISTORY_DISPLAY = {
4477
4621
  },
4478
4622
  spawned: {
4479
4623
  title: "Spawned child",
4480
- description: "A task's `subworkflows` declaration spawned a child workflow instance."
4624
+ description: "An activity's `subworkflows` declaration spawned a child workflow instance."
4481
4625
  },
4482
4626
  aborted: {
4483
4627
  title: "Instance aborted",
@@ -4486,6 +4630,10 @@ const HISTORY_DISPLAY = {
4486
4630
  opApplied: {
4487
4631
  title: "Op applied",
4488
4632
  description: "An inline field-mutation op (field.set / field.append / status.set / etc.) ran during an action commit."
4633
+ },
4634
+ fieldQueryDiscarded: {
4635
+ title: "Query result discarded",
4636
+ description: "A query-sourced field's GROQ result didn't fit the field's declared kind, so it was discarded and the field fell back to its default."
4489
4637
  }
4490
4638
  }, FIELD_SLOT_DISPLAY = {
4491
4639
  "doc.ref": {
@@ -4500,50 +4648,58 @@ const HISTORY_DISPLAY = {
4500
4648
  title: "Content Release reference",
4501
4649
  description: "GDR pointer at a Content Release system doc, carrying the release name the engine derives the instance's read perspective from."
4502
4650
  },
4503
- query: {
4504
- title: "Query result",
4505
- description: "Snapshot of a GROQ projection captured at entry-resolve time; resolvedAt is stamped on the value."
4506
- },
4507
- "value.string": {
4651
+ string: {
4508
4652
  title: "String value",
4509
- description: "Free-form string entry."
4653
+ description: "Free-form single-line string entry."
4510
4654
  },
4511
- "value.url": {
4512
- title: "URL value",
4513
- description: "Validated URL entry."
4655
+ text: {
4656
+ title: "Text value",
4657
+ description: "Free-form multiline string entry."
4514
4658
  },
4515
- "value.number": {
4659
+ number: {
4516
4660
  title: "Number value",
4517
4661
  description: "Numeric entry."
4518
4662
  },
4519
- "value.boolean": {
4663
+ boolean: {
4520
4664
  title: "Boolean value",
4521
4665
  description: "True/false entry."
4522
4666
  },
4523
- "value.dateTime": {
4667
+ date: {
4668
+ title: "Date value",
4669
+ description: "Date-only (YYYY-MM-DD) entry, no time component."
4670
+ },
4671
+ datetime: {
4524
4672
  title: "Date / time value",
4525
- description: "ISO-timestamp entry."
4673
+ description: "ISO-8601 timestamp entry."
4526
4674
  },
4527
- "value.actor": {
4528
- title: "Actor value",
4529
- description: "A typed (user|ai|system) actor identity."
4675
+ url: {
4676
+ title: "URL value",
4677
+ description: "Validated URL entry."
4530
4678
  },
4531
- checklist: {
4532
- title: "Checklist",
4533
- description: "Append-only list of checkable items; ops add/update/remove entries."
4679
+ actor: {
4680
+ title: "Actor value",
4681
+ description: "A typed (user|ai|system) actor identity \u2014 a concrete principal."
4534
4682
  },
4535
- notes: {
4536
- title: "Notes log",
4537
- description: "Append-only structured-note log (signoffs, comments, audit rows)."
4683
+ assignee: {
4684
+ title: "Assignee",
4685
+ description: "A single typed (user|role) assignee \u2014 who is expected/responsible."
4538
4686
  },
4539
4687
  assignees: {
4540
4688
  title: "Assignees",
4541
4689
  description: "Ordered list of typed (user|role) assignees."
4690
+ },
4691
+ object: {
4692
+ title: "Object",
4693
+ description: "An object with named sub-fields; the value is keyed by sub-field name."
4694
+ },
4695
+ array: {
4696
+ title: "Array",
4697
+ description: "An array of objects shaped by the declared `of` sub-fields; ops add/update/remove rows."
4542
4698
  }
4543
4699
  }, OP_DISPLAY = {
4544
4700
  "field.set": {
4545
4701
  title: "Set field entry",
4546
- description: "Overwrite a field entry's value with a resolved Source."
4702
+ description: "Overwrite a field entry's value with a resolved value expression."
4547
4703
  },
4548
4704
  "field.unset": {
4549
4705
  title: "Unset field entry",
@@ -4551,7 +4707,7 @@ const HISTORY_DISPLAY = {
4551
4707
  },
4552
4708
  "field.append": {
4553
4709
  title: "Append to field entry",
4554
- description: "Push a resolved item onto an array-kind entry (notes, checklist, assignees, refs)."
4710
+ description: "Push a resolved item onto an array-kind entry (array, assignees, doc.refs)."
4555
4711
  },
4556
4712
  "field.updateWhere": {
4557
4713
  title: "Update matching rows",
@@ -4562,8 +4718,8 @@ const HISTORY_DISPLAY = {
4562
4718
  description: "Drop rows of an array-kind entry that match the predicate."
4563
4719
  },
4564
4720
  "status.set": {
4565
- title: "Set task status",
4566
- description: "Flip a task's status explicitly (the action-level `status:` sugar desugars to this)."
4721
+ title: "Set activity status",
4722
+ description: "Flip an activity's status explicitly (the action-level `status:` sugar desugars to this)."
4567
4723
  }
4568
4724
  }, EFFECTS_CONTEXT_DISPLAY = {
4569
4725
  "effectsContext.string": {
@@ -4603,25 +4759,25 @@ const HISTORY_DISPLAY = {
4603
4759
  title: "Workflow instance",
4604
4760
  description: "A running (or finished) workflow against its declared fields."
4605
4761
  }
4606
- }, TASK_KIND_DISPLAY = {
4762
+ }, ACTIVITY_KIND_DISPLAY = {
4607
4763
  user: {
4608
- title: "User task",
4764
+ title: "User activity",
4609
4765
  description: "A person acts via the app \u2014 renders action buttons ranked by outcome."
4610
4766
  },
4611
4767
  service: {
4612
- title: "Service task",
4768
+ title: "Service activity",
4613
4769
  description: "An automated system or effect does the work \u2014 renders effect drain status."
4614
4770
  },
4615
4771
  script: {
4616
- title: "Script task",
4772
+ title: "Script activity",
4617
4773
  description: "The engine runs a step inline and resolves on activation \u2014 an audit row."
4618
4774
  },
4619
4775
  manual: {
4620
- title: "Manual task",
4776
+ title: "Manual activity",
4621
4777
  description: "Work done off-system, acknowledged by a person or verified by a predicate \u2014 renders an instruction card with a deep-link."
4622
4778
  },
4623
4779
  receive: {
4624
- title: "Receive task",
4780
+ title: "Receive activity",
4625
4781
  description: 'No actor; the engine waits on a condition \u2014 renders "waiting until \u2026", no buttons.'
4626
4782
  }
4627
4783
  }, DRIVER_KIND_DISPLAY = {
@@ -4646,10 +4802,11 @@ function displayDescription(typeKey) {
4646
4802
  if (typeKey)
4647
4803
  return DISPLAY[typeKey]?.description;
4648
4804
  }
4805
+ exports.ACTIVITY_KINDS = schema.ACTIVITY_KINDS;
4649
4806
  exports.DRIVER_KINDS = schema.DRIVER_KINDS;
4650
- exports.TASK_KINDS = schema.TASK_KINDS;
4651
4807
  exports.WORKFLOW_DEFINITION_TYPE = schema.WORKFLOW_DEFINITION_TYPE;
4652
4808
  exports.isStartableDefinition = schema.isStartableDefinition;
4809
+ exports.ACTIVITY_KIND_DISPLAY = ACTIVITY_KIND_DISPLAY;
4653
4810
  exports.AUTHORING_DISPLAY = AUTHORING_DISPLAY;
4654
4811
  exports.ActionDisabledError = ActionDisabledError;
4655
4812
  exports.ActionParamsInvalidError = ActionParamsInvalidError;
@@ -4670,11 +4827,11 @@ exports.MutationGuardDeniedError = MutationGuardDeniedError;
4670
4827
  exports.OP_DISPLAY = OP_DISPLAY;
4671
4828
  exports.PartialGuardDeployError = PartialGuardDeployError;
4672
4829
  exports.RequiredFieldNotProvidedError = RequiredFieldNotProvidedError;
4673
- exports.TASK_KIND_DISPLAY = TASK_KIND_DISPLAY;
4674
4830
  exports.WORKFLOW_INSTANCE_TYPE = WORKFLOW_INSTANCE_TYPE;
4675
4831
  exports.WorkflowStateDivergedError = WorkflowStateDivergedError;
4676
4832
  exports.abortReason = abortReason;
4677
4833
  exports.actionVerdict = actionVerdict;
4834
+ exports.activityKind = activityKind;
4678
4835
  exports.availableActions = availableActions;
4679
4836
  exports.buildSnapshot = buildSnapshot;
4680
4837
  exports.compileGuard = compileGuard;
@@ -4685,7 +4842,7 @@ exports.datasetResourceParts = datasetResourceParts;
4685
4842
  exports.defaultLoggerFactory = defaultLoggerFactory;
4686
4843
  exports.denyingGuards = denyingGuards;
4687
4844
  exports.deployStageGuards = deployStageGuards;
4688
- exports.deriveTaskKind = deriveTaskKind;
4845
+ exports.deriveActivityKind = deriveActivityKind;
4689
4846
  exports.diagnoseInputFromEvaluation = diagnoseInputFromEvaluation;
4690
4847
  exports.diagnoseInstance = diagnoseInstance;
4691
4848
  exports.diffEntry = diffEntry;
@@ -4703,8 +4860,9 @@ exports.guardMatches = guardMatches;
4703
4860
  exports.guardsForDefinition = guardsForDefinition;
4704
4861
  exports.guardsForInstance = guardsForInstance;
4705
4862
  exports.guardsForResource = guardsForResource;
4863
+ exports.hashDefinitionContent = hashDefinitionContent;
4706
4864
  exports.instanceGuardQuery = instanceGuardQuery;
4707
- exports.isDefinitionUnchanged = isDefinitionUnchanged;
4865
+ exports.instanceWatchesDocument = instanceWatchesDocument;
4708
4866
  exports.isGdr = isGdr;
4709
4867
  exports.isTerminalStage = isTerminalStage;
4710
4868
  exports.lakeGuardId = lakeGuardId;
@@ -4723,7 +4881,6 @@ exports.stripSystemFields = stripSystemFields;
4723
4881
  exports.subscriptionDocument = subscriptionDocument;
4724
4882
  exports.subscriptionDocumentsForInstance = subscriptionDocumentsForInstance;
4725
4883
  exports.tagScopeFilter = tagScopeFilter;
4726
- exports.taskKind = taskKind;
4727
4884
  exports.validateDefinition = validateDefinition;
4728
4885
  exports.validateTag = validateTag;
4729
4886
  exports.verdictGuardsForInstance = verdictGuardsForInstance;