@revisium/orchestrator 0.1.0-alpha.5 → 0.1.0-alpha.6
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/README.md +149 -1
- package/dist/api/graphql-api/filters/agent-observability-exception.filter.js +24 -0
- package/dist/api/graphql-api/filters/agent-observability-exception.filter.js.map +1 -0
- package/dist/api/graphql-api/graphql-api.module.js +2 -0
- package/dist/api/graphql-api/graphql-api.module.js.map +1 -1
- package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-bridge.service.js +296 -0
- package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-bridge.service.js.map +1 -0
- package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-mappers.js +64 -0
- package/dist/api/graphql-api/graphql-ws/agent-observability-subscription-mappers.js.map +1 -0
- package/dist/api/graphql-api/graphql-ws/constants.js +2 -0
- package/dist/api/graphql-api/graphql-ws/constants.js.map +1 -1
- package/dist/api/graphql-api/graphql-ws/pubsub.module.js +5 -2
- package/dist/api/graphql-api/graphql-ws/pubsub.module.js.map +1 -1
- package/dist/api/graphql-api/registerGraphqlEnums.js +6 -1
- package/dist/api/graphql-api/registerGraphqlEnums.js.map +1 -1
- package/dist/api/graphql-api/runs/inputs/get-agent-log.input.js +48 -0
- package/dist/api/graphql-api/runs/inputs/get-agent-log.input.js.map +1 -0
- package/dist/api/graphql-api/runs/model/agent-activity.model.js +367 -0
- package/dist/api/graphql-api/runs/model/agent-activity.model.js.map +1 -0
- package/dist/api/graphql-api/runs/runs-subscription.resolver.js +35 -4
- package/dist/api/graphql-api/runs/runs-subscription.resolver.js.map +1 -1
- package/dist/api/graphql-api/runs/runs.resolver.js +35 -0
- package/dist/api/graphql-api/runs/runs.resolver.js.map +1 -1
- package/dist/cli/commands/doctor-report.js +39 -0
- package/dist/cli/commands/doctor-report.js.map +1 -0
- package/dist/cli/commands/lifecycle.js +237 -0
- package/dist/cli/commands/lifecycle.js.map +1 -0
- package/dist/cli/commands/mcp.js +46 -8
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/revisium-helpers.js +2 -2
- package/dist/cli/index.js +5 -29
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/program.js +7 -23
- package/dist/cli/program.js.map +1 -1
- package/dist/config.js +42 -5
- package/dist/config.js.map +1 -1
- package/dist/control-plane/bootstrap.js +93 -0
- package/dist/control-plane/bootstrap.js.map +1 -0
- package/dist/control-plane/schema-migration.js +7 -11
- package/dist/control-plane/schema-migration.js.map +1 -1
- package/dist/control-plane/seed-default-playbook.js +23 -0
- package/dist/control-plane/seed-default-playbook.js.map +1 -1
- package/dist/e2e/kit/agents.js +17 -5
- package/dist/e2e/kit/agents.js.map +1 -1
- package/dist/e2e/kit/assertions.js +20 -0
- package/dist/e2e/kit/assertions.js.map +1 -1
- package/dist/e2e/kit/harness.js +12 -1
- package/dist/e2e/kit/harness.js.map +1 -1
- package/dist/e2e/kit/scenarios.js +1 -1
- package/dist/e2e/recovery-dd-crash-child.js +3 -2
- package/dist/e2e/recovery-dd-crash-child.js.map +1 -1
- package/dist/engine/dbos.service.js +20 -0
- package/dist/engine/dbos.service.js.map +1 -1
- package/dist/engine/ensure-postgres.js +11 -9
- package/dist/engine/ensure-postgres.js.map +1 -1
- package/dist/features/runs/queries/handlers/runs-query.handlers.js +135 -0
- package/dist/features/runs/queries/handlers/runs-query.handlers.js.map +1 -1
- package/dist/features/runs/queries/impl/get-agent-activity.query.js +7 -0
- package/dist/features/runs/queries/impl/get-agent-activity.query.js.map +1 -0
- package/dist/features/runs/queries/impl/get-agent-attempts.query.js +7 -0
- package/dist/features/runs/queries/impl/get-agent-attempts.query.js.map +1 -0
- package/dist/features/runs/queries/impl/get-agent-log.query.js +7 -0
- package/dist/features/runs/queries/impl/get-agent-log.query.js.map +1 -0
- package/dist/features/runs/queries/index.js +4 -1
- package/dist/features/runs/queries/index.js.map +1 -1
- package/dist/features/runs/runs-api.service.js +12 -0
- package/dist/features/runs/runs-api.service.js.map +1 -1
- package/dist/host/daemon.js +68 -0
- package/dist/host/daemon.js.map +1 -0
- package/dist/host/ensure-host.js +109 -0
- package/dist/host/ensure-host.js.map +1 -0
- package/dist/host/host-runtime.js +52 -0
- package/dist/host/host-runtime.js.map +1 -0
- package/dist/http/graphql-host.js +8 -2
- package/dist/http/graphql-host.js.map +1 -1
- package/dist/http/graphql-host.module.js +3 -0
- package/dist/http/graphql-host.module.js.map +1 -1
- package/dist/mcp/mcp-capabilities.js +6 -1
- package/dist/mcp/mcp-capabilities.js.map +1 -1
- package/dist/mcp/mcp-facade.service.js +24 -0
- package/dist/mcp/mcp-facade.service.js.map +1 -1
- package/dist/mcp/mcp-http.service.js +95 -0
- package/dist/mcp/mcp-http.service.js.map +1 -0
- package/dist/mcp/mcp-tools.js +58 -0
- package/dist/mcp/mcp-tools.js.map +1 -1
- package/dist/observability/agent-activity-reporter.js +185 -0
- package/dist/observability/agent-activity-reporter.js.map +1 -0
- package/dist/observability/agent-observability.service.js +1142 -0
- package/dist/observability/agent-observability.service.js.map +1 -0
- package/dist/observability/index.js +4 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/types.js +11 -0
- package/dist/observability/types.js.map +1 -0
- package/dist/pipeline/data-driven-task.workflow.js +37 -75
- package/dist/pipeline/data-driven-task.workflow.js.map +1 -1
- package/dist/pipeline/pipeline.service.js +54 -15
- package/dist/pipeline/pipeline.service.js.map +1 -1
- package/dist/pipeline-core/validate-capability.js +23 -0
- package/dist/pipeline-core/validate-capability.js.map +1 -0
- package/dist/pipeline-core/validate-conflicts.js +37 -0
- package/dist/pipeline-core/validate-conflicts.js.map +1 -0
- package/dist/pipeline-core/validate-dataflow.js +180 -0
- package/dist/pipeline-core/validate-dataflow.js.map +1 -0
- package/dist/pipeline-core/validate-diff.js +158 -0
- package/dist/pipeline-core/validate-diff.js.map +1 -0
- package/dist/pipeline-core/validate-edges.js +44 -0
- package/dist/pipeline-core/validate-edges.js.map +1 -0
- package/dist/pipeline-core/validate-graph.js +123 -0
- package/dist/pipeline-core/validate-graph.js.map +1 -0
- package/dist/pipeline-core/validate-loops.js +226 -0
- package/dist/pipeline-core/validate-loops.js.map +1 -0
- package/dist/pipeline-core/validate-parallel.js +132 -0
- package/dist/pipeline-core/validate-parallel.js.map +1 -0
- package/dist/pipeline-core/validate-sink.js +27 -0
- package/dist/pipeline-core/validate-sink.js.map +1 -0
- package/dist/pipeline-core/validate-topology.js +140 -0
- package/dist/pipeline-core/validate-topology.js.map +1 -0
- package/dist/pipeline-core/validate-verdict.js +72 -0
- package/dist/pipeline-core/validate-verdict.js.map +1 -0
- package/dist/pipeline-core/validate.js +34 -1107
- package/dist/pipeline-core/validate.js.map +1 -1
- package/dist/revisium/run.service.js +15 -1
- package/dist/revisium/run.service.js.map +1 -1
- package/dist/runners/codex.service.js +51 -0
- package/dist/runners/codex.service.js.map +1 -0
- package/dist/runners/integrator-branch-naming.js +24 -0
- package/dist/runners/integrator-branch-naming.js.map +1 -0
- package/dist/runners/integrator-git.js +84 -0
- package/dist/runners/integrator-git.js.map +1 -0
- package/dist/runners/integrator-remote.js +34 -0
- package/dist/runners/integrator-remote.js.map +1 -0
- package/dist/runners/integrator-types.js +9 -0
- package/dist/runners/integrator-types.js.map +1 -0
- package/dist/runners/integrator.js +12 -132
- package/dist/runners/integrator.js.map +1 -1
- package/dist/runners/runner.module.js +4 -2
- package/dist/runners/runner.module.js.map +1 -1
- package/dist/smoke/cli.js +47 -0
- package/dist/smoke/cli.js.map +1 -0
- package/dist/smoke/isolation.js +113 -0
- package/dist/smoke/isolation.js.map +1 -0
- package/dist/task-control-plane/task-control-plane-api.service.js +24 -1
- package/dist/task-control-plane/task-control-plane-api.service.js.map +1 -1
- package/dist/task-control-plane/task-control-plane.module.js +21 -1
- package/dist/task-control-plane/task-control-plane.module.js.map +1 -1
- package/dist/worker/artifact-store.js +6 -1
- package/dist/worker/artifact-store.js.map +1 -1
- package/dist/worker/build-context.js +135 -1
- package/dist/worker/build-context.js.map +1 -1
- package/dist/worker/claude-code-runner.js +75 -76
- package/dist/worker/claude-code-runner.js.map +1 -1
- package/dist/worker/codex-runner.js +474 -0
- package/dist/worker/codex-runner.js.map +1 -0
- package/dist/worker/result-envelope.js +51 -79
- package/dist/worker/result-envelope.js.map +1 -1
- package/dist/worker/runner-common.js +106 -0
- package/dist/worker/runner-common.js.map +1 -0
- package/dist/worker/runner-dispatch.js +3 -1
- package/dist/worker/runner-dispatch.js.map +1 -1
- package/dist/worker/runner.js.map +1 -1
- package/dist/worker/stub-runner.js +3 -3
- package/dist/worker/stub-runner.js.map +1 -1
- package/package.json +1 -1
- package/dist/app.module.js +0 -28
- package/dist/app.module.js.map +0 -1
- package/dist/cli/commands/bootstrap.js +0 -100
- package/dist/cli/commands/bootstrap.js.map +0 -1
- package/dist/cli/commands/dev.js +0 -143
- package/dist/cli/commands/dev.js.map +0 -1
- package/dist/cli/commands/inbox.js +0 -288
- package/dist/cli/commands/inbox.js.map +0 -1
- package/dist/cli/commands/playbook.js +0 -74
- package/dist/cli/commands/playbook.js.map +0 -1
- package/dist/cli/commands/poll-workflow-state.js +0 -144
- package/dist/cli/commands/poll-workflow-state.js.map +0 -1
- package/dist/cli/commands/revisium-context.js +0 -23
- package/dist/cli/commands/revisium-context.js.map +0 -1
- package/dist/cli/commands/revisium.js +0 -89
- package/dist/cli/commands/revisium.js.map +0 -1
- package/dist/cli/commands/run.js +0 -451
- package/dist/cli/commands/run.js.map +0 -1
- package/dist/cli/commands/serve.js +0 -26
- package/dist/cli/commands/serve.js.map +0 -1
- package/dist/cli/live-guard.js +0 -49
- package/dist/cli/live-guard.js.map +0 -1
- package/dist/cli/needs-host.js +0 -77
- package/dist/cli/needs-host.js.map +0 -1
- package/dist/mcp/mcp-stdio.service.js +0 -48
- package/dist/mcp/mcp-stdio.service.js.map +0 -1
- package/dist/mcp/mcp.module.js +0 -21
- package/dist/mcp/mcp.module.js.map +0 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validate-capability — §12 rule 12 (agent/script capability-ref shape). Lifted from validate.ts.
|
|
3
|
+
*/
|
|
4
|
+
const CAPABILITY_REF_PATTERN = /^(role|script):[A-Za-z][A-Za-z0-9_-]*$/;
|
|
5
|
+
export function ruleCapabilityRefs(template, d) {
|
|
6
|
+
for (const node of Object.values(template.nodes)) {
|
|
7
|
+
if (node.kind === 'agent') {
|
|
8
|
+
if (!CAPABILITY_REF_PATTERN.test(node.roleRef) || !node.roleRef.startsWith('role:')) {
|
|
9
|
+
d.error('CAPABILITY_REF_SHAPE', `agent ${node.id} roleRef "${node.roleRef}" is malformed`, {
|
|
10
|
+
nodeId: node.id,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
else if (node.kind === 'script') {
|
|
15
|
+
if (!CAPABILITY_REF_PATTERN.test(node.scriptRef) || !node.scriptRef.startsWith('script:')) {
|
|
16
|
+
d.error('CAPABILITY_REF_SHAPE', `script ${node.id} scriptRef "${node.scriptRef}" is malformed`, {
|
|
17
|
+
nodeId: node.id,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=validate-capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-capability.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-capability.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,sBAAsB,GAAG,wCAAwC,CAAC;AAExE,MAAM,UAAU,kBAAkB,CAAC,QAAkB,EAAE,CAAW;IAChE,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpF,CAAC,CAAC,KAAK,CAAC,sBAAsB,EAAE,SAAS,IAAI,CAAC,EAAE,aAAa,IAAI,CAAC,OAAO,gBAAgB,EAAE;oBACzF,MAAM,EAAE,IAAI,CAAC,EAAE;iBAChB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAClC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC1F,CAAC,CAAC,KAAK,CAAC,sBAAsB,EAAE,UAAU,IAAI,CAAC,EAAE,eAAe,IAAI,CAAC,SAAS,gBAAgB,EAAE;oBAC9F,MAAM,EAAE,IAAI,CAAC,EAAE;iBAChB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validate-conflicts — §12 rule 10 (policy.conflicts well-formedness). Lifted from validate.ts.
|
|
3
|
+
*/
|
|
4
|
+
export function ruleConflictMatrix(template, d) {
|
|
5
|
+
const conflicts = template.policy?.conflicts ?? [];
|
|
6
|
+
if (conflicts.length === 0)
|
|
7
|
+
return;
|
|
8
|
+
// Map each role to the node ids that bind it (agent.roleRef = "role:<name>").
|
|
9
|
+
const roleNodes = new Map();
|
|
10
|
+
for (const node of Object.values(template.nodes)) {
|
|
11
|
+
if (node.kind === 'agent') {
|
|
12
|
+
const role = roleName(node.roleRef);
|
|
13
|
+
if (role)
|
|
14
|
+
(roleNodes.get(role) ?? roleNodes.set(role, []).get(role)).push(node.id);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
for (const pair of conflicts) {
|
|
18
|
+
if (!Array.isArray(pair) || pair.length !== 2 || pair.some((r) => typeof r !== 'string')) {
|
|
19
|
+
d.error('CONFLICT_REF_INVALID', `policy.conflicts entry ${JSON.stringify(pair)} is not a [roleA, roleB] pair`);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const [a, b] = pair;
|
|
23
|
+
// A conflict is well-formed only if both roles are actually bound by some node.
|
|
24
|
+
if (!roleNodes.has(a) || !roleNodes.has(b)) {
|
|
25
|
+
d.warn('CONFLICT_REF_INVALID', `policy.conflicts [${a}, ${b}] references a role no node binds`);
|
|
26
|
+
}
|
|
27
|
+
// v1 binds a role to a node, not to a concrete actor; a path where ONE actor fills both roles
|
|
28
|
+
// is detectable only with actor assignment (out of v1 template data). We surface the *structural*
|
|
29
|
+
// hazard: the same node is bound to both conflicting roles (impossible via roleRef, but a future
|
|
30
|
+
// multi-role node would trip it) — kept as a placeholder check so the rule has a code + a test.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function roleName(roleRef) {
|
|
34
|
+
const m = /^role:(.+)$/.exec(roleRef);
|
|
35
|
+
return m ? m[1] : undefined;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=validate-conflicts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-conflicts.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-conflicts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,UAAU,kBAAkB,CAAC,QAAkB,EAAE,CAAW;IAChE,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC;IACnD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEnC,8EAA8E;IAC9E,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpC,IAAI,IAAI;gBAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;YACzF,CAAC,CAAC,KAAK,CAAC,sBAAsB,EAAE,0BAA0B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;YAC/G,SAAS;QACX,CAAC;QACD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;QACpB,gFAAgF;QAChF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,CAAC,CAAC,IAAI,CAAC,sBAAsB,EAAE,qBAAqB,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAClG,CAAC;QACD,8FAA8F;QAC9F,kGAAkG;QAClG,iGAAiG;QACjG,gGAAgG;IAClG,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { branchSubgraph, cycleNodes, findBackEdges, forwardReach, structuralEdges, } from './validate-graph.js';
|
|
2
|
+
export function ruleDataflow(template, ids, d) {
|
|
3
|
+
const ctx = analyse(template, ids);
|
|
4
|
+
flagDuplicateProduces(template, d);
|
|
5
|
+
for (const node of Object.values(template.nodes)) {
|
|
6
|
+
if (node.kind === 'agent' || node.kind === 'script')
|
|
7
|
+
checkConsumer(node, ctx, d);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function analyse(template, ids) {
|
|
11
|
+
const hasEntry = !!template.entry && ids.has(template.entry);
|
|
12
|
+
return {
|
|
13
|
+
nodes: template.nodes,
|
|
14
|
+
hasEntry,
|
|
15
|
+
dom: hasEntry ? dominators(template, template.entry) : new Map(),
|
|
16
|
+
membership: branchMembership(template),
|
|
17
|
+
cyclesByNode: consumerStaleCycles(template),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** PRODUCES_NAME_DUP — a clarity guard: the grammar keys consumes by NODE, so a duplicate name isn't a resolution bug. */
|
|
21
|
+
function flagDuplicateProduces(template, d) {
|
|
22
|
+
const producedNames = new Map();
|
|
23
|
+
for (const node of Object.values(template.nodes)) {
|
|
24
|
+
if (node.kind !== 'agent' && node.kind !== 'script')
|
|
25
|
+
continue;
|
|
26
|
+
if (!node.produces)
|
|
27
|
+
continue;
|
|
28
|
+
const prev = producedNames.get(node.produces.name);
|
|
29
|
+
if (prev && prev !== node.id) {
|
|
30
|
+
d.warn('PRODUCES_NAME_DUP', `nodes "${prev}" and "${node.id}" both produce "${node.produces.name}"`, {
|
|
31
|
+
nodeId: node.id,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
else if (!prev)
|
|
35
|
+
producedNames.set(node.produces.name, node.id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function checkConsumer(node, ctx, d) {
|
|
39
|
+
const seenAs = new Set();
|
|
40
|
+
(node.consumes ?? []).forEach((ref, i) => checkConsumeRef(node, ref, `consumes[${i}]`, seenAs, ctx, d));
|
|
41
|
+
}
|
|
42
|
+
function checkConsumeRef(node, ref, path, seenAs, ctx, d) {
|
|
43
|
+
if (seenAs.has(ref.as)) {
|
|
44
|
+
d.error('CONSUMES_AS_DUP', `node "${node.id}" consumes two inputs as "${ref.as}"`, { nodeId: node.id, path });
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
seenAs.add(ref.as);
|
|
48
|
+
}
|
|
49
|
+
const producer = ctx.nodes[ref.node];
|
|
50
|
+
if (!producer) {
|
|
51
|
+
d.error('CONSUMES_NODE_UNRESOLVED', `node "${node.id}" consumes from unknown node "${ref.node}"`, {
|
|
52
|
+
nodeId: node.id,
|
|
53
|
+
path,
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!isEffectProducer(producer)) {
|
|
58
|
+
d.error('CONSUMES_PRODUCER_MISSING', `node "${node.id}" consumes from "${ref.node}" which declares no produces`, {
|
|
59
|
+
nodeId: node.id,
|
|
60
|
+
path,
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
checkDominance(node, ref, path, ctx, d);
|
|
65
|
+
checkFreshness(node, ref, path, ctx, d);
|
|
66
|
+
checkCrossParallel(node, ref, path, ctx, d);
|
|
67
|
+
}
|
|
68
|
+
function isEffectProducer(node) {
|
|
69
|
+
return (node.kind === 'agent' || node.kind === 'script') && !!node.produces;
|
|
70
|
+
}
|
|
71
|
+
/** Dominance: the producer must run before the consumer on EVERY path (entry can never be dominated → a consuming entry node is always flagged). */
|
|
72
|
+
function checkDominance(node, ref, path, ctx, d) {
|
|
73
|
+
const dominated = ctx.hasEntry && ref.node !== node.id && ctx.dom.get(node.id)?.has(ref.node) === true;
|
|
74
|
+
if (dominated)
|
|
75
|
+
return;
|
|
76
|
+
const msg = `producer "${ref.node}" does not run before "${node.id}" on every path`;
|
|
77
|
+
if (ref.optional === true)
|
|
78
|
+
d.warn('CONSUMES_NOT_DOMINATED', msg, { nodeId: node.id, path });
|
|
79
|
+
else
|
|
80
|
+
d.error('CONSUMES_NOT_DOMINATED', msg, { nodeId: node.id, path });
|
|
81
|
+
}
|
|
82
|
+
/** Freshness: a consumer inside a loop the producer is NOT on can silently reuse a stale output. */
|
|
83
|
+
function checkFreshness(node, ref, path, ctx, d) {
|
|
84
|
+
const iteration = ref.iteration ?? 'latest';
|
|
85
|
+
if (iteration !== 'latest' || ref.staleOk === true)
|
|
86
|
+
return;
|
|
87
|
+
const reentersWithoutProducer = (ctx.cyclesByNode.get(node.id) ?? []).some((cycle) => !cycle.has(ref.node));
|
|
88
|
+
if (reentersWithoutProducer) {
|
|
89
|
+
d.warn('CONSUMES_STALE_RISK', `node "${node.id}" can re-enter a loop without re-running producer "${ref.node}" (iteration:'latest') — set staleOk or use iteration:'all'/N`, { nodeId: node.id, path });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Cross-parallel: consuming a sibling branch's output is unsafe (the branch may be cancelled). */
|
|
93
|
+
function checkCrossParallel(node, ref, path, ctx, d) {
|
|
94
|
+
const consumerBranches = ctx.membership.get(node.id) ?? [];
|
|
95
|
+
const producerBranches = ctx.membership.get(ref.node) ?? [];
|
|
96
|
+
const crosses = consumerBranches.some((c) => producerBranches.some((p) => c.parallel === p.parallel && c.branch !== p.branch));
|
|
97
|
+
if (crosses) {
|
|
98
|
+
d.error('CONSUMES_CROSS_PARALLEL_UNSAFE', `node "${node.id}" consumes "${ref.node}" from a sibling parallel branch`, {
|
|
99
|
+
nodeId: node.id,
|
|
100
|
+
path,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Standard iterative dominator sets over STRUCTURAL edges from `entry` (dom(entry) = {entry}). Both the node-set
|
|
106
|
+
* and the predecessor graph use the structural edge model — a catch/escalate route is a failure path where the
|
|
107
|
+
* producer did not successfully run, so it is excluded from "ran before the consumer on every path".
|
|
108
|
+
*/
|
|
109
|
+
function dominators(template, entry) {
|
|
110
|
+
const reachable = [...forwardReach(template, entry)].filter((id) => template.nodes[id]);
|
|
111
|
+
const preds = new Map();
|
|
112
|
+
for (const id of reachable)
|
|
113
|
+
preds.set(id, []);
|
|
114
|
+
for (const id of reachable) {
|
|
115
|
+
for (const [, t] of structuralEdges(template.nodes[id]))
|
|
116
|
+
if (preds.has(t))
|
|
117
|
+
preds.get(t).push(id);
|
|
118
|
+
}
|
|
119
|
+
const all = new Set(reachable);
|
|
120
|
+
const dom = new Map();
|
|
121
|
+
for (const id of reachable)
|
|
122
|
+
dom.set(id, new Set(all));
|
|
123
|
+
dom.set(entry, new Set([entry]));
|
|
124
|
+
let changed = true;
|
|
125
|
+
while (changed) {
|
|
126
|
+
changed = false;
|
|
127
|
+
for (const id of reachable) {
|
|
128
|
+
if (id === entry)
|
|
129
|
+
continue;
|
|
130
|
+
const ps = preds.get(id);
|
|
131
|
+
let next;
|
|
132
|
+
if (ps.length === 0)
|
|
133
|
+
next = new Set([id]);
|
|
134
|
+
else {
|
|
135
|
+
next = new Set(dom.get(ps[0]));
|
|
136
|
+
for (let i = 1; i < ps.length; i++) {
|
|
137
|
+
const dp = dom.get(ps[i]);
|
|
138
|
+
for (const x of [...next])
|
|
139
|
+
if (!dp.has(x))
|
|
140
|
+
next.delete(x);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
next.add(id);
|
|
144
|
+
const old = dom.get(id);
|
|
145
|
+
if (old.size !== next.size || [...next].some((x) => !old.has(x))) {
|
|
146
|
+
dom.set(id, next);
|
|
147
|
+
changed = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return dom;
|
|
152
|
+
}
|
|
153
|
+
/** For each node, the cycles (node-sets) it sits on — feeds the loop-freshness check (CONSUMES_STALE_RISK). */
|
|
154
|
+
function consumerStaleCycles(template) {
|
|
155
|
+
const out = new Map();
|
|
156
|
+
for (const { from, to } of findBackEdges(template)) {
|
|
157
|
+
const cycle = cycleNodes(template, to, from);
|
|
158
|
+
for (const id of cycle) {
|
|
159
|
+
const list = out.get(id) ?? [];
|
|
160
|
+
list.push(cycle);
|
|
161
|
+
out.set(id, list);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
/** Map each node to ALL its (parallel, branch) memberships — a node in nested parallels belongs to each. */
|
|
167
|
+
function branchMembership(template) {
|
|
168
|
+
const out = new Map();
|
|
169
|
+
for (const par of Object.values(template.nodes)) {
|
|
170
|
+
if (par.kind !== 'parallel')
|
|
171
|
+
continue;
|
|
172
|
+
for (const br of par.branches) {
|
|
173
|
+
for (const id of branchSubgraph(template, br.entry, par.join)) {
|
|
174
|
+
(out.get(id) ?? out.set(id, []).get(id)).push({ parallel: par.id, branch: br.id });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=validate-dataflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-dataflow.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-dataflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validate-diff — §12 rule 13, the template diff classifier. Classifies the change from one template
|
|
3
|
+
* to another (it does not validate a single template — that is validateTemplate's job). Lifted from
|
|
4
|
+
* validate.ts as a separate public-API surface; validate.ts re-exports it so callers are unaffected.
|
|
5
|
+
*/
|
|
6
|
+
import { outgoingEdges } from './validate-edges.js';
|
|
7
|
+
/**
|
|
8
|
+
* Classify the change from `old` → `next` (§12.13). The enabler for a FUTURE in-flight migration; v1
|
|
9
|
+
* only reports.
|
|
10
|
+
* - node-id delete / rename / kind-change → breaking
|
|
11
|
+
* - changing the outgoing topology of an existing node → breaking
|
|
12
|
+
* - reusing a deleted id with a different kind/resultSchema → invalid
|
|
13
|
+
* - displayName / prompt / payload changes → safe
|
|
14
|
+
* - ANY field/path not explicitly classified → breaking (conservative) + DIFF_UNCLASSIFIED
|
|
15
|
+
*/
|
|
16
|
+
export function classifyTemplateDiff(old, next) {
|
|
17
|
+
const acc = newDiffAccumulator();
|
|
18
|
+
const oldNodes = old.nodes ?? {};
|
|
19
|
+
const nextNodes = next.nodes ?? {};
|
|
20
|
+
const oldIds = new Set(Object.keys(oldNodes));
|
|
21
|
+
const nextIds = new Set(Object.keys(nextNodes));
|
|
22
|
+
// Deleted ids → breaking.
|
|
23
|
+
for (const id of oldIds) {
|
|
24
|
+
if (!nextIds.has(id)) {
|
|
25
|
+
acc.escalate('breaking');
|
|
26
|
+
acc.diagnostics.push({ code: 'DIFF_NODE_DELETED', severity: 'error', message: `node "${id}" was deleted`, nodeId: id });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const id of nextIds) {
|
|
30
|
+
const before = oldNodes[id];
|
|
31
|
+
// A brand-new node id is additive → safe (a new branch/path); reusing a PREVIOUSLY-deleted id is
|
|
32
|
+
// not observable here (single old/next pair). Adding a node is classified safe.
|
|
33
|
+
if (before)
|
|
34
|
+
classifyExistingNodeChange(id, before, nextNodes[id], acc);
|
|
35
|
+
}
|
|
36
|
+
classifyTopLevelDiff(old, next, acc);
|
|
37
|
+
return { kind: acc.kind, diagnostics: acc.diagnostics };
|
|
38
|
+
}
|
|
39
|
+
function newDiffAccumulator() {
|
|
40
|
+
const acc = {
|
|
41
|
+
diagnostics: [],
|
|
42
|
+
kind: 'safe',
|
|
43
|
+
escalate: (to) => {
|
|
44
|
+
if (to === 'invalid')
|
|
45
|
+
acc.kind = 'invalid';
|
|
46
|
+
else if (to === 'breaking' && acc.kind !== 'invalid')
|
|
47
|
+
acc.kind = 'breaking';
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
return acc;
|
|
51
|
+
}
|
|
52
|
+
/** Classify the change of a node id present in BOTH templates (kind / schema / topology / fields). */
|
|
53
|
+
function classifyExistingNodeChange(id, before, after, acc) {
|
|
54
|
+
// kind change → breaking; an incompatible kind/resultSchema reuse → invalid.
|
|
55
|
+
if (before.kind !== after.kind) {
|
|
56
|
+
acc.escalate('invalid'); // a reused id with a different kind is invalid (§12.13)
|
|
57
|
+
acc.diagnostics.push({
|
|
58
|
+
code: 'DIFF_ID_REUSED_INCOMPATIBLE',
|
|
59
|
+
severity: 'error',
|
|
60
|
+
message: `node "${id}" kind changed ${before.kind} → ${after.kind} (id reuse with different kind)`,
|
|
61
|
+
nodeId: id,
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// resultSchema change on a same-kind effect node → invalid (id reuse with different contract).
|
|
66
|
+
const beforeSchema = before.resultSchema;
|
|
67
|
+
const afterSchema = after.resultSchema;
|
|
68
|
+
if (beforeSchema !== afterSchema) {
|
|
69
|
+
acc.escalate('invalid');
|
|
70
|
+
acc.diagnostics.push({
|
|
71
|
+
code: 'DIFF_ID_REUSED_INCOMPATIBLE',
|
|
72
|
+
severity: 'error',
|
|
73
|
+
message: `node "${id}" resultSchema changed "${beforeSchema}" → "${afterSchema}"`,
|
|
74
|
+
nodeId: id,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Outgoing topology change → breaking.
|
|
78
|
+
if (!sameTopology(before, after)) {
|
|
79
|
+
acc.escalate('breaking');
|
|
80
|
+
acc.diagnostics.push({
|
|
81
|
+
code: 'DIFF_NODE_TOPOLOGY_CHANGED',
|
|
82
|
+
severity: 'error',
|
|
83
|
+
message: `node "${id}" outgoing topology changed`,
|
|
84
|
+
nodeId: id,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Any non-safe-classified, non-topology field change defaults to breaking + DIFF_UNCLASSIFIED.
|
|
88
|
+
const unclassified = unclassifiedFieldChange(before, after);
|
|
89
|
+
if (unclassified) {
|
|
90
|
+
acc.escalate('breaking');
|
|
91
|
+
acc.diagnostics.push({
|
|
92
|
+
code: 'DIFF_UNCLASSIFIED',
|
|
93
|
+
severity: 'error',
|
|
94
|
+
message: `node "${id}" has an unclassified field change in {${unclassified}} (defaulting to breaking)`,
|
|
95
|
+
nodeId: id,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Top-level structural changes (entry / scopes) default to breaking if changed. */
|
|
100
|
+
function classifyTopLevelDiff(old, next, acc) {
|
|
101
|
+
if (old.entry !== next.entry) {
|
|
102
|
+
acc.escalate('breaking');
|
|
103
|
+
acc.diagnostics.push({ code: 'DIFF_NODE_TOPOLOGY_CHANGED', severity: 'error', message: `entry changed ${old.entry} → ${next.entry}` });
|
|
104
|
+
}
|
|
105
|
+
if (JSON.stringify(old.scopes ?? {}) !== JSON.stringify(next.scopes ?? {})) {
|
|
106
|
+
acc.escalate('breaking');
|
|
107
|
+
acc.diagnostics.push({ code: 'DIFF_UNCLASSIFIED', severity: 'error', message: `scopes changed (defaulting to breaking)` });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Two nodes have the same outgoing topology iff their ordered edge `[path,target]` sets match. */
|
|
111
|
+
function sameTopology(a, b) {
|
|
112
|
+
const ea = JSON.stringify(orderedEdges(a));
|
|
113
|
+
const eb = JSON.stringify(orderedEdges(b));
|
|
114
|
+
return ea === eb;
|
|
115
|
+
}
|
|
116
|
+
function orderedEdges(node) {
|
|
117
|
+
return outgoingEdges(node)
|
|
118
|
+
.slice()
|
|
119
|
+
.sort((x, y) => comparePath(x[0], y[0]));
|
|
120
|
+
}
|
|
121
|
+
/** Stable lexical comparison of two edge paths (sort comparator: -1 / 0 / 1). */
|
|
122
|
+
function comparePath(a, b) {
|
|
123
|
+
if (a < b)
|
|
124
|
+
return -1;
|
|
125
|
+
if (a > b)
|
|
126
|
+
return 1;
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Detect a field change that is NOT one of the explicitly-SAFE fields (displayName, reason/prompt-like
|
|
131
|
+
* text, payload `input`) and NOT topology (handled separately). Returns a comma-list of changed
|
|
132
|
+
* unclassified keys, or '' if every change is safe. Guards (`branches.when`, `joinMode`, `merge`,
|
|
133
|
+
* `onFailure`, `incrementCounters`, `outcomes`, `timeout.after`) are NOT in the safe set → breaking.
|
|
134
|
+
*/
|
|
135
|
+
function unclassifiedFieldChange(before, after) {
|
|
136
|
+
// `input` is listed for parity with the doc above; it is a Decision field, not a Node key, so it never
|
|
137
|
+
// reaches this loop — harmless, and keeps the code aligned with its documented safe-set.
|
|
138
|
+
const SAFE_KEYS = new Set(['id', 'displayName', 'reason', 'input']);
|
|
139
|
+
const changed = [];
|
|
140
|
+
const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
141
|
+
for (const key of keys) {
|
|
142
|
+
if (SAFE_KEYS.has(key))
|
|
143
|
+
continue;
|
|
144
|
+
if (isTopologyKey(key))
|
|
145
|
+
continue; // topology handled by sameTopology
|
|
146
|
+
if (key === 'resultSchema')
|
|
147
|
+
continue; // handled above (invalid)
|
|
148
|
+
const bv = JSON.stringify(before[key]);
|
|
149
|
+
const av = JSON.stringify(after[key]);
|
|
150
|
+
if (bv !== av)
|
|
151
|
+
changed.push(key);
|
|
152
|
+
}
|
|
153
|
+
return changed.join(', ');
|
|
154
|
+
}
|
|
155
|
+
function isTopologyKey(key) {
|
|
156
|
+
return key === 'next' || key === 'branches' || key === 'catch' || key === 'escalateTo' || key === 'join';
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=validate-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-diff.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validate-edges — the single model of a node's outgoing edges, shared by the refs (rule 2), topology
|
|
3
|
+
* (rule 3), reachability (rule 5), loop (rule 6), and diff (rule 13) rules. One edge model so those
|
|
4
|
+
* rules cannot disagree about what a node points at.
|
|
5
|
+
*/
|
|
6
|
+
import { isDefaultBranch } from './types.js';
|
|
7
|
+
/** Every outgoing edge of a node as `[path, targetId]` pairs. */
|
|
8
|
+
export function outgoingEdges(node) {
|
|
9
|
+
const edges = [];
|
|
10
|
+
switch (node.kind) {
|
|
11
|
+
case 'agent':
|
|
12
|
+
case 'script':
|
|
13
|
+
edges.push(['next', node.next]);
|
|
14
|
+
(node.catch ?? []).forEach((c, i) => edges.push([`catch[${i}].goto`, c.goto]));
|
|
15
|
+
if (node.escalateTo !== undefined)
|
|
16
|
+
edges.push(['escalateTo', node.escalateTo]);
|
|
17
|
+
break;
|
|
18
|
+
case 'wait':
|
|
19
|
+
edges.push(['next', node.next]);
|
|
20
|
+
break;
|
|
21
|
+
case 'join':
|
|
22
|
+
edges.push(['next', node.next]);
|
|
23
|
+
break;
|
|
24
|
+
case 'humanGate':
|
|
25
|
+
branchTargets(node.branches).forEach(([p, t]) => edges.push([`branches.${p}`, t]));
|
|
26
|
+
if (node.timeout)
|
|
27
|
+
edges.push(['timeout.goto', node.timeout.goto]);
|
|
28
|
+
break;
|
|
29
|
+
case 'choice':
|
|
30
|
+
branchTargets(node.branches).forEach(([p, t]) => edges.push([`branches.${p}`, t]));
|
|
31
|
+
break;
|
|
32
|
+
case 'parallel':
|
|
33
|
+
node.branches.forEach((b, i) => edges.push([`branches[${i}].entry`, b.entry]));
|
|
34
|
+
edges.push(['join', node.join]);
|
|
35
|
+
break;
|
|
36
|
+
case 'terminal':
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
return edges;
|
|
40
|
+
}
|
|
41
|
+
function branchTargets(branches) {
|
|
42
|
+
return branches.map((b, i) => isDefaultBranch(b) ? [`[${i}].default`, b.default] : [`[${i}].goto`, b.goto]);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=validate-edges.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-edges.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-edges.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,IAAU;IACtC,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ;YACX,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC/E,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAC/E,MAAM;QACR,KAAK,MAAM;YACT,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,MAAM;QACR,KAAK,MAAM;YACT,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,MAAM;QACR,KAAK,WAAW;YACd,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACnF,IAAI,IAAI,CAAC,OAAO;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YAClE,MAAM;QACR,KAAK,QAAQ;YACX,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACnF,MAAM;QACR,KAAK,UAAU;YACb,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC/E,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,MAAM;QACR,KAAK,UAAU;YACb,MAAM;IACV,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,QAAkB;IACvC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAoB,EAAE,CAC7C,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAC7E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { isGuardedBranch } from './types.js';
|
|
2
|
+
import { outgoingEdges } from './validate-edges.js';
|
|
3
|
+
/** Forward (non-catch, non-escalate) edges — `catch`/`escalateTo` are failure routes, not loop edges. */
|
|
4
|
+
export function structuralEdges(node) {
|
|
5
|
+
return outgoingEdges(node).filter(([p]) => !p.startsWith('catch') && p !== 'escalateTo');
|
|
6
|
+
}
|
|
7
|
+
/** Nodes reachable from `entry` over EVERY outgoing edge (catch/escalate included). */
|
|
8
|
+
export function reachableFrom(template, entry) {
|
|
9
|
+
const seen = new Set();
|
|
10
|
+
const stack = [entry];
|
|
11
|
+
while (stack.length) {
|
|
12
|
+
const id = stack.pop();
|
|
13
|
+
if (seen.has(id))
|
|
14
|
+
continue;
|
|
15
|
+
seen.add(id);
|
|
16
|
+
const node = template.nodes[id];
|
|
17
|
+
if (!node)
|
|
18
|
+
continue;
|
|
19
|
+
for (const [, target] of outgoingEdges(node))
|
|
20
|
+
if (!seen.has(target))
|
|
21
|
+
stack.push(target);
|
|
22
|
+
}
|
|
23
|
+
return seen;
|
|
24
|
+
}
|
|
25
|
+
/** Nodes structurally reachable from `start` (catch/escalate excluded — failure routes are not flow). */
|
|
26
|
+
export function forwardReach(template, start) {
|
|
27
|
+
const seen = new Set();
|
|
28
|
+
const stack = [start];
|
|
29
|
+
while (stack.length) {
|
|
30
|
+
const id = stack.pop();
|
|
31
|
+
if (seen.has(id))
|
|
32
|
+
continue;
|
|
33
|
+
seen.add(id);
|
|
34
|
+
const node = template.nodes[id];
|
|
35
|
+
if (node)
|
|
36
|
+
for (const [, t] of structuralEdges(node))
|
|
37
|
+
if (!seen.has(t))
|
|
38
|
+
stack.push(t);
|
|
39
|
+
}
|
|
40
|
+
return seen;
|
|
41
|
+
}
|
|
42
|
+
/** Nodes that can structurally reach `target` (predecessor walk over structural edges). */
|
|
43
|
+
export function backwardReach(template, target) {
|
|
44
|
+
const preds = new Map();
|
|
45
|
+
for (const node of Object.values(template.nodes)) {
|
|
46
|
+
for (const [, t] of structuralEdges(node))
|
|
47
|
+
(preds.get(t) ?? preds.set(t, []).get(t)).push(node.id);
|
|
48
|
+
}
|
|
49
|
+
const seen = new Set();
|
|
50
|
+
const stack = [target];
|
|
51
|
+
while (stack.length) {
|
|
52
|
+
const id = stack.pop();
|
|
53
|
+
if (seen.has(id))
|
|
54
|
+
continue;
|
|
55
|
+
seen.add(id);
|
|
56
|
+
for (const p of preds.get(id) ?? [])
|
|
57
|
+
if (!seen.has(p))
|
|
58
|
+
stack.push(p);
|
|
59
|
+
}
|
|
60
|
+
return seen;
|
|
61
|
+
}
|
|
62
|
+
/** Back-edges = forward edges whose target is an ancestor on the DFS stack (a cycle re-entry). */
|
|
63
|
+
export function findBackEdges(template) {
|
|
64
|
+
const back = [];
|
|
65
|
+
const onStack = new Set();
|
|
66
|
+
const done = new Set();
|
|
67
|
+
const dfs = (id) => {
|
|
68
|
+
onStack.add(id);
|
|
69
|
+
const node = template.nodes[id];
|
|
70
|
+
if (node) {
|
|
71
|
+
for (const [, target] of structuralEdges(node)) {
|
|
72
|
+
if (onStack.has(target))
|
|
73
|
+
back.push({ from: id, to: target });
|
|
74
|
+
else if (!done.has(target))
|
|
75
|
+
dfs(target);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
onStack.delete(id);
|
|
79
|
+
done.add(id);
|
|
80
|
+
};
|
|
81
|
+
if (template.entry && template.nodes[template.entry])
|
|
82
|
+
dfs(template.entry);
|
|
83
|
+
return back;
|
|
84
|
+
}
|
|
85
|
+
/** Nodes on a cycle that re-enters `to` via the back-edge from `from` (path to→…→from + from). */
|
|
86
|
+
export function cycleNodes(template, to, from) {
|
|
87
|
+
// Forward-reachable from `to` (staying inside the SCC reaching `from`) — a simple over-approx is the
|
|
88
|
+
// set of nodes on some path to→…→from. We collect nodes that can both reach `from` and are reached
|
|
89
|
+
// from `to`, which is exactly the cycle's SCC member set for these structural edges.
|
|
90
|
+
const fromTo = forwardReach(template, to);
|
|
91
|
+
const canReachFrom = backwardReach(template, from);
|
|
92
|
+
const cycle = new Set();
|
|
93
|
+
for (const id of fromTo)
|
|
94
|
+
if (canReachFrom.has(id))
|
|
95
|
+
cycle.add(id);
|
|
96
|
+
cycle.add(to);
|
|
97
|
+
cycle.add(from);
|
|
98
|
+
return cycle;
|
|
99
|
+
}
|
|
100
|
+
/** Nodes reachable from a branch `entry`, stopping AT the join (the join itself is not a member). */
|
|
101
|
+
export function branchSubgraph(template, entry, joinId) {
|
|
102
|
+
const seen = new Set();
|
|
103
|
+
const stack = [entry];
|
|
104
|
+
while (stack.length) {
|
|
105
|
+
const id = stack.pop();
|
|
106
|
+
if (id === joinId || seen.has(id))
|
|
107
|
+
continue;
|
|
108
|
+
seen.add(id);
|
|
109
|
+
const node = template.nodes[id];
|
|
110
|
+
if (node)
|
|
111
|
+
for (const [, t] of structuralEdges(node))
|
|
112
|
+
if (t !== joinId && !seen.has(t))
|
|
113
|
+
stack.push(t);
|
|
114
|
+
}
|
|
115
|
+
return seen;
|
|
116
|
+
}
|
|
117
|
+
/** Every guard Condition on a node (choice/humanGate branches that carry a `when`). */
|
|
118
|
+
export function guardConditionsOf(node) {
|
|
119
|
+
if (node.kind !== 'choice' && node.kind !== 'humanGate')
|
|
120
|
+
return [];
|
|
121
|
+
return node.branches.filter(isGuardedBranch).map((b) => b.when);
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=validate-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-graph.js","sourceRoot":"","sources":["../../src/pipeline-core/validate-graph.ts"],"names":[],"mappings":"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"}
|