@mesh-tech/mesh-cli 0.17.0 → 0.18.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/bin/mesh.js CHANGED
@@ -21134,7 +21134,9 @@ function computeHappyPath(nodes, edges) {
21134
21134
  );
21135
21135
  const allEndIds = new Set(endNodes.map((n) => n.id));
21136
21136
  function bfs(targetIds, allowExceptional) {
21137
- const queue = [{ id: startNode.id, path: [startNode.id] }];
21137
+ const queue = [
21138
+ { id: startNode.id, path: [startNode.id] }
21139
+ ];
21138
21140
  const visited = /* @__PURE__ */ new Set([startNode.id]);
21139
21141
  while (queue.length > 0) {
21140
21142
  const { id, path: path44 } = queue.shift();
@@ -21155,7 +21157,8 @@ function computeHappyPath(nodes, edges) {
21155
21157
  });
21156
21158
  for (const neighbor of sorted) {
21157
21159
  if (visited.has(neighbor.to)) continue;
21158
- if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to))) continue;
21160
+ if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to)))
21161
+ continue;
21159
21162
  visited.add(neighbor.to);
21160
21163
  queue.push({ id: neighbor.to, path: [...path44, neighbor.to] });
21161
21164
  }
@@ -21164,6 +21167,37 @@ function computeHappyPath(nodes, edges) {
21164
21167
  }
21165
21168
  return bfs(successEndIds, false) ?? bfs(allEndIds, false) ?? bfs(allEndIds, true) ?? [];
21166
21169
  }
21170
+ function isProcessArtifactProvenance(value) {
21171
+ return typeof value === "object" && value !== null && value.source === "process-artifact";
21172
+ }
21173
+ function childGraphsOf(node) {
21174
+ const graphs = [];
21175
+ const withChild = node;
21176
+ if (withChild.childGraph) graphs.push(withChild.childGraph);
21177
+ const withBacking = node;
21178
+ if (withBacking.backing?.backingGraph) graphs.push(withBacking.backing.backingGraph);
21179
+ return graphs;
21180
+ }
21181
+ function walkProcessNodes(ir, scope = []) {
21182
+ const out = [];
21183
+ for (const node of ir.nodes) {
21184
+ if (isProcessArtifactProvenance(node.provenance)) {
21185
+ out.push({ nodeId: node.id, scope, node, provenance: node.provenance });
21186
+ }
21187
+ for (const child of childGraphsOf(node)) {
21188
+ out.push(...walkProcessNodes(child, [...scope, node.id]));
21189
+ }
21190
+ }
21191
+ return out;
21192
+ }
21193
+ function findProcessNodesByCommand(ir, command) {
21194
+ return walkProcessNodes(ir).filter((m) => m.provenance.bind?.commands?.includes(command));
21195
+ }
21196
+ function findProcessNodesByElementId(ir, elementId, element) {
21197
+ return walkProcessNodes(ir).filter(
21198
+ (m) => m.provenance.elementId === elementId && (element === void 0 || m.provenance.element === element)
21199
+ );
21200
+ }
21167
21201
  function cloneWorkflow(workflow) {
21168
21202
  return JSON.parse(JSON.stringify(workflow));
21169
21203
  }
@@ -21215,13 +21249,17 @@ function sanitizeMainPath(workflow, mainPath) {
21215
21249
  }
21216
21250
  function getChildGraph(node) {
21217
21251
  if ("childGraph" in node && node.childGraph) return node.childGraph;
21218
- if ("backing" in node && node.backing?.backingGraph) return node.backing.backingGraph;
21252
+ if ("backing" in node && node.backing?.backingGraph)
21253
+ return node.backing.backingGraph;
21219
21254
  return null;
21220
21255
  }
21221
21256
  function setChildGraph(node, childGraph) {
21222
21257
  if ("childGraph" in node) return { ...node, childGraph };
21223
21258
  if ("backing" in node && node.backing?.backingGraph) {
21224
- return { ...node, backing: { ...node.backing, backingGraph: childGraph } };
21259
+ return {
21260
+ ...node,
21261
+ backing: { ...node.backing, backingGraph: childGraph }
21262
+ };
21225
21263
  }
21226
21264
  return { ...node, childGraph };
21227
21265
  }
@@ -21378,7 +21416,9 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
21378
21416
  let mainBody = inParentMain.length ? inParentMain : nodeIds;
21379
21417
  let mainTail = [];
21380
21418
  if (successExit) {
21381
- const exitSources = new Set(exitChildEdges.filter((e) => e.to === successExit.id).map((e) => e.from));
21419
+ const exitSources = new Set(
21420
+ exitChildEdges.filter((e) => e.to === successExit.id).map((e) => e.from)
21421
+ );
21382
21422
  const adj = /* @__PURE__ */ new Map();
21383
21423
  for (const e of internal) {
21384
21424
  if (e.isExceptional || e.isTimeout) continue;
@@ -21417,7 +21457,11 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
21417
21457
  childGraph = {
21418
21458
  name: label,
21419
21459
  description: "",
21420
- nodes: [{ id: startId, type: "start", label: "Start" }, ...groupedNodes, ...extraChildNodes],
21460
+ nodes: [
21461
+ { id: startId, type: "start", label: "Start" },
21462
+ ...groupedNodes,
21463
+ ...extraChildNodes
21464
+ ],
21421
21465
  edges: [
21422
21466
  { from: startId, to: entry, isMainPath: true },
21423
21467
  ...internal.map(markMain),
@@ -21426,7 +21470,13 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
21426
21470
  mainPath: [startId, ...mainBody, ...mainTail]
21427
21471
  };
21428
21472
  }
21429
- const groupNode = { id: groupId, type: "group", groupType, label, childGraph };
21473
+ const groupNode = {
21474
+ id: groupId,
21475
+ type: "group",
21476
+ groupType,
21477
+ label,
21478
+ childGraph
21479
+ };
21430
21480
  const newNodes = [];
21431
21481
  let placed = false;
21432
21482
  for (const n of workflow.nodes) {
@@ -21472,7 +21522,10 @@ function applyWorkflowPatch(workflow, patch) {
21472
21522
  return applyScopedPatch(workflow, patch.scope, patch);
21473
21523
  }
21474
21524
  if (patch.op === "batch") {
21475
- return patch.patches.reduce((current, operation) => applyWorkflowPatch(current, operation), workflow);
21525
+ return patch.patches.reduce(
21526
+ (current, operation) => applyWorkflowPatch(current, operation),
21527
+ workflow
21528
+ );
21476
21529
  }
21477
21530
  if (patch.op === "replace") {
21478
21531
  return cloneWorkflow(patch.workflow);
@@ -21506,7 +21559,9 @@ function applyWorkflowPatch(workflow, patch) {
21506
21559
  next.mainPath = next.mainPath.filter((nodeId) => nodeId !== patch.nodeId);
21507
21560
  const shouldRemoveEdges = patch.removeAttachedEdges ?? true;
21508
21561
  if (shouldRemoveEdges) {
21509
- next.edges = next.edges.filter((edge) => edge.from !== patch.nodeId && edge.to !== patch.nodeId);
21562
+ next.edges = next.edges.filter(
21563
+ (edge) => edge.from !== patch.nodeId && edge.to !== patch.nodeId
21564
+ );
21510
21565
  }
21511
21566
  return next;
21512
21567
  }
@@ -21554,7 +21609,13 @@ function applyWorkflowPatch(workflow, patch) {
21554
21609
  return next;
21555
21610
  }
21556
21611
  case "group": {
21557
- return wrapIntoGroup(next, patch.nodeIds, patch.groupId, patch.label, patch.groupType ?? "function");
21612
+ return wrapIntoGroup(
21613
+ next,
21614
+ patch.nodeIds,
21615
+ patch.groupId,
21616
+ patch.label,
21617
+ patch.groupType ?? "function"
21618
+ );
21558
21619
  }
21559
21620
  case "setMainPath": {
21560
21621
  next.mainPath = sanitizeMainPath(next, patch.mainPath);
@@ -21852,6 +21913,78 @@ var init_change_counts = __esm({
21852
21913
 
21853
21914
  // libs/workflow-model/src/process-artifact.ts
21854
21915
  import { z as z4 } from "zod";
21916
+ function outcomeTriggers(outcome) {
21917
+ const raw = Array.isArray(outcome.when) ? outcome.when : [outcome.when];
21918
+ return raw.map((trigger) => {
21919
+ if ("predicate" in trigger) return { kind: "predicate", name: trigger.predicate };
21920
+ if ("timeoutOf" in trigger) return { kind: "timeout", name: trigger.timeoutOf };
21921
+ return { kind: "command", name: trigger.command };
21922
+ });
21923
+ }
21924
+ function resolveSubstepActor(artifact, stage, substep) {
21925
+ const actorId = substep.actor ?? stage.actor;
21926
+ if (!actorId) return void 0;
21927
+ const actor = artifact.process.actors[actorId];
21928
+ if (!actor) return void 0;
21929
+ return {
21930
+ id: actorId,
21931
+ label: actor.label,
21932
+ ...actor.category ? { category: actor.category } : {},
21933
+ ...substep.selector ?? actor.selector ? { selector: substep.selector ?? actor.selector } : {},
21934
+ ...actor.cardinality ? { cardinality: actor.cardinality } : {}
21935
+ };
21936
+ }
21937
+ function resolveActorRef(artifact, actorId) {
21938
+ const actor = artifact.process.actors[actorId];
21939
+ if (!actor) return void 0;
21940
+ return {
21941
+ id: actorId,
21942
+ label: actor.label,
21943
+ ...actor.category ? { category: actor.category } : {},
21944
+ ...actor.selector ? { selector: actor.selector } : {},
21945
+ ...actor.cardinality ? { cardinality: actor.cardinality } : {}
21946
+ };
21947
+ }
21948
+ function buildProcessManifest(artifact, outcomeNodeIds) {
21949
+ const { process: process2 } = artifact;
21950
+ const actors = {};
21951
+ for (const actorId of Object.keys(process2.actors)) {
21952
+ actors[actorId] = resolveActorRef(artifact, actorId);
21953
+ }
21954
+ let selectors;
21955
+ if (process2.selectors) {
21956
+ selectors = {};
21957
+ for (const [name, selector] of Object.entries(process2.selectors)) {
21958
+ selectors[name] = {
21959
+ name,
21960
+ label: selector.label,
21961
+ ...selector.description ? { description: selector.description } : {}
21962
+ };
21963
+ }
21964
+ }
21965
+ const outcomes = process2.outcomes.map((outcome) => {
21966
+ const nodeId = outcomeNodeIds?.get(outcome.id);
21967
+ return {
21968
+ id: outcome.id,
21969
+ label: outcome.label,
21970
+ kind: outcome.kind,
21971
+ triggers: outcomeTriggers(outcome),
21972
+ ...outcome.description ? { description: outcome.description } : {},
21973
+ ...nodeId ? { nodeId } : {}
21974
+ };
21975
+ });
21976
+ const pointOfNoReturnStage = process2.stages.find((stage) => stage.pointOfNoReturn);
21977
+ return {
21978
+ source: "process-artifact",
21979
+ artifactVersion: process2.version,
21980
+ revision: process2.revision,
21981
+ workflowType: process2.workflowType,
21982
+ actors,
21983
+ ...selectors ? { selectors } : {},
21984
+ outcomes,
21985
+ ...pointOfNoReturnStage ? { pointOfNoReturnStageId: pointOfNoReturnStage.id } : {}
21986
+ };
21987
+ }
21855
21988
  function formatPath(path44) {
21856
21989
  return path44.length > 0 ? z4.core.toDotPath(path44) : "(root)";
21857
21990
  }
@@ -21871,19 +22004,74 @@ function issueToDiagnostic(issue) {
21871
22004
  path: path44
21872
22005
  };
21873
22006
  }
22007
+ function upgradeV1(artifact) {
22008
+ const diagnostics = [
22009
+ {
22010
+ severity: "warning",
22011
+ code: "DEPRECATED_ARTIFACT_VERSION",
22012
+ message: `process.version: artifact is version 1; upgraded in memory to version ${CURRENT_PROCESS_VERSION}. Migrate the file (add process.revision, declare actor categories/selectors) \u2014 version 1 is a deprecated input, not a second authoring format.`,
22013
+ path: "process.version"
22014
+ },
22015
+ {
22016
+ severity: "warning",
22017
+ code: "MISSING_PROCESS_REVISION",
22018
+ message: `process.revision: version 1 has no revision; recorded as "${UNVERSIONED_REVISION}". Anything pinned to this artifact cannot name which version of the process it ran.`,
22019
+ path: "process.revision"
22020
+ }
22021
+ ];
22022
+ const process2 = artifact.process;
22023
+ const actors = {};
22024
+ for (const [actorId, actor] of Object.entries(process2.actors)) {
22025
+ const legacyCategory = LEGACY_V1_ACTOR_CATEGORY[actorId];
22026
+ actors[actorId] = {
22027
+ ...actor,
22028
+ ...actor.category === void 0 && legacyCategory ? { category: legacyCategory } : {}
22029
+ };
22030
+ }
22031
+ return {
22032
+ artifact: {
22033
+ ...artifact,
22034
+ process: {
22035
+ ...process2,
22036
+ version: CURRENT_PROCESS_VERSION,
22037
+ revision: UNVERSIONED_REVISION,
22038
+ actors
22039
+ }
22040
+ },
22041
+ diagnostics
22042
+ };
22043
+ }
21874
22044
  function parseProcessArtifact(json) {
21875
22045
  const result = processArtifactSchema.safeParse(json);
21876
- if (result.success) {
21877
- return { artifact: result.data, diagnostics: [] };
22046
+ if (!result.success) {
22047
+ return { diagnostics: result.error.issues.map(issueToDiagnostic) };
21878
22048
  }
21879
- return { diagnostics: result.error.issues.map(issueToDiagnostic) };
22049
+ if (result.data.process.version < CURRENT_PROCESS_VERSION) {
22050
+ return upgradeV1(result.data);
22051
+ }
22052
+ return { artifact: result.data, diagnostics: [] };
21880
22053
  }
21881
- var actorSchema, substepBindSchema, substepSchema, outcomeWhenSchema, outcomeSchema, stageCompleteSchema, stageSchema, processBlockSchema, processArtifactSchema;
22054
+ var CURRENT_PROCESS_VERSION, SUPPORTED_PROCESS_VERSIONS, UNVERSIONED_REVISION, LEGACY_V1_ACTOR_CATEGORY, actorSchema, selectorSchema, substepBindSchema, substepSchema, outcomeTriggerSchema, outcomeWhenSchema, outcomeSchema, stageCompleteSchema, stageSchema, processBlockSchema, processArtifactSchema;
21882
22055
  var init_process_artifact = __esm({
21883
22056
  "libs/workflow-model/src/process-artifact.ts"() {
21884
22057
  "use strict";
22058
+ CURRENT_PROCESS_VERSION = 2;
22059
+ SUPPORTED_PROCESS_VERSIONS = [1, 2];
22060
+ UNVERSIONED_REVISION = "unversioned";
22061
+ LEGACY_V1_ACTOR_CATEGORY = {
22062
+ customer: "human",
22063
+ banker: "approval",
22064
+ system: "data"
22065
+ };
21885
22066
  actorSchema = z4.object({
21886
- label: z4.string()
22067
+ label: z4.string(),
22068
+ category: z4.string().min(1).optional(),
22069
+ selector: z4.string().min(1).optional(),
22070
+ cardinality: z4.enum(["one", "many"]).optional()
22071
+ }).strict();
22072
+ selectorSchema = z4.object({
22073
+ label: z4.string(),
22074
+ description: z4.string().optional()
21887
22075
  }).strict();
21888
22076
  substepBindSchema = z4.object({
21889
22077
  commands: z4.array(z4.string()).optional(),
@@ -21901,11 +22089,30 @@ var init_process_artifact = __esm({
21901
22089
  id: z4.string(),
21902
22090
  label: z4.string(),
21903
22091
  actor: z4.string().optional(),
22092
+ /**
22093
+ * Overrides the actor's own `selector` for this substep only — the case
22094
+ * where one cast entry is resolved differently at one step (a handoff
22095
+ * target, an escalation pool). Must be declared in `process.selectors`.
22096
+ */
22097
+ selector: z4.string().min(1).optional(),
21904
22098
  bind: substepBindSchema
21905
22099
  }).strict();
22100
+ outcomeTriggerSchema = z4.union([
22101
+ z4.object({ predicate: z4.string().min(1) }).strict(),
22102
+ z4.object({ timeoutOf: z4.string().min(1) }).strict(),
22103
+ z4.object({ command: z4.string().min(1) }).strict()
22104
+ ]);
21906
22105
  outcomeWhenSchema = z4.union([
21907
- z4.object({ predicate: z4.string() }).strict(),
21908
- z4.object({ timeoutOf: z4.string() }).strict()
22106
+ outcomeTriggerSchema,
22107
+ z4.array(outcomeTriggerSchema).superRefine((triggers, ctx) => {
22108
+ if (triggers.length === 0) {
22109
+ ctx.addIssue({
22110
+ code: "custom",
22111
+ message: "when must declare at least one trigger",
22112
+ params: { code: "EMPTY_OUTCOME_TRIGGER" }
22113
+ });
22114
+ }
22115
+ })
21909
22116
  ]);
21910
22117
  outcomeSchema = z4.object({
21911
22118
  id: z4.string(),
@@ -21928,20 +22135,64 @@ var init_process_artifact = __esm({
21928
22135
  }).strict();
21929
22136
  processBlockSchema = z4.object({
21930
22137
  version: z4.number(),
22138
+ /**
22139
+ * Pinned identity of this authored content. Opaque to the platform —
22140
+ * a date, a semver, a content hash, a monotonic counter all work. What
22141
+ * matters is that changing the process changes it, so an execution can
22142
+ * be pinned to the revision it started under. Required from V2; a V1
22143
+ * artifact is upgraded with {@link UNVERSIONED_REVISION} and a warning.
22144
+ */
22145
+ revision: z4.string().min(1).optional(),
21931
22146
  workflowType: z4.string(),
21932
22147
  actors: z4.record(z4.string(), actorSchema),
22148
+ /** Named selector registry — every `selector` reference resolves here. */
22149
+ selectors: z4.record(z4.string(), selectorSchema).optional(),
21933
22150
  stages: z4.array(stageSchema),
21934
22151
  outcomes: z4.array(outcomeSchema),
21935
22152
  internalCommands: z4.array(z4.string()).optional()
21936
22153
  }).strict().superRefine((process2, ctx) => {
21937
- if (process2.version !== 1) {
22154
+ if (!SUPPORTED_PROCESS_VERSIONS.includes(process2.version)) {
21938
22155
  ctx.addIssue({
21939
22156
  code: "custom",
21940
22157
  path: ["version"],
21941
- message: `Unsupported process.version ${JSON.stringify(process2.version)}; this parser only understands version 1. Upgrade the parser or downgrade the artifact.`,
22158
+ message: `Unsupported process.version ${JSON.stringify(process2.version)}; this parser understands ${SUPPORTED_PROCESS_VERSIONS.join(" and ")}. Upgrade the parser or downgrade the artifact.`,
21942
22159
  params: { code: "UNSUPPORTED_VERSION" }
21943
22160
  });
21944
22161
  }
22162
+ if (process2.version >= CURRENT_PROCESS_VERSION && process2.revision === void 0) {
22163
+ ctx.addIssue({
22164
+ code: "custom",
22165
+ path: ["revision"],
22166
+ message: `process.revision is required from version ${CURRENT_PROCESS_VERSION}. Give this authored content a pinned identity (a date, a semver, or a content hash) so an execution can name the revision it is running.`,
22167
+ params: { code: "MISSING_PROCESS_REVISION" }
22168
+ });
22169
+ }
22170
+ const declaredSelectors = new Set(Object.keys(process2.selectors ?? {}));
22171
+ const checkActor = (actorId, path44) => {
22172
+ if (actorId === void 0) return;
22173
+ if (!Object.prototype.hasOwnProperty.call(process2.actors, actorId)) {
22174
+ ctx.addIssue({
22175
+ code: "custom",
22176
+ path: path44,
22177
+ message: `Actor "${actorId}" is referenced at ${z4.core.toDotPath(path44)} but not declared in process.actors. Declare the actor (label, and optionally category/selector) or fix the reference.`,
22178
+ params: { code: "UNDECLARED_ACTOR" }
22179
+ });
22180
+ }
22181
+ };
22182
+ const checkSelector = (selector, path44) => {
22183
+ if (selector === void 0) return;
22184
+ if (!declaredSelectors.has(selector)) {
22185
+ ctx.addIssue({
22186
+ code: "custom",
22187
+ path: path44,
22188
+ message: `Selector "${selector}" is referenced at ${z4.core.toDotPath(path44)} but not declared in process.selectors. Declare the selector or fix the reference.`,
22189
+ params: { code: "UNDECLARED_SELECTOR" }
22190
+ });
22191
+ }
22192
+ };
22193
+ for (const [actorId, actor] of Object.entries(process2.actors)) {
22194
+ checkSelector(actor.selector, ["actors", actorId, "selector"]);
22195
+ }
21945
22196
  const SAME_KIND_CODE = {
21946
22197
  stage: "DUPLICATE_STAGE_ID",
21947
22198
  substep: "DUPLICATE_SUBSTEP_ID",
@@ -21966,8 +22217,17 @@ var init_process_artifact = __esm({
21966
22217
  };
21967
22218
  process2.stages.forEach((stage, stageIndex) => {
21968
22219
  checkId(stage.id, "stage", ["stages", stageIndex, "id"]);
22220
+ checkActor(stage.actor, ["stages", stageIndex, "actor"]);
21969
22221
  stage.substeps?.forEach((substep, substepIndex) => {
21970
22222
  checkId(substep.id, "substep", ["stages", stageIndex, "substeps", substepIndex, "id"]);
22223
+ checkActor(substep.actor, ["stages", stageIndex, "substeps", substepIndex, "actor"]);
22224
+ checkSelector(substep.selector, [
22225
+ "stages",
22226
+ stageIndex,
22227
+ "substeps",
22228
+ substepIndex,
22229
+ "selector"
22230
+ ]);
21971
22231
  });
21972
22232
  });
21973
22233
  process2.outcomes.forEach((outcome, outcomeIndex) => {
@@ -21990,7 +22250,7 @@ function stagePath(stageIndex) {
21990
22250
  function substepPath(stageIndex, substepIndex) {
21991
22251
  return `${stagePath(stageIndex)}.substeps[${substepIndex}]`;
21992
22252
  }
21993
- function lintProcess(artifact, inventory, predicateNames) {
22253
+ function lintProcess(artifact, inventory, predicateNames, options = {}) {
21994
22254
  const diagnostics = [];
21995
22255
  const process2 = artifact.process;
21996
22256
  const commandNames = new Set(inventory.commands.map((c) => c.name));
@@ -22009,6 +22269,19 @@ function lintProcess(artifact, inventory, predicateNames) {
22009
22269
  });
22010
22270
  }
22011
22271
  };
22272
+ if (options.selectorNames) {
22273
+ const selectorSet = new Set(options.selectorNames);
22274
+ for (const [name] of Object.entries(process2.selectors ?? {})) {
22275
+ if (!selectorSet.has(name)) {
22276
+ diagnostics.push({
22277
+ severity: "error",
22278
+ code: "UNKNOWN_SELECTOR",
22279
+ message: `Selector "${name}" declared at process.selectors.${name} is not in the selector registry; the application has no rule resolving it to subjects.`,
22280
+ path: `process.selectors.${name}`
22281
+ });
22282
+ }
22283
+ }
22284
+ }
22012
22285
  process2.stages.forEach((stage, stageIndex) => {
22013
22286
  const predicatePath = `${stagePath(stageIndex)}.complete.predicate`;
22014
22287
  const predicate = stage.complete?.predicate;
@@ -22050,9 +22323,25 @@ function lintProcess(artifact, inventory, predicateNames) {
22050
22323
  });
22051
22324
  });
22052
22325
  process2.outcomes.forEach((outcome, outcomeIndex) => {
22053
- if ("predicate" in outcome.when) {
22054
- checkPredicate(outcome.when.predicate, `process.outcomes[${outcomeIndex}].when.predicate`);
22055
- }
22326
+ const whenPath = `process.outcomes[${outcomeIndex}].when`;
22327
+ outcomeTriggers(outcome).forEach((trigger, triggerIndex) => {
22328
+ const path44 = Array.isArray(outcome.when) ? `${whenPath}[${triggerIndex}]` : whenPath;
22329
+ if (trigger.kind === "predicate") {
22330
+ checkPredicate(trigger.name, `${path44}.predicate`);
22331
+ } else if (trigger.kind === "command") {
22332
+ if (!boundCommandPaths.has(trigger.name)) {
22333
+ boundCommandPaths.set(trigger.name, `${path44}.command`);
22334
+ }
22335
+ if (!commandNames.has(trigger.name)) {
22336
+ diagnostics.push({
22337
+ severity: "error",
22338
+ code: "BOUND_COMMAND_NOT_IN_INVENTORY",
22339
+ message: `Command "${trigger.name}" triggering outcome "${outcome.id}" (${path44}.command) does not exist in the inventory.`,
22340
+ path: `${path44}.command`
22341
+ });
22342
+ }
22343
+ }
22344
+ });
22056
22345
  });
22057
22346
  const hasSuccessOutcome = process2.outcomes.some((o) => o.kind === "success");
22058
22347
  const hasFailureOutcome = process2.outcomes.some((o) => o.kind === "failure");
@@ -22097,7 +22386,7 @@ function lintProcess(artifact, inventory, predicateNames) {
22097
22386
  diagnostics.push({
22098
22387
  severity: "error",
22099
22388
  code: "UNBOUND_INVENTORY_COMMAND",
22100
- message: `Inventory command "${command.name}" is neither bound by any substep nor listed in process.internalCommands.`,
22389
+ message: `Inventory command "${command.name}" is neither bound by any substep, nor named as an outcome trigger, nor listed in process.internalCommands.`,
22101
22390
  path: "process.internalCommands"
22102
22391
  });
22103
22392
  }
@@ -22115,26 +22404,37 @@ function lintProcess(artifact, inventory, predicateNames) {
22115
22404
  var init_process_lint = __esm({
22116
22405
  "libs/workflow-model/src/process-lint.ts"() {
22117
22406
  "use strict";
22407
+ init_process_artifact();
22118
22408
  }
22119
22409
  });
22120
22410
 
22121
22411
  // libs/workflow-model/src/index.ts
22122
22412
  var src_exports2 = {};
22123
22413
  __export(src_exports2, {
22414
+ CURRENT_PROCESS_VERSION: () => CURRENT_PROCESS_VERSION,
22415
+ SUPPORTED_PROCESS_VERSIONS: () => SUPPORTED_PROCESS_VERSIONS,
22416
+ UNVERSIONED_REVISION: () => UNVERSIONED_REVISION,
22124
22417
  WorkflowPatchError: () => WorkflowPatchError,
22125
22418
  WorkflowPatchStream: () => WorkflowPatchStream,
22126
22419
  applyPreviewPatch: () => applyPreviewPatch,
22127
22420
  applyWorkflowPatch: () => applyWorkflowPatch,
22128
22421
  applyWorkflowPatches: () => applyWorkflowPatches,
22422
+ buildProcessManifest: () => buildProcessManifest,
22129
22423
  computeHappyPath: () => computeHappyPath,
22130
22424
  countChanges: () => countChanges,
22131
22425
  describeChanges: () => describeChanges,
22132
22426
  emptyWorkflowIR: () => emptyWorkflowIR,
22427
+ findProcessNodesByCommand: () => findProcessNodesByCommand,
22428
+ findProcessNodesByElementId: () => findProcessNodesByElementId,
22133
22429
  lintProcess: () => lintProcess,
22430
+ outcomeTriggers: () => outcomeTriggers,
22134
22431
  parseProcessArtifact: () => parseProcessArtifact,
22135
22432
  processArtifactSchema: () => processArtifactSchema,
22433
+ resolveActorRef: () => resolveActorRef,
22434
+ resolveSubstepActor: () => resolveSubstepActor,
22136
22435
  validateWorkflowIR: () => validateWorkflowIR,
22137
- validateWorkflowPatches: () => validateWorkflowPatches
22436
+ validateWorkflowPatches: () => validateWorkflowPatches,
22437
+ walkProcessNodes: () => walkProcessNodes
22138
22438
  });
22139
22439
  var init_src3 = __esm({
22140
22440
  "libs/workflow-model/src/index.ts"() {
@@ -22358,6 +22658,9 @@ async function uploadToS3(bucket, appName, workflows) {
22358
22658
  })
22359
22659
  );
22360
22660
  }
22661
+ function parseNameList(value) {
22662
+ return value ? value.split(",").map((name) => name.trim()).filter((name) => name.length > 0) : void 0;
22663
+ }
22361
22664
  function registerWorkflowCommands(program2) {
22362
22665
  const workflow = program2.command("workflow").description("Workflow tooling \u2014 IR extraction, visualization");
22363
22666
  workflow.command("extract-ir <path>").description(
@@ -22464,13 +22767,16 @@ function registerWorkflowCommands(program2) {
22464
22767
  }
22465
22768
  });
22466
22769
  workflow.command("lint-process <path>").description(
22467
- "Lint a process artifact's bindings against the as-built code inventory\n\nReuses the inventory extraction's aux/activity discovery, resolves the process\nartifact (--process, or the same sibling process/*.process.json discovery\nextract-ir uses), then runs parseProcessArtifact + lintProcess against it.\n\nWithout --predicates, the CLI cannot verify predicate names against the worker's\nregistry (it has no way to execute it) \u2014 predicate-existence findings are skipped\nand a note is printed; every other rule (bindings, coverage, ids, outcomes,\nworkflowType) still runs. Pass --predicates for full conformance, or rely on the\nworker's own process-conformance test which has the registry in-process."
22770
+ "Lint a process artifact's bindings against the as-built code inventory\n\nReuses the inventory extraction's aux/activity discovery, resolves the process\nartifact (--process, or the same sibling process/*.process.json discovery\nextract-ir uses), then runs parseProcessArtifact + lintProcess against it.\n\nWithout --predicates / --selectors, the CLI cannot verify those names against the\nworker's registries (it has no way to execute them) \u2014 the corresponding findings\nare skipped and a note is printed; every other rule (bindings, coverage, ids,\nactors, outcomes, workflowType) still runs. Pass both for full conformance, or rely\non the worker's own process-conformance test which has the registries in-process."
22468
22771
  ).option(
22469
22772
  "--process <path>",
22470
22773
  "Path to a process artifact JSON file. Absent this flag, a sibling process/*.process.json is auto-discovered per workflow file."
22471
22774
  ).option(
22472
22775
  "--predicates <names>",
22473
22776
  "Comma-separated named-predicate registry (enables UNKNOWN_PREDICATE checks)"
22777
+ ).option(
22778
+ "--selectors <names>",
22779
+ "Comma-separated named-selector registry (enables UNKNOWN_SELECTOR checks)"
22474
22780
  ).action(async (targetPath, opts) => {
22475
22781
  try {
22476
22782
  const resolvedPath = path43.resolve(targetPath);
@@ -22508,12 +22814,16 @@ function registerWorkflowCommands(program2) {
22508
22814
  process.exitCode = 1;
22509
22815
  return;
22510
22816
  }
22511
- const predicateNames = opts.predicates ? opts.predicates.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : void 0;
22817
+ const predicateNames = parseNameList(opts.predicates);
22818
+ const selectorNames = parseNameList(opts.selectors);
22512
22819
  if (!predicateNames) {
22513
22820
  logWarn(
22514
22821
  "predicate checks skipped \u2014 pass --predicates or run the worker conformance test"
22515
22822
  );
22516
22823
  }
22824
+ if (!selectorNames) {
22825
+ logWarn("selector checks skipped \u2014 pass --selectors or run the worker conformance test");
22826
+ }
22517
22827
  logInfo(`Linting process artifact(s) against ${resolvedPath}`);
22518
22828
  const results = runLintExtraction(resolvedPath, extractorPath, resolvedProcessPath);
22519
22829
  if (!results || results.length === 0) {
@@ -22535,7 +22845,12 @@ ${inventory.workflowType} (${sourceFile})`);
22535
22845
  const { artifact, diagnostics: parseDiagnostics } = parseProcessArtifact2(processArtifact);
22536
22846
  let diagnostics = parseDiagnostics;
22537
22847
  if (artifact) {
22538
- const lintDiagnostics = lintProcess2(artifact, inventory, predicateNames ?? []);
22848
+ const lintDiagnostics = lintProcess2(
22849
+ artifact,
22850
+ inventory,
22851
+ predicateNames ?? [],
22852
+ selectorNames ? { selectorNames } : {}
22853
+ );
22539
22854
  const reportable = predicateNames ? lintDiagnostics : lintDiagnostics.filter((d) => d.code !== "UNKNOWN_PREDICATE");
22540
22855
  diagnostics = diagnostics.concat(reportable);
22541
22856
  }