mirror of
https://github.com/Hestia-Homes/agentic-toolkit.git
synced 2026-06-08 11:37:26 +00:00
Initial implementation of Domna's agentic toolkit per PRD #1: - Runner CLI (src/cli.ts) wrapping sandcastle.run() with Docker provider - Pure modules: PhaseScheduler, PromptBuilder, FailureHandler with tests - Project Status v2 GraphQL client + parsers with tests - BranchManager (git/gh wrapper) and LoopOrchestrator (per-tick algorithm) - Variant-aware: per-ticket (one PR per issue, phase-gated, exit between phases) vs single-pr (one PR for the whole DAG, halt on failure) - /to-project skill that creates a repo-level project, configures the Status schema the runner expects, and sets initial issue statuses - setup.sh that installs Matt Pocock skills + Domna skills via npx skills Out of scope at v1: remote runners, Slack notifications, stacked PRs, cross-repo projects, SHA-pinning of upstream skills (tracks HEAD until the skills CLI supports repo#sha). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { FailureKind, Variant } from "../types.js";
|
|
import { decide } from "./failure-handler.js";
|
|
|
|
const FAILURES: FailureKind[] = [
|
|
"agent-error",
|
|
"tests-failed",
|
|
"build-failed",
|
|
"sandbox-timeout",
|
|
"unknown",
|
|
];
|
|
const VARIANTS: Variant[] = ["per-ticket", "single-pr"];
|
|
|
|
describe("decide", () => {
|
|
describe("first failure", () => {
|
|
for (const variant of VARIANTS) {
|
|
for (const failure of FAILURES) {
|
|
it(`retries on first failure (variant=${variant}, kind=${failure})`, () => {
|
|
const action = decide(failure, { variant, retryCount: 0 });
|
|
expect(action.kind).toBe("retry");
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
describe("after retry", () => {
|
|
for (const failure of FAILURES) {
|
|
it(`per-ticket variant skips after second failure (kind=${failure})`, () => {
|
|
const action = decide(failure, {
|
|
variant: "per-ticket",
|
|
retryCount: 1,
|
|
});
|
|
expect(action.kind).toBe("skip");
|
|
if (action.kind === "skip") {
|
|
expect(action.reason).toContain(failure);
|
|
}
|
|
});
|
|
|
|
it(`single-pr variant halts after second failure (kind=${failure})`, () => {
|
|
const action = decide(failure, {
|
|
variant: "single-pr",
|
|
retryCount: 1,
|
|
});
|
|
expect(action.kind).toBe("halt");
|
|
if (action.kind === "halt") {
|
|
expect(action.reason).toContain(failure);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
it("halts/skips on third+ failure too (defensive)", () => {
|
|
expect(
|
|
decide("unknown", { variant: "per-ticket", retryCount: 2 }).kind,
|
|
).toBe("skip");
|
|
expect(decide("unknown", { variant: "single-pr", retryCount: 5 }).kind).toBe(
|
|
"halt",
|
|
);
|
|
});
|
|
});
|