@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,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* data-driven-task.workflow.ts — the DBOS effect-adapter for a DATA-DRIVEN pipeline (plan 0015 §10).
|
|
3
|
+
*
|
|
4
|
+
* Runs the pure `pipeline-core` graph on REAL DBOS. As of slice 3 this is the SOLE pipeline engine:
|
|
5
|
+
* selection routes EVERY pipeline here (TaskControlPlaneApiService), executing the state-machine
|
|
6
|
+
* template pinned for the run; a pipeline without a valid template FAILS LOUD at selection. The old
|
|
7
|
+
* hardcoded `developTask` workflow + its role→phase classifiers were removed.
|
|
8
|
+
*
|
|
9
|
+
* INVARIANT: `src/pipeline/*` imports NO `@dbos-inc/dbos-sdk` (M1 — DBOS sealed). All DBOS interaction
|
|
10
|
+
* goes through the generic DbosService verbs (registerStep/registerWorkflow/awaitDecision) injected as
|
|
11
|
+
* deps. This file is registered via the engine seam in PipelineService.
|
|
12
|
+
*
|
|
13
|
+
* GENERIC ENGINE: zero role-ids / pipeline shapes here. `roleRef`/`scriptRef` are opaque capability
|
|
14
|
+
* handles resolved against the run's route bindings + the existing runner machinery (§1/§10).
|
|
15
|
+
*
|
|
16
|
+
* The loop (§10):
|
|
17
|
+
* { state, decision } = core.step(template, state, lastResult)
|
|
18
|
+
* → execute `decision` as a durable DBOS step (runner / human gate / integrator / fork)
|
|
19
|
+
* → validate result vs the node's `resultSchema` + redact at this boundary (reuse runStep/append)
|
|
20
|
+
* → record the result as the next `lastResult`
|
|
21
|
+
* → repeat until `decision.type === 'complete'` → finish the run.
|
|
22
|
+
*
|
|
23
|
+
* RECOVERY: DBOS replays the workflow body; `core.step` is deterministic and every effect is a durable
|
|
24
|
+
* memoized DBOS step, so a replay re-derives the identical Decision sequence and consumes the recorded
|
|
25
|
+
* step results — no live race, no duplicate effects (deterministic ids + ROW_CONFLICT no-op).
|
|
26
|
+
*
|
|
27
|
+
* C1 architecture: the body is an extracted DBOS-free builder (`makeDataDrivenTask`) registered via the
|
|
28
|
+
* engine seam, so tests exercise the SAME production logic directly with a plain step fn.
|
|
29
|
+
*/
|
|
30
|
+
import { step as coreStep, initialState, validateTemplate, InterpretError, } from '../pipeline-core/index.js';
|
|
31
|
+
import { runnerNeedsLivePreflight, runnerUsesRealIntegrator } from './route-contract.js';
|
|
32
|
+
const MAX_STEPS = 1_000; // a VALID template terminates; guards a data/loop authoring mistake at runtime.
|
|
33
|
+
/**
|
|
34
|
+
* Reserved engine error codes (matched only by a node's `catch`, §3/§6).
|
|
35
|
+
*
|
|
36
|
+
* A built-in script (the integrator) has TWO distinct failure modes the routing data discriminates:
|
|
37
|
+
* - `revo.ScriptBlocked` — the script needs a human (nothing-to-integrate, ambiguous PRs, a refused
|
|
38
|
+
* pinned identity, a non-JSON `pr view`). NOT a crash; a `catch` routes it to a `blocked` terminal,
|
|
39
|
+
* and the adapter surfaces the human-readable lesson on a `pipeline_blocked` event (parity with the
|
|
40
|
+
* old engine's `blockPipeline({ reason:'integrate' })`).
|
|
41
|
+
* - `revo.ScriptFailed` — the script THREW (a gh/push error). A `catch` routes it to a `failed`
|
|
42
|
+
* terminal (parity with the old engine's top-level catch → failRun).
|
|
43
|
+
*/
|
|
44
|
+
const REVO_SCRIPT_FAILED = 'revo.ScriptFailed';
|
|
45
|
+
const REVO_SCRIPT_BLOCKED = 'revo.ScriptBlocked';
|
|
46
|
+
const REVO_RESULT_INVALID = 'revo.ResultInvalid';
|
|
47
|
+
function isRecord(value) {
|
|
48
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract the DOMAIN verdict from a recorded agent/script result, restricted to the node's declared
|
|
52
|
+
* `outcomes`/domain vocabulary. The engine treats domain verdicts as opaque labels (§8): we read the
|
|
53
|
+
* agent's free output for a label the template can route on, never inventing engine semantics.
|
|
54
|
+
*
|
|
55
|
+
* Resolution order: explicit `output.verdict` (string) → a leading-token string output → undefined.
|
|
56
|
+
* The value is lowercased to match the spec's domain vocabulary (`approved|blocker|clean|…`).
|
|
57
|
+
*/
|
|
58
|
+
function domainVerdictOf(result) {
|
|
59
|
+
const output = result.output;
|
|
60
|
+
const raw = isRecord(output) ? output.verdict : output;
|
|
61
|
+
if (typeof raw !== 'string')
|
|
62
|
+
return undefined;
|
|
63
|
+
return normalizeDomainLabel(raw);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Normalize an agent's free-text verdict token to a domain label. The seeded test/agent vocabulary
|
|
67
|
+
* uses PASS/BLOCKER/MAJOR/etc.; the data-driven templates use approved/blocker/clean/dirty/… . We map
|
|
68
|
+
* the well-known structural tokens to the closest domain label and otherwise pass the lowercased token
|
|
69
|
+
* through (so a template that declares its own labels just works). Total + deterministic.
|
|
70
|
+
*/
|
|
71
|
+
function normalizeDomainLabel(raw) {
|
|
72
|
+
const token = raw.trim().toUpperCase().replace(/[\s-]+/g, '_');
|
|
73
|
+
switch (token) {
|
|
74
|
+
case 'PASS':
|
|
75
|
+
case 'PASSED':
|
|
76
|
+
case 'APPROVE':
|
|
77
|
+
case 'APPROVED':
|
|
78
|
+
case 'CLEAN':
|
|
79
|
+
case 'READY':
|
|
80
|
+
case 'VERIFIED':
|
|
81
|
+
// A passing structural token maps to the canonical "good" labels; the interpreter only routes on
|
|
82
|
+
// labels a guard names, so emitting both-equivalents is unnecessary — return the lowercased token.
|
|
83
|
+
return token === 'CLEAN' ? 'clean' : 'approved';
|
|
84
|
+
case 'MINOR':
|
|
85
|
+
return 'approved'; // MINOR proceeds (parity with the hardcoded isBlocking)
|
|
86
|
+
case 'MAJOR':
|
|
87
|
+
case 'REQUEST_CHANGES':
|
|
88
|
+
case 'CHANGES_REQUESTED':
|
|
89
|
+
return 'changes_requested';
|
|
90
|
+
case 'BLOCKER':
|
|
91
|
+
case 'BLOCK':
|
|
92
|
+
case 'DIRTY':
|
|
93
|
+
return token === 'DIRTY' ? 'dirty' : 'blocker';
|
|
94
|
+
default:
|
|
95
|
+
return raw.trim().toLowerCase();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Validate a recorded result against a node's `resultSchema` at the ADAPTER boundary (§10). MVP
|
|
100
|
+
* contract (resolves §14 Q3 — resultSchema is a DATA handle, validated structurally here): a declared
|
|
101
|
+
* `resultSchema` requires a non-empty object/string output. Token redaction already happens inside
|
|
102
|
+
* `runStep`/`appendEvent`; this guards a malformed effect result → `revo.ResultInvalid` (§6 precedence).
|
|
103
|
+
*/
|
|
104
|
+
function resultSatisfiesSchema(node, result) {
|
|
105
|
+
if (!('resultSchema' in node) || !node.resultSchema)
|
|
106
|
+
return true;
|
|
107
|
+
const output = result.output;
|
|
108
|
+
if (output === null || output === undefined)
|
|
109
|
+
return false;
|
|
110
|
+
if (typeof output === 'string')
|
|
111
|
+
return output.length > 0;
|
|
112
|
+
if (isRecord(output))
|
|
113
|
+
return true;
|
|
114
|
+
return Array.isArray(output);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Map an approve/reject human decision onto a DOMAIN verdict the template's gate `outcomes` can route.
|
|
118
|
+
* The core never sees approve/reject — it routes on a domain label (§8). Approve → the first declared
|
|
119
|
+
* outcome (the "proceed" label by template convention, e.g. `approved`); reject → fail-closed to a
|
|
120
|
+
* NON-first outcome if one exists (e.g. `changes_requested`), else undefined so the gate's `default`
|
|
121
|
+
* (typically a `blocked` terminal) fires. This keeps gate semantics 100% in the routing data (§6/§8).
|
|
122
|
+
*/
|
|
123
|
+
function gateVerdict(decision, outcomes) {
|
|
124
|
+
if (decision.decision === 'approve')
|
|
125
|
+
return outcomes[0];
|
|
126
|
+
// reject: prefer a declared "rework/changes" outcome; else let the default branch route (blocked).
|
|
127
|
+
return outcomes.length > 1 ? outcomes.at(-1) : undefined;
|
|
128
|
+
}
|
|
129
|
+
/** Stable gate topic from a node's reason (plan-review → 'plan', merge-review → 'merge', else 'plan'). */
|
|
130
|
+
function gateTopicFor(reason) {
|
|
131
|
+
return /merge/i.test(reason) ? 'merge' : 'plan';
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* makeDataDrivenTask — DBOS-free factory for the dataDrivenTask async function (C1).
|
|
135
|
+
*
|
|
136
|
+
* Receives the (DBOS-wrapped in prod, plain in tests) `runStepFn` so the body is IDENTICAL across
|
|
137
|
+
* production and tests. Returns a plain async function with the workflow signature.
|
|
138
|
+
*/
|
|
139
|
+
export function makeDataDrivenTask(runStepFn, deps) {
|
|
140
|
+
const { appendEvent, awaitHuman, completeRun, failRun, blockRun, loadRunTaskContext, integrateFn, runStub, preflightFn } = deps;
|
|
141
|
+
return async function dataDrivenTaskImpl(runId, opts) {
|
|
142
|
+
try {
|
|
143
|
+
return await runBody(runId, opts);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
// TERMINAL failure: mark the Revisium run-row `failed` (idempotent, event-first) before
|
|
147
|
+
// re-throwing so DBOS still records the workflow ERROR (progress truth) and the run-row stops
|
|
148
|
+
// lying. The run's terminal-failure surfacing contract (0008 #2).
|
|
149
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
150
|
+
try {
|
|
151
|
+
await failRun(runId, reason);
|
|
152
|
+
}
|
|
153
|
+
catch (failErr) {
|
|
154
|
+
console.error(`[data-driven] failRun(${runId}) itself failed: ${String(failErr)}`);
|
|
155
|
+
}
|
|
156
|
+
throw err;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
async function runBody(runId, opts) {
|
|
160
|
+
const { route, template } = opts;
|
|
161
|
+
// Defense-in-depth: the pinned template is validated at the boundary (§9 — pipeline-core is the
|
|
162
|
+
// authoritative validator even though selection already validated it). A broken pin fails the run.
|
|
163
|
+
const diagnostics = validateTemplate(template).filter((d) => d.severity === 'error');
|
|
164
|
+
if (diagnostics.length > 0) {
|
|
165
|
+
throw new Error(`PINNED_TEMPLATE_INVALID: ${template.pipelineId} — ${diagnostics.map((d) => d.code).join(', ')}`);
|
|
166
|
+
}
|
|
167
|
+
const { taskId, title, base } = await loadRunTaskContext(runId);
|
|
168
|
+
// B5/B7 — live preflight: one memoized DBOS step, evaluated exactly once BEFORE the graph runs.
|
|
169
|
+
// Skipped entirely when every selected binding resolves to a stub/script runner (mirrors the old
|
|
170
|
+
// engine's gate). A `needsHuman` preflight blocks the run (clean/base invariant unmet) and surfaces
|
|
171
|
+
// the lesson — the run never enters the graph. This is an engine-level guard (infrastructure), not a
|
|
172
|
+
// template node: it is cross-cutting and identical for every pinned pipeline (zero role-ids/shapes).
|
|
173
|
+
if (route.roleBindings.some((b) => runnerNeedsLivePreflight(b.resolvedRunnerId))) {
|
|
174
|
+
const pf = await preflightFn(taskId, base);
|
|
175
|
+
if ('needsHuman' in pf) {
|
|
176
|
+
return await blockWithLesson(runId, taskId, 'preflight', pf.lesson, 0);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Capability resolution map (GENERIC — no role-ids in the engine): roleRef/scriptRef → route binding.
|
|
180
|
+
// The route's bindings are authoritative for runner dispatch (and durable on recovery via the DBOS
|
|
181
|
+
// workflow input). A `role:<id>` handle resolves to the binding whose roleId matches `<id>`. A
|
|
182
|
+
// `script:<id>` handle likewise resolves by roleId; additionally the canonical `script:integrator`
|
|
183
|
+
// handle resolves to whichever binding's RESOLVED runner mechanically performs the merge (runner-
|
|
184
|
+
// wins, D7) so the integrator script dispatches real-vs-stub exactly like the hardcoded path.
|
|
185
|
+
const bindingByRef = new Map();
|
|
186
|
+
for (const binding of route.roleBindings) {
|
|
187
|
+
bindingByRef.set(`role:${binding.roleId}`, binding);
|
|
188
|
+
bindingByRef.set(`script:${binding.roleId}`, binding);
|
|
189
|
+
bindingByRef.set(binding.roleId, binding);
|
|
190
|
+
if (runnerUsesRealIntegrator(binding.resolvedRunnerId) && !bindingByRef.has('script:integrator')) {
|
|
191
|
+
bindingByRef.set('script:integrator', binding);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const executionProfile = route.executionProfile;
|
|
195
|
+
let state = initialState(template);
|
|
196
|
+
let lastResult;
|
|
197
|
+
let lastVerdict = '';
|
|
198
|
+
let stepCount = 0;
|
|
199
|
+
for (let i = 0; i < MAX_STEPS; i++) {
|
|
200
|
+
const { state: nextState, decision } = coreStep(template, state, lastResult);
|
|
201
|
+
state = nextState;
|
|
202
|
+
if (decision.type === 'complete') {
|
|
203
|
+
return await finish(runId, decision.status, lastVerdict, stepCount);
|
|
204
|
+
}
|
|
205
|
+
const eff = await applyDecision(decision, {
|
|
206
|
+
runId, template, bindingByRef, executionProfile, taskId, title, base,
|
|
207
|
+
});
|
|
208
|
+
lastResult = eff.lastResult;
|
|
209
|
+
if (eff.lastVerdict !== undefined)
|
|
210
|
+
lastVerdict = eff.lastVerdict;
|
|
211
|
+
stepCount += eff.stepDelta;
|
|
212
|
+
}
|
|
213
|
+
throw new InterpretError(`data-driven ${template.pipelineId} did not terminate within ${MAX_STEPS} steps (template loop bug)`);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Execute ONE non-terminal Decision as a durable effect and return the next `lastResult`/`lastVerdict`
|
|
217
|
+
* + the step-count delta. Extracted from the `runBody` loop so each is small + independently testable
|
|
218
|
+
* (the loop just threads the result; this is the per-kind dispatch).
|
|
219
|
+
*/
|
|
220
|
+
async function applyDecision(decision, ctx) {
|
|
221
|
+
const { runId, template, bindingByRef, executionProfile, taskId, title, base } = ctx;
|
|
222
|
+
switch (decision.type) {
|
|
223
|
+
case 'invokeRole': {
|
|
224
|
+
const node = resolveNode(template, decision.nodeId);
|
|
225
|
+
const result = await invokeRole(runId, decision, node, bindingByRef, executionProfile);
|
|
226
|
+
if (result.failed) {
|
|
227
|
+
return { lastResult: { outcome: 'failed', errorCode: result.errorCode }, lastVerdict: 'failed', stepDelta: 1 };
|
|
228
|
+
}
|
|
229
|
+
const verdict = result.verdict;
|
|
230
|
+
return {
|
|
231
|
+
lastResult: { outcome: 'succeeded', ...(verdict ? { verdict } : {}) },
|
|
232
|
+
...(verdict ? { lastVerdict: verdict } : {}),
|
|
233
|
+
stepDelta: 1,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
case 'invokeScript': {
|
|
237
|
+
const scriptResult = await invokeScript(runId, decision, { taskId, title, base }, bindingByRef);
|
|
238
|
+
// A blocked script (needsHuman) routes via revo.ScriptBlocked → a `blocked` terminal; a thrown
|
|
239
|
+
// script routes via revo.ScriptFailed → a `failed` terminal (§6 catch). The lesson-bearing
|
|
240
|
+
// pipeline_blocked is emitted inside invokeScript for the block path (parity with the old engine).
|
|
241
|
+
if (scriptResult.outcome === 'blocked') {
|
|
242
|
+
return { lastResult: { outcome: 'failed', errorCode: REVO_SCRIPT_BLOCKED }, lastVerdict: 'blocked', stepDelta: 1 };
|
|
243
|
+
}
|
|
244
|
+
if (scriptResult.outcome === 'failed') {
|
|
245
|
+
return { lastResult: { outcome: 'failed', errorCode: REVO_SCRIPT_FAILED }, lastVerdict: 'failed', stepDelta: 1 };
|
|
246
|
+
}
|
|
247
|
+
return { lastResult: { outcome: 'succeeded' }, stepDelta: 1 };
|
|
248
|
+
}
|
|
249
|
+
case 'awaitGate': {
|
|
250
|
+
const topic = gateTopicFor(decision.reason);
|
|
251
|
+
const human = await awaitHuman(runId, topic, `${decision.reason} approval`, {
|
|
252
|
+
nodeId: decision.nodeId,
|
|
253
|
+
outcomes: decision.outcomes,
|
|
254
|
+
});
|
|
255
|
+
const verdict = gateVerdict(human, decision.outcomes);
|
|
256
|
+
return { lastResult: verdict ? { verdict } : {}, ...(verdict ? { lastVerdict: verdict } : {}), stepDelta: 0 };
|
|
257
|
+
}
|
|
258
|
+
case 'fork': {
|
|
259
|
+
// Fork/join is supported by the core; the MVP feature-development pipeline has none. Record a
|
|
260
|
+
// deterministic barrier arrival per branch (verdict undefined) so an `all` join proceeds. A richer
|
|
261
|
+
// concurrent-branch executor (DBOS child workflows) is a later slice (§14 Q1).
|
|
262
|
+
await appendEvent({
|
|
263
|
+
runId,
|
|
264
|
+
taskId,
|
|
265
|
+
stepId: '',
|
|
266
|
+
stepKey: `fork:${decision.nodeId}`,
|
|
267
|
+
type: 'pipeline_fork',
|
|
268
|
+
payload: { nodeId: decision.nodeId, branches: decision.branches.map((b) => b.id), joinId: decision.joinId },
|
|
269
|
+
});
|
|
270
|
+
return {
|
|
271
|
+
lastResult: { joinArrivals: decision.branches.map((b, idx) => ({ branchId: b.id, seq: idx + 1 })) },
|
|
272
|
+
stepDelta: 0,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
case 'startTimer':
|
|
276
|
+
// `wait` nodes are rare (§1) and unused by the MVP templates; a durable timer executor is a later
|
|
277
|
+
// slice. Treat as an immediate (recorded) resume so a template using it still advances.
|
|
278
|
+
return { lastResult: {}, stepDelta: 0 };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// ── Effect executors ─────────────────────────────────────────────────────────
|
|
282
|
+
/** invokeRole → dispatch via the existing runStep DBOS step; validate result vs resultSchema. */
|
|
283
|
+
async function invokeRole(runId, decision, node, bindingByRef, executionProfile) {
|
|
284
|
+
const binding = bindingByRef.get(decision.roleRef);
|
|
285
|
+
if (!binding) {
|
|
286
|
+
// A VALID template's caps resolve at run start; an unresolved roleRef is a fatal config gap.
|
|
287
|
+
throw new Error(`CAPABILITY_UNRESOLVED: roleRef ${decision.roleRef} has no route binding`);
|
|
288
|
+
}
|
|
289
|
+
// stepKey carries the nodeId so per-node attempts/events are distinct (and stable across replay).
|
|
290
|
+
const result = await runStepFn(runId, binding.rowId, decision.nodeId, { nodeId: decision.nodeId }, binding.resolvedRunnerId, executionProfile);
|
|
291
|
+
// runStep converts a runner-process crash into a blocking attempt (needsHuman + verdict BLOCKER);
|
|
292
|
+
// that is a domain failure of the node → route via §6 precedence as a result-invalid/failed effect.
|
|
293
|
+
if (result.needsHuman || !resultSatisfiesSchema(node, result)) {
|
|
294
|
+
return { failed: true, errorCode: REVO_RESULT_INVALID };
|
|
295
|
+
}
|
|
296
|
+
return { failed: false, verdict: domainVerdictOf(result) };
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* invokeScript → the built-in system SCRIPT library. The only built-in v1 script is the integrator
|
|
300
|
+
* (`script:integrator`); it dispatches to the real integrator (live) or the stub (script) exactly as
|
|
301
|
+
* the hardcoded path does — runner-wins via the resolved binding (D7).
|
|
302
|
+
*
|
|
303
|
+
* Two distinct outcomes the routing data discriminates (parity with the old engine):
|
|
304
|
+
* - `blocked`: the integrator needs a human (nothing to integrate, ambiguous PRs, refused identity,
|
|
305
|
+
* non-JSON pr view). We emit a `pipeline_blocked` event carrying the human-readable lesson (so the
|
|
306
|
+
* human sees WHY — the persist boundary redacts any token), then the node's `catch[revo.ScriptBlocked]`
|
|
307
|
+
* routes to a `blocked` terminal.
|
|
308
|
+
* - `failed`: the integrator THREW (a gh/push error). `catch[revo.ScriptFailed]` routes to a `failed`
|
|
309
|
+
* terminal; the run's top-level catch failRuns (the thrown reason is recorded, redacted).
|
|
310
|
+
*/
|
|
311
|
+
async function invokeScript(runId, decision, ctx, bindingByRef) {
|
|
312
|
+
const binding = bindingByRef.get(decision.scriptRef) ?? bindingByRef.get('script:integrator');
|
|
313
|
+
const integratorInput = { runId, taskId: ctx.taskId, title: ctx.title, base: ctx.base };
|
|
314
|
+
let result;
|
|
315
|
+
try {
|
|
316
|
+
// A script node whose resolved runner mechanically performs the merge uses the real integrator;
|
|
317
|
+
// otherwise the pure stub (zero git/gh). Absent a binding (template-only script), default to stub.
|
|
318
|
+
result = binding && runnerUsesRealIntegrator(binding.resolvedRunnerId)
|
|
319
|
+
? await integrateFn(integratorInput)
|
|
320
|
+
: runStub(integratorInput);
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
await appendEvent({
|
|
324
|
+
runId,
|
|
325
|
+
taskId: ctx.taskId,
|
|
326
|
+
stepId: '',
|
|
327
|
+
stepKey: decision.nodeId,
|
|
328
|
+
type: 'step_failed',
|
|
329
|
+
payload: { scriptRef: decision.scriptRef, error: err instanceof Error ? err.message : String(err) },
|
|
330
|
+
});
|
|
331
|
+
return { outcome: 'failed' };
|
|
332
|
+
}
|
|
333
|
+
if ('needsHuman' in result) {
|
|
334
|
+
// Surface the blocking reason as pipeline_blocked (the persist boundary redacts any token, D15) so
|
|
335
|
+
// the human sees WHY the integrator could not proceed — exactly as the old engine's blockPipeline.
|
|
336
|
+
await appendEvent({
|
|
337
|
+
runId,
|
|
338
|
+
taskId: ctx.taskId,
|
|
339
|
+
stepId: '',
|
|
340
|
+
stepKey: 'pipeline',
|
|
341
|
+
type: 'pipeline_blocked',
|
|
342
|
+
payload: { reason: 'integrate', lesson: result.lesson, nodeId: decision.nodeId },
|
|
343
|
+
});
|
|
344
|
+
return { outcome: 'blocked' };
|
|
345
|
+
}
|
|
346
|
+
await appendEvent({
|
|
347
|
+
runId,
|
|
348
|
+
taskId: ctx.taskId,
|
|
349
|
+
stepId: '',
|
|
350
|
+
stepKey: decision.nodeId,
|
|
351
|
+
type: 'integrate_succeeded',
|
|
352
|
+
payload: { prUrl: result.prUrl, branch: result.branch, prNumber: result.prNumber },
|
|
353
|
+
});
|
|
354
|
+
return { outcome: 'ok' };
|
|
355
|
+
}
|
|
356
|
+
/** Terminal: finish the run in Revisium per the core's terminal status. */
|
|
357
|
+
async function finish(runId, status, verdict, steps) {
|
|
358
|
+
if (status === 'succeeded') {
|
|
359
|
+
await completeRun(runId, { actor: 'pipeline', source: 'data-driven-complete', verdict, iterations: steps });
|
|
360
|
+
}
|
|
361
|
+
else if (status === 'blocked') {
|
|
362
|
+
await appendEvent({
|
|
363
|
+
runId,
|
|
364
|
+
taskId: '',
|
|
365
|
+
stepId: '',
|
|
366
|
+
stepKey: 'pipeline',
|
|
367
|
+
type: 'pipeline_blocked',
|
|
368
|
+
payload: { reason: 'route-terminal', lastVerdict: verdict, steps },
|
|
369
|
+
});
|
|
370
|
+
await blockRun(runId, { actor: 'pipeline', source: 'data-driven-blocked', reason: 'route-terminal' });
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
await failRun(runId, `data-driven pipeline reached a failed terminal (lastVerdict=${verdict})`);
|
|
374
|
+
}
|
|
375
|
+
return { runId, status, verdict, steps };
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Block the run early with a human-readable lesson (the engine-level preflight guard — there is no
|
|
379
|
+
* graph node for it). Emits a lesson-bearing `pipeline_blocked` (token-redacted at the persist
|
|
380
|
+
* boundary) + marks the Revisium run-row blocked, then returns a `blocked` result WITHOUT entering the
|
|
381
|
+
* graph. Mirrors the old engine's `blockPipeline({ reason })`.
|
|
382
|
+
*/
|
|
383
|
+
async function blockWithLesson(runId, taskId, reason, lesson, steps) {
|
|
384
|
+
await appendEvent({
|
|
385
|
+
runId,
|
|
386
|
+
taskId,
|
|
387
|
+
stepId: '',
|
|
388
|
+
stepKey: 'pipeline',
|
|
389
|
+
type: 'pipeline_blocked',
|
|
390
|
+
payload: { reason, lesson },
|
|
391
|
+
});
|
|
392
|
+
await blockRun(runId, { actor: 'pipeline', source: `data-driven-${reason}`, reason });
|
|
393
|
+
return { runId, status: 'blocked', verdict: 'blocked', steps };
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/** Resolve a node id against the template (a VALID template never dangles — guarded defensively). */
|
|
397
|
+
function resolveNode(template, nodeId) {
|
|
398
|
+
const node = template.nodes[nodeId];
|
|
399
|
+
if (!node)
|
|
400
|
+
throw new InterpretError(`unknown node id "${nodeId}" (invalid template)`);
|
|
401
|
+
return node;
|
|
402
|
+
}
|
|
403
|
+
//# sourceMappingURL=data-driven-task.workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-driven-task.workflow.js","sourceRoot":"","sources":["../../src/pipeline/data-driven-task.workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* data-driven-template.ts — read + validate the PINNED state-machine template for a run (0015 §9/§11).
|
|
3
|
+
*
|
|
4
|
+
* MVP STORAGE (resolves §14 Q4 for now): the data-driven template is carried inside the pipeline row's
|
|
5
|
+
* `execution_policy` under a `template_json` key — the only free-form slot that round-trips through
|
|
6
|
+
* playbook install with NO control-plane schema migration. A pipeline is DATA-DRIVEN iff that template
|
|
7
|
+
* parses, declares `specVersion` + `nodes`, and validates clean via `pipeline-core.validateTemplate`
|
|
8
|
+
* (the authoritative validator, §9). Native Revisium typing / a dedicated `template_json` column is a
|
|
9
|
+
* strictly-additive later upgrade (the Desktop handoff tracks it).
|
|
10
|
+
*
|
|
11
|
+
* Pure + I/O-free: it inspects already-loaded `executionPolicy` data. The adapter pins the returned
|
|
12
|
+
* template as a DBOS workflow argument (durable on recovery), so selection reads it once at run start.
|
|
13
|
+
*/
|
|
14
|
+
import { validateTemplate } from '../pipeline-core/index.js';
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A loose structural pre-check before handing to the authoritative validator: a `specVersion` string
|
|
20
|
+
* and a `nodes` object. (validateTemplate then does the full §12 closure.)
|
|
21
|
+
*/
|
|
22
|
+
function looksLikeTemplate(value) {
|
|
23
|
+
return isRecord(value) && typeof value.specVersion === 'string' && isRecord(value.nodes);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extract a data-driven template from a pipeline's executionPolicy, if present + valid.
|
|
27
|
+
*
|
|
28
|
+
* Returns the validated `Template` when the pipeline carries a clean state-machine spec, else `null`
|
|
29
|
+
* (→ the run takes the existing hardcoded `develop-task` path). A present-but-INVALID template throws,
|
|
30
|
+
* so a broken data-driven pipeline fails loudly at run start rather than silently degrading to the
|
|
31
|
+
* hardcoded engine (which would mask the authoring bug).
|
|
32
|
+
*/
|
|
33
|
+
export function templateFromExecutionPolicy(executionPolicy) {
|
|
34
|
+
if (!isRecord(executionPolicy))
|
|
35
|
+
return null;
|
|
36
|
+
const raw = executionPolicy.template_json ?? executionPolicy.templateJson;
|
|
37
|
+
if (raw === undefined || raw === null)
|
|
38
|
+
return null;
|
|
39
|
+
const parsed = typeof raw === 'string' ? safeParse(raw) : raw;
|
|
40
|
+
if (!looksLikeTemplate(parsed)) {
|
|
41
|
+
throw new Error('DATA_DRIVEN_TEMPLATE_MALFORMED: execution_policy.template_json is not a template');
|
|
42
|
+
}
|
|
43
|
+
const errors = validateTemplate(parsed).filter((d) => d.severity === 'error');
|
|
44
|
+
if (errors.length > 0) {
|
|
45
|
+
throw new Error(`DATA_DRIVEN_TEMPLATE_INVALID: ${parsed.pipelineId} — ${errors.map((d) => d.code).join(', ')}`);
|
|
46
|
+
}
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
function safeParse(raw) {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(raw);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw new Error('DATA_DRIVEN_TEMPLATE_MALFORMED: execution_policy.template_json is not valid JSON');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=data-driven-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-driven-template.js","sourceRoot":"","sources":["../../src/pipeline/data-driven-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,gBAAgB,EAAiB,MAAM,2BAA2B,CAAC;AAE5E,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,KAAc;IACvC,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAAC,eAAwB;IAClE,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,GAAG,GAAG,eAAe,CAAC,aAAa,IAAI,eAAe,CAAC,YAAY,CAAC;IAC1E,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEnD,MAAM,MAAM,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9D,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;IACtG,CAAC;IACD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IAC9E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,iCAAiC,MAAM,CAAC,UAAU,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;IACtG,CAAC;AACH,CAAC"}
|