agentic-toolkit/src/modules/failure-handler.ts
Khalim Conn-Kowlessar 1d8a77b29b feat: scaffold agentic-toolkit (runner + skills + setup)
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>
2026-05-03 12:40:26 +01:00

30 lines
942 B
TypeScript

import type { FailureAction, FailureContext, FailureKind } from "../types.js";
const MAX_RETRIES = 1;
/**
* Decide what to do when a ticket execution fails. Pure state machine.
*
* Rules:
* - First failure (any kind, any variant): retry once.
* - Second failure on per-ticket variant: skip this ticket, continue with peers
* in the same phase. The phase gate naturally blocks advance until the human
* resolves the failed issue.
* - Second failure on single-pr variant: halt. The shared branch is corrupted
* and continuing would compound the damage.
*/
export function decide(
failure: FailureKind,
ctx: FailureContext,
): FailureAction {
if (ctx.retryCount < MAX_RETRIES) {
return { kind: "retry" };
}
const reason = `Ticket failed after ${ctx.retryCount + 1} attempt(s) with: ${failure}`;
if (ctx.variant === "per-ticket") {
return { kind: "skip", reason };
}
return { kind: "halt", reason };
}