@revisium/orchestrator 0.1.0-alpha.1 → 0.1.0-alpha.3
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/control-plane/bootstrap.config.json +47 -2
- package/control-plane/default-playbook/catalog/pipelines.json +329 -0
- package/control-plane/default-playbook/catalog/roles.json +56 -0
- package/control-plane/default-playbook/package.json +5 -0
- package/control-plane/default-playbook/playbook.json +13 -0
- package/control-plane/default-playbook/prompts/analyst.md +27 -0
- package/control-plane/default-playbook/prompts/developer.md +25 -0
- package/control-plane/default-playbook/prompts/integrator.md +20 -0
- package/control-plane/default-playbook/prompts/orchestrator.md +23 -0
- package/control-plane/default-playbook/prompts/reviewer.md +27 -0
- package/control-plane/default-playbook/prompts/watcher.md +24 -0
- package/dist/cli/commands/bootstrap.js +68 -7
- package/dist/cli/commands/bootstrap.js.map +1 -1
- package/dist/cli/commands/run.js +76 -49
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/needs-host.js +7 -8
- package/dist/cli/needs-host.js.map +1 -1
- package/dist/cli/program.js +0 -2
- package/dist/cli/program.js.map +1 -1
- package/dist/config.js +18 -1
- package/dist/config.js.map +1 -1
- package/dist/control-plane/client-transport.js +73 -40
- package/dist/control-plane/client-transport.js.map +1 -1
- package/dist/control-plane/data-access.js +3 -2
- package/dist/control-plane/data-access.js.map +1 -1
- package/dist/control-plane/definitions.js +1 -1
- package/dist/control-plane/definitions.js.map +1 -1
- package/dist/control-plane/json-fields.js +1 -0
- package/dist/control-plane/json-fields.js.map +1 -1
- package/dist/control-plane/schema-migration.js +79 -0
- package/dist/control-plane/schema-migration.js.map +1 -0
- package/dist/control-plane/seed-default-playbook.js +71 -0
- package/dist/control-plane/seed-default-playbook.js.map +1 -0
- package/dist/e2e/kit/agents.js +113 -0
- package/dist/e2e/kit/agents.js.map +1 -0
- package/dist/e2e/kit/assertions.js +119 -0
- package/dist/e2e/kit/assertions.js.map +1 -0
- package/dist/e2e/kit/crash.js +44 -0
- package/dist/e2e/kit/crash.js.map +1 -0
- package/dist/e2e/kit/drive.js +49 -0
- package/dist/e2e/kit/drive.js.map +1 -0
- package/dist/e2e/kit/env.js +16 -0
- package/dist/e2e/kit/env.js.map +1 -0
- package/dist/e2e/kit/fake-integrator.js +42 -0
- package/dist/e2e/kit/fake-integrator.js.map +1 -0
- package/dist/e2e/kit/gh-emulator.js +49 -0
- package/dist/e2e/kit/gh-emulator.js.map +1 -0
- package/dist/e2e/kit/git-target-repo.js +83 -0
- package/dist/e2e/kit/git-target-repo.js.map +1 -0
- package/dist/e2e/kit/harness.js +59 -0
- package/dist/e2e/kit/harness.js.map +1 -0
- package/dist/e2e/kit/index.js +14 -0
- package/dist/e2e/kit/index.js.map +1 -0
- package/dist/e2e/kit/mcp.js +32 -0
- package/dist/e2e/kit/mcp.js.map +1 -0
- package/dist/e2e/kit/scenarios.js +196 -0
- package/dist/e2e/kit/scenarios.js.map +1 -0
- package/dist/e2e/recovery-crash-child.js +29 -0
- package/dist/e2e/recovery-crash-child.js.map +1 -0
- package/dist/e2e/recovery-dd-crash-child.js +42 -0
- package/dist/e2e/recovery-dd-crash-child.js.map +1 -0
- package/dist/engine/ensure-postgres.js +13 -1
- package/dist/engine/ensure-postgres.js.map +1 -1
- package/dist/mcp/mcp-capabilities.js +1 -0
- package/dist/mcp/mcp-capabilities.js.map +1 -1
- package/dist/mcp/mcp-facade.service.js +24 -3
- package/dist/mcp/mcp-facade.service.js.map +1 -1
- package/dist/mcp/mcp-tools.js +16 -7
- package/dist/mcp/mcp-tools.js.map +1 -1
- package/dist/pipeline/data-driven-task.workflow.js +403 -0
- package/dist/pipeline/data-driven-task.workflow.js.map +1 -0
- package/dist/pipeline/data-driven-template.js +57 -0
- package/dist/pipeline/data-driven-template.js.map +1 -0
- package/dist/pipeline/develop-task.workflow.js +150 -308
- package/dist/pipeline/develop-task.workflow.js.map +1 -1
- package/dist/pipeline/pipeline.module.js +1 -1
- package/dist/pipeline/route-contract.js +82 -0
- package/dist/pipeline/route-contract.js.map +1 -0
- package/dist/pipeline-core/index.js +16 -0
- package/dist/pipeline-core/index.js.map +1 -0
- package/dist/pipeline-core/interpret.js +351 -0
- package/dist/pipeline-core/interpret.js.map +1 -0
- package/dist/pipeline-core/kit/assertions.js +61 -0
- package/dist/pipeline-core/kit/assertions.js.map +1 -0
- package/dist/pipeline-core/kit/builders.js +134 -0
- package/dist/pipeline-core/kit/builders.js.map +1 -0
- package/dist/pipeline-core/kit/drive.js +96 -0
- package/dist/pipeline-core/kit/drive.js.map +1 -0
- package/dist/pipeline-core/kit/fixtures.js +248 -0
- package/dist/pipeline-core/kit/fixtures.js.map +1 -0
- package/dist/pipeline-core/kit/index.js +13 -0
- package/dist/pipeline-core/kit/index.js.map +1 -0
- package/dist/pipeline-core/types.js +124 -0
- package/dist/pipeline-core/types.js.map +1 -0
- package/dist/pipeline-core/validate.js +1032 -0
- package/dist/pipeline-core/validate.js.map +1 -0
- package/dist/playbook/catalog-loader.js +14 -1
- package/dist/playbook/catalog-loader.js.map +1 -1
- package/dist/playbook/import-mapper.js +14 -38
- package/dist/playbook/import-mapper.js.map +1 -1
- package/dist/playbook/manifest.js +1 -1
- package/dist/revisium/playbooks.service.js +95 -17
- package/dist/revisium/playbooks.service.js.map +1 -1
- package/dist/revisium/roles.service.js +1 -1
- package/dist/revisium/roles.service.js.map +1 -1
- package/dist/revisium/run.service.js +16 -0
- package/dist/revisium/run.service.js.map +1 -1
- package/dist/run/append-event.js +24 -1
- package/dist/run/append-event.js.map +1 -1
- package/dist/run/block-run.js +17 -0
- package/dist/run/block-run.js.map +1 -0
- package/dist/run/complete-run.js +18 -0
- package/dist/run/complete-run.js.map +1 -0
- package/dist/run/create-run.js +28 -5
- package/dist/run/create-run.js.map +1 -1
- package/dist/run/inspect-run.js +9 -0
- package/dist/run/inspect-run.js.map +1 -1
- package/dist/run/terminal-run-status.js +89 -7
- package/dist/run/terminal-run-status.js.map +1 -1
- package/dist/runners/claude-code.service.js +4 -0
- package/dist/runners/claude-code.service.js.map +1 -1
- package/dist/runners/integrator.js +25 -9
- package/dist/runners/integrator.js.map +1 -1
- package/dist/task-control-plane/task-control-plane-api.service.js +247 -19
- package/dist/task-control-plane/task-control-plane-api.service.js.map +1 -1
- package/dist/worker/artifact-store.js +111 -0
- package/dist/worker/artifact-store.js.map +1 -0
- package/dist/worker/claude-code-runner.js +44 -5
- package/dist/worker/claude-code-runner.js.map +1 -1
- package/dist/worker/process-executor.js +23 -2
- package/dist/worker/process-executor.js.map +1 -1
- package/dist/worker/runner-dispatch.js +1 -0
- package/dist/worker/runner-dispatch.js.map +1 -1
- package/dist/worker/runner.js +11 -1
- package/dist/worker/runner.js.map +1 -1
- package/dist/worker/stub-runner.js +7 -45
- package/dist/worker/stub-runner.js.map +1 -1
- package/package.json +3 -1
- package/dist/cli/commands/work.js +0 -149
- package/dist/cli/commands/work.js.map +0 -1
- package/dist/worker/git-worktree-manager.js +0 -84
- package/dist/worker/git-worktree-manager.js.map +0 -1
- package/dist/worker/loop.js +0 -122
- package/dist/worker/loop.js.map +0 -1
- package/dist/worker/script-runner.js +0 -28
- package/dist/worker/script-runner.js.map +0 -1
- package/dist/worker/worker-id.js +0 -18
- package/dist/worker/worker-id.js.map +0 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const GATE_ID_BY_CANONICAL_LABEL = {
|
|
2
|
+
'task spec approval': 'plan',
|
|
3
|
+
'merge approval': 'merge',
|
|
4
|
+
};
|
|
5
|
+
function asRecord(value) {
|
|
6
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
7
|
+
return null;
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
function asStringMap(value) {
|
|
11
|
+
const record = asRecord(value);
|
|
12
|
+
if (!record)
|
|
13
|
+
return {};
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const [key, item] of Object.entries(record)) {
|
|
16
|
+
if (typeof item === 'string' && item.trim() !== '')
|
|
17
|
+
out[key] = item;
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
function asStringArray(value) {
|
|
22
|
+
if (!Array.isArray(value))
|
|
23
|
+
return undefined;
|
|
24
|
+
return value.filter((item) => typeof item === 'string' && item.trim() !== '');
|
|
25
|
+
}
|
|
26
|
+
export function normalizeParams(value) {
|
|
27
|
+
const record = asRecord(value);
|
|
28
|
+
if (!record)
|
|
29
|
+
return {};
|
|
30
|
+
const { executionProfile: _executionProfile, execution_profile: _executionProfileSnake, runnerOverrides: _runnerOverrides, runner_overrides: _runnerOverridesSnake, availableRunners: _availableRunners, available_runners: _availableRunnersSnake, ...publicParams } = record;
|
|
31
|
+
return publicParams;
|
|
32
|
+
}
|
|
33
|
+
export function normalizeRouteGates(value) {
|
|
34
|
+
if (!Array.isArray(value))
|
|
35
|
+
return [];
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const item of value) {
|
|
39
|
+
if (typeof item !== 'string')
|
|
40
|
+
continue;
|
|
41
|
+
const gate = item.trim();
|
|
42
|
+
if (!gate)
|
|
43
|
+
continue;
|
|
44
|
+
const normalized = GATE_ID_BY_CANONICAL_LABEL[gate.toLowerCase()] ?? gate;
|
|
45
|
+
if (seen.has(normalized))
|
|
46
|
+
continue;
|
|
47
|
+
seen.add(normalized);
|
|
48
|
+
out.push(normalized);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
export function normalizeExecutionProfile(value) {
|
|
53
|
+
const raw = asRecord(value) ?? {};
|
|
54
|
+
return {
|
|
55
|
+
id: typeof raw.id === 'string' && raw.id.trim() !== '' ? raw.id : 'default',
|
|
56
|
+
runnerOverrides: {
|
|
57
|
+
...asStringMap(raw.runnerOverrides),
|
|
58
|
+
...asStringMap(raw.runner_overrides),
|
|
59
|
+
},
|
|
60
|
+
availableRunners: asStringArray(raw.availableRunners) ?? asStringArray(raw.available_runners),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function resolveRunnerForProfile(runnerId, executionProfile) {
|
|
64
|
+
const resolved = executionProfile.runnerOverrides[runnerId];
|
|
65
|
+
if (resolved)
|
|
66
|
+
return { runnerId: resolved, source: 'execution-profile' };
|
|
67
|
+
return { runnerId, source: 'playbook' };
|
|
68
|
+
}
|
|
69
|
+
export function dispatchRunnerId(runnerId) {
|
|
70
|
+
if (runnerId === 'stub-agent')
|
|
71
|
+
return 'script';
|
|
72
|
+
if (runnerId === 'claude-code' || runnerId === 'codex' || runnerId === 'script')
|
|
73
|
+
return runnerId;
|
|
74
|
+
return runnerId.startsWith('revo-') ? 'script' : runnerId;
|
|
75
|
+
}
|
|
76
|
+
export function runnerNeedsLivePreflight(runnerId) {
|
|
77
|
+
return runnerId === 'claude-code' || runnerId === 'codex' || runnerId === 'revo-integrator' || runnerId === 'revo-merger';
|
|
78
|
+
}
|
|
79
|
+
export function runnerUsesRealIntegrator(runnerId) {
|
|
80
|
+
return runnerId === 'revo-integrator' || runnerId === 'revo-merger';
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=route-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-contract.js","sourceRoot":"","sources":["../../src/pipeline/route-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pipeline-core — the pure, framework-free pipeline state machine (plan 0015, slice 1).
|
|
3
|
+
*
|
|
4
|
+
* ZERO imports from NestJS / DBOS / Revisium / runners / any I/O; deterministic (no clocks/randomness).
|
|
5
|
+
* The DBOS effect-adapter (slice 2) consumes this surface:
|
|
6
|
+
* - `validateTemplate` / `classifyTemplateDiff` — install-time validation (§12).
|
|
7
|
+
* - `step` — the pure reducer `(template, state, lastResult) -> { state, decision }` (§10).
|
|
8
|
+
* - `initialState` — build the entry cursor.
|
|
9
|
+
*
|
|
10
|
+
* The `kit/` sub-path is a TEST helper (builders/fixtures/drive/assertions) and is intentionally NOT
|
|
11
|
+
* re-exported here — production code depends only on these primitives.
|
|
12
|
+
*/
|
|
13
|
+
export * from './types.js';
|
|
14
|
+
export { validateTemplate, classifyTemplateDiff } from './validate.js';
|
|
15
|
+
export { step, initialState, evalCondition, selectJoinWinner, applyCounterMutations, InterpretError } from './interpret.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pipeline-core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEvE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pipeline-core/interpret.ts — the pure deterministic reducer (§10).
|
|
3
|
+
*
|
|
4
|
+
* step(template, state, lastResult) -> { state, decision }
|
|
5
|
+
*
|
|
6
|
+
* One call advances the run by exactly one OBSERVABLE step. Given the current cursor and the RECORDED
|
|
7
|
+
* result of the previously-emitted Decision, it computes the NEXT cursor (updated/reset scoped
|
|
8
|
+
* counters + activeNodeIds + status) AND the next effect `decision`. Total + deterministic: no clocks,
|
|
9
|
+
* no randomness, no live reads — every external fact arrives as recorded data on `lastResult` (§10).
|
|
10
|
+
*
|
|
11
|
+
* The adapter loop (§10): `{state, decision} = step(t, state, lastResult)` → execute `decision` as a
|
|
12
|
+
* durable step → record its result → feed that back as the next `lastResult` → repeat until
|
|
13
|
+
* `decision.type === 'complete'`.
|
|
14
|
+
*
|
|
15
|
+
* Model. The active node is the one whose Decision was last emitted (or the entry on the first call).
|
|
16
|
+
* `step` first RESOLVES that node against `lastResult` (route a gate/choice by its branches; route an
|
|
17
|
+
* agent/script by its core outcome via §6 precedence; aggregate a join's recorded arrivals), walking
|
|
18
|
+
* through pure routing nodes (`choice`/`join`) until it reaches a node that emits an effect
|
|
19
|
+
* (`agent`/`script`/`humanGate`/`wait`/`parallel`/`terminal`). Loop-entry counter mutations (§7) are
|
|
20
|
+
* applied as each node is entered, BEFORE its guards are evaluated, so a cap-guard sees the
|
|
21
|
+
* post-increment value. Core verdicts route STRUCTURALLY (catch/onFailure/terminal/timeout) and NEVER
|
|
22
|
+
* via branch guards (§3/§6).
|
|
23
|
+
*/
|
|
24
|
+
/** Thrown when the interpreter hits a state a VALID template can never produce (a caller/data bug). */
|
|
25
|
+
export class InterpretError extends Error {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'InterpretError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const STATUS_BY_TERMINAL = {
|
|
32
|
+
succeeded: 'succeeded',
|
|
33
|
+
failed: 'failed',
|
|
34
|
+
blocked: 'blocked',
|
|
35
|
+
};
|
|
36
|
+
/** Kinds that SUSPEND: they emit a Decision and stay active until their result is fed back. */
|
|
37
|
+
function suspends(node) {
|
|
38
|
+
return (node.kind === 'agent' ||
|
|
39
|
+
node.kind === 'script' ||
|
|
40
|
+
node.kind === 'humanGate' ||
|
|
41
|
+
node.kind === 'wait' ||
|
|
42
|
+
node.kind === 'parallel' ||
|
|
43
|
+
node.kind === 'terminal');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build the initial cursor: the entry node active, every declared scope at 0, no recorded result.
|
|
47
|
+
* The first `step(t, initialState(t), undefined)` emits the entry node's first Decision.
|
|
48
|
+
*/
|
|
49
|
+
export function initialState(template) {
|
|
50
|
+
const scopedCounters = {};
|
|
51
|
+
for (const scopeId of Object.keys(template.scopes ?? {}))
|
|
52
|
+
scopedCounters[scopeId] = 0;
|
|
53
|
+
return { activeNodeIds: new Set([template.entry]), scopedCounters, status: 'running' };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Advance one observable step. Resolves the active node against `lastResult`, walks any pure routing
|
|
57
|
+
* nodes, and returns the next suspending node's Decision plus the next cursor.
|
|
58
|
+
*
|
|
59
|
+
* On the FIRST call (entry just activated, no Decision emitted yet) the active node has not run, so
|
|
60
|
+
* `lastResult` describes nothing for it: we emit its Decision directly. On every later call the active
|
|
61
|
+
* node is a suspended one whose recorded result is in `lastResult`: we route PAST it.
|
|
62
|
+
*/
|
|
63
|
+
export function step(template, state, lastResult) {
|
|
64
|
+
const active = resolveNode(template, soleActiveNodeId(state));
|
|
65
|
+
// First emission of a freshly-entered suspending node: no result to consume yet — emit its Decision.
|
|
66
|
+
if (lastResult === undefined && suspends(active)) {
|
|
67
|
+
return emit(template, active, state);
|
|
68
|
+
}
|
|
69
|
+
// Otherwise consume the active node's recorded result and route onward.
|
|
70
|
+
return routeFrom(template, active, state, lastResult);
|
|
71
|
+
}
|
|
72
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
73
|
+
// Routing — consume the active node's result, compute the successor id, then enter it.
|
|
74
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
75
|
+
function routeFrom(template, node, state, lastResult) {
|
|
76
|
+
switch (node.kind) {
|
|
77
|
+
case 'terminal':
|
|
78
|
+
// A terminal has no successor; re-resolving it just re-asserts completion (idempotent). The
|
|
79
|
+
// cursor SITS on the terminal (activeNodeIds = {terminal}); the adapter detects done via
|
|
80
|
+
// status/decision, not an empty active set.
|
|
81
|
+
return {
|
|
82
|
+
state: { ...state, activeNodeIds: new Set([node.id]), status: STATUS_BY_TERMINAL[node.status] },
|
|
83
|
+
decision: { type: 'complete', status: node.status },
|
|
84
|
+
};
|
|
85
|
+
case 'agent':
|
|
86
|
+
case 'script':
|
|
87
|
+
return routeEffect(template, node, state, lastResult);
|
|
88
|
+
case 'humanGate': {
|
|
89
|
+
// A gate's recorded timeout firing routes via timeout.goto (§6), NOT a branch guard.
|
|
90
|
+
if (lastResult?.outcome === 'timed_out') {
|
|
91
|
+
if (!node.timeout) {
|
|
92
|
+
throw new InterpretError(`gate ${node.id} timed_out but has no timeout edge (invalid)`);
|
|
93
|
+
}
|
|
94
|
+
return enter(template, node.timeout.goto, state, undefined);
|
|
95
|
+
}
|
|
96
|
+
const goto = evalBranches(template, node.id, node.branches, state, lastResult);
|
|
97
|
+
return enter(template, goto, state, carryVerdict(lastResult));
|
|
98
|
+
}
|
|
99
|
+
case 'choice': {
|
|
100
|
+
const goto = evalBranches(template, node.id, node.branches, state, lastResult);
|
|
101
|
+
return enter(template, goto, state, carryVerdict(lastResult));
|
|
102
|
+
}
|
|
103
|
+
case 'wait':
|
|
104
|
+
return enter(template, node.next, state, undefined);
|
|
105
|
+
case 'join': {
|
|
106
|
+
const arrivals = lastResult?.joinArrivals ?? [];
|
|
107
|
+
const winner = selectJoinWinner(node.joinMode, arrivals, node.id);
|
|
108
|
+
const forward = winner?.verdict === undefined ? undefined : { verdict: winner.verdict };
|
|
109
|
+
return enter(template, node.next, state, forward);
|
|
110
|
+
}
|
|
111
|
+
case 'parallel':
|
|
112
|
+
// A parallel's "result" is the join's arrivals; routing past a parallel is meaningless —
|
|
113
|
+
// the join consumes the arrivals. Reaching here means the loop mis-fed a result.
|
|
114
|
+
throw new InterpretError(`routeFrom called on parallel ${node.id} (feed the join, not the fork)`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** §6 failure-precedence routing for an `agent`/`script` node from its RECORDED outcome. */
|
|
118
|
+
function routeEffect(template, node, state, lastResult) {
|
|
119
|
+
const outcome = lastResult?.outcome;
|
|
120
|
+
if (outcome === undefined || outcome === 'succeeded') {
|
|
121
|
+
return enter(template, node.next, state, carryVerdict(lastResult));
|
|
122
|
+
}
|
|
123
|
+
// Failure (`failed`/`errored`). Precedence: matching catch → onFailure (abort/route/escalate).
|
|
124
|
+
const code = lastResult?.errorCode;
|
|
125
|
+
const matched = code ? node.catch?.find((c) => c.onError === code) : undefined;
|
|
126
|
+
if (matched)
|
|
127
|
+
return enter(template, matched.goto, state, undefined);
|
|
128
|
+
const policy = node.onFailure ?? 'abort';
|
|
129
|
+
if (policy === 'abort') {
|
|
130
|
+
// The run fails AT the aborting node; the cursor stays on it (no terminal node to route to).
|
|
131
|
+
return {
|
|
132
|
+
state: { ...state, activeNodeIds: new Set([node.id]), status: 'failed' },
|
|
133
|
+
decision: { type: 'complete', status: 'failed' },
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (policy === 'escalate') {
|
|
137
|
+
if (!node.escalateTo) {
|
|
138
|
+
throw new InterpretError(`node ${node.id} onFailure=escalate but escalateTo missing (invalid)`);
|
|
139
|
+
}
|
|
140
|
+
return enter(template, node.escalateTo, state, undefined);
|
|
141
|
+
}
|
|
142
|
+
// 'route' with no matching catch — a VALID template forbids this (§6 / §12 FAILURE_ROUTE_NO_CATCH).
|
|
143
|
+
throw new InterpretError(`node ${node.id} onFailure=route but no catch matched ${code ?? '<no code>'} (invalid)`);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* ENTER a node: apply its loop-entry counter mutations (§7), make it the sole active node, then
|
|
147
|
+
* either emit its Decision (suspending kinds) or recursively route through it (pure `choice`/`join`).
|
|
148
|
+
* `incoming` is the verdict/arrivals context the entered node's guards may read.
|
|
149
|
+
*/
|
|
150
|
+
function enter(template, nodeId, state, incoming) {
|
|
151
|
+
const node = resolveNode(template, nodeId);
|
|
152
|
+
const scopedCounters = applyCounterMutations(template, state.scopedCounters, incrementCountersOf(node));
|
|
153
|
+
const entered = { ...state, scopedCounters, activeNodeIds: new Set([nodeId]) };
|
|
154
|
+
if (suspends(node))
|
|
155
|
+
return emit(template, node, entered);
|
|
156
|
+
// Pure routing node — resolve it immediately against the incoming context.
|
|
157
|
+
return routeFrom(template, node, entered, incoming);
|
|
158
|
+
}
|
|
159
|
+
/** Emit the Decision of a freshly-entered SUSPENDING node and set the matching run status. */
|
|
160
|
+
function emit(template, node, state) {
|
|
161
|
+
switch (node.kind) {
|
|
162
|
+
case 'agent':
|
|
163
|
+
return {
|
|
164
|
+
state: activate(state, node.id, 'running'),
|
|
165
|
+
decision: { type: 'invokeRole', nodeId: node.id, roleRef: node.roleRef, input: {} },
|
|
166
|
+
};
|
|
167
|
+
case 'script':
|
|
168
|
+
return {
|
|
169
|
+
state: activate(state, node.id, 'running'),
|
|
170
|
+
decision: { type: 'invokeScript', nodeId: node.id, scriptRef: node.scriptRef, input: {} },
|
|
171
|
+
};
|
|
172
|
+
case 'humanGate':
|
|
173
|
+
return {
|
|
174
|
+
state: activate(state, node.id, 'awaiting_gate'),
|
|
175
|
+
decision: {
|
|
176
|
+
type: 'awaitGate',
|
|
177
|
+
nodeId: node.id,
|
|
178
|
+
reason: node.reason,
|
|
179
|
+
outcomes: node.outcomes,
|
|
180
|
+
...(node.timeout ? { timeout: node.timeout } : {}),
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
case 'wait':
|
|
184
|
+
return {
|
|
185
|
+
state: activate(state, node.id, 'running'),
|
|
186
|
+
decision: { type: 'startTimer', nodeId: node.id, duration: node.duration },
|
|
187
|
+
};
|
|
188
|
+
case 'parallel':
|
|
189
|
+
return {
|
|
190
|
+
state: { ...state, activeNodeIds: new Set(node.branches.map((b) => b.entry)), status: 'running' },
|
|
191
|
+
decision: {
|
|
192
|
+
type: 'fork',
|
|
193
|
+
nodeId: node.id,
|
|
194
|
+
branches: node.branches,
|
|
195
|
+
joinId: node.join,
|
|
196
|
+
mode: joinModeOf(template, node.join),
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
case 'terminal':
|
|
200
|
+
return {
|
|
201
|
+
state: { ...state, activeNodeIds: new Set([node.id]), status: STATUS_BY_TERMINAL[node.status] },
|
|
202
|
+
decision: { type: 'complete', status: node.status },
|
|
203
|
+
};
|
|
204
|
+
default:
|
|
205
|
+
// choice/join never suspend — emit() is only called on suspending kinds.
|
|
206
|
+
throw new InterpretError(`emit() called on non-suspending node ${node.id} (${node.kind})`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function activate(state, nodeId, status) {
|
|
210
|
+
return { ...state, activeNodeIds: new Set([nodeId]), status };
|
|
211
|
+
}
|
|
212
|
+
/** Forward only a domain verdict (core outcomes are consumed structurally, never carried forward). */
|
|
213
|
+
function carryVerdict(lastResult) {
|
|
214
|
+
return lastResult?.verdict === undefined ? undefined : { verdict: lastResult.verdict };
|
|
215
|
+
}
|
|
216
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
217
|
+
// Guard evaluation (§3) — total, first-true-wins, mandatory default.
|
|
218
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
219
|
+
function evalBranches(template, nodeId, branches, state, lastResult) {
|
|
220
|
+
let fallback;
|
|
221
|
+
for (const b of branches) {
|
|
222
|
+
if (b.default !== undefined) {
|
|
223
|
+
fallback = b.default;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (b.when && b.goto !== undefined && evalCondition(b.when, state, lastResult))
|
|
227
|
+
return b.goto;
|
|
228
|
+
}
|
|
229
|
+
if (fallback === undefined) {
|
|
230
|
+
throw new InterpretError(`node ${nodeId} has no default branch (invalid template)`);
|
|
231
|
+
}
|
|
232
|
+
return fallback;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Evaluate a v1 Condition (§3). Reads ONLY the recorded DOMAIN verdict (`lastResult.verdict`) and the
|
|
236
|
+
* scoped counters in `state` — never a core verdict (those route structurally, §3/§6).
|
|
237
|
+
*/
|
|
238
|
+
export function evalCondition(cond, state, lastResult) {
|
|
239
|
+
switch (cond.op) {
|
|
240
|
+
case 'verdict.eq':
|
|
241
|
+
return lastResult?.verdict === cond.value;
|
|
242
|
+
case 'verdict.in':
|
|
243
|
+
return lastResult?.verdict !== undefined && cond.value.includes(lastResult.verdict);
|
|
244
|
+
case 'counter.lt':
|
|
245
|
+
return counterValue(state, cond.scope) < cond.value;
|
|
246
|
+
case 'counter.gte':
|
|
247
|
+
return counterValue(state, cond.scope) >= cond.value;
|
|
248
|
+
case 'all':
|
|
249
|
+
return cond.of.every((c) => evalCondition(c, state, lastResult));
|
|
250
|
+
case 'any':
|
|
251
|
+
return cond.of.some((c) => evalCondition(c, state, lastResult));
|
|
252
|
+
case 'not':
|
|
253
|
+
return !evalCondition(cond.cond, state, lastResult);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function counterValue(state, scope) {
|
|
257
|
+
return state.scopedCounters[scope] ?? 0;
|
|
258
|
+
}
|
|
259
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
260
|
+
// §7 Counter mutation — increment + descendant reset, deterministic & pure.
|
|
261
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
262
|
+
/**
|
|
263
|
+
* Apply a node's `incrementCounters` to a counter map: +1 each named scope and RESET every
|
|
264
|
+
* descendant scope of each (entering a scope resets its descendants, §7). Returns a fresh map.
|
|
265
|
+
*/
|
|
266
|
+
export function applyCounterMutations(template, current, increments) {
|
|
267
|
+
if (increments.length === 0)
|
|
268
|
+
return { ...current };
|
|
269
|
+
const next = { ...current };
|
|
270
|
+
const scopes = template.scopes ?? {};
|
|
271
|
+
for (const scopeId of increments) {
|
|
272
|
+
next[scopeId] = (next[scopeId] ?? 0) + 1;
|
|
273
|
+
for (const descendant of Object.keys(scopes)) {
|
|
274
|
+
if (descendant !== scopeId && isDescendantScope(scopes, descendant, scopeId)) {
|
|
275
|
+
next[descendant] = 0;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return next;
|
|
280
|
+
}
|
|
281
|
+
/** True when `candidate`'s ancestor chain (via `parent`) passes through `ancestor`. */
|
|
282
|
+
function isDescendantScope(scopes, candidate, ancestor) {
|
|
283
|
+
let cursor = scopes[candidate]?.parent ?? null;
|
|
284
|
+
const seen = new Set();
|
|
285
|
+
while (cursor !== null) {
|
|
286
|
+
if (cursor === ancestor)
|
|
287
|
+
return true;
|
|
288
|
+
if (seen.has(cursor))
|
|
289
|
+
break; // defensive against a malformed cycle (validation rejects these)
|
|
290
|
+
seen.add(cursor);
|
|
291
|
+
cursor = scopes[cursor]?.parent ?? null;
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
function incrementCountersOf(node) {
|
|
296
|
+
return 'incrementCounters' in node ? (node.incrementCounters ?? []) : [];
|
|
297
|
+
}
|
|
298
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
299
|
+
// §4 Join — consume recorded arrivals; pick a deterministic winner.
|
|
300
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
301
|
+
/**
|
|
302
|
+
* Select the join winner from durably-recorded arrivals (§4), in the canonical recorded order
|
|
303
|
+
* (`seq` asc, branchId tie-break):
|
|
304
|
+
* - `all` → the barrier; the winner is the last arrival (its verdict is forwarded);
|
|
305
|
+
* - `any` → the first arrival;
|
|
306
|
+
* - `quorum` → the K-th arrival.
|
|
307
|
+
* The core never sees the live race; the adapter supplies the recorded order + cancelled set.
|
|
308
|
+
*/
|
|
309
|
+
export function selectJoinWinner(mode, arrivals, joinId) {
|
|
310
|
+
const ordered = [...arrivals].sort((a, b) => a.seq - b.seq || cmp(a.branchId, b.branchId));
|
|
311
|
+
if (mode.kind === 'any')
|
|
312
|
+
return ordered[0];
|
|
313
|
+
if (mode.kind === 'quorum') {
|
|
314
|
+
if (ordered.length < mode.count) {
|
|
315
|
+
throw new InterpretError(`join ${joinId} quorum needs ${mode.count}, has ${ordered.length} recorded arrivals`);
|
|
316
|
+
}
|
|
317
|
+
return ordered[mode.count - 1];
|
|
318
|
+
}
|
|
319
|
+
return ordered.at(-1); // 'all'
|
|
320
|
+
}
|
|
321
|
+
function cmp(a, b) {
|
|
322
|
+
if (a < b)
|
|
323
|
+
return -1;
|
|
324
|
+
if (a > b)
|
|
325
|
+
return 1;
|
|
326
|
+
return 0;
|
|
327
|
+
}
|
|
328
|
+
function joinModeOf(template, joinId) {
|
|
329
|
+
const join = resolveNode(template, joinId);
|
|
330
|
+
if (join.kind !== 'join') {
|
|
331
|
+
throw new InterpretError(`fork target ${joinId} is not a join (${join.kind})`);
|
|
332
|
+
}
|
|
333
|
+
return join.joinMode;
|
|
334
|
+
}
|
|
335
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
336
|
+
// Helpers.
|
|
337
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
338
|
+
function resolveNode(template, id) {
|
|
339
|
+
const node = template.nodes[id];
|
|
340
|
+
if (!node)
|
|
341
|
+
throw new InterpretError(`unknown node id "${id}" (invalid template)`);
|
|
342
|
+
return node;
|
|
343
|
+
}
|
|
344
|
+
function soleActiveNodeId(state) {
|
|
345
|
+
const ids = [...state.activeNodeIds];
|
|
346
|
+
if (ids.length !== 1) {
|
|
347
|
+
throw new InterpretError(`step() expects exactly one active node, got ${ids.length} [${ids.join(', ')}]`);
|
|
348
|
+
}
|
|
349
|
+
return ids[0];
|
|
350
|
+
}
|
|
351
|
+
//# sourceMappingURL=interpret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interpret.js","sourceRoot":"","sources":["../../src/pipeline-core/interpret.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pipeline-core/kit/assertions.ts — readable assertions over validation + drive results.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the e2e kit's assertion style (`assertCompleted`, `assertEventsPresent`): each helper states
|
|
5
|
+
* an intent in one call with a clear failure message, so the test body reads as a spec sentence.
|
|
6
|
+
*/
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { validateTemplate } from '../validate.js';
|
|
9
|
+
/** Assert the template has NO diagnostics (valid). On failure, prints the offending codes. */
|
|
10
|
+
export function assertValid(template) {
|
|
11
|
+
const diags = validateTemplate(template);
|
|
12
|
+
const errors = diags.filter((d) => d.severity === 'error');
|
|
13
|
+
const offending = errors.map((d) => `${d.code}@${d.nodeId ?? d.scope ?? '-'}`).join(', ');
|
|
14
|
+
assert.equal(errors.length, 0, `expected a valid template, got: ${offending}`);
|
|
15
|
+
return diags;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Assert validation produces EXACTLY the expected ERROR codes (set equality, order-free). Advisory
|
|
19
|
+
* WARNING codes (e.g. an unused domain label) are orthogonal to a rule violation and are ignored here
|
|
20
|
+
* — assert those explicitly via {@link assertHasDiagnostic}. Use {@link assertHasDiagnostic} when a
|
|
21
|
+
* fixture legitimately trips several error rules and you assert one.
|
|
22
|
+
*/
|
|
23
|
+
export function assertDiagnostics(template, codes) {
|
|
24
|
+
const diags = validateTemplate(template);
|
|
25
|
+
const got = new Set(diags.filter((d) => d.severity === 'error').map((d) => d.code));
|
|
26
|
+
const want = new Set(codes);
|
|
27
|
+
const missing = [...want].filter((c) => !got.has(c));
|
|
28
|
+
const extra = [...got].filter((c) => !want.has(c));
|
|
29
|
+
assert.ok(missing.length === 0 && extra.length === 0, `diagnostic codes mismatch\n missing: [${missing.join(', ')}]\n extra: [${extra.join(', ')}]\n all: [${[...got].join(', ')}]`);
|
|
30
|
+
return diags;
|
|
31
|
+
}
|
|
32
|
+
/** Assert at least one diagnostic with `code` was produced (the fixture may trip other rules too). */
|
|
33
|
+
export function assertHasDiagnostic(template, code) {
|
|
34
|
+
const diags = validateTemplate(template);
|
|
35
|
+
const found = diags.find((d) => d.code === code);
|
|
36
|
+
assert.ok(found, `expected diagnostic "${code}", got: [${diags.map((d) => d.code).join(', ')}]`);
|
|
37
|
+
return found;
|
|
38
|
+
}
|
|
39
|
+
/** Assert NO diagnostic with `code` was produced. */
|
|
40
|
+
export function assertNoDiagnostic(template, code) {
|
|
41
|
+
const diags = validateTemplate(template);
|
|
42
|
+
assert.ok(!diags.some((d) => d.code === code), `unexpected diagnostic "${code}"`);
|
|
43
|
+
}
|
|
44
|
+
/** Assert a drive reached a terminal with `status`. */
|
|
45
|
+
export function assertReachesTerminal(result, status) {
|
|
46
|
+
assert.equal(result.status, status, `expected terminal "${status}", got "${result.status}" via ${result.path.join(' → ')}`);
|
|
47
|
+
}
|
|
48
|
+
/** Assert the recorded path of emitted Decisions equals `expected` (node ids, in order). */
|
|
49
|
+
export function assertPath(result, expected) {
|
|
50
|
+
assert.deepEqual(result.path, expected, `path mismatch\n expected: ${expected.join(' → ')}\n actual: ${result.path.join(' → ')}`);
|
|
51
|
+
}
|
|
52
|
+
/** Assert a node appears in the path exactly `times` (e.g. a rework node hit at the loop cap). */
|
|
53
|
+
export function assertVisitCount(result, nodeId, times) {
|
|
54
|
+
const count = result.path.filter((id) => id === nodeId).length;
|
|
55
|
+
assert.equal(count, times, `expected ${nodeId} visited ${times}×, got ${count} (path: ${result.path.join(' → ')})`);
|
|
56
|
+
}
|
|
57
|
+
/** Assert a scope's final counter value. */
|
|
58
|
+
export function assertCounter(result, scope, value) {
|
|
59
|
+
assert.equal(result.counters[scope] ?? 0, value, `expected counter ${scope}=${value}, got ${result.counters[scope] ?? 0}`);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=assertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../../../src/pipeline-core/kit/assertions.ts"],"names":[],"mappings":"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"}
|