@sanity/workflow-engine 0.8.0 → 0.10.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
@@ -126,7 +126,7 @@ function buildParams(args) {
126
126
  const { instance, now, snapshot, extra } = args, currentTasks2 = findOpenStageEntry(instance)?.tasks ?? [];
127
127
  return {
128
128
  self: selfGdr(instance),
129
- state: renderedState(instance.state ?? [], snapshot),
129
+ fields: renderedFields(instance.fields ?? [], snapshot),
130
130
  parent: instance.ancestors.at(-1)?.id ?? null,
131
131
  ancestors: instance.ancestors.map((a) => a.id),
132
132
  stage: instance.currentStage,
@@ -141,7 +141,7 @@ function buildParams(args) {
141
141
  ...extra
142
142
  };
143
143
  }
144
- function renderedState(entries, snapshot) {
144
+ function renderedFields(entries, snapshot) {
145
145
  const out = {};
146
146
  for (const entry of entries) out[entry.name] = renderedValue(entry, snapshot);
147
147
  return out;
@@ -149,17 +149,17 @@ function renderedState(entries, snapshot) {
149
149
  function renderedValue(entry, snapshot) {
150
150
  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
151
  }
152
- function scopedStateOverlay(instance, snapshot, taskName) {
152
+ function scopedFieldOverlay(instance, snapshot, taskName) {
153
153
  const stageEntry = findOpenStageEntry(instance);
154
154
  if (stageEntry === void 0) return {};
155
- const stageState = renderedState(stageEntry.state ?? [], snapshot), task = taskName ? stageEntry.tasks.find((t) => t.name === taskName) : void 0;
156
- return { ...stageState, ...renderedState(task?.state ?? [], snapshot) };
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
157
  }
158
- function assignedFor(instance, taskName, actor) {
158
+ function assignedFor(instance, taskName, actor, roleAliases) {
159
159
  if (actor === void 0) return !1;
160
- const entry = findOpenStageEntry(instance)?.tasks.find((t) => t.name === taskName)?.state?.find((s) => s._type === "assignees");
160
+ const entry = findOpenStageEntry(instance)?.tasks.find((t) => t.name === taskName)?.fields?.find((s) => s._type === "assignees");
161
161
  return entry === void 0 ? !1 : entry.value.some(
162
- (a) => a.type === "user" ? a.id === actor.id : (actor.roles ?? []).includes(a.role)
162
+ (a) => a.type === "user" ? a.id === actor.id : schema.actorFulfillsRole(actor.roles, a.role, roleAliases)
163
163
  );
164
164
  }
165
165
  function effectsContextMap(instance) {
@@ -184,17 +184,17 @@ function paramsForLake(params) {
184
184
  self: typeof params.self == "string" ? bareId(params.self) : params.self,
185
185
  parent: typeof params.parent == "string" ? bareId(params.parent) : params.parent,
186
186
  ancestors: Array.isArray(params.ancestors) ? params.ancestors.map((a) => typeof a == "string" ? bareId(a) : a) : params.ancestors,
187
- state: stripStateForLake(params.state)
187
+ fields: stripFieldsForLake(params.fields)
188
188
  };
189
189
  }
190
- function stripStateForLake(value) {
190
+ function stripFieldsForLake(value) {
191
191
  if (value == null) return value;
192
192
  if (typeof value == "string") return bareId(value);
193
- if (Array.isArray(value)) return value.map(stripStateForLake);
193
+ if (Array.isArray(value)) return value.map(stripFieldsForLake);
194
194
  if (typeof value == "object") {
195
195
  const out = {};
196
196
  for (const [k, v2] of Object.entries(value))
197
- out[k] = stripStateForLake(v2);
197
+ out[k] = stripFieldsForLake(v2);
198
198
  return out;
199
199
  }
200
200
  return value;
@@ -213,17 +213,17 @@ function getPath(value, path) {
213
213
  function resourceOf(p) {
214
214
  return p.scheme === "dataset" ? { type: "dataset", id: `${p.projectId}.${p.dataset}` } : { type: p.scheme, id: p.resourceId ?? "" };
215
215
  }
216
- const STATE_READ = /^\$state\.(\w+)(?:\.(.+))?$/, EFFECTS_READ = /^\$effects\['([^']+)'\](?:\.(.+))?$/;
216
+ const FIELD_READ = /^\$fields\.(\w+)(?:\.(.+))?$/, EFFECTS_READ = /^\$effects\['([^']+)'\](?:\.(.+))?$/;
217
217
  function isGuardReadExpr(expr) {
218
- return expr === "$self" || expr === "$now" || STATE_READ.test(expr) || EFFECTS_READ.test(expr);
218
+ return expr === "$self" || expr === "$now" || FIELD_READ.test(expr) || EFFECTS_READ.test(expr);
219
219
  }
220
220
  function resolveGuardRead(expr, ctx) {
221
221
  if (expr === "$self") return selfGdr(ctx.instance);
222
222
  if (expr === "$now") return ctx.now;
223
- const stateRead = STATE_READ.exec(expr);
224
- if (stateRead !== null) {
225
- const value = (ctx.instance.state ?? []).find((s) => s.name === stateRead[1])?.value;
226
- return stateRead[2] !== void 0 ? getPath(value, stateRead[2]) : value;
223
+ const fieldRead = FIELD_READ.exec(expr);
224
+ if (fieldRead !== null) {
225
+ const value = (ctx.instance.fields ?? []).find((s) => s.name === fieldRead[1])?.value;
226
+ return fieldRead[2] !== void 0 ? getPath(value, fieldRead[2]) : value;
227
227
  }
228
228
  const effectsRead = EFFECTS_READ.exec(expr);
229
229
  if (effectsRead !== null) {
@@ -231,7 +231,7 @@ function resolveGuardRead(expr, ctx) {
231
231
  return effectsRead[2] !== void 0 ? getPath(outputs, effectsRead[2]) : outputs;
232
232
  }
233
233
  throw new Error(
234
- `Guard read "${expr}" is not a supported deploy-time value \u2014 use "$self", "$now", "$state.<name>[.path]", or "$effects['<name>'][.path]" (guards store resolved values; the lake cannot see $state or $effects)`
234
+ `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)`
235
235
  );
236
236
  }
237
237
  function resolveIdRefTarget(expr, ctx) {
@@ -281,8 +281,8 @@ function resolveMetadata(metadata, ctx) {
281
281
  }
282
282
  function validateDefinition(definition) {
283
283
  const v2 = createDefinitionValidator();
284
- for (const entry of definition.state ?? [])
285
- v2.checkEntry(entry, `workflow.state "${entry.name}"`);
284
+ for (const entry of definition.fields ?? [])
285
+ v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
286
286
  for (const [name, groq] of Object.entries(definition.predicates ?? {}))
287
287
  v2.checkCondition(groq, `predicate "${name}"`);
288
288
  for (const stage of definition.stages)
@@ -304,7 +304,7 @@ function createDefinitionValidator() {
304
304
  }
305
305
  }, rejectTypeScan = (groq, where) => {
306
306
  /\*\s*\[\s*_type\b/.test(groq) && errors.push(
307
- ` \xB7 ${where}: condition scans by \`_type\` \u2014 that's a discovery query, not a predicate. Conditions evaluate against the in-memory snapshot (instance + ancestors + state-declared docs). To bring extra docs in scope, declare a \`doc.ref\` (or \`doc.refs\`) state entry on the workflow or this stage. For lake scans like "all articles in this release", use \`subworkflows.forEach\` instead.`
307
+ ` \xB7 ${where}: condition scans by \`_type\` \u2014 that's a discovery query, not a predicate. Conditions evaluate against the in-memory snapshot (instance + ancestors + field-declared docs). To bring extra docs in scope, declare a \`doc.ref\` (or \`doc.refs\`) field entry on the workflow or this stage. For lake scans like "all articles in this release", use \`subworkflows.forEach\` instead.`
308
308
  );
309
309
  };
310
310
  return { errors, tryParse, checkCondition: (groq, where) => {
@@ -313,13 +313,13 @@ function createDefinitionValidator() {
313
313
  entry.source.type === "query" && tryParse(entry.source.query, `${label}.query`);
314
314
  }, checkGuardRead: (expr, where) => {
315
315
  isGuardReadExpr(expr) || errors.push(
316
- ` \xB7 ${where}: guard read "${expr}" is not a supported deploy-time value \u2014 use "$self", "$now", "$state.<name>[.path]", or "$effects['<name>'][.path]" (guards store resolved values; the lake cannot see $state or $effects)`
316
+ ` \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)`
317
317
  );
318
318
  } };
319
319
  }
320
320
  function validateStage(v2, stage) {
321
- for (const entry of stage.state ?? [])
322
- v2.checkEntry(entry, `stage "${stage.name}".state "${entry.name}"`);
321
+ for (const entry of stage.fields ?? [])
322
+ v2.checkEntry(entry, `stage "${stage.name}".fields "${entry.name}"`);
323
323
  for (const guard of stage.guards ?? [])
324
324
  validateGuardReads(v2, stage.name, guard);
325
325
  for (const t of stage.transitions ?? []) {
@@ -339,8 +339,8 @@ function validateGuardReads(v2, stageName, guard) {
339
339
  }
340
340
  function validateTask(v2, stageName, task) {
341
341
  const where = `stage "${stageName}" task "${task.name}"`;
342
- for (const entry of task.state ?? [])
343
- v2.checkEntry(entry, `${where}.state "${entry.name}"`);
342
+ for (const entry of task.fields ?? [])
343
+ v2.checkEntry(entry, `${where}.fields "${entry.name}"`);
344
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
345
  for (const [name, groq] of Object.entries(task.requirements ?? {}))
346
346
  v2.checkCondition(groq, `${where}.requirements "${name}"`);
@@ -512,7 +512,7 @@ function openStage(instance) {
512
512
  return findOpenStageEntry(instance);
513
513
  }
514
514
  function assigneesOf(stage, taskName) {
515
- return (stage?.tasks.find((t) => t.name === taskName)?.state ?? []).filter((s) => s._type === "assignees").flatMap((s) => s.value);
515
+ return (stage?.tasks.find((t) => t.name === taskName)?.fields ?? []).filter((s) => s._type === "assignees").flatMap((s) => s.value);
516
516
  }
517
517
  function isResolved(status) {
518
518
  return status === "done" || status === "skipped";
@@ -625,17 +625,17 @@ class ActionParamsInvalidError extends Error {
625
625
  ${lines}`), this.name = "ActionParamsInvalidError", this.action = args.action, this.task = args.task, this.issues = args.issues;
626
626
  }
627
627
  }
628
- class RequiredStateNotProvidedError extends Error {
628
+ class RequiredFieldNotProvidedError extends Error {
629
629
  definition;
630
630
  missing;
631
631
  constructor(args) {
632
632
  const lines = args.missing.map((m) => ` - ${m.name} (${m.type})`).join(`
633
633
  `), where = args.definition !== void 0 ? ` for workflow "${args.definition}"` : "";
634
634
  super(
635
- `Required init state${where} was not provided:
635
+ `Required init fields${where} was not provided:
636
636
  ${lines}
637
- Provide each via \`initialState\` when starting standalone, or via the parent's \`subworkflows.with\` when spawned.`
638
- ), this.name = "RequiredStateNotProvidedError", args.definition !== void 0 && (this.definition = args.definition), this.missing = args.missing;
637
+ Provide each via \`initialFields\` when starting standalone, or via the parent's \`subworkflows.with\` when spawned.`
638
+ ), this.name = "RequiredFieldNotProvidedError", args.definition !== void 0 && (this.definition = args.definition), this.missing = args.missing;
639
639
  }
640
640
  }
641
641
  class WorkflowStateDivergedError extends Error {
@@ -678,6 +678,17 @@ function isRevisionConflict(error) {
678
678
  const { statusCode, message } = error;
679
679
  return statusCode === 409 ? !0 : typeof message == "string" && message.includes("ifRevisionId check failed");
680
680
  }
681
+ class ConcurrentEditFieldError extends Error {
682
+ instanceId;
683
+ target;
684
+ attempts;
685
+ constructor(args) {
686
+ const where = args.target.task !== void 0 ? `${args.target.task}.${args.target.field}` : args.target.field;
687
+ super(
688
+ `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
+ ), this.name = "ConcurrentEditFieldError", this.instanceId = args.instanceId, this.target = args.target, this.attempts = args.attempts;
690
+ }
691
+ }
681
692
  class CascadeLimitError extends Error {
682
693
  instanceId;
683
694
  limit;
@@ -723,10 +734,10 @@ function collectWatchRefs(instance) {
723
734
  return [
724
735
  gdrRef(instance.workflowResource, instance._id, instance._type),
725
736
  ...instance.ancestors,
726
- ...entryDocRefs(instance.state),
727
- ...entryDocRefs(stage?.state),
728
- ...entryReleaseRefs(instance.state),
729
- ...entryReleaseRefs(stage?.state)
737
+ ...entryDocRefs(instance.fields),
738
+ ...entryDocRefs(stage?.fields),
739
+ ...entryReleaseRefs(instance.fields),
740
+ ...entryReleaseRefs(stage?.fields)
730
741
  ];
731
742
  }
732
743
  function readsRaw(ref) {
@@ -750,21 +761,21 @@ function subscriptionDocumentsForInstance(instance) {
750
761
  ...instance.perspective !== void 0 ? { perspective: instance.perspective } : {}
751
762
  };
752
763
  }
753
- function entryDocRefs(state) {
754
- return stateEntries(state).flatMap((entry) => entry._type === "doc.ref" ? isGdr(entry.value) ? [entry.value] : [] : entry._type === "doc.refs" ? Array.isArray(entry.value) ? entry.value.filter(isGdr) : [] : []);
764
+ function entryDocRefs(entries) {
765
+ 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) : [] : []);
755
766
  }
756
- function entryReleaseRefs(state) {
757
- return stateEntries(state).flatMap(
767
+ function entryReleaseRefs(entries) {
768
+ return fieldEntries(entries).flatMap(
758
769
  (entry) => entry._type === "release.ref" && isGdr(entry.value) ? [entry.value] : []
759
770
  );
760
771
  }
761
- function entrySubjectRefs(state) {
762
- return stateEntries(state).flatMap(
772
+ function entrySubjectRefs(entries) {
773
+ return fieldEntries(entries).flatMap(
763
774
  (entry) => (entry._type === "doc.ref" || entry._type === "release.ref") && isGdr(entry.value) ? [entry.value] : []
764
775
  );
765
776
  }
766
- function stateEntries(state) {
767
- return Array.isArray(state) ? state.filter(
777
+ function fieldEntries(entries) {
778
+ return Array.isArray(entries) ? entries.filter(
768
779
  (entry) => !!entry && typeof entry == "object"
769
780
  ) : [];
770
781
  }
@@ -810,8 +821,8 @@ async function readDoc(client, id, perspective) {
810
821
  function resourceFromParsed(parsed) {
811
822
  return parsed.scheme === "dataset" ? { type: "dataset", id: `${parsed.projectId}.${parsed.dataset}` } : { type: parsed.scheme, id: parsed.resourceId ?? "" };
812
823
  }
813
- function collectEntryDocUris(resolvedStateEntries) {
814
- return entryDocRefs(resolvedStateEntries).map((ref) => ref.id);
824
+ function collectEntryDocUris(resolvedFieldEntries) {
825
+ return entryDocRefs(resolvedFieldEntries).map((ref) => ref.id);
815
826
  }
816
827
  const resourceKey = (r) => `${r.type}.${r.id}`;
817
828
  function guardsForResource(client) {
@@ -829,8 +840,8 @@ function verdictGuardsForInstance(client, instanceId) {
829
840
  }
830
841
  async function guardsForInstance(args) {
831
842
  const { client, clientForGdr, instance } = args, stateUris = [
832
- ...collectEntryDocUris(instance.state),
833
- ...instance.stages.flatMap((stage) => collectEntryDocUris(stage.state))
843
+ ...collectEntryDocUris(instance.fields),
844
+ ...instance.stages.flatMap((stage) => collectEntryDocUris(stage.fields))
834
845
  ], clients = resourceClientMap(
835
846
  instance.workflowResource,
836
847
  client,
@@ -862,16 +873,16 @@ function guardIdRefs(definition) {
862
873
  );
863
874
  }
864
875
  function staticIdRefGdr(idRef, stage, definition) {
865
- const read = /^\$state\.([\w-]+)/.exec(idRef);
876
+ const read = /^\$fields\.([\w-]+)/.exec(idRef);
866
877
  if (read === null) return;
867
878
  const source = entriesInScope(stage, definition).find((e) => e.name === read[1])?.source;
868
879
  return source?.type === "literal" ? gdrFromValue(source.value) : void 0;
869
880
  }
870
881
  function entriesInScope(stage, definition) {
871
882
  return [
872
- ...definition.state ?? [],
873
- ...stage.state ?? [],
874
- ...(stage.tasks ?? []).flatMap((task) => task.state ?? [])
883
+ ...definition.fields ?? [],
884
+ ...stage.fields ?? [],
885
+ ...(stage.tasks ?? []).flatMap((task) => task.fields ?? [])
875
886
  ];
876
887
  }
877
888
  function gdrFromValue(value) {
@@ -1021,15 +1032,15 @@ async function runGroq(groq, params, snapshot) {
1021
1032
  const tree = groqJs.parse(groq, { params });
1022
1033
  return (await groqJs.evaluate(tree, { dataset: snapshot.docs, params })).get();
1023
1034
  }
1024
- class StateValueShapeError extends Error {
1035
+ class FieldValueShapeError extends Error {
1025
1036
  entryType;
1026
1037
  entryName;
1027
1038
  issues;
1028
1039
  constructor(args) {
1029
1040
  const issueText = args.issues.join("; ");
1030
1041
  super(
1031
- `State entry ${args.mode} shape invalid for "${args.entryName}" (${args.entryType}): ${issueText}`
1032
- ), this.name = "StateValueShapeError", this.entryType = args.entryType, this.entryName = args.entryName, this.issues = args.issues;
1042
+ `Field entry ${args.mode} shape invalid for "${args.entryName}" (${args.entryType}): ${issueText}`
1043
+ ), this.name = "FieldValueShapeError", this.entryType = args.entryType, this.entryName = args.entryName, this.issues = args.issues;
1033
1044
  }
1034
1045
  }
1035
1046
  const GdrShape = v__namespace.looseObject({
@@ -1096,35 +1107,35 @@ const GdrShape = v__namespace.looseObject({
1096
1107
  function isAppendable(entryType) {
1097
1108
  return entryType in itemSchemas;
1098
1109
  }
1099
- function validateStateValue(args) {
1110
+ function validateFieldValue(args) {
1100
1111
  const schema2 = valueSchemas[args.entryType];
1101
1112
  if (schema2 === void 0)
1102
- throw new StateValueShapeError({
1113
+ throw new FieldValueShapeError({
1103
1114
  entryType: args.entryType,
1104
1115
  entryName: args.entryName,
1105
- issues: [`unknown state entry type ${args.entryType}`],
1116
+ issues: [`unknown field entry type ${args.entryType}`],
1106
1117
  mode: "value"
1107
1118
  });
1108
1119
  const result = v__namespace.safeParse(schema2, args.value);
1109
1120
  if (!result.success)
1110
- throw new StateValueShapeError({
1121
+ throw new FieldValueShapeError({
1111
1122
  entryType: args.entryType,
1112
1123
  entryName: args.entryName,
1113
1124
  issues: formatIssues(result.issues),
1114
1125
  mode: "value"
1115
1126
  });
1116
1127
  }
1117
- function validateStateAppendItem(args) {
1128
+ function validateFieldAppendItem(args) {
1118
1129
  if (!isAppendable(args.entryType))
1119
- throw new StateValueShapeError({
1130
+ throw new FieldValueShapeError({
1120
1131
  entryType: args.entryType,
1121
1132
  entryName: args.entryName,
1122
- issues: [`state entry type ${args.entryType} does not support append`],
1133
+ issues: [`field entry type ${args.entryType} does not support append`],
1123
1134
  mode: "item"
1124
1135
  });
1125
1136
  const schema2 = itemSchemas[args.entryType], result = v__namespace.safeParse(schema2, args.item);
1126
1137
  if (!result.success)
1127
- throw new StateValueShapeError({
1138
+ throw new FieldValueShapeError({
1128
1139
  entryType: args.entryType,
1129
1140
  entryName: args.entryName,
1130
1141
  issues: formatIssues(result.issues),
@@ -1137,7 +1148,7 @@ function formatIssues(issues) {
1137
1148
  return `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${i.message}`;
1138
1149
  });
1139
1150
  }
1140
- function derivePerspectiveFromState(entries) {
1151
+ function derivePerspectiveFromFields(entries) {
1141
1152
  if (entries !== void 0) {
1142
1153
  for (const entry of entries)
1143
1154
  if (entry._type === "release.ref" && entry.value !== null) {
@@ -1147,24 +1158,24 @@ function derivePerspectiveFromState(entries) {
1147
1158
  }
1148
1159
  }
1149
1160
  }
1150
- async function resolveDeclaredState(args) {
1151
- const { entryDefs, initialState, ctx, randomKey: randomKey2 } = args;
1161
+ async function resolveDeclaredFields(args) {
1162
+ const { entryDefs, initialFields, ctx, randomKey: randomKey2 } = args;
1152
1163
  if (entryDefs === void 0 || entryDefs.length === 0) return [];
1153
- assertRequiredInitProvided(entryDefs, initialState, ctx.definitionName);
1164
+ assertRequiredInitProvided(entryDefs, initialFields, ctx.definitionName);
1154
1165
  const out = [];
1155
1166
  for (const entry of entryDefs) {
1156
- const scopedCtx = { ...ctx, resolvedState: out };
1157
- out.push(await resolveOneEntry(entry, initialState, scopedCtx, randomKey2));
1167
+ const scopedCtx = { ...ctx, resolvedFields: out };
1168
+ out.push(await resolveOneEntry(entry, initialFields, scopedCtx, randomKey2));
1158
1169
  }
1159
1170
  return out;
1160
1171
  }
1161
- function assertRequiredInitProvided(entryDefs, initialState, definitionName) {
1172
+ function assertRequiredInitProvided(entryDefs, initialFields, definitionName) {
1162
1173
  const missing = entryDefs.filter((entry) => entry.required === !0 && entry.source.type === "init").filter((entry) => {
1163
- const match = initialState.find((s) => s.name === entry.name && s.type === entry.type);
1174
+ const match = initialFields.find((s) => s.name === entry.name && s.type === entry.type);
1164
1175
  return match === void 0 || match.value === void 0 || match.value === null;
1165
1176
  }).map((entry) => ({ name: entry.name, type: entry.type }));
1166
1177
  if (missing.length !== 0)
1167
- throw new RequiredStateNotProvidedError({
1178
+ throw new RequiredFieldNotProvidedError({
1168
1179
  ...definitionName !== void 0 ? { definition: definitionName } : {},
1169
1180
  missing
1170
1181
  });
@@ -1178,18 +1189,18 @@ const ARRAY_SLOT_TYPES = /* @__PURE__ */ new Set([
1178
1189
  function defaultEntryValue(entryType) {
1179
1190
  return ARRAY_SLOT_TYPES.has(entryType) ? [] : null;
1180
1191
  }
1181
- function resolveInitValue(entry, initialState, defaultValue) {
1182
- const initMatch = initialState.find((s) => s.name === entry.name && s.type === entry.type);
1192
+ function resolveInitValue(entry, initialFields, defaultValue) {
1193
+ const initMatch = initialFields.find((s) => s.name === entry.name && s.type === entry.type);
1183
1194
  return initMatch === void 0 ? defaultValue : (assertInitValueShape(entry, initMatch.value), initMatch.value);
1184
1195
  }
1185
- function resolveStateReadValue(source, ctx, defaultValue) {
1186
- const target = (source.scope === "workflow" ? ctx.workflowState ?? [] : ctx.resolvedState ?? []).find((s) => s.name === source.state), targetValue = target === void 0 ? void 0 : target.value;
1196
+ function resolveFieldReadValue(source, ctx, defaultValue) {
1197
+ const target = (source.scope === "workflow" ? ctx.workflowFields ?? [] : ctx.resolvedFields ?? []).find((s) => s.name === source.field), targetValue = target === void 0 ? void 0 : target.value;
1187
1198
  return (source.path !== void 0 ? getPath(targetValue, source.path) : targetValue) ?? defaultValue;
1188
1199
  }
1189
1200
  async function resolveQueryValue(entry, source, ctx, client, defaultValue) {
1190
1201
  const params = paramsForLake({
1191
1202
  self: ctx.selfId ?? null,
1192
- state: stateMapFromResolved(ctx.resolvedState ?? []),
1203
+ fields: fieldMapFromResolved(ctx.resolvedFields ?? []),
1193
1204
  stage: ctx.stageName ?? null,
1194
1205
  task: ctx.taskName ?? null,
1195
1206
  now: ctx.now,
@@ -1205,9 +1216,9 @@ async function resolveQueryValue(entry, source, ctx, client, defaultValue) {
1205
1216
  );
1206
1217
  }
1207
1218
  }
1208
- async function resolveEntryValue(entry, initialState, ctx, defaultValue) {
1219
+ async function resolveEntryValue(entry, initialFields, ctx, defaultValue) {
1209
1220
  const source = entry.source;
1210
- return source.type === "init" ? resolveInitValue(entry, initialState, defaultValue) : source.type === "literal" ? source.value ?? defaultValue : source.type === "stateRead" ? resolveStateReadValue(source, ctx, defaultValue) : source.type === "query" && ctx.client !== void 0 ? resolveQueryValue(entry, source, ctx, ctx.client, defaultValue) : defaultValue;
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;
1211
1222
  }
1212
1223
  function buildResolvedEntry(entry, value, _key, now) {
1213
1224
  const titleProp = entry.title !== void 0 ? { title: entry.title } : {}, descriptionProp = entry.description !== void 0 ? { description: entry.description } : {};
@@ -1228,36 +1239,36 @@ function buildResolvedEntry(entry, value, _key, now) {
1228
1239
  value
1229
1240
  };
1230
1241
  }
1231
- async function resolveOneEntry(entry, initialState, ctx, randomKey2) {
1232
- const defaultValue = defaultEntryValue(entry.type), value = await resolveEntryValue(entry, initialState, ctx, defaultValue);
1233
- return validateStateValue({ entryType: entry.type, entryName: entry.name, value }), buildResolvedEntry(entry, value, randomKey2(), ctx.now);
1242
+ async function resolveOneEntry(entry, initialFields, ctx, randomKey2) {
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);
1234
1245
  }
1235
- function stateMapFromResolved(entries) {
1246
+ function fieldMapFromResolved(entries) {
1236
1247
  const out = {};
1237
1248
  for (const s of entries) out[s.name] = s.value;
1238
1249
  return out;
1239
1250
  }
1240
1251
  function assertInitValueShape(entry, value) {
1241
1252
  if (entry.type === "doc.ref") {
1242
- assertGdrShape(value, `state entry "${entry.name}" (doc.ref)`);
1253
+ assertGdrShape(value, `field entry "${entry.name}" (doc.ref)`);
1243
1254
  return;
1244
1255
  }
1245
1256
  if (entry.type === "release.ref") {
1246
- assertGdrShape(value, `state entry "${entry.name}" (release.ref)`);
1257
+ assertGdrShape(value, `field entry "${entry.name}" (release.ref)`);
1247
1258
  const v2 = value;
1248
1259
  if (typeof v2.releaseName != "string" || v2.releaseName.length === 0)
1249
1260
  throw new Error(
1250
- `Invalid init value for state entry "${entry.name}" (release.ref): \`releaseName\` must be a non-empty string. Got ${JSON.stringify(v2.releaseName)}.`
1261
+ `Invalid init value for field entry "${entry.name}" (release.ref): \`releaseName\` must be a non-empty string. Got ${JSON.stringify(v2.releaseName)}.`
1251
1262
  );
1252
1263
  return;
1253
1264
  }
1254
1265
  if (entry.type === "doc.refs") {
1255
1266
  if (!Array.isArray(value))
1256
1267
  throw new Error(
1257
- `Invalid init value for state entry "${entry.name}" (doc.refs): expected an array of GDRs, got ${typeof value}.`
1268
+ `Invalid init value for field entry "${entry.name}" (doc.refs): expected an array of GDRs, got ${typeof value}.`
1258
1269
  );
1259
1270
  for (const [i, item] of value.entries())
1260
- assertGdrShape(item, `state entry "${entry.name}" (doc.refs) item [${i}]`);
1271
+ assertGdrShape(item, `field entry "${entry.name}" (doc.refs) item [${i}]`);
1261
1272
  }
1262
1273
  }
1263
1274
  function assertGdrShape(value, context) {
@@ -1297,6 +1308,12 @@ function coerceToGdr(raw, workflowResource) {
1297
1308
  function gdrToBareId(uri) {
1298
1309
  return uri.includes(":") ? extractDocumentId(uri) : uri;
1299
1310
  }
1311
+ function loadCallContext(client, instanceId, options) {
1312
+ return loadContext(client, instanceId, {
1313
+ ...options?.clock ? { clock: options.clock } : {},
1314
+ ...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
1315
+ });
1316
+ }
1300
1317
  async function loadContext(client, instanceId, options) {
1301
1318
  const instance = await client.getDocument(instanceId);
1302
1319
  if (!instance)
@@ -1310,14 +1327,14 @@ async function loadContext(client, instanceId, options) {
1310
1327
  return { client, clientForGdr, clock, now: clock(), instance, definition, snapshot };
1311
1328
  }
1312
1329
  async function ctxConditionParams(ctx, opts) {
1313
- const base = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), state = {
1314
- ...base.state,
1315
- ...scopedStateOverlay(ctx.instance, ctx.snapshot, opts?.taskName)
1330
+ const base = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), fields = {
1331
+ ...base.fields,
1332
+ ...scopedFieldOverlay(ctx.instance, ctx.snapshot, opts?.taskName)
1316
1333
  }, params = {
1317
1334
  ...base,
1318
- state,
1335
+ fields,
1319
1336
  actor: opts?.actor,
1320
- assigned: opts?.taskName !== void 0 ? assignedFor(ctx.instance, opts.taskName, opts?.actor) : !1,
1337
+ assigned: opts?.taskName !== void 0 ? assignedFor(ctx.instance, opts.taskName, opts?.actor, ctx.definition.roleAliases) : !1,
1321
1338
  ...opts?.vars
1322
1339
  };
1323
1340
  return { ...await evaluatePredicates({
@@ -1354,11 +1371,11 @@ function findStage(definition, stageName) {
1354
1371
  throw new Error(`Stage "${stageName}" not found in definition ${definition.name}`);
1355
1372
  return stage;
1356
1373
  }
1357
- async function resolveStageStateEntries(args) {
1358
- const { client, instance, stage, now, initialState } = args;
1359
- return resolveDeclaredState({
1360
- entryDefs: stage.state,
1361
- initialState: initialState ?? [],
1374
+ async function resolveStageFieldEntries(args) {
1375
+ const { client, instance, stage, now, initialFields } = args;
1376
+ return resolveDeclaredFields({
1377
+ entryDefs: stage.fields,
1378
+ initialFields: initialFields ?? [],
1362
1379
  ctx: {
1363
1380
  client,
1364
1381
  now,
@@ -1366,19 +1383,19 @@ async function resolveStageStateEntries(args) {
1366
1383
  tag: instance.tag,
1367
1384
  stageName: stage.name,
1368
1385
  workflowResource: instance.workflowResource,
1369
- // Allow stage-scope entries' `source: { type: "stateRead", scope:
1370
- // "workflow", ... }` to see the already-resolved workflow-scope state.
1371
- workflowState: instance.state ?? [],
1386
+ // Allow stage-scope entries' `source: { type: "fieldRead", scope:
1387
+ // "workflow", ... }` to see the already-resolved workflow-scope fields.
1388
+ workflowFields: instance.fields ?? [],
1372
1389
  ...instance.perspective !== void 0 ? { perspective: instance.perspective } : {}
1373
1390
  },
1374
1391
  randomKey
1375
1392
  });
1376
1393
  }
1377
- async function resolveTaskStateEntries(args) {
1394
+ async function resolveTaskFieldEntries(args) {
1378
1395
  const { client, instance, stage, task, now } = args;
1379
- return resolveDeclaredState({
1380
- entryDefs: task.state,
1381
- initialState: [],
1396
+ return resolveDeclaredFields({
1397
+ entryDefs: task.fields,
1398
+ initialFields: [],
1382
1399
  ctx: {
1383
1400
  client,
1384
1401
  now,
@@ -1387,7 +1404,7 @@ async function resolveTaskStateEntries(args) {
1387
1404
  stageName: stage.name,
1388
1405
  workflowResource: instance.workflowResource,
1389
1406
  taskName: task.name,
1390
- workflowState: instance.state ?? [],
1407
+ workflowFields: instance.fields ?? [],
1391
1408
  ...instance.perspective !== void 0 ? { perspective: instance.perspective } : {}
1392
1409
  },
1393
1410
  randomKey
@@ -1469,15 +1486,15 @@ function stageTransitionHistory(args) {
1469
1486
  function startMutation(instance) {
1470
1487
  return {
1471
1488
  currentStage: instance.currentStage,
1472
- // Deep-ish copy. Per-scope state entry arrays need their own copies
1489
+ // Deep-ish copy. Per-scope field entry arrays need their own copies
1473
1490
  // so ops can mutate without leaking into the source.
1474
- state: (instance.state ?? []).map((s) => ({ ...s })),
1491
+ fields: (instance.fields ?? []).map((s) => ({ ...s })),
1475
1492
  stages: instance.stages.map((s) => ({
1476
1493
  ...s,
1477
- state: (s.state ?? []).map((entry) => ({ ...entry })),
1494
+ fields: (s.fields ?? []).map((entry) => ({ ...entry })),
1478
1495
  tasks: s.tasks.map((t) => ({
1479
1496
  ...t,
1480
- ...t.state !== void 0 ? { state: t.state.map((entry) => ({ ...entry })) } : {}
1497
+ ...t.fields !== void 0 ? { fields: t.fields.map((entry) => ({ ...entry })) } : {}
1481
1498
  }))
1482
1499
  })),
1483
1500
  pendingEffects: [...instance.pendingEffects],
@@ -1493,7 +1510,7 @@ function startMutation(instance) {
1493
1510
  function instanceStateFields(src) {
1494
1511
  const fields = {
1495
1512
  currentStage: src.currentStage,
1496
- state: src.state,
1513
+ fields: src.fields,
1497
1514
  stages: src.stages,
1498
1515
  pendingEffects: src.pendingEffects,
1499
1516
  effectHistory: src.effectHistory,
@@ -1506,7 +1523,7 @@ function materializeInstance(base, mutation) {
1506
1523
  return {
1507
1524
  ...base,
1508
1525
  currentStage: mutation.currentStage,
1509
- state: mutation.state,
1526
+ fields: mutation.fields,
1510
1527
  stages: mutation.stages,
1511
1528
  effectsContext: mutation.effectsContext
1512
1529
  };
@@ -1628,7 +1645,7 @@ function buildInstanceBase(args) {
1628
1645
  definition: args.definitionName,
1629
1646
  pinnedVersion: args.pinnedVersion,
1630
1647
  definitionSnapshot: JSON.stringify(args.definition),
1631
- state: args.state,
1648
+ fields: args.fields,
1632
1649
  effectsContext: args.effectsContext,
1633
1650
  ancestors: args.ancestors,
1634
1651
  ...perspective !== void 0 ? { perspective } : {},
@@ -1686,7 +1703,7 @@ async function spawnSubworkflows(ctx, mutation, task, sub, actor) {
1686
1703
  ...actor ? { actor } : {}
1687
1704
  }), { rows, discoveryResource } = await resolveSpawnRows(ctx, sub), effectsContext = await resolveContextHandoff(ctx, sub, parentScope), refs = [];
1688
1705
  for (const row of rows) {
1689
- const initialState = await projectRowState(
1706
+ const initialFields = await projectRowFields(
1690
1707
  ctx,
1691
1708
  sub,
1692
1709
  definition,
@@ -1697,7 +1714,7 @@ async function spawnSubworkflows(ctx, mutation, task, sub, actor) {
1697
1714
  client: ctx.client,
1698
1715
  parent: ctx.instance,
1699
1716
  definition,
1700
- initialState,
1717
+ initialFields,
1701
1718
  effectsContext,
1702
1719
  actor,
1703
1720
  now
@@ -1710,7 +1727,7 @@ async function resolveSpawnRows(ctx, sub) {
1710
1727
  const params = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), groq = sub.forEach;
1711
1728
  let value, discoveryResource;
1712
1729
  if (groq.includes("*")) {
1713
- const routingUri = entrySubjectRefs(ctx.instance.state)[0]?.id, client = clientForDiscovery(ctx.client, ctx.clientForGdr, routingUri);
1730
+ const routingUri = entrySubjectRefs(ctx.instance.fields)[0]?.id, client = clientForDiscovery(ctx.client, ctx.clientForGdr, routingUri);
1714
1731
  client !== ctx.client && routingUri !== void 0 && (discoveryResource = resourceFromGdrUri(routingUri)), value = await discoverItems({
1715
1732
  client,
1716
1733
  groq,
@@ -1726,13 +1743,13 @@ async function resolveSpawnRows(ctx, sub) {
1726
1743
  }
1727
1744
  return Array.isArray(value) ? { rows: value, discoveryResource } : value == null ? { rows: [], discoveryResource } : { rows: [value], discoveryResource };
1728
1745
  }
1729
- async function projectRowState(ctx, sub, childDefinition, parentScope, row, discoveryResource) {
1746
+ async function projectRowFields(ctx, sub, childDefinition, parentScope, row, discoveryResource) {
1730
1747
  const out = [];
1731
1748
  for (const [name, groq] of Object.entries(sub.with ?? {})) {
1732
- const declared = (childDefinition.state ?? []).find((e) => e.name === name);
1749
+ const declared = (childDefinition.fields ?? []).find((e) => e.name === name);
1733
1750
  if (declared === void 0)
1734
1751
  throw new Error(
1735
- `subworkflows.with["${name}"]: subworkflow "${childDefinition.name}" declares no state entry "${name}"`
1752
+ `subworkflows.with["${name}"]: subworkflow "${childDefinition.name}" declares no field entry "${name}"`
1736
1753
  );
1737
1754
  const raw = await runGroq(groq, { ...parentScope, row }, ctx.snapshot), value = canonicaliseSpawnValue(declared.type, raw, {
1738
1755
  parentResource: ctx.instance.workflowResource,
@@ -1787,15 +1804,15 @@ async function resolveDefinitionRef(client, ref, tag) {
1787
1804
  ) ?? null;
1788
1805
  }
1789
1806
  async function prepareChildInstance(args) {
1790
- const { client, parent, definition, initialState, 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 = [
1807
+ 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 = [
1791
1808
  ...parent.ancestors,
1792
1809
  {
1793
1810
  id: selfGdr(parent),
1794
1811
  type: WORKFLOW_INSTANCE_TYPE
1795
1812
  }
1796
- ], inheritedPerspective = parent.perspective, childState = await resolveDeclaredState({
1797
- entryDefs: definition.state,
1798
- initialState,
1813
+ ], inheritedPerspective = parent.perspective, childFields = await resolveDeclaredFields({
1814
+ entryDefs: definition.fields,
1815
+ initialFields,
1799
1816
  ctx: {
1800
1817
  client,
1801
1818
  now,
@@ -1806,7 +1823,7 @@ async function prepareChildInstance(args) {
1806
1823
  ...inheritedPerspective !== void 0 ? { perspective: inheritedPerspective } : {}
1807
1824
  },
1808
1825
  randomKey
1809
- }), childPerspective = derivePerspectiveFromState(childState) ?? inheritedPerspective, effectsContextEntries = Object.entries(effectsContext).map(
1826
+ }), childPerspective = derivePerspectiveFromFields(childFields) ?? inheritedPerspective, effectsContextEntries = Object.entries(effectsContext).map(
1810
1827
  ([key, value]) => {
1811
1828
  if (typeof value == "object" && value !== null && "id" in value && "type" in value) {
1812
1829
  if (!isGdrUri(value.id))
@@ -1825,7 +1842,7 @@ async function prepareChildInstance(args) {
1825
1842
  definitionName: definition.name,
1826
1843
  pinnedVersion: definition.version,
1827
1844
  definition,
1828
- state: childState,
1845
+ fields: childFields,
1829
1846
  effectsContext: effectsContextEntries,
1830
1847
  ancestors,
1831
1848
  perspective: childPerspective,
@@ -1990,15 +2007,15 @@ function resolveStaticSource(src, ctx) {
1990
2007
  return { handled: !1 };
1991
2008
  }
1992
2009
  }
1993
- const STATE_OP_TYPES = [
1994
- "state.set",
1995
- "state.unset",
1996
- "state.append",
1997
- "state.updateWhere",
1998
- "state.removeWhere"
2010
+ const FIELD_OP_TYPES = [
2011
+ "field.set",
2012
+ "field.unset",
2013
+ "field.append",
2014
+ "field.updateWhere",
2015
+ "field.removeWhere"
1999
2016
  ];
2000
- function isStateOp(summary) {
2001
- return STATE_OP_TYPES.includes(summary.opType);
2017
+ function isFieldOp(summary) {
2018
+ return FIELD_OP_TYPES.includes(summary.opType);
2002
2019
  }
2003
2020
  function validateActionParams(action, taskName, callerParams) {
2004
2021
  const declared = action.params ?? [], params = callerParams ?? {}, issues = [];
@@ -2048,41 +2065,46 @@ function runOps(args) {
2048
2065
  const summaries = [];
2049
2066
  for (const op of ops) {
2050
2067
  const summary = applyOp(op, { mutation, stage, taskName: origin.task, params, actor, self, now });
2051
- summaries.push(summary), mutation.history.push({
2052
- _key: randomKey(),
2053
- _type: "opApplied",
2054
- at: now,
2055
- stage,
2056
- ...origin.task !== void 0 ? { task: origin.task } : {},
2057
- ...origin.action !== void 0 ? { action: origin.action } : {},
2058
- ...origin.transition !== void 0 ? { transition: origin.transition } : {},
2059
- opType: summary.opType,
2060
- ...summary.target !== void 0 ? { target: summary.target } : {},
2061
- ...summary.resolved !== void 0 ? { resolved: summary.resolved } : {},
2062
- ...actor !== void 0 ? { actor } : {}
2063
- });
2068
+ summaries.push(summary), mutation.history.push(opAppliedEntry({ origin, summary, stage, actor, now }));
2064
2069
  }
2065
2070
  return summaries;
2066
2071
  }
2072
+ function opAppliedEntry(args) {
2073
+ const { origin, summary, stage, actor, now } = args;
2074
+ return {
2075
+ _key: randomKey(),
2076
+ _type: "opApplied",
2077
+ at: now,
2078
+ stage,
2079
+ ...origin.task !== void 0 ? { task: origin.task } : {},
2080
+ ...origin.action !== void 0 ? { action: origin.action } : {},
2081
+ ...origin.transition !== void 0 ? { transition: origin.transition } : {},
2082
+ ...origin.edit === !0 ? { edit: !0 } : {},
2083
+ opType: summary.opType,
2084
+ ...summary.target !== void 0 ? { target: summary.target } : {},
2085
+ ...summary.resolved !== void 0 ? { resolved: summary.resolved } : {},
2086
+ ...actor !== void 0 ? { actor } : {}
2087
+ };
2088
+ }
2067
2089
  function applyOp(op, ctx) {
2068
2090
  switch (op.type) {
2069
- case "state.set":
2070
- return applyStateSet(op, ctx);
2071
- case "state.unset":
2072
- return applyStateUnset(op, ctx);
2073
- case "state.append":
2074
- return applyStateAppend(op, ctx);
2075
- case "state.updateWhere":
2076
- return applyStateUpdateWhere(op, ctx);
2077
- case "state.removeWhere":
2078
- return applyStateRemoveWhere(op, ctx);
2091
+ case "field.set":
2092
+ return applyFieldSet(op, ctx);
2093
+ case "field.unset":
2094
+ return applyFieldUnset(op, ctx);
2095
+ case "field.append":
2096
+ return applyFieldAppend(op, ctx);
2097
+ case "field.updateWhere":
2098
+ return applyFieldUpdateWhere(op, ctx);
2099
+ case "field.removeWhere":
2100
+ return applyFieldRemoveWhere(op, ctx);
2079
2101
  case "status.set":
2080
2102
  return applyStatusSet(op, ctx);
2081
2103
  }
2082
2104
  }
2083
- function applyStateSet(op, ctx) {
2105
+ function applyFieldSet(op, ctx) {
2084
2106
  const value = resolveOpSource(op.value, ctx), entry = locateEntry(ctx, op.target);
2085
- return validateStateValue({ entryType: entry._type, entryName: entry.name, value }), setEntryValue(entry, value), { opType: op.type, target: op.target, resolved: { value } };
2107
+ return validateFieldValue({ entryType: entry._type, entryName: entry.name, value }), setEntryValue(entry, value), { opType: op.type, target: op.target, resolved: { value } };
2086
2108
  }
2087
2109
  const EMPTY_BY_KIND = {
2088
2110
  "doc.refs": [],
@@ -2090,24 +2112,24 @@ const EMPTY_BY_KIND = {
2090
2112
  notes: [],
2091
2113
  assignees: []
2092
2114
  };
2093
- function applyStateUnset(op, ctx) {
2115
+ function applyFieldUnset(op, ctx) {
2094
2116
  const entry = locateEntry(ctx, op.target);
2095
2117
  return setEntryValue(entry, EMPTY_BY_KIND[entry._type] ?? null), { opType: op.type, target: op.target };
2096
2118
  }
2097
- function applyStateAppend(op, ctx) {
2119
+ function applyFieldAppend(op, ctx) {
2098
2120
  const item = resolveOpSource(op.value, ctx), entry = locateEntry(ctx, op.target);
2099
- validateStateAppendItem({ entryType: entry._type, entryName: entry.name, item });
2121
+ validateFieldAppendItem({ entryType: entry._type, entryName: entry.name, item });
2100
2122
  const current = Array.isArray(entry.value) ? entry.value : [];
2101
2123
  return setEntryValue(entry, [...current, withRowKey(item)]), { opType: op.type, target: op.target, resolved: { item } };
2102
2124
  }
2103
2125
  function withRowKey(item) {
2104
2126
  return typeof item == "object" && item !== null && !Array.isArray(item) && !("_key" in item) ? { _key: randomKey(), ...item } : item;
2105
2127
  }
2106
- function applyStateUpdateWhere(op, ctx) {
2128
+ function applyFieldUpdateWhere(op, ctx) {
2107
2129
  const entry = locateEntry(ctx, op.target), rows = requireArrayValue(entry, op), merge = resolveOpSource(op.value, ctx);
2108
2130
  if (merge === null || typeof merge != "object" || Array.isArray(merge))
2109
2131
  throw new Error(
2110
- `state.updateWhere value must resolve to an object of fields to merge (target "${op.target.state}")`
2132
+ `field.updateWhere value must resolve to an object of fields to merge (target "${op.target.field}")`
2111
2133
  );
2112
2134
  return setEntryValue(
2113
2135
  entry,
@@ -2116,7 +2138,7 @@ function applyStateUpdateWhere(op, ctx) {
2116
2138
  )
2117
2139
  ), { opType: op.type, target: op.target, resolved: { merge } };
2118
2140
  }
2119
- function applyStateRemoveWhere(op, ctx) {
2141
+ function applyFieldRemoveWhere(op, ctx) {
2120
2142
  const entry = locateEntry(ctx, op.target), rows = requireArrayValue(entry, op);
2121
2143
  return setEntryValue(
2122
2144
  entry,
@@ -2126,7 +2148,7 @@ function applyStateRemoveWhere(op, ctx) {
2126
2148
  function requireArrayValue(entry, op) {
2127
2149
  if (!Array.isArray(entry.value))
2128
2150
  throw new Error(
2129
- `${op.type} target ${op.target.scope}:"${op.target.state}" holds a non-array ${entry._type} value \u2014 where-ops operate on array entries`
2151
+ `${op.type} target ${op.target.scope}:"${op.target.field}" holds a non-array ${entry._type} value \u2014 where-ops operate on array entries`
2130
2152
  );
2131
2153
  return entry.value;
2132
2154
  }
@@ -2147,19 +2169,19 @@ function setEntryValue(entry, value) {
2147
2169
  entry.value = value;
2148
2170
  }
2149
2171
  function locateEntry(ctx, target) {
2150
- const entry = stateHost(ctx, target.scope)?.find((s) => s.name === target.state);
2172
+ const entry = fieldHost(ctx, target.scope)?.find((s) => s.name === target.field);
2151
2173
  if (entry === void 0)
2152
2174
  throw new Error(
2153
- `Op target ${target.scope}:"${target.state}" has no resolved state entry on this instance \u2014 the deployed definition and the instance state have diverged`
2175
+ `Op target ${target.scope}:"${target.field}" has no resolved field entry on this instance \u2014 the deployed definition and the instance state have diverged`
2154
2176
  );
2155
2177
  return entry;
2156
2178
  }
2157
- function stateHost(ctx, scope) {
2158
- if (scope === "workflow") return ctx.mutation.state;
2179
+ function fieldHost(ctx, scope) {
2180
+ if (scope === "workflow") return ctx.mutation.fields;
2159
2181
  const stageEntry = findOpenStageEntry(ctx.mutation);
2160
- if (scope === "stage") return stageEntry?.state;
2182
+ if (scope === "stage") return stageEntry?.fields;
2161
2183
  const task = stageEntry?.tasks.find((t) => t.name === ctx.taskName);
2162
- return task !== void 0 && task.state === void 0 && (task.state = []), task?.state;
2184
+ return task !== void 0 && task.fields === void 0 && (task.fields = []), task?.fields;
2163
2185
  }
2164
2186
  function resolveOpSource(src, ctx) {
2165
2187
  const staticValue = resolveStaticSource(src, ctx);
@@ -2169,7 +2191,7 @@ function resolveOpSource(src, ctx) {
2169
2191
  return ctx.self;
2170
2192
  case "stage":
2171
2193
  return ctx.stage;
2172
- case "stateRead": {
2194
+ case "fieldRead": {
2173
2195
  const value = readEntryFromMutation(ctx, src);
2174
2196
  return src.path !== void 0 ? getPath(value, src.path) : value;
2175
2197
  }
@@ -2180,7 +2202,7 @@ function resolveOpSource(src, ctx) {
2180
2202
  return out;
2181
2203
  }
2182
2204
  default:
2183
- throw new Error(`Source type "${src.type}" is a state-entry origin, not a value`);
2205
+ throw new Error(`Source type "${src.type}" is a field-entry origin, not a value`);
2184
2206
  }
2185
2207
  }
2186
2208
  function entryValue(entries, name) {
@@ -2189,7 +2211,7 @@ function entryValue(entries, name) {
2189
2211
  function readEntryFromMutation(ctx, src) {
2190
2212
  const scopes = src.scope !== void 0 ? [src.scope] : ["stage", "workflow"], stageEntry = findOpenStageEntry(ctx.mutation);
2191
2213
  for (const scope of scopes) {
2192
- const host = scope === "workflow" ? ctx.mutation.state : stageEntry?.state, value = entryValue(host, src.state);
2214
+ const host = scope === "workflow" ? ctx.mutation.fields : stageEntry?.fields, value = entryValue(host, src.field);
2193
2215
  if (value !== void 0) return value;
2194
2216
  }
2195
2217
  }
@@ -2221,9 +2243,9 @@ async function commitInvoke(ctx, taskName, actor) {
2221
2243
  }
2222
2244
  async function activateTask(ctx, mutation, task, entry, actor) {
2223
2245
  const now = ctx.now;
2224
- if (entry.status = "active", entry.startedAt = now, task.state !== void 0 && task.state.length > 0) {
2246
+ if (entry.status = "active", entry.startedAt = now, task.fields !== void 0 && task.fields.length > 0) {
2225
2247
  const stage = findStage(ctx.definition, mutation.currentStage);
2226
- entry.state = await resolveTaskStateEntries({
2248
+ entry.fields = await resolveTaskFieldEntries({
2227
2249
  client: ctx.client,
2228
2250
  instance: ctx.instance,
2229
2251
  stage,
@@ -2328,7 +2350,7 @@ async function enterStage(ctx, mutation, nextStage, actor, at) {
2328
2350
  _key: randomKey(),
2329
2351
  name: nextStage.name,
2330
2352
  enteredAt: at,
2331
- state: await resolveStageStateEntries({
2353
+ fields: await resolveStageFieldEntries({
2332
2354
  client: ctx.client,
2333
2355
  instance: ctx.instance,
2334
2356
  stage: nextStage,
@@ -2468,7 +2490,7 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
2468
2490
  _key: randomKey(),
2469
2491
  name: stage.name,
2470
2492
  enteredAt: now,
2471
- state: await resolveStageStateEntries({ client, instance, stage, now }),
2493
+ fields: await resolveStageFieldEntries({ client, instance, stage, now }),
2472
2494
  tasks: await buildStageTasks(ctx, stage)
2473
2495
  }, committed = await client.patch(instance._id).set({
2474
2496
  stages: [initialStageEntry],
@@ -2697,6 +2719,85 @@ async function fetchGrantsCached(requestFn, resourcePath) {
2697
2719
  return;
2698
2720
  }
2699
2721
  }
2722
+ function effectiveEditable(baseline, override) {
2723
+ if (baseline === void 0) return;
2724
+ if (override === void 0) return baseline;
2725
+ if (baseline === !0 && override === !0) return !0;
2726
+ const parts = [baseline, override].map((e) => e === !0 ? void 0 : e);
2727
+ return schema.andConditions(parts) ?? !0;
2728
+ }
2729
+ function slotSites(definition, stage) {
2730
+ const sites = [];
2731
+ for (const entry of definition.fields ?? []) sites.push({ scope: "workflow", entry });
2732
+ for (const entry of stage.fields ?? []) sites.push({ scope: "stage", entry });
2733
+ for (const task of stage.tasks ?? [])
2734
+ for (const entry of task.fields ?? []) sites.push({ scope: "task", task: task.name, entry });
2735
+ return sites;
2736
+ }
2737
+ function toResolvedSlot(site, stage) {
2738
+ return {
2739
+ scope: site.scope,
2740
+ ...site.task !== void 0 ? { task: site.task } : {},
2741
+ name: site.entry.name,
2742
+ type: site.entry.type,
2743
+ ...site.entry.title !== void 0 ? { title: site.entry.title } : {},
2744
+ ref: { scope: site.scope, field: site.entry.name },
2745
+ effective: effectiveEditable(site.entry.editable, stage.editable?.[site.entry.name])
2746
+ };
2747
+ }
2748
+ function editableSlotsInStage(definition, stage) {
2749
+ return slotSites(definition, stage).filter((site) => site.entry.editable !== void 0).map((site) => toResolvedSlot(site, stage));
2750
+ }
2751
+ function editTargetMatches(slot, target) {
2752
+ 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;
2753
+ }
2754
+ const SCOPE_PRECEDENCE = { task: 0, stage: 1, workflow: 2 };
2755
+ function resolveEditTarget(args) {
2756
+ const { definition, stage, target } = args, found = slotSites(definition, stage).filter(
2757
+ (site) => editTargetMatches(
2758
+ {
2759
+ scope: site.scope,
2760
+ name: site.entry.name,
2761
+ ...site.task !== void 0 ? { task: site.task } : {}
2762
+ },
2763
+ target
2764
+ )
2765
+ ).sort((a, b) => SCOPE_PRECEDENCE[a.scope] - SCOPE_PRECEDENCE[b.scope])[0];
2766
+ return found ? toResolvedSlot(found, stage) : void 0;
2767
+ }
2768
+ function slotWindowOpen(instance, slot) {
2769
+ if (instance.completedAt !== void 0) return { open: !1, detail: "instance completed" };
2770
+ if (instance.abortedAt !== void 0) return { open: !1, detail: "instance aborted" };
2771
+ if (slot.scope !== "task") return { open: !0 };
2772
+ const status = findOpenStageEntry(instance)?.tasks.find((t) => t.name === slot.task)?.status;
2773
+ return status === "active" ? { open: !0 } : { open: !1, detail: `task "${slot.task}" is ${status ?? "not active"}` };
2774
+ }
2775
+ function readSlotValue(instance, slot) {
2776
+ return slotFieldHost(instance, slot)?.find((e) => e.name === slot.name)?.value;
2777
+ }
2778
+ function slotFieldHost(instance, slot) {
2779
+ if (slot.scope === "workflow") return instance.fields;
2780
+ const stageEntry = findOpenStageEntry(instance);
2781
+ return slot.scope === "stage" ? stageEntry?.fields : stageEntry?.tasks.find((t) => t.name === slot.task)?.fields;
2782
+ }
2783
+ function slotProvenance(instance, ref) {
2784
+ let latest;
2785
+ for (const entry of instance.history)
2786
+ entry._type === "opApplied" && (entry.target?.scope !== ref.scope || entry.target.field !== ref.field || (latest = { at: entry.at, ...entry.actor !== void 0 ? { actor: entry.actor } : {} }));
2787
+ return latest === void 0 ? {} : {
2788
+ ...latest.actor !== void 0 ? { setBy: latest.actor } : {},
2789
+ setAt: latest.at
2790
+ };
2791
+ }
2792
+ function editDisabledReason(args) {
2793
+ const { effective, instance, window, guardDenial, predicateSatisfied } = args;
2794
+ if (effective === void 0) return { kind: "not-editable" };
2795
+ if (!window.open)
2796
+ return instance.completedAt !== void 0 ? { kind: "instance-completed", completedAt: instance.completedAt } : { kind: "edit-window-closed", detail: window.detail ?? "closed" };
2797
+ if (guardDenial !== void 0) return guardDenial;
2798
+ if (!predicateSatisfied)
2799
+ return { kind: "editor-not-permitted", predicate: effective === !0 ? "true" : effective };
2800
+ }
2700
2801
  async function evaluateInstance(args) {
2701
2802
  const { client, tag, instanceId, resourceClients } = args, now = (args.clock ?? wallClock)();
2702
2803
  validateTag(tag);
@@ -2730,6 +2831,7 @@ async function evaluateFromSnapshot(args) {
2730
2831
  actor,
2731
2832
  snapshot,
2732
2833
  scope,
2834
+ roleAliases: definition.roleAliases,
2733
2835
  stageHasExits: !isTerminalStage(stage),
2734
2836
  guardDenial
2735
2837
  })
@@ -2751,16 +2853,65 @@ async function evaluateFromSnapshot(args) {
2751
2853
  stage,
2752
2854
  tasks: taskEvaluations,
2753
2855
  transitions: transitionEvaluations
2754
- }, pendingOnYou = taskEvaluations.filter((t) => t.pendingOnActor), canInteract = taskEvaluations.some((t) => t.actions.some((a) => a.allowed));
2856
+ }, 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({
2857
+ instance,
2858
+ definition,
2859
+ stage,
2860
+ actor,
2861
+ snapshot,
2862
+ scope,
2863
+ guardDenial: editGuardDenial
2864
+ });
2755
2865
  return {
2756
2866
  instance,
2757
2867
  definition,
2758
2868
  actor,
2759
2869
  currentStage,
2760
2870
  pendingOnYou,
2761
- canInteract
2871
+ canInteract,
2872
+ editableSlots
2762
2873
  };
2763
2874
  }
2875
+ async function evaluateEditableSlots(args) {
2876
+ const { instance, definition, stage, actor, snapshot, scope, guardDenial } = args, slots = [];
2877
+ for (const slot of editableSlotsInStage(definition, stage)) {
2878
+ const window = slotWindowOpen(instance, slot), predicateSatisfied = await editPredicateSatisfied({
2879
+ slot,
2880
+ window,
2881
+ guardDenial,
2882
+ instance,
2883
+ snapshot,
2884
+ scope,
2885
+ actor,
2886
+ roleAliases: definition.roleAliases
2887
+ }), reason = editDisabledReason({
2888
+ effective: slot.effective,
2889
+ instance,
2890
+ window,
2891
+ guardDenial,
2892
+ predicateSatisfied
2893
+ }), value = readSlotValue(instance, slot);
2894
+ slots.push({
2895
+ scope: slot.scope,
2896
+ ...slot.task !== void 0 ? { task: slot.task } : {},
2897
+ name: slot.name,
2898
+ type: slot.type,
2899
+ ...slot.title !== void 0 ? { title: slot.title } : {},
2900
+ value,
2901
+ editable: reason === void 0,
2902
+ ...reason !== void 0 ? { disabledReason: reason } : {},
2903
+ ...slotProvenance(instance, slot.ref)
2904
+ });
2905
+ }
2906
+ return slots;
2907
+ }
2908
+ async function editPredicateSatisfied(args) {
2909
+ const { slot, window, guardDenial, instance, snapshot, scope, actor, roleAliases } = args;
2910
+ if (!window.open || guardDenial !== void 0) return !1;
2911
+ if (slot.effective === !0) return !0;
2912
+ const params = slot.scope === "task" && slot.task !== void 0 ? taskScopeFor({ scope, instance, snapshot, actor, taskName: slot.task, roleAliases }) : scope;
2913
+ return evaluateCondition({ condition: slot.effective, snapshot, params });
2914
+ }
2764
2915
  async function renderScope(args) {
2765
2916
  const { instance, definition, actor, grants, snapshot, now } = args, params = {
2766
2917
  ...buildParams({ instance, now, snapshot }),
@@ -2786,15 +2937,36 @@ async function advisoryCan(instance, actor, grants) {
2786
2937
  });
2787
2938
  return can;
2788
2939
  }
2789
- async function evaluateTask(args) {
2790
- const { task, statusEntry, instance, actor, snapshot, scope, stageHasExits, guardDenial } = args, status = statusEntry?.status ?? "pending", assigned = assignedFor(instance, task.name, actor), taskScope = {
2940
+ function taskScopeFor(args) {
2941
+ const { scope, instance, snapshot, actor, taskName, roleAliases } = args;
2942
+ return {
2791
2943
  ...scope,
2792
- state: {
2793
- ...scope.state,
2794
- ...scopedStateOverlay(instance, snapshot, task.name)
2944
+ fields: {
2945
+ ...scope.fields,
2946
+ ...scopedFieldOverlay(instance, snapshot, taskName)
2795
2947
  },
2796
- assigned
2797
- }, unmetRequirements = await evaluateRequirements({
2948
+ assigned: assignedFor(instance, taskName, actor, roleAliases)
2949
+ };
2950
+ }
2951
+ async function evaluateTask(args) {
2952
+ const {
2953
+ task,
2954
+ statusEntry,
2955
+ instance,
2956
+ actor,
2957
+ snapshot,
2958
+ scope,
2959
+ roleAliases,
2960
+ stageHasExits,
2961
+ guardDenial
2962
+ } = args, status = statusEntry?.status ?? "pending", taskScope = taskScopeFor({
2963
+ scope,
2964
+ instance,
2965
+ snapshot,
2966
+ actor,
2967
+ taskName: task.name,
2968
+ roleAliases
2969
+ }), assigned = taskScope.assigned === !0, unmetRequirements = await evaluateRequirements({
2798
2970
  requirements: task.requirements,
2799
2971
  snapshot,
2800
2972
  params: taskScope
@@ -2817,8 +2989,8 @@ async function evaluateTask(args) {
2817
2989
  status,
2818
2990
  // "pending on actor" treats both `pending` and `active` as waiting on
2819
2991
  // the assignee — pending tasks just haven't been activated yet, but
2820
- // they're still inbox items. The inbox reads the assignees-kind state
2821
- // entry BY KIND (who-data is state).
2992
+ // they're still inbox items. The inbox reads the assignees-kind field
2993
+ // entry BY KIND (who-data is fields).
2822
2994
  pendingOnActor: (status === "active" || status === "pending") && assigned,
2823
2995
  ...unmetRequirements.length > 0 ? { unmetRequirements } : {},
2824
2996
  actions
@@ -3021,10 +3193,7 @@ async function commitAbort(ctx, reason, actor) {
3021
3193
  async function fireAction(args) {
3022
3194
  const { client, instanceId, task, action, params, options } = args;
3023
3195
  for (let attempt = 1; attempt <= CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS; attempt++) {
3024
- const ctx = await loadContext(client, instanceId, {
3025
- ...options?.clock ? { clock: options.clock } : {},
3026
- ...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
3027
- });
3196
+ const ctx = await loadCallContext(client, instanceId, options);
3028
3197
  try {
3029
3198
  return await commitAction(ctx, task, action, params, options);
3030
3199
  } catch (error) {
@@ -3090,7 +3259,7 @@ async function commitAction(ctx, taskName, actionName, callerParams, options) {
3090
3259
  }), await persistThenDeploy(
3091
3260
  ctx,
3092
3261
  mutation,
3093
- () => ranOps.some(isStateOp) ? refreshStageGuards(ctx, mutation, stage.name) : Promise.resolve()
3262
+ () => ranOps.some(isFieldOp) ? refreshStageGuards(ctx, mutation, stage.name) : Promise.resolve()
3094
3263
  ), {
3095
3264
  fired: !0,
3096
3265
  task: taskName,
@@ -3119,6 +3288,114 @@ function formatDisabledReason(task, action, reason) {
3119
3288
  const detail = disabledReasonDetail[reason.kind](reason);
3120
3289
  return `Action "${task}:${action}" is not allowed: ${detail}`;
3121
3290
  }
3291
+ class EditFieldDeniedError extends Error {
3292
+ reason;
3293
+ target;
3294
+ constructor(args) {
3295
+ super(formatEditDisabledReason(args.target, args.reason)), this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
3296
+ }
3297
+ }
3298
+ const editDisabledReasonDetail = {
3299
+ "not-editable": () => "slot is not declared editable",
3300
+ "instance-completed": (r) => `instance completed at ${r.completedAt}`,
3301
+ "edit-window-closed": (r) => `edit window closed (${r.detail})`,
3302
+ "editor-not-permitted": (r) => `editor not permitted (${r.predicate})`,
3303
+ "mutation-guard-denied": (r) => `blocked by mutation guard(s) [${r.guardIds.join(", ")}]${r.detail ? ` (${r.detail})` : ""}`
3304
+ };
3305
+ function formatEditDisabledReason(target, reason) {
3306
+ const detail = editDisabledReasonDetail[reason.kind](
3307
+ reason
3308
+ ), where = target.task !== void 0 ? `${target.task}.${target.field}` : target.field;
3309
+ return `Field "${target.scope}:${where}" is not editable: ${detail}`;
3310
+ }
3311
+ async function editField(args) {
3312
+ const { client, instanceId, target, mode = "set", value, options } = args;
3313
+ for (let attempt = 1; attempt <= CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS; attempt++) {
3314
+ const ctx = await loadCallContext(client, instanceId, options);
3315
+ try {
3316
+ return await commitEdit(ctx, target, mode, value, options);
3317
+ } catch (error) {
3318
+ if (!isRevisionConflict(error)) throw error;
3319
+ }
3320
+ }
3321
+ throw new ConcurrentEditFieldError({
3322
+ instanceId,
3323
+ target: labelTarget(target),
3324
+ attempts: CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS
3325
+ });
3326
+ }
3327
+ async function commitEdit(ctx, target, mode, value, options) {
3328
+ const actor = options?.actor, stage = findStage(ctx.definition, ctx.instance.currentStage), slot = resolveEditTarget({ definition: ctx.definition, stage, target });
3329
+ if (slot === void 0)
3330
+ throw new Error(
3331
+ `No field slot "${describeTarget(target)}" resolves in current stage "${stage.name}" of ${ctx.definition.name}`
3332
+ );
3333
+ await assertSlotEditable(ctx, slot, options);
3334
+ const op = buildEditOp(slot, mode, value), mutation = startMutation(ctx.instance), ranOps = runOps({
3335
+ ops: [op],
3336
+ mutation,
3337
+ stage: stage.name,
3338
+ origin: {
3339
+ edit: !0,
3340
+ ...slot.scope === "task" && slot.task !== void 0 ? { task: slot.task } : {}
3341
+ },
3342
+ params: {},
3343
+ actor,
3344
+ self: selfGdr(ctx.instance),
3345
+ now: ctx.now
3346
+ });
3347
+ return await persistThenDeploy(
3348
+ ctx,
3349
+ mutation,
3350
+ () => ranOps.some(isFieldOp) ? refreshStageGuards(ctx, mutation, stage.name) : Promise.resolve()
3351
+ ), {
3352
+ edited: !0,
3353
+ target: slotTarget(slot),
3354
+ ...ranOps.length > 0 ? { ranOps } : {}
3355
+ };
3356
+ }
3357
+ async function assertSlotEditable(ctx, slot, options) {
3358
+ 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, {
3359
+ ...slot.task !== void 0 ? { taskName: slot.task } : {},
3360
+ ...actor !== void 0 ? { actor } : {},
3361
+ ...can !== void 0 ? { vars: { can } } : {}
3362
+ }) : slot.effective === !0, reason = editDisabledReason({
3363
+ effective: slot.effective,
3364
+ instance: ctx.instance,
3365
+ window,
3366
+ guardDenial: void 0,
3367
+ predicateSatisfied
3368
+ });
3369
+ if (reason !== void 0) throw new EditFieldDeniedError({ target: slotTarget(slot), reason });
3370
+ }
3371
+ function buildEditOp(slot, mode, value) {
3372
+ if (mode === "unset") return { type: "field.unset", target: slot.ref };
3373
+ if (value === void 0)
3374
+ throw new Error(`editField mode "${mode}" requires a value for slot "${slot.name}"`);
3375
+ return {
3376
+ type: mode === "append" ? "field.append" : "field.set",
3377
+ target: slot.ref,
3378
+ value: { type: "literal", value }
3379
+ };
3380
+ }
3381
+ function slotTarget(slot) {
3382
+ return {
3383
+ scope: slot.scope,
3384
+ field: slot.name,
3385
+ ...slot.task !== void 0 ? { task: slot.task } : {}
3386
+ };
3387
+ }
3388
+ function labelTarget(target) {
3389
+ return {
3390
+ scope: target.scope ?? (target.task !== void 0 ? "task" : "workflow"),
3391
+ field: target.field,
3392
+ ...target.task !== void 0 ? { task: target.task } : {}
3393
+ };
3394
+ }
3395
+ function describeTarget(target) {
3396
+ const where = target.task !== void 0 ? `${target.task}.${target.field}` : target.field;
3397
+ return target.scope !== void 0 ? `${target.scope}:${where}` : where;
3398
+ }
3122
3399
  function bareIdFromSpawnRef(uri) {
3123
3400
  if (!uri.includes(":")) return [uri];
3124
3401
  try {
@@ -3165,18 +3442,60 @@ function buildClientForGdr(defaultClient, resolver) {
3165
3442
  function toEffectsContextEntries(ctx) {
3166
3443
  return Object.entries(ctx).map(([id, value]) => effectsContextEntry(id, value));
3167
3444
  }
3445
+ async function dispatchGatedWrite(args) {
3446
+ const { client, tag, instanceId, resourceClients, access, apply } = args, evaluation = await evaluateInstance({
3447
+ client,
3448
+ tag,
3449
+ instanceId,
3450
+ access,
3451
+ clock: args.clock,
3452
+ ...resourceClients !== void 0 ? { resourceClients } : {}
3453
+ }), ranOps = await apply(args.before, evaluation), cascaded = await cascade(client, instanceId, args.actor, args.clientForGdr, args.clock);
3454
+ return {
3455
+ instance: await reload(client, instanceId, tag),
3456
+ cascaded,
3457
+ fired: !0,
3458
+ ...ranOps !== void 0 ? { ranOps } : {}
3459
+ };
3460
+ }
3168
3461
  function assertActionAllowed(evaluation, task, action) {
3169
3462
  const actionEval = evaluation.currentStage.tasks.find((t) => t.task.name === task)?.actions.find((a) => a.action.name === action);
3170
3463
  if (actionEval?.allowed === !1 && actionEval.disabledReason)
3171
3464
  throw new ActionDisabledError({ task, action, reason: actionEval.disabledReason });
3172
3465
  }
3173
- async function applyAction(args) {
3174
- const { client, instance, task, action, params, actor, grants, clock, clientForGdr } = args, options = {
3175
- actor,
3176
- ...grants !== void 0 ? { grants } : {},
3177
- ...clock !== void 0 ? { clock } : {},
3178
- ...clientForGdr !== void 0 ? { clientForGdr } : {}
3466
+ function assertEditAllowed(evaluation, target) {
3467
+ const slot = evaluation.editableSlots.filter((s) => editTargetMatches(s, target)).sort((a, b) => SCOPE_PRECEDENCE[a.scope] - SCOPE_PRECEDENCE[b.scope])[0];
3468
+ if (slot !== void 0 && !slot.editable && slot.disabledReason !== void 0)
3469
+ throw new EditFieldDeniedError({
3470
+ target: {
3471
+ scope: slot.scope,
3472
+ field: slot.name,
3473
+ ...slot.task !== void 0 ? { task: slot.task } : {}
3474
+ },
3475
+ reason: slot.disabledReason
3476
+ });
3477
+ }
3478
+ async function applyEdit(args) {
3479
+ const { client, instance, target, mode, value, actor, grants, clock, clientForGdr } = args;
3480
+ return (await editField({
3481
+ client,
3482
+ instanceId: instance._id,
3483
+ target,
3484
+ ...mode !== void 0 ? { mode } : {},
3485
+ ...value !== void 0 ? { value } : {},
3486
+ options: buildEngineCallOptions({ actor, grants, clock, clientForGdr })
3487
+ })).ranOps;
3488
+ }
3489
+ function buildEngineCallOptions(args) {
3490
+ return {
3491
+ actor: args.actor,
3492
+ ...args.grants !== void 0 ? { grants: args.grants } : {},
3493
+ ...args.clock !== void 0 ? { clock: args.clock } : {},
3494
+ ...args.clientForGdr !== void 0 ? { clientForGdr: args.clientForGdr } : {}
3179
3495
  };
3496
+ }
3497
+ async function applyAction(args) {
3498
+ const { client, instance, task, action, params, actor, grants, clock, clientForGdr } = args, options = buildEngineCallOptions({ actor, grants, clock, clientForGdr });
3180
3499
  return findOpenStageEntry(instance)?.tasks.find((t) => t.name === task)?.status === "pending" && await invokeTask({ client, instanceId: instance._id, task, options }), (await fireAction({
3181
3500
  client,
3182
3501
  instanceId: instance._id,
@@ -3331,7 +3650,7 @@ const workflow = {
3331
3650
  workflowResource,
3332
3651
  definition: definitionName,
3333
3652
  version,
3334
- initialState,
3653
+ initialFields,
3335
3654
  ancestors,
3336
3655
  effectsContext,
3337
3656
  instanceId,
@@ -3341,9 +3660,9 @@ const workflow = {
3341
3660
  throw new Error(
3342
3661
  `Initial stage "${definition.initialStage}" missing in ${definitionName} v${definition.version}`
3343
3662
  );
3344
- const now = clock(), effectsContextEntries = effectsContext ? toEffectsContextEntries(effectsContext) : [], resolvedState = await resolveDeclaredState({
3345
- entryDefs: definition.state ?? [],
3346
- initialState: initialState ?? [],
3663
+ const now = clock(), effectsContextEntries = effectsContext ? toEffectsContextEntries(effectsContext) : [], resolvedFields = await resolveDeclaredFields({
3664
+ entryDefs: definition.fields ?? [],
3665
+ initialFields: initialFields ?? [],
3347
3666
  ctx: {
3348
3667
  client,
3349
3668
  now,
@@ -3354,7 +3673,7 @@ const workflow = {
3354
3673
  ...perspective !== void 0 ? { perspective } : {}
3355
3674
  },
3356
3675
  randomKey
3357
- }), effectivePerspective = perspective ?? derivePerspectiveFromState(resolvedState), base = buildInstanceBase({
3676
+ }), effectivePerspective = perspective ?? derivePerspectiveFromFields(resolvedFields), base = buildInstanceBase({
3358
3677
  id,
3359
3678
  now,
3360
3679
  tag,
@@ -3362,7 +3681,7 @@ const workflow = {
3362
3681
  definitionName: definition.name,
3363
3682
  pinnedVersion: definition.version,
3364
3683
  definition,
3365
- state: resolvedState,
3684
+ fields: resolvedFields,
3366
3685
  effectsContext: effectsContextEntries,
3367
3686
  ancestors: ancestors ?? [],
3368
3687
  perspective: effectivePerspective,
@@ -3392,34 +3711,68 @@ const workflow = {
3392
3711
  idempotent,
3393
3712
  resourceClients
3394
3713
  } = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload(client, instanceId, tag);
3395
- if (findOpenStageEntry(before)?.tasks.find((t) => t.name === task) === void 0 && idempotent === !0)
3396
- return { instance: before, cascaded: 0, fired: !1 };
3397
- const evaluation = await evaluateInstance({
3714
+ return findOpenStageEntry(before)?.tasks.find((t) => t.name === task) === void 0 && idempotent === !0 ? { instance: before, cascaded: 0, fired: !1 } : dispatchGatedWrite({
3398
3715
  client,
3399
3716
  tag,
3400
3717
  instanceId,
3401
3718
  access,
3719
+ actor,
3720
+ clientForGdr,
3402
3721
  clock,
3403
- ...resourceClients !== void 0 ? { resourceClients } : {}
3722
+ before,
3723
+ ...resourceClients !== void 0 ? { resourceClients } : {},
3724
+ apply: (instance, evaluation) => (assertActionAllowed(evaluation, task, action), applyAction({
3725
+ client,
3726
+ instance,
3727
+ task,
3728
+ action,
3729
+ actor,
3730
+ ...access.grants !== void 0 ? { grants: access.grants } : {},
3731
+ clock,
3732
+ clientForGdr,
3733
+ ...params !== void 0 ? { params } : {}
3734
+ }))
3404
3735
  });
3405
- assertActionAllowed(evaluation, task, action);
3406
- const ranOps = await applyAction({
3736
+ },
3737
+ /**
3738
+ * Edit a declared-editable field slot directly — reassign, reschedule,
3739
+ * claim-by-hand, append to a running log — through the generic edit seam,
3740
+ * instead of a bespoke action per field. Soft-gates on the slot's declared
3741
+ * editability (the same projection a UI renders), applies the edit as a
3742
+ * `field.*` op (so provenance + history are stamped by the op path),
3743
+ * refreshes the stage's guards, then cascades — an edit to a value a
3744
+ * transition reads can and should move the instance. Advisory like every
3745
+ * engine gate; the lake ACL + guards are the only hard locks.
3746
+ *
3747
+ * Each call is a discrete COMMIT (a history entry, a guard refresh, a
3748
+ * cascade, an `ifRevisionId` write), not a draft patch — so an inline-field
3749
+ * UI must bind it to a deliberate boundary (blur / Enter / Save / debounce),
3750
+ * never an `onChange` per keystroke.
3751
+ */
3752
+ editField: async (args) => {
3753
+ const { client, tag, instanceId, target, mode, value, resourceClients } = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload(client, instanceId, tag);
3754
+ return dispatchGatedWrite({
3407
3755
  client,
3408
- instance: before,
3409
- task,
3410
- action,
3756
+ tag,
3757
+ instanceId,
3758
+ access,
3411
3759
  actor,
3412
- ...access.grants !== void 0 ? { grants: access.grants } : {},
3413
- clock,
3414
3760
  clientForGdr,
3415
- ...params !== void 0 ? { params } : {}
3416
- }), cascaded = await cascade(client, instanceId, actor, clientForGdr, clock);
3417
- return {
3418
- instance: await reload(client, instanceId, tag),
3419
- cascaded,
3420
- fired: !0,
3421
- ...ranOps !== void 0 ? { ranOps } : {}
3422
- };
3761
+ clock,
3762
+ before,
3763
+ ...resourceClients !== void 0 ? { resourceClients } : {},
3764
+ apply: (instance, evaluation) => (assertEditAllowed(evaluation, target), applyEdit({
3765
+ client,
3766
+ instance,
3767
+ target,
3768
+ ...mode !== void 0 ? { mode } : {},
3769
+ ...value !== void 0 ? { value } : {},
3770
+ actor,
3771
+ ...access.grants !== void 0 ? { grants: access.grants } : {},
3772
+ clock,
3773
+ clientForGdr
3774
+ }))
3775
+ });
3423
3776
  },
3424
3777
  /**
3425
3778
  * Report a queued effect's outcome. Drains it from `pendingEffects`,
@@ -3561,7 +3914,7 @@ const workflow = {
3561
3914
  * Hydrates the instance's snapshot (instance + ancestors + every doc
3562
3915
  * declared by a `doc.ref` / `doc.refs` entry in scope), then
3563
3916
  * evaluates the supplied GROQ in groq-js against that dataset. The
3564
- * rendered scope is auto-bound: `$self`, `$state`, `$parent`,
3917
+ * rendered scope is auto-bound: `$self`, `$fields`, `$parent`,
3565
3918
  * `$ancestors`, `$stage`, `$now`, `$effects`, `$tasks`,
3566
3919
  * `$allTasksDone`, `$anyTaskFailed` — ids in GDR URI form to match
3567
3920
  * the snapshot's keying.
@@ -3837,6 +4190,9 @@ function createInstanceSession(args) {
3837
4190
  ...clock !== void 0 ? { now: clock() } : {},
3838
4191
  ...grants !== void 0 ? { grants } : {}
3839
4192
  });
4193
+ }, settleAfterApply = async (actor, held, ranOps) => {
4194
+ const cascaded = await cascade(client, instance._id, actor, clientForGdr, clock, held);
4195
+ return instance = await reload(client, instance._id, tag), { instance, cascaded, fired: !0, ...ranOps !== void 0 ? { ranOps } : {} };
3840
4196
  }, commit = async (run) => {
3841
4197
  committing = !0;
3842
4198
  const held = new Map(overlay);
@@ -3887,8 +4243,25 @@ function createInstanceSession(args) {
3887
4243
  ...grants !== void 0 ? { grants } : {},
3888
4244
  ...clock !== void 0 ? { clock } : {},
3889
4245
  ...params !== void 0 ? { params } : {}
3890
- }), cascaded = await cascade(client, instance._id, actor, clientForGdr, clock, held);
3891
- return instance = await reload(client, instance._id, tag), { instance, cascaded, fired: !0, ...ranOps !== void 0 ? { ranOps } : {} };
4246
+ });
4247
+ return settleAfterApply(actor, held, ranOps);
4248
+ });
4249
+ },
4250
+ editField({ target, mode, value }) {
4251
+ return commit(async (actor, held) => {
4252
+ assertEditAllowed(await evaluateWith(held, heldGuards), target);
4253
+ const { grants } = await access(), ranOps = await applyEdit({
4254
+ client,
4255
+ instance,
4256
+ target,
4257
+ ...mode !== void 0 ? { mode } : {},
4258
+ ...value !== void 0 ? { value } : {},
4259
+ actor,
4260
+ clientForGdr,
4261
+ ...grants !== void 0 ? { grants } : {},
4262
+ ...clock !== void 0 ? { clock } : {}
4263
+ });
4264
+ return settleAfterApply(actor, held, ranOps);
3892
4265
  });
3893
4266
  }
3894
4267
  };
@@ -3936,6 +4309,7 @@ function createEngine(args) {
3936
4309
  deployDefinitions: (rest) => workflow.deployDefinitions(bind(rest)),
3937
4310
  startInstance: (rest) => workflow.startInstance(bind(rest)),
3938
4311
  fireAction: (rest) => workflow.fireAction(bind(rest)),
4312
+ editField: (rest) => workflow.editField(bind(rest)),
3939
4313
  completeEffect: (rest) => workflow.completeEffect(bind(rest)),
3940
4314
  tick: (rest) => workflow.tick(bind(rest)),
3941
4315
  evaluateInstance: (rest) => evaluateInstance(bind(rest)),
@@ -4094,9 +4468,9 @@ const HISTORY_DISPLAY = {
4094
4468
  },
4095
4469
  opApplied: {
4096
4470
  title: "Op applied",
4097
- description: "An inline state-mutation op (state.set / state.append / status.set / etc.) ran during an action commit."
4471
+ description: "An inline field-mutation op (field.set / field.append / status.set / etc.) ran during an action commit."
4098
4472
  }
4099
- }, STATE_SLOT_DISPLAY = {
4473
+ }, FIELD_SLOT_DISPLAY = {
4100
4474
  "doc.ref": {
4101
4475
  title: "Document reference",
4102
4476
  description: "Single GDR pointer at a document in this or another resource."
@@ -4150,23 +4524,23 @@ const HISTORY_DISPLAY = {
4150
4524
  description: "Ordered list of typed (user|role) assignees."
4151
4525
  }
4152
4526
  }, OP_DISPLAY = {
4153
- "state.set": {
4154
- title: "Set state entry",
4155
- description: "Overwrite a state entry's value with a resolved Source."
4527
+ "field.set": {
4528
+ title: "Set field entry",
4529
+ description: "Overwrite a field entry's value with a resolved Source."
4156
4530
  },
4157
- "state.unset": {
4158
- title: "Unset state entry",
4159
- description: "Reset a state entry to its default (null for scalars, [] for array kinds)."
4531
+ "field.unset": {
4532
+ title: "Unset field entry",
4533
+ description: "Reset a field entry to its default (null for scalars, [] for array kinds)."
4160
4534
  },
4161
- "state.append": {
4162
- title: "Append to state entry",
4535
+ "field.append": {
4536
+ title: "Append to field entry",
4163
4537
  description: "Push a resolved item onto an array-kind entry (notes, checklist, assignees, refs)."
4164
4538
  },
4165
- "state.updateWhere": {
4539
+ "field.updateWhere": {
4166
4540
  title: "Update matching rows",
4167
4541
  description: "Merge fields into rows of an array-kind entry that match the predicate."
4168
4542
  },
4169
- "state.removeWhere": {
4543
+ "field.removeWhere": {
4170
4544
  title: "Remove matching rows",
4171
4545
  description: "Drop rows of an array-kind entry that match the predicate."
4172
4546
  },
@@ -4210,11 +4584,11 @@ const HISTORY_DISPLAY = {
4210
4584
  },
4211
4585
  [WORKFLOW_INSTANCE_TYPE]: {
4212
4586
  title: "Workflow instance",
4213
- description: "A running (or finished) workflow against its declared state."
4587
+ description: "A running (or finished) workflow against its declared fields."
4214
4588
  }
4215
4589
  }, DISPLAY = {
4216
4590
  ...HISTORY_DISPLAY,
4217
- ...STATE_SLOT_DISPLAY,
4591
+ ...FIELD_SLOT_DISPLAY,
4218
4592
  ...OP_DISPLAY,
4219
4593
  ...EFFECTS_CONTEXT_DISPLAY,
4220
4594
  ...AUTHORING_DISPLAY
@@ -4232,10 +4606,13 @@ exports.AUTHORING_DISPLAY = AUTHORING_DISPLAY;
4232
4606
  exports.ActionDisabledError = ActionDisabledError;
4233
4607
  exports.ActionParamsInvalidError = ActionParamsInvalidError;
4234
4608
  exports.CascadeLimitError = CascadeLimitError;
4609
+ exports.ConcurrentEditFieldError = ConcurrentEditFieldError;
4235
4610
  exports.ConcurrentFireActionError = ConcurrentFireActionError;
4236
4611
  exports.DEFAULT_CONTENT_PERSPECTIVE = DEFAULT_CONTENT_PERSPECTIVE;
4237
4612
  exports.DISPLAY = DISPLAY;
4238
4613
  exports.EFFECTS_CONTEXT_DISPLAY = EFFECTS_CONTEXT_DISPLAY;
4614
+ exports.EditFieldDeniedError = EditFieldDeniedError;
4615
+ exports.FIELD_SLOT_DISPLAY = FIELD_SLOT_DISPLAY;
4239
4616
  exports.GUARD_DOC_TYPE = GUARD_DOC_TYPE;
4240
4617
  exports.GUARD_LIFTED_PREDICATE = GUARD_LIFTED_PREDICATE;
4241
4618
  exports.GUARD_OWNER = GUARD_OWNER;
@@ -4243,8 +4620,7 @@ exports.HISTORY_DISPLAY = HISTORY_DISPLAY;
4243
4620
  exports.MutationGuardDeniedError = MutationGuardDeniedError;
4244
4621
  exports.OP_DISPLAY = OP_DISPLAY;
4245
4622
  exports.PartialGuardDeployError = PartialGuardDeployError;
4246
- exports.RequiredStateNotProvidedError = RequiredStateNotProvidedError;
4247
- exports.STATE_SLOT_DISPLAY = STATE_SLOT_DISPLAY;
4623
+ exports.RequiredFieldNotProvidedError = RequiredFieldNotProvidedError;
4248
4624
  exports.WORKFLOW_INSTANCE_TYPE = WORKFLOW_INSTANCE_TYPE;
4249
4625
  exports.WorkflowStateDivergedError = WorkflowStateDivergedError;
4250
4626
  exports.abortReason = abortReason;