first commit
This commit is contained in:
248
tests/orchestration-engine.test.ts
Normal file
248
tests/orchestration-engine.test.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtemp, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { resolve } from "node:path";
|
||||
import { SchemaDrivenExecutionEngine } from "../src/agents/orchestration.js";
|
||||
import type { ActorExecutionResult } from "../src/agents/pipeline.js";
|
||||
|
||||
function createManifest(): unknown {
|
||||
return {
|
||||
schemaVersion: "1",
|
||||
topologies: ["hierarchical", "retry-unrolled", "sequential"],
|
||||
personas: [
|
||||
{
|
||||
id: "product",
|
||||
displayName: "Product",
|
||||
systemPromptTemplate: "Product planning for {{repo}}",
|
||||
toolClearance: {
|
||||
allowlist: ["read_file"],
|
||||
banlist: ["delete_file"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "task",
|
||||
displayName: "Task",
|
||||
systemPromptTemplate: "Task planning for {{repo}}",
|
||||
toolClearance: {
|
||||
allowlist: ["read_file", "write_file"],
|
||||
banlist: ["git_reset"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "coder",
|
||||
displayName: "Coder",
|
||||
systemPromptTemplate: "Coder implements {{ticket}}",
|
||||
toolClearance: {
|
||||
allowlist: ["read_file", "write_file"],
|
||||
banlist: ["rm"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "qa",
|
||||
displayName: "QA",
|
||||
systemPromptTemplate: "QA validates {{ticket}}",
|
||||
toolClearance: {
|
||||
allowlist: ["read_file"],
|
||||
banlist: ["write_file"],
|
||||
},
|
||||
},
|
||||
],
|
||||
relationships: [
|
||||
{
|
||||
parentPersonaId: "product",
|
||||
childPersonaId: "task",
|
||||
constraints: {
|
||||
maxChildren: 2,
|
||||
maxDepth: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
parentPersonaId: "task",
|
||||
childPersonaId: "coder",
|
||||
constraints: {
|
||||
maxChildren: 3,
|
||||
maxDepth: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
topologyConstraints: {
|
||||
maxDepth: 6,
|
||||
maxRetries: 2,
|
||||
},
|
||||
pipeline: {
|
||||
entryNodeId: "project-gate",
|
||||
nodes: [
|
||||
{
|
||||
id: "project-gate",
|
||||
actorId: "project_gate",
|
||||
personaId: "product",
|
||||
},
|
||||
{
|
||||
id: "task-plan",
|
||||
actorId: "task_plan",
|
||||
personaId: "task",
|
||||
},
|
||||
{
|
||||
id: "coder-1",
|
||||
actorId: "coder",
|
||||
personaId: "coder",
|
||||
constraints: {
|
||||
maxRetries: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "qa-1",
|
||||
actorId: "qa",
|
||||
personaId: "qa",
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
from: "project-gate",
|
||||
to: "task-plan",
|
||||
on: "success",
|
||||
when: [
|
||||
{
|
||||
type: "state_flag",
|
||||
key: "needs_bootstrap",
|
||||
equals: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
from: "project-gate",
|
||||
to: "coder-1",
|
||||
on: "success",
|
||||
when: [
|
||||
{
|
||||
type: "state_flag",
|
||||
key: "needs_bootstrap",
|
||||
equals: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
from: "task-plan",
|
||||
to: "coder-1",
|
||||
on: "success",
|
||||
},
|
||||
{
|
||||
from: "coder-1",
|
||||
to: "qa-1",
|
||||
on: "success",
|
||||
when: [
|
||||
{
|
||||
type: "history_has_event",
|
||||
event: "validation_fail",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test("runs DAG pipeline with state-dependent routing and retry behavior", async () => {
|
||||
const workspaceRoot = await mkdtemp(resolve(tmpdir(), "ai-ops-workspace-"));
|
||||
const stateRoot = await mkdtemp(resolve(tmpdir(), "ai-ops-session-state-"));
|
||||
|
||||
await writeFile(resolve(workspaceRoot, "PRD.md"), "# PRD\n", "utf8");
|
||||
|
||||
let coderAttempts = 0;
|
||||
|
||||
const engine = new SchemaDrivenExecutionEngine({
|
||||
manifest: createManifest(),
|
||||
settings: {
|
||||
workspaceRoot,
|
||||
stateRoot,
|
||||
runtimeContext: {
|
||||
repo: "ai_ops",
|
||||
ticket: "AIOPS-123",
|
||||
},
|
||||
maxChildren: 4,
|
||||
maxDepth: 8,
|
||||
maxRetries: 3,
|
||||
},
|
||||
actorExecutors: {
|
||||
project_gate: async () => ({
|
||||
status: "success",
|
||||
payload: {
|
||||
phase: "gate",
|
||||
},
|
||||
stateFlags: {
|
||||
needs_bootstrap: true,
|
||||
},
|
||||
}),
|
||||
task_plan: async (input) => {
|
||||
assert.match(input.prompt, /ai_ops/);
|
||||
return {
|
||||
status: "success",
|
||||
payload: {
|
||||
plan: "roadmap",
|
||||
},
|
||||
stateFlags: {
|
||||
roadmap_ready: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
coder: async (input): Promise<ActorExecutionResult> => {
|
||||
assert.match(input.prompt, /AIOPS-123/);
|
||||
assert.deepEqual(input.toolClearance.allowlist, ["read_file", "write_file"]);
|
||||
coderAttempts += 1;
|
||||
if (coderAttempts === 1) {
|
||||
return {
|
||||
status: "validation_fail",
|
||||
payload: {
|
||||
issue: "missing test",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
payload: {
|
||||
code: "done",
|
||||
},
|
||||
};
|
||||
},
|
||||
qa: async () => ({
|
||||
status: "success",
|
||||
payload: {
|
||||
qa: "ok",
|
||||
},
|
||||
}),
|
||||
},
|
||||
behaviorHandlers: {
|
||||
coder: {
|
||||
onValidationFail: () => ({
|
||||
lastValidationFailure: "coder-1",
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = await engine.runSession({
|
||||
sessionId: "session-orchestration-1",
|
||||
initialPayload: {
|
||||
task: "Implement pipeline",
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
result.records.map((record) => `${record.nodeId}:${record.status}:${String(record.attempt)}`),
|
||||
[
|
||||
"project-gate:success:1",
|
||||
"task-plan:success:1",
|
||||
"coder-1:validation_fail:1",
|
||||
"coder-1:success:2",
|
||||
"qa-1:success:1",
|
||||
],
|
||||
);
|
||||
|
||||
assert.equal(result.finalState.flags.needs_bootstrap, true);
|
||||
assert.equal(result.finalState.flags.roadmap_ready, true);
|
||||
assert.equal(result.finalState.metadata.lastValidationFailure, "coder-1");
|
||||
|
||||
assert.deepEqual(engine.planChildPersonas({ parentPersonaId: "task", depth: 1 }), ["coder"]);
|
||||
});
|
||||
Reference in New Issue
Block a user