@revisium/orchestrator 0.1.0-alpha.5 → 0.1.0-alpha.7

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.
Files changed (193) hide show
  1. package/README.md +149 -1
  2. package/dist/api/graphql-api/filters/agent-observability-exception.filter.js +24 -0
  3. package/dist/api/graphql-api/filters/agent-observability-exception.filter.js.map +1 -0
  4. package/dist/api/graphql-api/graphql-api.module.js +2 -0
  5. package/dist/api/graphql-api/graphql-api.module.js.map +1 -1
  6. package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-bridge.service.js +296 -0
  7. package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-bridge.service.js.map +1 -0
  8. package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-mappers.js +64 -0
  9. package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-mappers.js.map +1 -0
  10. package/dist/api/graphql-api/graphql-ws/constants.js +2 -0
  11. package/dist/api/graphql-api/graphql-ws/constants.js.map +1 -1
  12. package/dist/api/graphql-api/graphql-ws/pubsub.module.js +5 -2
  13. package/dist/api/graphql-api/graphql-ws/pubsub.module.js.map +1 -1
  14. package/dist/api/graphql-api/registerGraphqlEnums.js +6 -1
  15. package/dist/api/graphql-api/registerGraphqlEnums.js.map +1 -1
  16. package/dist/api/graphql-api/runs/inputs/get-agent-log.input.js +48 -0
  17. package/dist/api/graphql-api/runs/inputs/get-agent-log.input.js.map +1 -0
  18. package/dist/api/graphql-api/runs/model/agent-activity.model.js +367 -0
  19. package/dist/api/graphql-api/runs/model/agent-activity.model.js.map +1 -0
  20. package/dist/api/graphql-api/runs/runs-subscription.resolver.js +35 -4
  21. package/dist/api/graphql-api/runs/runs-subscription.resolver.js.map +1 -1
  22. package/dist/api/graphql-api/runs/runs.resolver.js +35 -0
  23. package/dist/api/graphql-api/runs/runs.resolver.js.map +1 -1
  24. package/dist/cli/commands/doctor-report.js +52 -0
  25. package/dist/cli/commands/doctor-report.js.map +1 -0
  26. package/dist/cli/commands/lifecycle.js +304 -0
  27. package/dist/cli/commands/lifecycle.js.map +1 -0
  28. package/dist/cli/commands/mcp.js +46 -8
  29. package/dist/cli/commands/mcp.js.map +1 -1
  30. package/dist/cli/commands/revisium-helpers.js +2 -2
  31. package/dist/cli/index.js +5 -29
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/cli/program.js +7 -23
  34. package/dist/cli/program.js.map +1 -1
  35. package/dist/config.js +42 -5
  36. package/dist/config.js.map +1 -1
  37. package/dist/control-plane/bootstrap.js +93 -0
  38. package/dist/control-plane/bootstrap.js.map +1 -0
  39. package/dist/control-plane/schema-migration.js +7 -11
  40. package/dist/control-plane/schema-migration.js.map +1 -1
  41. package/dist/control-plane/seed-default-playbook.js +23 -0
  42. package/dist/control-plane/seed-default-playbook.js.map +1 -1
  43. package/dist/e2e/kit/agents.js +17 -5
  44. package/dist/e2e/kit/agents.js.map +1 -1
  45. package/dist/e2e/kit/assertions.js +20 -0
  46. package/dist/e2e/kit/assertions.js.map +1 -1
  47. package/dist/e2e/kit/harness.js +12 -1
  48. package/dist/e2e/kit/harness.js.map +1 -1
  49. package/dist/e2e/kit/scenarios.js +1 -1
  50. package/dist/e2e/recovery-dd-crash-child.js +3 -2
  51. package/dist/e2e/recovery-dd-crash-child.js.map +1 -1
  52. package/dist/engine/dbos.service.js +20 -0
  53. package/dist/engine/dbos.service.js.map +1 -1
  54. package/dist/engine/ensure-postgres.js +11 -9
  55. package/dist/engine/ensure-postgres.js.map +1 -1
  56. package/dist/features/runs/queries/handlers/runs-query.handlers.js +135 -0
  57. package/dist/features/runs/queries/handlers/runs-query.handlers.js.map +1 -1
  58. package/dist/features/runs/queries/impl/get-agent-activity.query.js +7 -0
  59. package/dist/features/runs/queries/impl/get-agent-activity.query.js.map +1 -0
  60. package/dist/features/runs/queries/impl/get-agent-attempts.query.js +7 -0
  61. package/dist/features/runs/queries/impl/get-agent-attempts.query.js.map +1 -0
  62. package/dist/features/runs/queries/impl/get-agent-log.query.js +7 -0
  63. package/dist/features/runs/queries/impl/get-agent-log.query.js.map +1 -0
  64. package/dist/features/runs/queries/index.js +4 -1
  65. package/dist/features/runs/queries/index.js.map +1 -1
  66. package/dist/features/runs/runs-api.service.js +12 -0
  67. package/dist/features/runs/runs-api.service.js.map +1 -1
  68. package/dist/host/daemon.js +86 -0
  69. package/dist/host/daemon.js.map +1 -0
  70. package/dist/host/ensure-host.js +116 -0
  71. package/dist/host/ensure-host.js.map +1 -0
  72. package/dist/host/host-runtime.js +72 -0
  73. package/dist/host/host-runtime.js.map +1 -0
  74. package/dist/host/queue-ownership.js +53 -0
  75. package/dist/host/queue-ownership.js.map +1 -0
  76. package/dist/http/graphql-host.js +8 -2
  77. package/dist/http/graphql-host.js.map +1 -1
  78. package/dist/http/graphql-host.module.js +3 -0
  79. package/dist/http/graphql-host.module.js.map +1 -1
  80. package/dist/mcp/mcp-capabilities.js +6 -1
  81. package/dist/mcp/mcp-capabilities.js.map +1 -1
  82. package/dist/mcp/mcp-facade.service.js +24 -0
  83. package/dist/mcp/mcp-facade.service.js.map +1 -1
  84. package/dist/mcp/mcp-http.service.js +95 -0
  85. package/dist/mcp/mcp-http.service.js.map +1 -0
  86. package/dist/mcp/mcp-tools.js +58 -0
  87. package/dist/mcp/mcp-tools.js.map +1 -1
  88. package/dist/observability/agent-activity-reporter.js +185 -0
  89. package/dist/observability/agent-activity-reporter.js.map +1 -0
  90. package/dist/observability/agent-observability.service.js +1142 -0
  91. package/dist/observability/agent-observability.service.js.map +1 -0
  92. package/dist/observability/index.js +4 -0
  93. package/dist/observability/index.js.map +1 -0
  94. package/dist/observability/types.js +11 -0
  95. package/dist/observability/types.js.map +1 -0
  96. package/dist/pipeline/data-driven-task.workflow.js +37 -75
  97. package/dist/pipeline/data-driven-task.workflow.js.map +1 -1
  98. package/dist/pipeline/pipeline.service.js +54 -15
  99. package/dist/pipeline/pipeline.service.js.map +1 -1
  100. package/dist/pipeline-core/validate-capability.js +23 -0
  101. package/dist/pipeline-core/validate-capability.js.map +1 -0
  102. package/dist/pipeline-core/validate-conflicts.js +37 -0
  103. package/dist/pipeline-core/validate-conflicts.js.map +1 -0
  104. package/dist/pipeline-core/validate-dataflow.js +180 -0
  105. package/dist/pipeline-core/validate-dataflow.js.map +1 -0
  106. package/dist/pipeline-core/validate-diff.js +158 -0
  107. package/dist/pipeline-core/validate-diff.js.map +1 -0
  108. package/dist/pipeline-core/validate-edges.js +44 -0
  109. package/dist/pipeline-core/validate-edges.js.map +1 -0
  110. package/dist/pipeline-core/validate-graph.js +123 -0
  111. package/dist/pipeline-core/validate-graph.js.map +1 -0
  112. package/dist/pipeline-core/validate-loops.js +226 -0
  113. package/dist/pipeline-core/validate-loops.js.map +1 -0
  114. package/dist/pipeline-core/validate-parallel.js +132 -0
  115. package/dist/pipeline-core/validate-parallel.js.map +1 -0
  116. package/dist/pipeline-core/validate-sink.js +27 -0
  117. package/dist/pipeline-core/validate-sink.js.map +1 -0
  118. package/dist/pipeline-core/validate-topology.js +140 -0
  119. package/dist/pipeline-core/validate-topology.js.map +1 -0
  120. package/dist/pipeline-core/validate-verdict.js +72 -0
  121. package/dist/pipeline-core/validate-verdict.js.map +1 -0
  122. package/dist/pipeline-core/validate.js +34 -1107
  123. package/dist/pipeline-core/validate.js.map +1 -1
  124. package/dist/revisium/run.service.js +15 -1
  125. package/dist/revisium/run.service.js.map +1 -1
  126. package/dist/runners/codex.service.js +51 -0
  127. package/dist/runners/codex.service.js.map +1 -0
  128. package/dist/runners/integrator-branch-naming.js +24 -0
  129. package/dist/runners/integrator-branch-naming.js.map +1 -0
  130. package/dist/runners/integrator-git.js +84 -0
  131. package/dist/runners/integrator-git.js.map +1 -0
  132. package/dist/runners/integrator-remote.js +34 -0
  133. package/dist/runners/integrator-remote.js.map +1 -0
  134. package/dist/runners/integrator-types.js +9 -0
  135. package/dist/runners/integrator-types.js.map +1 -0
  136. package/dist/runners/integrator.js +12 -132
  137. package/dist/runners/integrator.js.map +1 -1
  138. package/dist/runners/runner.module.js +4 -2
  139. package/dist/runners/runner.module.js.map +1 -1
  140. package/dist/smoke/cli.js +47 -0
  141. package/dist/smoke/cli.js.map +1 -0
  142. package/dist/smoke/isolation.js +113 -0
  143. package/dist/smoke/isolation.js.map +1 -0
  144. package/dist/task-control-plane/task-control-plane-api.service.js +24 -1
  145. package/dist/task-control-plane/task-control-plane-api.service.js.map +1 -1
  146. package/dist/task-control-plane/task-control-plane.module.js +21 -1
  147. package/dist/task-control-plane/task-control-plane.module.js.map +1 -1
  148. package/dist/worker/artifact-store.js +6 -1
  149. package/dist/worker/artifact-store.js.map +1 -1
  150. package/dist/worker/build-context.js +135 -1
  151. package/dist/worker/build-context.js.map +1 -1
  152. package/dist/worker/claude-code-runner.js +132 -78
  153. package/dist/worker/claude-code-runner.js.map +1 -1
  154. package/dist/worker/codex-runner.js +474 -0
  155. package/dist/worker/codex-runner.js.map +1 -0
  156. package/dist/worker/result-envelope.js +91 -73
  157. package/dist/worker/result-envelope.js.map +1 -1
  158. package/dist/worker/runner-common.js +106 -0
  159. package/dist/worker/runner-common.js.map +1 -0
  160. package/dist/worker/runner-dispatch.js +3 -1
  161. package/dist/worker/runner-dispatch.js.map +1 -1
  162. package/dist/worker/runner.js.map +1 -1
  163. package/dist/worker/stub-runner.js +3 -3
  164. package/dist/worker/stub-runner.js.map +1 -1
  165. package/package.json +1 -1
  166. package/dist/app.module.js +0 -28
  167. package/dist/app.module.js.map +0 -1
  168. package/dist/cli/commands/bootstrap.js +0 -100
  169. package/dist/cli/commands/bootstrap.js.map +0 -1
  170. package/dist/cli/commands/dev.js +0 -143
  171. package/dist/cli/commands/dev.js.map +0 -1
  172. package/dist/cli/commands/inbox.js +0 -288
  173. package/dist/cli/commands/inbox.js.map +0 -1
  174. package/dist/cli/commands/playbook.js +0 -74
  175. package/dist/cli/commands/playbook.js.map +0 -1
  176. package/dist/cli/commands/poll-workflow-state.js +0 -144
  177. package/dist/cli/commands/poll-workflow-state.js.map +0 -1
  178. package/dist/cli/commands/revisium-context.js +0 -23
  179. package/dist/cli/commands/revisium-context.js.map +0 -1
  180. package/dist/cli/commands/revisium.js +0 -89
  181. package/dist/cli/commands/revisium.js.map +0 -1
  182. package/dist/cli/commands/run.js +0 -451
  183. package/dist/cli/commands/run.js.map +0 -1
  184. package/dist/cli/commands/serve.js +0 -26
  185. package/dist/cli/commands/serve.js.map +0 -1
  186. package/dist/cli/live-guard.js +0 -49
  187. package/dist/cli/live-guard.js.map +0 -1
  188. package/dist/cli/needs-host.js +0 -77
  189. package/dist/cli/needs-host.js.map +0 -1
  190. package/dist/mcp/mcp-stdio.service.js +0 -48
  191. package/dist/mcp/mcp-stdio.service.js.map +0 -1
  192. package/dist/mcp/mcp.module.js +0 -21
  193. package/dist/mcp/mcp.module.js.map +0 -1
@@ -0,0 +1,226 @@
1
+ import { isGuardedBranch } from './types.js';
2
+ import { backwardReach, cycleNodes, findBackEdges, forwardReach, guardConditionsOf } from './validate-graph.js';
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // Rule 6b — loop-cap presence.
5
+ //
6
+ // Spec note (two ambiguities resolved against the canonical §13 example, which §13 declares
7
+ // internally-consistent and the task names as THE fixture):
8
+ // 1. §12.6 says "a terminating cap-guard (counter.gte)", but §13 expresses the SAME bound as
9
+ // `counter.lt K` on the CONTINUE edge with the default routing OUT (once the counter reaches K the
10
+ // guard fails and control falls through to blockedEnd). Both make the loop finite, so we accept
11
+ // EITHER: a cycle is counter-bounded iff some `choice` on it has a guard referencing a `counter.*`
12
+ // over a scope INCREMENTED on the cycle.
13
+ // 2. §13's analyst↔planGate `changes_requested` loop carries NO counter — it is bounded only by human
14
+ // judgment. §6 makes a human-driven loop legitimately unbounded ("durable wait is free … for
15
+ // human-driven runs"). So a cycle that passes through a `humanGate` is also accepted.
16
+ // An AUTOMATED loop (agents/scripts/choices only) with no counter is rejected (LOOP_UNBOUNDED).
17
+ // ─────────────────────────────────────────────────────────────────────────────
18
+ export function ruleLoopCap(template, d) {
19
+ if (!template.entry || !template.nodes[template.entry])
20
+ return;
21
+ for (const { from, to } of findBackEdges(template)) {
22
+ const cycle = cycleNodes(template, to, from);
23
+ const incrementedOnCycle = scopesIncrementedOnCycle(template, cycle);
24
+ const counterBounded = [...cycle].some((id) => choiceGatesCycleByCounter(template, id, incrementedOnCycle));
25
+ const humanDriven = [...cycle].some((id) => template.nodes[id]?.kind === 'humanGate');
26
+ if (!counterBounded && !humanDriven) {
27
+ d.error('LOOP_UNBOUNDED', `back-edge ${from} → ${to} is not bounded by a counter cap-guard or a human gate`, {
28
+ nodeId: from,
29
+ });
30
+ }
31
+ }
32
+ }
33
+ /** Scope ids incremented by any node on the cycle (the loop's bound must count one of these). */
34
+ function scopesIncrementedOnCycle(template, cycle) {
35
+ const out = new Set();
36
+ for (const id of cycle) {
37
+ const node = template.nodes[id];
38
+ if (node && 'incrementCounters' in node)
39
+ for (const s of node.incrementCounters ?? [])
40
+ out.add(s);
41
+ }
42
+ return out;
43
+ }
44
+ /** A choice with a guard that references a `counter.*` over a scope incremented on the cycle. */
45
+ function choiceGatesCycleByCounter(template, id, incrementedOnCycle) {
46
+ const node = template.nodes[id];
47
+ if (node?.kind !== 'choice')
48
+ return false;
49
+ return node.branches.filter(isGuardedBranch).some((b) => conditionGatesOnScopes(b.when, incrementedOnCycle));
50
+ }
51
+ /** True when `cond` compares a `counter.*` against one of `scopes`. */
52
+ function conditionGatesOnScopes(cond, scopes) {
53
+ switch (cond.op) {
54
+ case 'counter.lt':
55
+ case 'counter.gte':
56
+ return scopes.has(cond.scope);
57
+ case 'all':
58
+ case 'any':
59
+ return cond.of.some((c) => conditionGatesOnScopes(c, scopes));
60
+ case 'not':
61
+ return conditionGatesOnScopes(cond.cond, scopes);
62
+ default:
63
+ return false;
64
+ }
65
+ }
66
+ // ─────────────────────────────────────────────────────────────────────────────
67
+ // Rule 7 — counter-scope well-formedness.
68
+ // ─────────────────────────────────────────────────────────────────────────────
69
+ export function ruleCounterScopes(template, d) {
70
+ const scopes = template.scopes ?? {};
71
+ const scopeIds = new Set(Object.keys(scopes));
72
+ checkScopeParentsAndCycles(scopes, scopeIds, d); // 7a parents resolve + no cycle
73
+ checkScopesDeclared(template, scopeIds, d); // 7b every referenced scope is declared
74
+ for (const scopeId of scopeIds)
75
+ checkScopeStrictAncestry(template, scopeId, d); // 7c reset = strict ancestor
76
+ for (const scopeId of Object.keys(scopes))
77
+ checkScopeDoesNotSpanParallel(template, scopeId, d); // 7d no parallel/join
78
+ }
79
+ /** 7a — every scope's parent resolves to a declared scope and the parent chain has no cycle. */
80
+ function checkScopeParentsAndCycles(scopes, scopeIds, d) {
81
+ for (const [scopeId, scope] of Object.entries(scopes)) {
82
+ if (scope.parent !== null && !scopeIds.has(scope.parent)) {
83
+ d.error('SCOPE_PARENT_UNRESOLVED', `scope "${scopeId}" parent "${scope.parent}" is not declared`, {
84
+ scope: scopeId,
85
+ });
86
+ }
87
+ }
88
+ for (const scopeId of scopeIds) {
89
+ if (scopeChainHasCycle(scopes, scopeId)) {
90
+ d.error('SCOPE_CYCLE', `scope "${scopeId}" has a parent cycle`, { scope: scopeId });
91
+ }
92
+ }
93
+ }
94
+ /** 7b — every scope referenced by a node (increment or guard) is declared. */
95
+ function checkScopesDeclared(template, scopeIds, d) {
96
+ for (const node of Object.values(template.nodes)) {
97
+ for (const scopeRef of referencedScopes(node)) {
98
+ if (!scopeIds.has(scopeRef)) {
99
+ d.error('SCOPE_UNDECLARED', `node ${node.id} references undeclared scope "${scopeRef}"`, {
100
+ nodeId: node.id,
101
+ scope: scopeRef,
102
+ });
103
+ }
104
+ }
105
+ }
106
+ }
107
+ /**
108
+ * 7c — a reset scope is a STRICT ancestor of every node that reads/increments it (§7/§12.7). The scope's
109
+ * region is its loop sub-graph: the node(s) that increment it + the guard node(s) that read it. A
110
+ * well-formed reader sits inside that loop — i.e. it shares a cycle with an increment site, or is
111
+ * forward-reachable from one (the cap-guard reads after a rework hop). A reader disconnected from
112
+ * every increment site is a cross-scope/out-of-scope reference; a scope read but never incremented
113
+ * can never advance (its cap is dead). Both are SCOPE_NOT_STRICT_ANCESTOR.
114
+ */
115
+ function checkScopeStrictAncestry(template, scopeId, d) {
116
+ const incrementSites = nodesIncrementing(template, scopeId);
117
+ const readers = nodesReadingScope(template, scopeId);
118
+ if (incrementSites.length === 0 && readers.length > 0) {
119
+ for (const r of readers) {
120
+ d.error('SCOPE_NOT_STRICT_ANCESTOR', `scope "${scopeId}" is read by ${r} but never incremented`, {
121
+ nodeId: r,
122
+ scope: scopeId,
123
+ });
124
+ }
125
+ return;
126
+ }
127
+ for (const reader of readers) {
128
+ const insideLoop = incrementSites.some((site) => sharesCycle(template, site, reader) || forwardReach(template, site).has(reader));
129
+ if (!insideLoop) {
130
+ d.error('SCOPE_NOT_STRICT_ANCESTOR', `scope "${scopeId}" reader ${reader} is outside its loop region`, {
131
+ nodeId: reader,
132
+ scope: scopeId,
133
+ });
134
+ }
135
+ }
136
+ }
137
+ /**
138
+ * 7d — a counter scope may not span a parallel/join boundary (v1). The region is the increment/read endpoints
139
+ * PLUS every node on a structural path between them: a counter incremented before a fan-out and read after the
140
+ * join (or whose endpoints sit in different branches) crosses the boundary and is unsafe in v1.
141
+ */
142
+ function checkScopeDoesNotSpanParallel(template, scopeId, d) {
143
+ const region = scopeRegionBetween(template, nodesIncrementing(template, scopeId), nodesReadingScope(template, scopeId));
144
+ for (const id of region) {
145
+ const node = template.nodes[id];
146
+ if (node && (node.kind === 'parallel' || node.kind === 'join')) {
147
+ d.error('SCOPE_SPANS_PARALLEL', `scope "${scopeId}" spans a ${node.kind} boundary (${id})`, {
148
+ nodeId: id,
149
+ scope: scopeId,
150
+ });
151
+ }
152
+ }
153
+ }
154
+ /** Endpoints plus every node on some structural path from an increment site to a reader (forwardReach ∩ backwardReach). */
155
+ function scopeRegionBetween(template, incrementSites, readers) {
156
+ const region = new Set([...incrementSites, ...readers]);
157
+ for (const inc of incrementSites) {
158
+ const fwd = forwardReach(template, inc);
159
+ for (const reader of readers) {
160
+ if (!fwd.has(reader))
161
+ continue;
162
+ const back = backwardReach(template, reader);
163
+ for (const id of fwd)
164
+ if (back.has(id))
165
+ region.add(id);
166
+ }
167
+ }
168
+ return region;
169
+ }
170
+ function scopeChainHasCycle(scopes, start) {
171
+ let cursor = scopes[start]?.parent ?? null;
172
+ const seen = new Set([start]);
173
+ while (cursor !== null) {
174
+ if (seen.has(cursor))
175
+ return true;
176
+ seen.add(cursor);
177
+ cursor = scopes[cursor]?.parent ?? null;
178
+ }
179
+ return false;
180
+ }
181
+ function referencedScopes(node) {
182
+ const out = [];
183
+ if ('incrementCounters' in node && node.incrementCounters)
184
+ out.push(...node.incrementCounters);
185
+ for (const cond of guardConditionsOf(node))
186
+ collectCounterScopes(cond, out);
187
+ return out;
188
+ }
189
+ function collectCounterScopes(cond, out) {
190
+ switch (cond.op) {
191
+ case 'counter.lt':
192
+ case 'counter.gte':
193
+ out.push(cond.scope);
194
+ break;
195
+ case 'all':
196
+ case 'any':
197
+ cond.of.forEach((c) => collectCounterScopes(c, out));
198
+ break;
199
+ case 'not':
200
+ collectCounterScopes(cond.cond, out);
201
+ break;
202
+ }
203
+ }
204
+ function nodesIncrementing(template, scopeId) {
205
+ return Object.values(template.nodes)
206
+ .filter((n) => 'incrementCounters' in n && (n.incrementCounters ?? []).includes(scopeId))
207
+ .map((n) => n.id);
208
+ }
209
+ function nodesReadingScope(template, scopeId) {
210
+ const out = [];
211
+ for (const node of Object.values(template.nodes)) {
212
+ for (const cond of guardConditionsOf(node)) {
213
+ const scopes = [];
214
+ collectCounterScopes(cond, scopes);
215
+ if (scopes.includes(scopeId)) {
216
+ out.push(node.id);
217
+ break;
218
+ }
219
+ }
220
+ }
221
+ return out;
222
+ }
223
+ function sharesCycle(template, a, b) {
224
+ return forwardReach(template, a).has(b) && forwardReach(template, b).has(a);
225
+ }
226
+ //# sourceMappingURL=validate-loops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-loops.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-loops.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ import { MERGE_REDUCERS } from './types.js';
2
+ import { branchSubgraph, forwardReach, structuralEdges } from './validate-graph.js';
3
+ export function ruleParallelJoin(template, d) {
4
+ const nodes = template.nodes;
5
+ const parallels = Object.values(nodes).filter((n) => n.kind === 'parallel');
6
+ const joinUsedBy = new Map();
7
+ for (const par of parallels)
8
+ checkParallelAgainstJoin(template, par, joinUsedBy, d);
9
+ for (const [joinId, owners] of joinUsedBy) {
10
+ if (owners.length > 1) {
11
+ d.error('JOIN_MULTIPLE_PARALLELS', `join "${joinId}" is the target of ${owners.length} parallels`, {
12
+ nodeId: joinId,
13
+ });
14
+ }
15
+ }
16
+ }
17
+ /** Validate one parallel against its declared join (resolution, kind, quorum, membership, merge). */
18
+ function checkParallelAgainstJoin(template, par, joinUsedBy, d) {
19
+ const join = template.nodes[par.join];
20
+ if (!join) {
21
+ d.error('PARALLEL_JOIN_UNRESOLVED', `parallel ${par.id} join "${par.join}" does not resolve`, { nodeId: par.id });
22
+ return;
23
+ }
24
+ if (join.kind !== 'join') {
25
+ d.error('PARALLEL_JOIN_KIND', `parallel ${par.id} join "${par.join}" is a ${join.kind}, not a join`, {
26
+ nodeId: par.id,
27
+ });
28
+ return;
29
+ }
30
+ (joinUsedBy.get(par.join) ?? joinUsedBy.set(par.join, []).get(par.join)).push(par.id);
31
+ checkQuorumBound(par, join, d);
32
+ // Branch membership + cross-branch goto + all-reachability.
33
+ checkBranchMembership(template, par, join.id, d);
34
+ // Multi-writer fields need a merge reducer.
35
+ checkMergeReducers(template, par, join, d);
36
+ checkRejectedMergeReducers(join, d);
37
+ }
38
+ /** Quorum K must satisfy 1 ≤ K ≤ N (branch count). */
39
+ function checkQuorumBound(par, join, d) {
40
+ if (join.joinMode.kind !== 'quorum')
41
+ return;
42
+ const K = join.joinMode.count;
43
+ if (!Number.isInteger(K) || K < 1 || K > par.branches.length) {
44
+ d.error('QUORUM_K_GT_N', `join ${join.id} quorum K=${K} but parallel has ${par.branches.length} branches`, {
45
+ nodeId: join.id,
46
+ });
47
+ }
48
+ }
49
+ /** Reject any merge reducer outside the allowed set (defensive; the type forbids it but data may carry it). */
50
+ function checkRejectedMergeReducers(join, d) {
51
+ for (const [field, reducer] of Object.entries(join.merge ?? {})) {
52
+ if (!MERGE_REDUCERS.includes(reducer)) {
53
+ d.error('MERGE_LASTWRITE_REJECTED', `join ${join.id} merge.${field} = "${reducer}" is not allowed`, {
54
+ nodeId: join.id,
55
+ path: `merge.${field}`,
56
+ });
57
+ }
58
+ }
59
+ }
60
+ function checkBranchMembership(template, par, joinId, d) {
61
+ const memberOf = buildBranchMembership(template, par, joinId, d);
62
+ checkCrossBranchGotos(template, memberOf, joinId, d);
63
+ }
64
+ /**
65
+ * Assign each branch sub-graph node to its owning branch (flagging shared membership) and, for an
66
+ * `all` join, flag any branch that cannot reach the join. Returns the node→branch ownership map.
67
+ */
68
+ function buildBranchMembership(template, par, joinId, d) {
69
+ const memberOf = new Map();
70
+ for (const branch of par.branches) {
71
+ for (const m of branchSubgraph(template, branch.entry, joinId)) {
72
+ if (memberOf.has(m) && memberOf.get(m) !== branch.id) {
73
+ d.error('BRANCH_MEMBERSHIP', `node ${m} is a member of branches "${memberOf.get(m)}" and "${branch.id}"`, {
74
+ nodeId: m,
75
+ });
76
+ }
77
+ memberOf.set(m, branch.id);
78
+ }
79
+ checkAllJoinReachable(template, branch, joinId, d);
80
+ }
81
+ return memberOf;
82
+ }
83
+ /** all-reachability: an `all`-join branch must be able to reach the join (no deadlock). */
84
+ function checkAllJoinReachable(template, branch, joinId, d) {
85
+ const join = template.nodes[joinId];
86
+ if (join?.kind !== 'join' || join.joinMode.kind !== 'all')
87
+ return;
88
+ if (!forwardReach(template, branch.entry).has(joinId)) {
89
+ d.error('JOIN_UNREACHABLE_BRANCH', `branch "${branch.id}" (all) cannot reach join ${joinId}`, {
90
+ nodeId: branch.entry,
91
+ });
92
+ }
93
+ }
94
+ /** Cross-branch goto: a branch member may only goto within its own branch sub-graph or to the join. */
95
+ function checkCrossBranchGotos(template, memberOf, joinId, d) {
96
+ for (const [member, branchId] of memberOf) {
97
+ const node = template.nodes[member];
98
+ if (!node)
99
+ continue;
100
+ for (const [path, target] of structuralEdges(node)) {
101
+ if (target === joinId)
102
+ continue;
103
+ const targetBranch = memberOf.get(target);
104
+ if (targetBranch !== undefined && targetBranch !== branchId) {
105
+ d.error('BRANCH_CROSS_GOTO', `node ${member} (branch ${branchId}) goto ${target} crosses into branch ${targetBranch}`, {
106
+ nodeId: member,
107
+ path,
108
+ });
109
+ }
110
+ }
111
+ }
112
+ }
113
+ function checkMergeReducers(template, par, join, d) {
114
+ // v1 cannot statically know a node's written fields without a resultSchema model, so we use a
115
+ // conservative proxy: a parallel fanning out ≥2 effect (agent/script) branches writes results from
116
+ // >1 branch; absent a `merge` reducer those fields are unguarded (§4/§12.8). A real per-field model
117
+ // lands with native Revisium typing; until then the entry-kind heuristic surfaces the hazard.
118
+ if (par.branches.length < 2)
119
+ return;
120
+ if (Object.keys(join.merge ?? {}).length > 0)
121
+ return;
122
+ const writers = par.branches.filter((b) => {
123
+ const entry = template.nodes[b.entry];
124
+ return entry?.kind === 'agent' || entry?.kind === 'script';
125
+ });
126
+ if (writers.length >= 2) {
127
+ d.error('MERGE_MISSING', `join ${join.id} has ${writers.length} writer branches but no merge reducer`, {
128
+ nodeId: join.id,
129
+ });
130
+ }
131
+ }
132
+ //# sourceMappingURL=validate-parallel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-parallel.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-parallel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * validate-sink — the shared error/warning collector every §12 rule writes into.
3
+ *
4
+ * Lifted out of validate.ts so each rule module can take a `DiagSink` by type without a circular import
5
+ * back into the orchestrator. `validateTemplate` constructs one and passes it to every rule; rules only
6
+ * call `error`/`warn` on the instance they are handed.
7
+ */
8
+ export class DiagSink {
9
+ items = [];
10
+ error(code, message, where = {}) {
11
+ this.items.push({ code, severity: 'error', message, ...strip(where) });
12
+ }
13
+ warn(code, message, where = {}) {
14
+ this.items.push({ code, severity: 'warning', message, ...strip(where) });
15
+ }
16
+ }
17
+ function strip(where) {
18
+ const out = {};
19
+ if (where.nodeId !== undefined)
20
+ out.nodeId = where.nodeId;
21
+ if (where.scope !== undefined)
22
+ out.scope = where.scope;
23
+ if (where.path !== undefined)
24
+ out.path = where.path;
25
+ return out;
26
+ }
27
+ //# sourceMappingURL=validate-sink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-sink.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-sink.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,OAAO,QAAQ;IACV,KAAK,GAAiB,EAAE,CAAC;IAClC,KAAK,CAAC,IAAoB,EAAE,OAAe,EAAE,QAA6B,EAAE;QAC1E,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,CAAC,IAAoB,EAAE,OAAe,EAAE,QAA6B,EAAE;QACzE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;CACF;AAED,SAAS,KAAK,CAAC,KAA0B;IACvC,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC1D,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACvD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACpD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,140 @@
1
+ import { CONDITION_OPS, TERMINAL_STATUSES, isDefaultBranch } from './types.js';
2
+ import { outgoingEdges } from './validate-edges.js';
3
+ import { guardConditionsOf, reachableFrom } from './validate-graph.js';
4
+ // ─────────────────────────────────────────────────────────────────────────────
5
+ // Rule 1 — single entry.
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+ export function ruleSingleEntry(template, ids, d) {
8
+ if (!template.entry) {
9
+ d.error('ENTRY_MISSING', 'template has no `entry`');
10
+ return;
11
+ }
12
+ if (!ids.has(template.entry)) {
13
+ d.error('ENTRY_UNRESOLVED', `entry "${template.entry}" does not resolve to a node`, {
14
+ nodeId: template.entry,
15
+ });
16
+ }
17
+ }
18
+ // ─────────────────────────────────────────────────────────────────────────────
19
+ // Rule 2 — references resolve (every edge target exists).
20
+ // ─────────────────────────────────────────────────────────────────────────────
21
+ export function ruleReferencesResolve(template, ids, d) {
22
+ const check = (target, node, path) => {
23
+ if (target === undefined)
24
+ return;
25
+ if (!ids.has(target)) {
26
+ d.error('REF_UNRESOLVED', `${node.id}.${path} → unknown node "${target}"`, { nodeId: node.id, path });
27
+ }
28
+ };
29
+ for (const node of Object.values(template.nodes)) {
30
+ for (const [path, target] of outgoingEdges(node))
31
+ check(target, node, path);
32
+ }
33
+ }
34
+ // ─────────────────────────────────────────────────────────────────────────────
35
+ // Rule 3 — terminals & non-terminals.
36
+ // ─────────────────────────────────────────────────────────────────────────────
37
+ export function ruleTerminals(template, d) {
38
+ for (const node of Object.values(template.nodes)) {
39
+ if (node.kind === 'terminal') {
40
+ if (!TERMINAL_STATUSES.includes(node.status)) {
41
+ d.error('TERMINAL_BAD_STATUS', `terminal ${node.id} has bad status "${node.status}"`, {
42
+ nodeId: node.id,
43
+ });
44
+ }
45
+ // The discriminated union forbids exit fields on a terminal at the type level; nothing else to check.
46
+ }
47
+ else if (outgoingEdges(node).length === 0) {
48
+ d.error('NONTERMINAL_NO_EXIT', `non-terminal ${node.id} (${node.kind}) has no exit edge`, {
49
+ nodeId: node.id,
50
+ });
51
+ }
52
+ }
53
+ }
54
+ // ─────────────────────────────────────────────────────────────────────────────
55
+ // Condition grammar sanity — a malformed guard can't be evaluated (feeds rules 2/4/9).
56
+ // ─────────────────────────────────────────────────────────────────────────────
57
+ export function ruleConditionGrammar(template, d) {
58
+ for (const node of Object.values(template.nodes)) {
59
+ for (const cond of guardConditionsOf(node))
60
+ checkCondition(cond, node.id, d);
61
+ }
62
+ }
63
+ function checkCondition(cond, nodeId, d) {
64
+ const op = cond.op;
65
+ if (typeof op !== 'string' || !CONDITION_OPS.includes(op)) {
66
+ d.error('CONDITION_BAD_OP', `node ${nodeId} guard has unknown op "${String(op)}"`, { nodeId });
67
+ return;
68
+ }
69
+ switch (cond.op) {
70
+ case 'verdict.eq':
71
+ if (typeof cond.value !== 'string')
72
+ d.error('CONDITION_BAD_SHAPE', `verdict.eq value must be a string`, { nodeId });
73
+ break;
74
+ case 'verdict.in':
75
+ if (!Array.isArray(cond.value) || cond.value.some((v) => typeof v !== 'string')) {
76
+ d.error('CONDITION_BAD_SHAPE', `verdict.in value must be a string[]`, { nodeId });
77
+ }
78
+ break;
79
+ case 'counter.lt':
80
+ case 'counter.gte':
81
+ if (typeof cond.scope !== 'string' || !Number.isInteger(cond.value)) {
82
+ d.error('CONDITION_BAD_SHAPE', `${cond.op} needs a scope + integer value`, { nodeId });
83
+ }
84
+ break;
85
+ case 'all':
86
+ case 'any':
87
+ if (Array.isArray(cond.of))
88
+ cond.of.forEach((c) => checkCondition(c, nodeId, d));
89
+ else
90
+ d.error('CONDITION_BAD_SHAPE', `${cond.op} needs an "of" array`, { nodeId });
91
+ break;
92
+ case 'not':
93
+ if (cond.cond)
94
+ checkCondition(cond.cond, nodeId, d);
95
+ else
96
+ d.error('CONDITION_BAD_SHAPE', `not needs a "cond"`, { nodeId });
97
+ break;
98
+ }
99
+ }
100
+ // ─────────────────────────────────────────────────────────────────────────────
101
+ // Rule 4 — total routing (default present; no guard after default; one default).
102
+ // ─────────────────────────────────────────────────────────────────────────────
103
+ export function ruleTotalRouting(template, d) {
104
+ for (const node of Object.values(template.nodes)) {
105
+ if (node.kind !== 'choice' && node.kind !== 'humanGate')
106
+ continue;
107
+ const branches = node.branches;
108
+ const defaultIdxs = branches.map((b, i) => (isDefaultBranch(b) ? i : -1)).filter((i) => i >= 0);
109
+ if (defaultIdxs.length === 0) {
110
+ d.error('ROUTING_NO_DEFAULT', `${node.kind} ${node.id} has no trailing default branch`, {
111
+ nodeId: node.id,
112
+ });
113
+ continue;
114
+ }
115
+ if (defaultIdxs.length > 1) {
116
+ d.error('ROUTING_MULTIPLE_DEFAULT', `${node.kind} ${node.id} has ${defaultIdxs.length} defaults`, {
117
+ nodeId: node.id,
118
+ });
119
+ }
120
+ const firstDefault = defaultIdxs[0];
121
+ if (firstDefault !== branches.length - 1) {
122
+ d.error('ROUTING_GUARD_AFTER_DEFAULT', `${node.kind} ${node.id} has a guard after its default`, {
123
+ nodeId: node.id,
124
+ });
125
+ }
126
+ }
127
+ }
128
+ // ─────────────────────────────────────────────────────────────────────────────
129
+ // Rule 5 — reachability (every node reachable from entry; no dead nodes).
130
+ // ─────────────────────────────────────────────────────────────────────────────
131
+ export function ruleReachability(template, ids, d) {
132
+ if (!template.entry || !ids.has(template.entry))
133
+ return; // rule 1 already flagged it
134
+ const reachable = reachableFrom(template, template.entry);
135
+ for (const id of ids) {
136
+ if (!reachable.has(id))
137
+ d.error('UNREACHABLE_NODE', `node "${id}" is unreachable from entry`, { nodeId: id });
138
+ }
139
+ }
140
+ //# sourceMappingURL=validate-topology.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-topology.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-topology.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { CORE_VERDICTS } from './types.js';
2
+ import { guardConditionsOf } from './validate-graph.js';
3
+ export function ruleVerdictClosure(template, d) {
4
+ const domain = template.verdicts?.domain ?? [];
5
+ const domainSet = new Set(domain);
6
+ const core = new Set(CORE_VERDICTS);
7
+ // 9a no domain label shadows a core label.
8
+ for (const label of domain) {
9
+ if (core.has(label)) {
10
+ d.error('VERDICT_DOMAIN_SHADOWS_CORE', `domain verdict "${label}" shadows a core verdict`);
11
+ }
12
+ }
13
+ const used = new Set();
14
+ for (const node of Object.values(template.nodes)) {
15
+ checkGuardVerdictLabels(node, core, domainSet, used, d); // 9b verdict.* guard labels ∈ domain
16
+ checkGateOutcomesSubset(node, domainSet, used, d); // 9c humanGate.outcomes ⊆ domain
17
+ }
18
+ // 9d declared-but-unused (warning).
19
+ for (const label of domain) {
20
+ if (!used.has(label))
21
+ d.warn('VERDICT_DECLARED_UNUSED', `domain verdict "${label}" is declared but never used`);
22
+ }
23
+ }
24
+ /** 9b — every verdict.* guard label on a node is a declared domain label (a core label is a hard error). */
25
+ function checkGuardVerdictLabels(node, core, domainSet, used, d) {
26
+ for (const cond of guardConditionsOf(node)) {
27
+ collectVerdictLabels(cond, (label) => {
28
+ used.add(label);
29
+ if (core.has(label)) {
30
+ d.error('VERDICT_CORE_IN_GUARD', `node ${node.id} guard uses core verdict "${label}" (route it structurally)`, {
31
+ nodeId: node.id,
32
+ });
33
+ }
34
+ else if (!domainSet.has(label)) {
35
+ d.error('VERDICT_UNDECLARED', `node ${node.id} guard uses undeclared verdict "${label}"`, {
36
+ nodeId: node.id,
37
+ });
38
+ }
39
+ });
40
+ }
41
+ }
42
+ /** 9c — a humanGate's declared outcomes must all be declared domain labels. */
43
+ function checkGateOutcomesSubset(node, domainSet, used, d) {
44
+ if (node.kind !== 'humanGate')
45
+ return;
46
+ for (const o of node.outcomes) {
47
+ used.add(o);
48
+ if (!domainSet.has(o)) {
49
+ d.error('GATE_OUTCOME_NOT_SUBSET', `gate ${node.id} outcome "${o}" is not in verdicts.domain`, {
50
+ nodeId: node.id,
51
+ });
52
+ }
53
+ }
54
+ }
55
+ function collectVerdictLabels(cond, visit) {
56
+ switch (cond.op) {
57
+ case 'verdict.eq':
58
+ visit(cond.value);
59
+ break;
60
+ case 'verdict.in':
61
+ cond.value.forEach(visit);
62
+ break;
63
+ case 'all':
64
+ case 'any':
65
+ cond.of.forEach((c) => collectVerdictLabels(c, visit));
66
+ break;
67
+ case 'not':
68
+ collectVerdictLabels(cond.cond, visit);
69
+ break;
70
+ }
71
+ }
72
+ //# sourceMappingURL=validate-verdict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-verdict.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-verdict.ts"],"names":[],"mappings":"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"}