/** SDK-owned platform module. This implementation is maintained in goodvibes-sdk. */ /** * Phase-runner (see CHANGELOG 0.38.0), runs one WorkItem through one Phase: spawn * agent, await completion, verify claims, run gates, commit, cleanup. * * REUSES the hardened WRFC primitives verbatim (same functions WrfcController * itself calls, so behavior can't fork): verifyEngineerClaims * (wrfc-reporting.ts) for the phantom-work guard, runWrfcGateChecks * (wrfc-gate-runtime.ts) for quality gates, AgentWorktree.commitWorkingTree * for scoped commits, and the transport-retry / WrfcChainFailureKind pattern * (isTransportFailureMessage + getWrfcTransportRetryLimit/DelayMs) for * bounded respawn-on-transport-blip. * * REALITY-WINS DIVERGENCE from the brief's design (c): WrfcController itself * (wrfc-controller.ts, verified) never calls AgentWorktree.create() for its * role agents, engineer/reviewer/fixer/integrator all run in the SAME * shared `projectRoot` working directory; AgentWorktree is used ONLY for its * commitWorkingTree/merge/cleanup surface (merge/cleanup are safe no-ops * when no isolated worktree dir exists, which is always, today). There is no * per-agent `workingDirectory` override anywhere in AgentInput / * AgentOrchestratorRunContext.createRunContext() (verified: the latter is * fixed per AgentOrchestrator instance, not per-spawn), so a spawned agent * cannot actually be pointed at an isolated worktree directory without new * cross-cutting plumbing through AgentManager/AgentOrchestrator construction *, well beyond this module's boundary, and not something WrfcController * itself has either. This module therefore mirrors WrfcController's ACTUAL * (shared-directory) behavior rather than the brief's aspirational * per-item-isolated-worktree fan-out; true fan-out isolation is a valuable, * separately-scoped follow-up. * * SECOND REALITY-WINS DIVERGENCE: AgentManager.spawn()'s root-spawn * normalization (tools/agent/wrfc-batch-policy.ts) rewrites a PARENTLESS spawn * that reads as root review/test work into an 'engineer'-templated WRFC-owner * chain. Phase-runner spawns are always parentless (a workstream has no owning * AgentRecord), so both halves of that rule reach this module: * * - A DECLARED role template (reviewer/tester/verifier/qa/review/test) still * triggers the rewrite whatever this module passes in, so review-kind phases * must never be templated as one of those strings, use 'general' instead * (see templateForPhase). That part is still load-bearing. * - The task-WORDING match (ROLE_ACTION_RE/ROLE_PREFIX_RE, e.g. "review the * diff") no longer overrides the `dangerously_disable_wrfc: true` this module * passes on every phase spawn. The "assess/evaluate" phrasing in * buildPhaseTask is therefore no longer a dodge; it is kept because it reads * better in the prompt. */ import type { AgentManager } from '../tools/agent/manager.js'; import type { ConfigManager } from '../config/manager.js'; import type { RuntimeEventBus } from '../runtime/events/index.js'; import { type CommitWorkingTreeResult } from '../agents/worktree.js'; import type { CancellationRegistry } from './cancellation.js'; import { type DirtyLaunchSnapshot } from './dirty-guard.js'; import type { Phase, PhaseResult, PriceProvenanceFn, WorkItem, WorkItemUsage, Workstream } from './types.js'; /** Narrow structural pick, testable with stubs, mirrors AgentManagerLike (wrfc-config.ts). */ export type PhaseRunnerAgentManagerLike = Pick; /** Structural pick of AgentWorktree's surface, matches WrfcController's WrfcWorktreeOps injection seam exactly, so the same test doubles work for both. */ export interface WrfcWorktreeOps { merge(agentId: string): Promise; cleanup(agentId: string): Promise; commitWorkingTree(message: string, paths?: string[]): Promise; currentHead(): Promise; } /** * The minimal surface of an item's IsolatedWorktree (worktree.ts) that the * phase-runner needs in `worktree` isolation mode: the on-disk `path` (used as * the spawned agent's working directory) and a scoped `commit` onto the item * branch. Notably NOT merge/cleanup, in worktree mode the item worktree * persists across the item's phases and the engine's sequential integration * lane owns the merge-back and cleanup, so a phase NEVER merges to base or * removes the worktree. */ export interface PhaseItemWorktree { readonly path: string; commit(message: string, paths?: string[]): Promise; } export interface PhaseRunnerDeps { readonly agentManager: PhaseRunnerAgentManagerLike; readonly configManager: Pick; readonly runtimeBus: RuntimeEventBus; readonly projectRoot: string; readonly sessionId: string; readonly createWorktree?: (() => WrfcWorktreeOps) | undefined; readonly cancellation: CancellationRegistry; readonly priceUsage?: ((model: string | undefined, usage: WorkItemUsage) => number | null) | undefined; /** Provenance for the same resolution priceUsage prices with, stamped onto the committed usage record at pricing time. */ readonly priceProvenance?: PriceProvenanceFn | undefined; readonly skipClaimVerification?: boolean | undefined; /** * The dirty-tree snapshot taken synchronously at engine launch (see * CHANGELOG 0.38.0 and dirty-guard.ts). Absent (undefined) degrades to * today's behavior: no exclusion, every candidate path is committed. */ readonly launchDirtySnapshot?: DirtyLaunchSnapshot | undefined; /** * Present ONLY in `worktree` isolation mode: this item's dedicated worktree * (created by the engine at first claim). When set, the phase's scoped commit * lands on the item branch INSIDE this worktree (not the shared projectRoot), * and the spawned agent runs with its working directory set to the worktree * path. Absent ⇒ shared mode, every existing behavior unchanged. */ readonly itemWorktree?: PhaseItemWorktree | undefined; } export interface PhaseRunOutcome { readonly result: PhaseResult; readonly agentStatus: 'completed' | 'failed' | 'cancelled'; } /** * Combines a new phase's usage into a work item's running total. Single-source * cost (never independently re-priced here). Thin alias over the canonical * {@link mergeWorkItemUsage} (types.ts) so the phase-runner, the engine, and * the fleet rollup adapters all fold usage through exactly one implementation. */ export declare function mergeUsage(a: WorkItemUsage, b: WorkItemUsage): WorkItemUsage; /** Runs one WorkItem through one Phase to completion (or cancellation/failure). Recurses (bounded by transportRetryLimit) on a transport-classified spawn failure. */ export declare function runPhase(workstream: Workstream, item: WorkItem, phase: Phase, priorReports: readonly PhaseResult[], deps: PhaseRunnerDeps): Promise; //# sourceMappingURL=phase-runner.d.ts.map