/** * harness-run-context — shared types and helpers for active harness runs. * * Session entry `harness-run-context` is the live source of truth; disk mirrors: * - `.pi/harness/runs//run-context.yaml` * - `.pi/harness/active-run.json` (cross-session pointer) */ import { mkdir, readdir, readFile, realpath, stat, unlink, writeFile, } from "node:fs/promises"; import { isAbsolute, join, relative, resolve } from "node:path"; import { isPlanApprovalAskUser, PLAN_APPROVE_OPTION, PLAN_CANCEL_OPTION, } from "./ask-user/policy.js"; import { type BenchmarkLogLike, effectiveSteerMaxAttempts, type RemediationClass, type ReviewOutcomeLike, recommendedNextForRemediation, remediationClassFromEvalVerdict, steerBurstAllowed, synthesizeReviewOutcome, } from "./harness-remediation.js"; import { readYamlFile, writeYamlFile } from "./harness-yaml.js"; export type { RemediationClass, ReviewOutcomeLike, } from "./harness-remediation.js"; export { recommendedNextForRemediation, remediationClassFromEvalVerdict, } from "./harness-remediation.js"; import { readTaskClarificationDoc } from "./plan-task-clarification.js"; export { isPlanApprovalAskUser } from "./ask-user/policy.js"; export type HarnessPhase = | "plan" | "execute" | "evaluate" | "adversary" | "merge"; export type HarnessRunStatus = "active" | "aborted" | "completed"; export interface HarnessRunContext { schema_version: "1.0.0"; run_id: string; pi_session_id: string; project_root: string; phase: HarnessPhase; plan_id: string | null; plan_packet_path: string | null; plan_ready: boolean; task_summary: string | null; status: HarnessRunStatus; last_completed_step: string | null; last_outcome: string | null; next_recommended_command: string | null; owner_pi_session_id: string; updated_at: string; harness_run_started_emitted?: boolean; turn_override_run_id?: string | null; /** Persisted steer gate approval (cross-session via run-context.yaml). */ steer_approved?: boolean; steer_attempt?: number; steer_max_attempts?: number; /** Set after burst/inline repair subprocess completes in review cycle. */ inline_repair_attempted?: boolean; } export interface ProjectActiveRunPointer { schema_version: "1.0.0"; run_id: string; project_root: string; owner_pi_session_id: string; phase: HarnessPhase; plan_id: string | null; plan_ready: boolean; updated_at: string; } export interface PlanPacketSummary { plan_id: string; plan_packet_path: string; scope_one_liner: string; acceptance_check_count: number; plan_status: string; } export interface PlanPacketLike { schema_version?: string; contract_version?: string; plan_id?: string; task_id?: string; scope?: string; acceptance_checks?: unknown[]; risk_level?: string; assumptions?: unknown[]; rollback_plan?: unknown; execution_plan?: unknown; } export interface HarnessClearManifestItem { run_id: string; absolute_path: string; canonical_path: string; } export interface HarnessClearManifest { runs_root: string; protected_run_ids: string[]; candidates: ReadonlyArray; skipped: ReadonlyArray<{ run_id: string; reason: string }>; } interface SessionEntryLike { type?: string; customType?: string; data?: unknown; } const SCHEMA_VERSION = "1.0.0" as const; const HARNESS_COMMANDS = new Set([ "harness-plan", "harness-run", "harness-eval", "harness-review", "harness-steer", "harness-critic", "harness-trace", "harness-incident", "harness-abort", "harness-auto", "harness-new-run", "harness-run-status", "harness-use-run", "harness-drift-replan", "harness-drift-proceed", "harness-policy-status", "harness-trace-last", "harness-budget-status", "harness-clear", ]); export function harnessRunsRoot(projectRoot: string): string { return join(projectRoot, ".pi", "harness", "runs"); } export async function buildHarnessClearManifest( projectRoot: string, protectedRunIds: Iterable = [], ): Promise { const runsRoot = resolve(harnessRunsRoot(projectRoot)); const protectedSet = new Set( [...protectedRunIds] .filter( (id): id is string => typeof id === "string" && id.trim().length > 0, ) .map((id) => id.trim()), ); const protectedIds = [...protectedSet].sort(); let runsReal = runsRoot; try { runsReal = await realpath(runsRoot); } catch { return { runs_root: runsRoot, protected_run_ids: protectedIds, candidates: Object.freeze([]), skipped: Object.freeze([]), }; } let entries: Array<{ name: string; isDirectory(): boolean; isSymbolicLink(): boolean; }>; try { entries = await readdir(runsRoot, { withFileTypes: true, encoding: "utf8", }); } catch { return { runs_root: runsReal, protected_run_ids: protectedIds, candidates: Object.freeze([]), skipped: Object.freeze([]), }; } const candidates: HarnessClearManifestItem[] = []; const skipped: Array<{ run_id: string; reason: string }> = []; for (const entry of entries) { if (!entry.isDirectory() && !entry.isSymbolicLink()) continue; const runId = entry.name; if (protectedSet.has(runId)) { skipped.push({ run_id: runId, reason: "protected" }); continue; } const absPath = join(runsRoot, runId); let canonicalPath: string; try { canonicalPath = await realpath(absPath); } catch { skipped.push({ run_id: runId, reason: "unresolvable" }); continue; } const rel = relative(runsReal, canonicalPath); if (!rel || rel.startsWith("..") || isAbsolute(rel)) { skipped.push({ run_id: runId, reason: "out_of_root" }); continue; } if (rel !== runId) { skipped.push({ run_id: runId, reason: "non_canonical_child" }); continue; } try { const info = await stat(canonicalPath); if (!info.isDirectory()) { skipped.push({ run_id: runId, reason: "not_directory" }); continue; } } catch { skipped.push({ run_id: runId, reason: "missing" }); continue; } candidates.push({ run_id: runId, absolute_path: absPath, canonical_path: canonicalPath, }); } candidates.sort((a, b) => a.run_id.localeCompare(b.run_id)); skipped.sort((a, b) => a.run_id.localeCompare(b.run_id)); return { runs_root: runsReal, protected_run_ids: protectedIds, candidates: Object.freeze(candidates.map((item) => Object.freeze(item))), skipped: Object.freeze(skipped.map((item) => Object.freeze(item))), }; } export function activeRunPointerPath(projectRoot: string): string { return join(projectRoot, ".pi", "harness", "active-run.json"); } export function runContextDiskPath(runId: string, projectRoot: string): string { return join(harnessRunsRoot(projectRoot), runId, RUN_CONTEXT_BASENAME); } export function canonicalPlanPath(runId: string, projectRoot: string): string { return join(harnessRunsRoot(projectRoot), runId, PLAN_PACKET_BASENAME); } export function canonicalResearchBriefPath( runId: string, projectRoot: string, ): string { return join(harnessRunsRoot(projectRoot), runId, RESEARCH_BRIEF_BASENAME); } export function runArtifactsDir(runId: string, projectRoot: string): string { return join(harnessRunsRoot(projectRoot), runId, "artifacts"); } export const PLAN_REVIEW_BASENAME = "plan-review.md"; export function canonicalPlanReviewPath( runId: string, projectRoot: string, ): string { return join(harnessRunsRoot(projectRoot), runId, PLAN_REVIEW_BASENAME); } export const PLAN_PACKET_BASENAME = "plan-packet.yaml"; export const RUN_CONTEXT_BASENAME = "run-context.yaml"; export const RESEARCH_BRIEF_BASENAME = "research-brief.yaml"; const PLAN_RUN_SCOPED_ROOT_FILES = new Set([ PLAN_PACKET_BASENAME, RESEARCH_BRIEF_BASENAME, "plan-dag-validation.yaml", PLAN_REVIEW_BASENAME, ]); /** * Parent orchestrator artifacts writable during evaluate/adversary (ADR 0044). * Keep in sync with harness-review.md / harness-steer.md parent write_harness_yaml paths. */ export const EVALUATE_PHASE_ORCHESTRATOR_ARTIFACTS = new Set([ "benchmark-log.yaml", "review-outcome.yaml", "repair-brief.yaml", "steer-state.yaml", "eval-benchmark.yaml", "sentrux-signal.yaml", "ls-lint-signal.yaml", "sentrux-repair-plan.yaml", ]); /** Run-relative path like `artifacts/benchmark-log.yaml` (no run_id prefix). */ export function isEvaluatePhaseOrchestratorArtifactRel(rel: string): boolean { const norm = rel.replace(/\\/g, "/"); const parts = norm.split("/"); if (parts.length !== 2 || parts[0] !== "artifacts") return false; return EVALUATE_PHASE_ORCHESTRATOR_ARTIFACTS.has(parts[1]); } export const DEFAULT_STEER_MAX_ATTEMPTS = 3; export function isSteerBurstArgs(args: string): boolean { return /\b--burst\b/.test(args); } export function steerMaxAttemptsFromEnv(): number { const raw = process.env.HARNESS_STEER_MAX_ATTEMPTS?.trim(); if (!raw) return DEFAULT_STEER_MAX_ATTEMPTS; const n = Number.parseInt(raw, 10); return Number.isFinite(n) && n > 0 ? n : DEFAULT_STEER_MAX_ATTEMPTS; } const MUTATING_FILE_TOOLS = new Set(["write", "edit"]); export interface PlanUserApproval { plan_id: string | null; approved_at: string; source: | "ask_user" | "approve_plan" | "harness-plan-approval" | "noninteractive"; } /** Persisted on `input` when user invokes a raw `/harness-*` prompt template. */ export interface HarnessTurnEntry { schema_version: "1.0.0"; command: string; args: string; source: "slash"; invoked_at: string; } export const HARNESS_COMMAND_PHASE: Record = { "harness-plan": "plan", "harness-auto": "plan", "harness-run": "execute", "harness-eval": "evaluate", "harness-review": "evaluate", "harness-steer": "execute", "harness-critic": "adversary", "harness-trace": "evaluate", "harness-incident": "evaluate", "harness-drift-replan": "plan", "harness-drift-proceed": "execute", "harness-abort": "plan", "harness-new-run": "plan", "harness-run-status": "plan", "harness-use-run": "plan", "harness-policy-status": "merge", "harness-budget-status": "plan", "harness-clear": "plan", "harness-setup": "execute", }; export interface PlanPhaseMutationDecision { allowed: boolean; reason?: string; isScopedPlanWrite?: boolean; } /** Resolve path relative to project root when not absolute. */ export function normalizeHarnessPath( path: string, projectRoot: string, ): string { const trimmed = path.trim(); if (!trimmed) return resolve(projectRoot); if (isAbsolute(trimmed)) return resolve(trimmed); return resolve(projectRoot, trimmed); } /** Run-scoped artifact path without `.pi/harness/runs//` prefix (agent-friendly). */ export function isBareHarnessRunArtifactPath(rel: string): boolean { const norm = rel.replace(/\\/g, "/").replace(/^\.\//, ""); if (!norm || norm.startsWith("..") || isAbsolute(norm)) return false; if (norm.startsWith(".pi/harness/runs/")) return false; const parts = norm.split("/"); if (parts.length === 1 && PLAN_RUN_SCOPED_ROOT_FILES.has(parts[0])) { return true; } if (parts.length === 2 && parts[0] === "artifacts") { const file = parts[1]; return file.endsWith(".yaml") || file.endsWith(".yml"); } if ( parts.length === 3 && parts[0] === "artifacts" && parts[1] === "context-bundles" ) { const file = parts[2]; return file.endsWith(".yaml") || file.endsWith(".yml"); } return false; } /** * Resolve a harness write path to an absolute file and run-relative gate path. * Accepts `artifacts/foo.yaml`, `research-brief.yaml`, full `.pi/harness/runs//…`, or `/artifacts/…`. */ export function resolveHarnessRunWriteTarget( pathArg: string, runCtx: HarnessRunContext, projectRoot: string, ): { absPath: string; relUnderRun: string } | null { const trimmed = pathArg.trim().replace(/\\/g, "/"); if (!trimmed || !runCtx.run_id) return null; const runPrefix = `.pi/harness/runs/${runCtx.run_id}/`; let relUnderRun: string | null = null; if (trimmed.startsWith(runPrefix)) { relUnderRun = trimmed.slice(runPrefix.length); } else if (trimmed.startsWith(`${runCtx.run_id}/`)) { relUnderRun = trimmed.slice(`${runCtx.run_id}/`.length); } else if (isBareHarnessRunArtifactPath(trimmed)) { relUnderRun = trimmed.replace(/^\.\//, ""); } if (!relUnderRun) return null; const scopedCheck = `${runCtx.run_id}/${relUnderRun}`; if (!isPlanRunScopedRelativePath(scopedCheck)) return null; const absPath = join( harnessRunsRoot(projectRoot), runCtx.run_id, relUnderRun, ); return { absPath, relUnderRun }; } export function isCanonicalPlanPacketPath( absPath: string, projectRoot: string, runId: string, ): boolean { const expected = resolve(canonicalPlanPath(runId, projectRoot)); return resolve(absPath) === expected; } export function extractWritePathFromToolInput( input: Record, ): string { const raw = (typeof input.path === "string" && input.path) || (typeof input.filePath === "string" && input.filePath) || ""; return raw.trim(); } /** True when absPath is a plan-phase artifact under the active run directory. */ export function isPlanRunScopedRelativePath(rel: string): boolean { if (rel.startsWith("..") || isAbsolute(rel)) return false; const parts = rel.split(/[/\\]/); if (parts.length === 2 && PLAN_RUN_SCOPED_ROOT_FILES.has(parts[1])) { return true; } if (parts.length === 3 && parts[1] === "artifacts") { const file = parts[2]; return file.endsWith(".yaml") || file.endsWith(".yml"); } if ( parts.length === 4 && parts[1] === "artifacts" && parts[2] === "context-bundles" && (parts[3].endsWith(".yaml") || parts[3].endsWith(".yml")) ) { return true; } return false; } /** Scoped path under `.pi/harness/runs//` (includes run_id prefix). */ export function isEvaluatePhaseOrchestratorArtifact(rel: string): boolean { if (rel.startsWith("..") || isAbsolute(rel)) return false; const parts = rel.split(/[/\\]/); if (parts.length !== 3 || parts[1] !== "artifacts") return false; return EVALUATE_PHASE_ORCHESTRATOR_ARTIFACTS.has(parts[2]); } /** Strip `/` from a path relative to `.pi/harness/runs/`. */ export function stripRunIdFromHarnessScopedRelative( rel: string, runId: string, ): string { const norm = rel.replace(/\\/g, "/"); const prefix = `${runId}/`; return norm.startsWith(prefix) ? norm.slice(prefix.length) : norm; } /** Path under the run directory (e.g. `artifacts/foo.yaml`), for gates and artifact keys. */ export async function relPathUnderActiveRun( absPath: string, runCtx: HarnessRunContext, projectRoot: string, ): Promise { const rel = await planRunScopedRelative(absPath, runCtx, projectRoot); if (!rel) return null; return stripRunIdFromHarnessScopedRelative(rel, runCtx.run_id); } async function planRunScopedRelative( absPath: string, runCtx: HarnessRunContext, projectRoot: string, ): Promise { let resolved: string; try { resolved = await realpath(normalizeHarnessPath(absPath, projectRoot)); } catch { resolved = normalizeHarnessPath(absPath, projectRoot); } const runsRoot = resolve(harnessRunsRoot(projectRoot)); let runsReal: string; try { runsReal = await realpath(runsRoot); } catch { runsReal = runsRoot; } const rel = relative(runsReal, resolved); if (!isPlanRunScopedRelativePath(rel)) return null; const parts = rel.split(/[/\\]/); if (parts[0] !== runCtx.run_id) return null; return rel; } /** True when absPath is a writable plan-run artifact for the active run. */ export async function isPlanPhaseScopedWrite( absPath: string, runCtx: HarnessRunContext | null, projectRoot: string, ): Promise { if (!runCtx?.run_id) return false; let resolved: string; try { resolved = await realpath(normalizeHarnessPath(absPath, projectRoot)); } catch { resolved = normalizeHarnessPath(absPath, projectRoot); } const runsRoot = resolve(harnessRunsRoot(projectRoot)); let runsReal: string; try { runsReal = await realpath(runsRoot); } catch { runsReal = runsRoot; } const rel = relative(runsReal, resolved); if (!isPlanRunScopedRelativePath(rel)) return false; const parts = rel.split(/[/\\]/); return parts[0] === runCtx.run_id; } export function getLatestHarnessTurn( entries: unknown[], ): HarnessTurnEntry | null { for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom" || entry.customType !== "harness-turn") { continue; } const data = entry.data as Partial | undefined; if (data?.command && typeof data.command === "string") { return { schema_version: "1.0.0", command: data.command, args: typeof data.args === "string" ? data.args : "", source: "slash", invoked_at: typeof data.invoked_at === "string" ? data.invoked_at : nowIso(), }; } } return null; } export function indexOfLastPlanCommand(entries: unknown[]): number { for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike & { message?: { role?: string; content?: string | unknown[] }; }; if (entry.type === "custom" && entry.customType === "harness-turn") { const cmd = (entry.data as { command?: string })?.command; if (cmd === "harness-plan" || cmd === "harness-auto") { return i; } } if ( entry.type === "custom" && entry.customType === "harness-plan-attempt" ) { return i; } if (entry.type !== "message" || entry.message?.role !== "user") continue; const content = entry.message.content; const text = typeof content === "string" ? content : Array.isArray(content) ? content .filter( (c): c is { type: string; text?: string } => typeof c === "object" && c !== null && (c as { type?: string }).type === "text", ) .map((c) => c.text ?? "") .join("\n") : ""; const visible = userVisiblePromptSlice(text); const parsed = parseHarnessSlashInput(visible); if ( parsed?.command === "harness-plan" || parsed?.command === "harness-auto" ) { return i; } } return -1; } type PlanApprovalToolDetails = { cancelled?: boolean; plan_packet?: PlanPacketLike; response?: { kind?: string; text?: string; selections?: string[]; }; }; function planIdFromApprovalDetails( details: PlanApprovalToolDetails | undefined, ): string | null { const fromPacket = details?.plan_packet?.plan_id; return typeof fromPacket === "string" && fromPacket.length > 0 ? fromPacket : null; } export function parsePlanApprovalFromMessage(msg: { toolName?: string; details?: unknown; content?: { type?: string; text?: string }[]; }): PlanUserApproval | null { const toolName = msg.toolName; if (toolName !== "ask_user" && toolName !== "approve_plan") return null; const source = toolName === "approve_plan" ? "approve_plan" : "ask_user"; const details = msg.details as PlanApprovalToolDetails | undefined; if (details?.cancelled) return null; const response = details?.response; if (!response) return null; const plan_id = planIdFromApprovalDetails(details); if (response.kind === "freeform") { const text = (response.text ?? "").trim(); if (/^approve(d)?\b/i.test(text)) { return { plan_id, approved_at: nowIso(), source, }; } return null; } const selection = (response.selections?.[0] ?? "").trim(); if (!selection || PLAN_CANCEL_OPTION.test(selection)) return null; if (PLAN_APPROVE_OPTION.test(selection)) { return { plan_id, approved_at: nowIso(), source, }; } return null; } /** @deprecated Use parsePlanApprovalFromMessage */ export function parseAskUserApprovalFromMessage(msg: { toolName?: string; details?: unknown; content?: { type?: string; text?: string }[]; }): PlanUserApproval | null { return parsePlanApprovalFromMessage(msg); } export function getLatestPlanUserApproval( entries: unknown[], sinceIndex = 0, ): PlanUserApproval | null { for (let i = entries.length - 1; i >= sinceIndex; i--) { const entry = entries[i] as SessionEntryLike & { message?: { role?: string; toolName?: string; details?: unknown; content?: { type?: string; text?: string }[]; }; }; if ( entry.type === "custom" && entry.customType === "harness-plan-approval" ) { const data = entry.data as Partial | undefined; if (data?.approved_at) { return { plan_id: typeof data.plan_id === "string" ? data.plan_id : null, approved_at: data.approved_at, source: data.source === "noninteractive" ? "noninteractive" : "harness-plan-approval", }; } } if (entry.type !== "message" || entry.message?.role !== "toolResult") { continue; } const fromTool = parsePlanApprovalFromMessage(entry.message); if (fromTool) return fromTool; } return null; } export function hasPlanUserApproval( entries: unknown[], opts?: { planId?: string | null; sincePlanCommand?: boolean }, ): boolean { const since = opts?.sincePlanCommand ? Math.max(0, indexOfLastPlanCommand(entries)) : 0; const approval = getLatestPlanUserApproval(entries, since); if (!approval) return false; if (opts?.planId && approval.plan_id && approval.plan_id !== opts.planId) { return false; } return true; } export function isHarnessAutoSession(entries: unknown[]): boolean { const since = indexOfLastPlanCommand(entries); if (since < 0) return false; for (let i = since; i < entries.length; i++) { const entry = entries[i] as SessionEntryLike & { message?: { role?: string; content?: string }; }; if (entry.type === "custom" && entry.customType === "harness-turn") { const cmd = (entry.data as { command?: string })?.command; if (cmd === "harness-auto") return true; } if (entry.type !== "message" || entry.message?.role !== "user") continue; const text = typeof entry.message.content === "string" ? userVisiblePromptSlice(entry.message.content) : ""; const parsed = parseHarnessSlashInput(text); if (parsed?.command === "harness-auto") return true; } return false; } export async function isPlanPhaseAllowedMutation( toolName: string, input: Record, phase: HarnessPhase, runCtx: HarnessRunContext | null, projectRoot: string, opts: { aborted: boolean; entries: unknown[]; ownerSessionId?: string; currentSessionId?: string; }, ): Promise { if (!MUTATING_FILE_TOOLS.has(toolName)) { return { allowed: true }; } if ( runCtx?.owner_pi_session_id && opts.currentSessionId && runCtx.owner_pi_session_id !== opts.currentSessionId && !isHarnessSubprocess() ) { return { allowed: false, reason: "harness-run-context: this session does not own the active run; plan writes are read-only here.", }; } const target = extractWritePathFromToolInput(input); if (!target) { return { allowed: false, reason: "policy-gate: write/edit requires a path.", }; } const scoped = runCtx ? await isPlanPhaseScopedWrite(target, runCtx, projectRoot) : false; if (scoped) { if (!runCtx) { return { allowed: false, reason: 'policy-gate: no active harness run. Run /harness-plan "" first.', }; } if (opts.aborted) { return { allowed: true, isScopedPlanWrite: true }; } if (phase === "plan") { return { allowed: true, isScopedPlanWrite: true }; } if (phase === "execute" || phase === "merge") { return { allowed: true, isScopedPlanWrite: true }; } if (phase === "evaluate" || phase === "adversary") { const rel = await planRunScopedRelative(target, runCtx, projectRoot); const relForGate = rel ? stripRunIdFromHarnessScopedRelative(rel, runCtx.run_id) : null; if ( (rel && isEvaluatePhaseOrchestratorArtifact(rel)) || (relForGate && isEvaluatePhaseOrchestratorArtifactRel(relForGate)) ) { return { allowed: true, isScopedPlanWrite: true }; } } return { allowed: false, isScopedPlanWrite: true, reason: `harness-run-context: plan-run artifact is read-only in phase '${phase}'.`, }; } if (opts.aborted) { return { allowed: false, reason: "policy-gate: mutating tool blocked because harness-abort lock is active. Attach a new approved plan via plan-packet.yaml first.", }; } if (phase === "execute" || phase === "merge") { return { allowed: true }; } if (phase === "plan" && !runCtx) { return { allowed: false, reason: 'policy-gate: no active harness run. Run /harness-plan "" first.', }; } const allowedPath = runCtx?.run_id ? canonicalPlanPath(runCtx.run_id, projectRoot) : `.pi/harness/runs//${PLAN_PACKET_BASENAME}`; return { allowed: false, reason: `policy-gate: ${toolName} blocked in phase '${phase}'. In plan phase only ${allowedPath} is writable after ask_user approval.`, }; } export function allocateRunId(sessionId: string): string { return `${sessionId}-${Date.now()}`; } export function nowIso(): string { return new Date().toISOString(); } /** @deprecated Use parseHarnessSlashInput on raw `input` event text only. */ export function isHarnessSlashCommand(prompt: string): boolean { return parseHarnessSlashInput(prompt) !== null; } /** Parse raw user input before prompt-template expansion (`input` hook only). */ export function parseHarnessSlashInput( text: string, ): { command: string; args: string } | null { const trimmed = text.trim(); const match = trimmed.match(/^\/(harness-[a-z0-9-]+)(?:\s+([\s\S]*))?$/); if (!match) return null; const command = match[1]; if (!HARNESS_COMMANDS.has(command)) return null; return { command, args: (match[2] ?? "").trim() }; } /** @deprecated Prefer parseHarnessSlashInput on raw input; kept for expanded-prompt fallbacks. */ export function parseHarnessSlashCommand( prompt: string, ): { command: string; args: string } | null { return parseHarnessSlashInput(userVisiblePromptSlice(prompt)); } /** User-visible prompt slice for policy signals (exclude injected blocks). */ export function userVisiblePromptSlice(prompt: string): string { const markers = [ "\n\n[HarnessRunContext]", "\n\n[HarnessActivePlan]", "\n\n[PolicyGate]", ]; let slice = prompt; for (const marker of markers) { const idx = slice.indexOf(marker); if (idx >= 0) slice = slice.slice(0, idx); } return slice.trim(); } export function hasApprovedPlanSignalFromUserPrompt(prompt: string): boolean { const p = userVisiblePromptSlice(prompt).toLowerCase(); if (p.includes("user approved") || p.includes("already approved")) { return true; } if (/\bapprove(d)?\s+(this\s+)?plan\b/.test(p)) return true; if (p.includes("harness-plan-approval")) return true; return false; } export function appendPlanApprovalIfNew( appendEntry: (customType: string, data: unknown) => void, parentEntries: unknown[], approval: PlanUserApproval, runCtx: HarnessRunContext | null, opts?: { sincePlanCommand?: boolean }, ): boolean { const since = opts?.sincePlanCommand !== false ? Math.max(0, indexOfLastPlanCommand(parentEntries)) : 0; if (getLatestPlanUserApproval(parentEntries, since)) { return false; } appendEntry("harness-plan-approval", { plan_id: approval.plan_id ?? runCtx?.plan_id ?? null, approved_at: approval.approved_at, source: approval.source, }); return true; } /** Sync planner subagent approvals into the parent session (deduped). */ export function syncPlannerApprovalsToParent( appendEntry: (customType: string, data: unknown) => void, parentEntries: unknown[], subEntries: unknown[], runCtx: HarnessRunContext | null, ): number { let synced = 0; for (const approval of extractPlanApprovalsFromEntries(subEntries)) { if (appendPlanApprovalIfNew(appendEntry, parentEntries, approval, runCtx)) { synced++; } } return synced; } export function isDriftReplanPrompt(prompt: string): boolean { const p = userVisiblePromptSlice(prompt).toLowerCase(); return ( p.includes("harness-drift-replan") || p.includes("/harness-drift-replan") || p.includes("drift-replan") ); } export function getLatestRunContext( entries: unknown[], ): HarnessRunContext | null { for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom") continue; if (entry.customType === "harness-clear-result") { if (isConfirmedHarnessClearData(entry.data)) return null; continue; } if (entry.customType !== "harness-run-context") continue; const ctx = entry.data as Partial | undefined; if (ctx?.run_id && ctx.project_root) { if ( isRunClearedByClearEntriesAfterIndex(entries, i, ctx.run_id) || isRunTombstonedByPriorClear(entries, i, ctx.run_id) ) { continue; } return normalizeRunContext(ctx); } } return null; } function normalizeRunContext( partial: Partial, ): HarnessRunContext { return { schema_version: SCHEMA_VERSION, run_id: partial.run_id!, pi_session_id: partial.pi_session_id ?? partial.run_id!, project_root: partial.project_root!, phase: partial.phase ?? "plan", plan_id: partial.plan_id ?? null, plan_packet_path: partial.plan_packet_path ?? null, plan_ready: Boolean(partial.plan_ready), task_summary: partial.task_summary ?? null, status: partial.status ?? "active", last_completed_step: partial.last_completed_step ?? null, last_outcome: partial.last_outcome ?? null, next_recommended_command: partial.next_recommended_command ?? null, owner_pi_session_id: partial.owner_pi_session_id ?? partial.pi_session_id ?? partial.run_id!, updated_at: partial.updated_at ?? nowIso(), harness_run_started_emitted: partial.harness_run_started_emitted, turn_override_run_id: partial.turn_override_run_id ?? null, }; } export function createFreshRunContext( sessionId: string, projectRoot: string, taskSummary: string | null = null, ): HarnessRunContext { const runId = allocateRunId(sessionId); const ts = nowIso(); return { schema_version: SCHEMA_VERSION, run_id: runId, pi_session_id: sessionId, project_root: projectRoot, phase: "plan", plan_id: null, plan_packet_path: canonicalPlanPath(runId, projectRoot), plan_ready: false, task_summary: taskSummary, status: "active", last_completed_step: null, last_outcome: null, next_recommended_command: null, owner_pi_session_id: sessionId, updated_at: ts, harness_run_started_emitted: false, turn_override_run_id: null, }; } export async function loadRunContextFromDisk( runId: string, projectRoot: string, ): Promise { try { const doc = await readYamlFile( runContextDiskPath(runId, projectRoot), "run-context", ); return normalizeRunContext(doc as Partial); } catch { return null; } } export async function saveRunContextToDisk( ctx: HarnessRunContext, ): Promise { const dir = join(harnessRunsRoot(ctx.project_root), ctx.run_id); await mkdir(dir, { recursive: true }); await writeYamlFile(runContextDiskPath(ctx.run_id, ctx.project_root), ctx); } export async function loadProjectActiveRun( projectRoot: string, ): Promise { try { const raw = await readFile(activeRunPointerPath(projectRoot), "utf-8"); return JSON.parse(raw) as ProjectActiveRunPointer; } catch { return null; } } export async function deleteProjectActiveRun( projectRoot: string, ): Promise { try { await unlink(activeRunPointerPath(projectRoot)); return true; } catch { return false; } } export async function saveProjectActiveRun( ctx: HarnessRunContext, ): Promise { const pointer: ProjectActiveRunPointer = { schema_version: SCHEMA_VERSION, run_id: ctx.run_id, project_root: ctx.project_root, owner_pi_session_id: ctx.owner_pi_session_id, phase: ctx.phase, plan_id: ctx.plan_id, plan_ready: ctx.plan_ready, updated_at: ctx.updated_at, }; await mkdir(join(ctx.project_root, ".pi", "harness"), { recursive: true, }); await writeFile( activeRunPointerPath(ctx.project_root), `${JSON.stringify(pointer, null, 2)}\n`, "utf-8", ); } export function activeRunTtlHours(): number { const raw = Number(process.env.HARNESS_ACTIVE_RUN_TTL_HOURS ?? "72"); return Number.isFinite(raw) && raw > 0 ? raw : 72; } export function isStaleActiveRunPointer( pointer: ProjectActiveRunPointer, currentProjectRoot: string, ): boolean { if (resolve(pointer.project_root) !== resolve(currentProjectRoot)) { return true; } const ageMs = Date.now() - Date.parse(pointer.updated_at); if (!Number.isFinite(ageMs)) return true; return ageMs > activeRunTtlHours() * 60 * 60 * 1000; } export interface ActiveRunOwnershipConflict { runId: string; ownerPiSessionId: string; } /** True when another Pi session owns a non-stale active run on disk. */ export async function findActiveRunOwnershipConflict( projectRoot: string, sessionId: string, ): Promise { const pointer = await loadProjectActiveRun(projectRoot); if (!pointer || isStaleActiveRunPointer(pointer, projectRoot)) return null; if (pointer.owner_pi_session_id === sessionId) return null; const disk = await loadRunContextFromDisk(pointer.run_id, projectRoot); if (!disk || disk.status !== "active") return null; return { runId: pointer.run_id, ownerPiSessionId: pointer.owner_pi_session_id, }; } /** QA smoke: drop stale harness-qa-live ownership left by interrupted live QA runs. */ export async function releaseForeignQaRunOwnership( projectRoot: string, sessionId: string, ): Promise { if (process.env.HARNESS_QA_SMOKE !== "1") return false; const pointer = await loadProjectActiveRun(projectRoot); if (!pointer || pointer.owner_pi_session_id === sessionId) return false; if (!pointer.owner_pi_session_id.startsWith("harness-qa-live-")) { return false; } const disk = await loadRunContextFromDisk(pointer.run_id, projectRoot); if (disk && disk.status === "active") { await saveRunContextToDisk({ ...disk, status: "aborted", last_outcome: "abandoned", last_completed_step: "abort", updated_at: nowIso(), }); } await deleteProjectActiveRun(projectRoot); return true; } export interface CrossSessionResumeInfo { runId: string; resumeCommand: string; phase: HarnessPhase; planReady: boolean; nextAfterResume: string | null; taskSummary: string | null; } function isConfirmedHarnessClearData(data: unknown): boolean { const clearData = data as | { approved?: boolean; active_cleared?: boolean; cleared_all?: boolean; } | undefined; return ( clearData?.approved === true && (clearData.active_cleared === true || clearData.cleared_all === true) ); } function confirmedHarnessClearRunIds(data: unknown): string[] { if (!isConfirmedHarnessClearData(data)) return []; const clearData = data as { active_run_ids?: unknown } | undefined; if (!Array.isArray(clearData?.active_run_ids)) return []; return clearData.active_run_ids.filter( (runId): runId is string => typeof runId === "string" && runId.trim().length > 0, ); } export function runIdFromCrossSessionResumeCommand( command: string | null | undefined, ): string | null { if (!command) return null; const parts = command.trim().split(/\s+/); const commandIndex = parts.indexOf("/harness-use-run"); if (commandIndex < 0) return null; const runId = parts[commandIndex + 1]; return runId && !runId.startsWith("-") ? runId : null; } export function isRunIdTombstonedByConfirmedHarnessClear( entries: unknown[], runId: string, ): boolean { return entries.some((raw) => { const entry = raw as SessionEntryLike; return ( entry.type === "custom" && entry.customType === "harness-clear-result" && confirmedHarnessClearAppliesToRun(entry.data, runId) ); }); } function confirmedHarnessClearAppliesToRun( data: unknown, runId: string, ): boolean { if (!isConfirmedHarnessClearData(data)) return false; const runIds = confirmedHarnessClearRunIds(data); if (runIds.length === 0) return false; return runIds.includes(runId); } function isRunClearedByClearEntriesAfterIndex( entries: unknown[], runContextIndex: number, runId: string, ): boolean { for (let i = runContextIndex + 1; i < entries.length; i++) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom") continue; if (entry.customType !== "harness-clear-result") continue; if (confirmedHarnessClearAppliesToRun(entry.data, runId)) return true; } return false; } function isRunTombstonedByPriorClear( entries: unknown[], runContextIndex: number, runId: string, ): boolean { for (let i = 0; i < runContextIndex; i++) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom") continue; if (entry.customType !== "harness-clear-result") continue; if (confirmedHarnessClearAppliesToRun(entry.data, runId)) return true; } return false; } /** True when a confirmed clear tombstoned this run id in this session. */ export function isRunClearedByConfirmedHarnessClear( entries: unknown[], runId: string, ): boolean { if (isRunIdTombstonedByConfirmedHarnessClear(entries, runId)) { return true; } for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom" || entry.customType !== "harness-run-context") { continue; } const ctx = entry.data as Partial | undefined; if (ctx?.run_id !== runId) continue; return ( isRunClearedByClearEntriesAfterIndex(entries, i, runId) || isRunTombstonedByPriorClear(entries, i, runId) ); } return false; } /** True once this session has recorded a confirmed clear of active harness runs. */ export function hasConfirmedHarnessClear(entries: unknown[]): boolean { return entries.some((raw) => { const entry = raw as SessionEntryLike; return ( entry.type === "custom" && entry.customType === "harness-clear-result" && isConfirmedHarnessClearData(entry.data) ); }); } /** True when a confirmed clear is newer than the latest harness-run-context entry. */ export function hasConfirmedClearAfterLatestRunContext( entries: unknown[], ): boolean { for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom") continue; if (entry.customType === "harness-run-context") return false; if (entry.customType === "harness-clear-result") { return isConfirmedHarnessClearData(entry.data); } } return false; } /** True when this session already showed the cross-session resume prompt for runId. */ export function sessionHasResumePromptForRun( entries: unknown[], runId: string, ): boolean { for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom") continue; if (entry.customType !== "harness-session-resume-prompt") continue; const data = entry.data as { run_id?: string } | undefined; return data?.run_id === runId; } return false; } export function formatCrossSessionResumeMessage( info: CrossSessionResumeInfo, ): string { const lines = [ "Previous Pi session left an active harness run on disk.", ` run_id: ${info.runId}`, ` phase: ${info.phase}`, info.planReady ? " plan: approved" : " plan: not ready", ]; if (info.taskSummary) { const summary = info.taskSummary.length > 80 ? `${info.taskSummary.slice(0, 77)}...` : info.taskSummary; lines.push(` task: ${summary}`); } lines.push("", `Resume this session with: ${info.resumeCommand}`); if (info.nextAfterResume) { lines.push(`After binding, next step: ${info.nextAfterResume}`); } return lines.join("\n"); } export async function resolveCrossSessionResumeInfo( projectRoot: string, pointer: ProjectActiveRunPointer, ): Promise { if (isStaleActiveRunPointer(pointer, projectRoot)) return null; const disk = await loadRunContextFromDisk(pointer.run_id, projectRoot); if (!disk || disk.status !== "active") return null; if (disk.run_id !== pointer.run_id) return null; if (resolve(disk.project_root) !== resolve(projectRoot)) return null; const resumeCommand = `/harness-use-run ${pointer.run_id} --claim`; const statuses = await resolveCompletionStatuses( [], pointer.run_id, projectRoot, ); const nextAfterResume = nextStepAfterOutcome({ phase: disk.phase, planStatus: disk.plan_ready ? "ready" : null, lastCompletedStep: disk.last_completed_step, lastOutcome: disk.last_outcome, executionStatus: statuses.executionStatus, evalStatus: statuses.evalStatus, adversaryComplete: statuses.adversaryComplete, aborted: false, }); return { runId: pointer.run_id, resumeCommand, phase: disk.phase, planReady: disk.plan_ready, nextAfterResume, taskSummary: disk.task_summary, }; } /** Offer resume when disk has an active run but this Pi session is not bound yet. */ export async function evaluateCrossSessionResume( projectRoot: string, entries: unknown[], ): Promise { if (hasConfirmedClearAfterLatestRunContext(entries)) return null; const pointer = await loadProjectActiveRun(projectRoot); if (!pointer) return null; if (isRunIdTombstonedByConfirmedHarnessClear(entries, pointer.run_id)) { return null; } if (getLatestRunContext(entries)) return null; if (isRunClearedByConfirmedHarnessClear(entries, pointer.run_id)) { return null; } return resolveCrossSessionResumeInfo(projectRoot, pointer); } export async function readPlanPacketFromPath( planPath: string, ): Promise { try { const doc = await readYamlFile(planPath, planPath); return doc as PlanPacketLike; } catch { return null; } } /** * When plan-packet.yaml is missing (revision reset or pre-packet phase), derive * last_outcome from task-clarification instead of treating the run as invalid. */ const PLAN_REVIEW_COMMITTED_RE = /\*\*Status:\*\*\s*committed/i; /** True when plan-review.md on disk shows a committed plan (post create_plan). */ export async function isPlanCommittedOnDisk( projectRoot: string, runId: string, ): Promise { try { const raw = await readFile( canonicalPlanReviewPath(runId, projectRoot), "utf-8", ); return PLAN_REVIEW_COMMITTED_RE.test(raw); } catch { return false; } } /** * Align plan_ready / last_outcome with on-disk plan packet + plan-review.md * (survives -p sessions where approve_plan is not in the transcript). */ export async function syncPlanReadyFromDisk( projectRoot: string, ctx: HarnessRunContext, entries?: unknown[], ): Promise { const planPath = ctx.plan_packet_path ?? canonicalPlanPath(ctx.run_id, projectRoot); const packet = await readPlanPacketFromPath(planPath); if (!packet) { return syncPlanLastOutcomeFromTaskClarification(projectRoot, ctx); } const validation = validatePlanPacket(packet); if (!validation.valid) { const synced = await syncPlanLastOutcomeFromTaskClarification(projectRoot, { ...ctx, plan_packet_path: planPath, }); return { ...synced, plan_ready: false, last_outcome: "needs_clarification", }; } const committed = await isPlanCommittedOnDisk(projectRoot, ctx.run_id); const approved = committed || (entries ? hasPlanUserApproval(entries, { sincePlanCommand: true, planId: packet.plan_id ?? null, }) : false); const updated: HarnessRunContext = { ...ctx, plan_packet_path: planPath, plan_id: packet.plan_id ?? ctx.plan_id, updated_at: nowIso(), }; if (approved) { updated.plan_ready = true; const preservePostPlanProgress = ctx.last_completed_step === "execute" || ctx.last_completed_step === "steer" || ctx.last_completed_step === "review" || ctx.last_completed_step === "adversary"; if (!preservePostPlanProgress) { updated.last_completed_step = "plan"; updated.last_outcome = "ready"; updated.next_recommended_command = "/harness-run"; if ( updated.phase !== "execute" && updated.phase !== "evaluate" && updated.phase !== "adversary" ) { updated.phase = "plan"; } } return updated; } updated.plan_ready = false; if (updated.last_outcome !== "needs_clarification") { updated.last_outcome = "pending_approval"; } updated.next_recommended_command = nextStepAfterOutcome({ phase: updated.phase, planStatus: null, lastOutcome: updated.last_outcome, lastCompletedStep: updated.last_completed_step, }); return updated; } export async function syncPlanLastOutcomeFromTaskClarification( projectRoot: string, ctx: HarnessRunContext, ): Promise { const runDir = join(harnessRunsRoot(projectRoot), ctx.run_id); const doc = await readTaskClarificationDoc(runDir); if (!doc) return ctx; const status = String(doc.status ?? "").toLowerCase(); const updated: HarnessRunContext = { ...ctx, updated_at: nowIso() }; if (status === "ready") { if (updated.last_outcome === "needs_clarification") { updated.last_outcome = null; } } else if ( status === "needs_clarification" || status === "needs_user" || status === "draft" ) { updated.last_outcome = "needs_clarification"; } updated.next_recommended_command = nextStepAfterOutcome({ phase: updated.phase, planStatus: status === "ready" ? null : status, lastOutcome: updated.last_outcome, lastCompletedStep: updated.last_completed_step, }); return updated; } export function validatePlanPacket(packet: PlanPacketLike | null): { valid: boolean; errors: string[]; } { if (!packet) return { valid: false, errors: ["plan file missing or unreadable"] }; const errors: string[] = []; if (packet.schema_version !== "1.0.0") errors.push("schema_version must be 1.0.0"); if ( packet.contract_version !== "1.0.0" && packet.contract_version !== "1.1.0" ) errors.push("contract_version must be 1.0.0 or 1.1.0"); if (!packet.plan_id || typeof packet.plan_id !== "string") errors.push("plan_id required"); if (!packet.task_id || typeof packet.task_id !== "string") errors.push("task_id required"); if (!packet.scope || typeof packet.scope !== "string") errors.push("scope required"); if ( !Array.isArray(packet.acceptance_checks) || packet.acceptance_checks.length < 1 ) errors.push("acceptance_checks required"); if (!packet.risk_level) errors.push("risk_level required"); if (!packet.rollback_plan) errors.push("rollback_plan required"); if (packet.contract_version === "1.1.0" && !packet.execution_plan) { errors.push("execution_plan required for contract_version 1.1.0"); } return { valid: errors.length === 0, errors }; } export function planPacketSummary( packet: PlanPacketLike, planPath: string, planStatus = "ready", ): PlanPacketSummary { const scope = typeof packet.scope === "string" ? packet.scope : "(no scope)"; const oneLiner = scope.length > 120 ? `${scope.slice(0, 117)}...` : scope; return { plan_id: String(packet.plan_id ?? "unknown"), plan_packet_path: planPath, scope_one_liner: oneLiner, acceptance_check_count: Array.isArray(packet.acceptance_checks) ? packet.acceptance_checks.length : 0, plan_status: planStatus, }; } export function criticalPathWorkItemIdsFromPlanPacket( packet: PlanPacketLike | null | undefined, ): string[] | undefined { if (!packet?.execution_plan || typeof packet.execution_plan !== "object") { return undefined; } const ep = packet.execution_plan as Record; const meta = ep.schedule_metadata; if (!meta || typeof meta !== "object") return undefined; const ids = (meta as Record).critical_path_work_item_ids; if (!Array.isArray(ids)) return undefined; const out = ids.map((id) => String(id).trim()).filter((id) => id.length > 0); return out.length > 0 ? out : undefined; } export function buildHarnessSpawnContextSnippet( ctx: HarnessRunContext, opts?: { mode?: | "create" | "revise" | "execute" | "repair" | "benchmark" | "verdict" | "adversary"; risk_level?: string; quick?: boolean; critical_path_work_item_ids?: string[]; repair_brief_path?: string; }, ): string { const mode = opts?.mode ?? (ctx.plan_ready || ctx.status === "aborted" ? "revise" : "create"); const body: Record = { schema_version: "1.0.0", run_id: ctx.run_id, plan_packet_path: ctx.plan_packet_path, task_summary: ctx.task_summary, mode, risk_level: opts?.risk_level ?? "med", quick: opts?.quick ?? false, }; if ( opts?.critical_path_work_item_ids && opts.critical_path_work_item_ids.length > 0 ) { body.critical_path_work_item_ids = opts.critical_path_work_item_ids; } if (opts?.repair_brief_path) { body.repair_brief_path = opts.repair_brief_path; } return JSON.stringify(body, null, 2); } export function formatPlanContextBlock( ctx: HarnessRunContext, opts?: { mode?: | "create" | "revise" | "execute" | "repair" | "benchmark" | "verdict" | "adversary"; risk_level?: string; quick?: boolean; critical_path_work_item_ids?: string[]; repair_brief_path?: string; }, ): string { const lines = [ "[HarnessRunContext]", `run_id=${ctx.run_id}`, `phase=${ctx.phase}`, `status=${ctx.status}`, `plan_ready=${ctx.plan_ready}`, `plan_id=${ctx.plan_id ?? "none"}`, `last_completed_step=${ctx.last_completed_step ?? "none"}`, `last_outcome=${ctx.last_outcome ?? "none"}`, `next_recommended_command=${ctx.next_recommended_command ?? "none"}`, ]; if (ctx.plan_packet_path) { lines.push(`plan_packet_path=${ctx.plan_packet_path}`); lines.push( `plan_review_path=${canonicalPlanReviewPath(ctx.run_id, ctx.project_root)}`, ); } if (ctx.task_summary) { lines.push(`task_summary=${ctx.task_summary}`); } if ( opts?.critical_path_work_item_ids && opts.critical_path_work_item_ids.length > 0 ) { lines.push( `critical_path_work_item_ids=${opts.critical_path_work_item_ids.join(",")}`, ); } lines.push( `HarnessSpawnContext=${buildHarnessSpawnContextSnippet(ctx, opts)}`, ); return lines.join("\n"); } export function formatActivePlanBlock( ctx: HarnessRunContext, mode: "create" | "revise" | "execute" | "read", summary?: PlanPacketSummary | null, ): string { const lines = ["[HarnessActivePlan]"]; if (mode === "create") { lines.push( "No prior PlanPacket on disk. Create PlanPacket at the canonical path below.", ); } else if (mode === "revise") { lines.push( "Read the current PlanPacket from disk first, then revise per the user task.", ); if (ctx.status === "aborted") { lines.push( "Prior run was aborted; treat this as replan/amend of prior scope.", ); } } else if (mode === "execute") { lines.push( "Load PlanPacket from plan_packet_path and execute it. Do not parse --plan from user input on the happy path.", ); } else { lines.push( "Plan is read-only in this phase. Do not edit plan-packet.yaml.", ); } if (ctx.plan_packet_path) { lines.push(`plan_packet_path=${ctx.plan_packet_path}`); } if (ctx.task_summary) lines.push(`task_summary=${ctx.task_summary}`); if (summary) { lines.push(`plan_id=${summary.plan_id}`); lines.push(`scope=${summary.scope_one_liner}`); lines.push(`acceptance_checks=${summary.acceptance_check_count}`); lines.push(`plan_status=${summary.plan_status}`); } return lines.join("\n"); } export function parseArgFlag(args: string, flag: string): string | null { const re = new RegExp(`${flag}\\s+("([^"]+)"|(\\S+))`); const m = args.match(re); if (!m) return null; return m[2] ?? m[3] ?? null; } export function hasHarnessArgFlag(args: string, flag: string): boolean { return new RegExp(`(?:^|\\s)${flag}(?:\\s|$)`).test(args.trim()); } /** Split slash-command args into flags and positional tokens (run-id, task text, etc.). */ export function parseHarnessArgTokens(args: string): { flags: Set; positional: string[]; } { const flags = new Set(); const positional: string[] = []; for (const raw of args.trim().split(/\s+/)) { if (!raw) continue; if (raw.startsWith("--")) { flags.add(raw); continue; } positional.push(raw); } return { flags, positional }; } export interface HarnessUseRunArgs { runId: string | null; claim: boolean; readonly: boolean; } export function parseHarnessUseRunArgs(args: string): HarnessUseRunArgs { const { flags, positional } = parseHarnessArgTokens(args); return { runId: positional[0] ?? null, claim: flags.has("--claim"), readonly: flags.has("--readonly"), }; } /** Post-run orchestration commands that may take ownership of a resumed run. */ export const HARNESS_POST_RUN_CLAIM_COMMANDS = new Set([ "harness-review", "harness-steer", "harness-eval", "harness-critic", ]); export function shouldAutoClaimHarnessRun( command: string, args: string, ): boolean { if (hasHarnessArgFlag(args, "--readonly")) return false; return HARNESS_POST_RUN_CLAIM_COMMANDS.has(command); } export function claimRunOwnership( ctx: HarnessRunContext, sessionId: string, ): HarnessRunContext { return { ...ctx, pi_session_id: sessionId, owner_pi_session_id: sessionId, updated_at: nowIso(), }; } export interface EvalVerdictDisk { status?: string; recommended_action?: string; } export interface AdversaryReportDisk { block_merge?: boolean; severity?: string; } export async function readEvalVerdictFromRun( runId: string, projectRoot: string, ): Promise { try { const path = join( harnessRunsRoot(projectRoot), runId, "artifacts", "eval-verdict.yaml", ); return (await readYamlFile(path, "eval-verdict")) as EvalVerdictDisk; } catch { return null; } } export async function readAdversaryReportFromRun( runId: string, projectRoot: string, ): Promise { try { const path = join( harnessRunsRoot(projectRoot), runId, "artifacts", "adversary-report.yaml", ); return (await readYamlFile( path, "adversary-report", )) as AdversaryReportDisk; } catch { return null; } } export async function readBenchmarkLogFromRun( runId: string, projectRoot: string, ): Promise { try { const path = join( harnessRunsRoot(projectRoot), runId, "artifacts", "benchmark-log.yaml", ); return (await readYamlFile(path, "benchmark-log")) as BenchmarkLogLike; } catch { return null; } } export async function readRepairBriefFromRun( runId: string, projectRoot: string, ): Promise<{ gap_kind?: string; steer_attempt?: number; must_pass_before_handoff?: boolean; } | null> { try { const path = join( harnessRunsRoot(projectRoot), runId, "artifacts", "repair-brief.yaml", ); return (await readYamlFile(path, "repair-brief")) as { gap_kind?: string; steer_attempt?: number; must_pass_before_handoff?: boolean; }; } catch { return null; } } export interface SteerEntryEffects { incrementSteerAttempt: boolean; incrementHygieneRepairs: boolean; markBurstUsed: boolean; skipExecutor: boolean; } /** Steer entry at /harness-steer start — hygiene lane skips attempt increment. */ export async function resolveSteerEntryEffects( runId: string, projectRoot: string, args: string, ): Promise { const brief = await readRepairBriefFromRun(runId, projectRoot); const gapKind = brief?.gap_kind; const hygieneOnly = gapKind === "hygiene"; const burst = isSteerBurstArgs(args); return { incrementSteerAttempt: !hygieneOnly, incrementHygieneRepairs: hygieneOnly, markBurstUsed: burst, skipExecutor: hygieneOnly, }; } /** Mark eval-verdict stale after executor repair so review re-runs verdict. */ export async function invalidateEvalVerdictAfterRepair( runId: string, projectRoot: string, ): Promise { const path = join( harnessRunsRoot(projectRoot), runId, "artifacts", "eval-verdict.yaml", ); try { const doc = (await readYamlFile(path, "eval-verdict")) as Record< string, unknown >; doc.status = "stale"; doc.notes = "invalidated after steer repair; re-run verdict evaluator"; await writeYamlFile(path, doc); } catch { /* no prior verdict */ } } export async function updateSteerStateOnEntry( runId: string, projectRoot: string, effects: SteerEntryEffects, ctx: HarnessRunContext, ): Promise { const runRoot = join(harnessRunsRoot(projectRoot), runId); const steerPath = join(runRoot, "artifacts", "steer-state.yaml"); const existing = (await readSteerStateFromRun(runId, projectRoot)) ?? {}; const attempt = existing.attempt ?? ctx.steer_attempt ?? 0; const hygieneRepairs = existing.hygiene_repairs ?? 0; const nextState = { schema_version: "1.0.0", run_id: runId, attempt: effects.incrementSteerAttempt ? attempt + 1 : attempt, max_attempts: ctx.steer_max_attempts ?? steerMaxAttemptsFromEnv(), active: true, hygiene_repairs: effects.incrementHygieneRepairs ? hygieneRepairs + 1 : hygieneRepairs, burst_used: effects.markBurstUsed ? true : existing.burst_used, }; await mkdir(join(runRoot, "artifacts"), { recursive: true }); await writeYamlFile(steerPath, nextState); return { ...ctx, steer_attempt: nextState.attempt, steer_max_attempts: nextState.max_attempts, }; } export async function readSteerStateFromRun( runId: string, projectRoot: string, ): Promise<{ attempt?: number; max_attempts?: number; hygiene_repairs?: number; burst_used?: boolean; } | null> { try { const path = join( harnessRunsRoot(projectRoot), runId, "artifacts", "steer-state.yaml", ); return (await readYamlFile(path, "steer-state")) as { attempt?: number; max_attempts?: number; hygiene_repairs?: number; burst_used?: boolean; }; } catch { return null; } } export interface CompletionStatuses { planStatus: string | null; executionStatus: string | null; evalStatus: string | null; adversaryComplete: boolean; } /** Session handoff entries overlaid with canonical on-disk post-run artifacts. */ export async function resolveCompletionStatuses( entries: unknown[], runId: string | null, projectRoot: string, ): Promise { const session = extractCompletionStatuses(entries); if (!runId) { return { ...session, adversaryComplete: false }; } let evalStatus = session.evalStatus; let executionStatus = session.executionStatus; const verdict = await readEvalVerdictFromRun(runId, projectRoot); if (verdict?.status) { evalStatus = verdict.status; } const handoff = await readExecutorHandoffFromRun(runId, projectRoot); if (handoff?.execution_status && !executionStatus) { executionStatus = handoff.execution_status; } const adversary = await readAdversaryReportFromRun(runId, projectRoot); const adversaryComplete = adversary != null; return { planStatus: session.planStatus, executionStatus, evalStatus, adversaryComplete, }; } export function resolveArgsForCommand( command: string, args: string, ctx: HarnessRunContext | null, ): { runId: string | null; planPath: string | null; overrideRun: boolean } { let runId = ctx?.run_id ?? null; /** Only honor explicit `--plan`; never inherit stale session plan paths onto fresh runs. */ let planPath: string | null = null; let overrideRun = false; const explicitRun = parseArgFlag(args, "--run"); if (explicitRun) { runId = explicitRun; overrideRun = true; } const explicitPlan = parseArgFlag(args, "--plan"); if (explicitPlan) { planPath = explicitPlan; } if (command === "harness-use-run" && args.trim()) { const parsed = parseHarnessUseRunArgs(args); if (parsed.runId) { runId = parsed.runId; overrideRun = true; } } return { runId, planPath, overrideRun }; } export function validatePlanOverridePath( planPath: string, runId: string, projectRoot: string, ): { ok: boolean; reason?: string } { const absPlan = normalizeHarnessPath(planPath, projectRoot); if (!isCanonicalPlanPacketPath(absPlan, projectRoot, runId)) { return { ok: false, reason: `--plan must be runs/${runId}/${PLAN_PACKET_BASENAME} (canonical plan packet only)`, }; } return { ok: true }; } export function getRunIdFromSession( entries: unknown[], sessionId: string, ): string | null { const ctx = getLatestRunContext(entries); if (ctx?.turn_override_run_id) return ctx.turn_override_run_id; if (ctx?.status === "active" || ctx?.status === "aborted") return ctx.run_id; for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom" || entry.customType !== "harness-trace-state") continue; const runId = (entry.data as { run_id?: string })?.run_id; if (typeof runId === "string" && runId.length > 0) return runId; } return null; } export function harnessAutoTasksDiffer( ctx: HarnessRunContext, newTask: string, ): boolean { const prior = (ctx.task_summary ?? "").trim().toLowerCase(); const next = newTask.trim().toLowerCase(); return prior.length > 0 && next.length > 0 && prior !== next; } /** Full auto pipeline needs a clean run once execute/review has started. */ export function shouldReuseHarnessRunIdForAuto( ctx: HarnessRunContext, ): boolean { if (ctx.status === "aborted") return true; const step = ctx.last_completed_step; if (!step || step === "plan") return true; return false; } /** Reset in-run state when restarting /harness-auto on the same run directory. */ export function resetRunContextForHarnessAuto( ctx: HarnessRunContext, ): HarnessRunContext { return { ...ctx, phase: "plan", plan_ready: false, plan_id: null, plan_packet_path: canonicalPlanPath(ctx.run_id, ctx.project_root), status: "active", last_completed_step: null, last_outcome: null, next_recommended_command: null, steer_attempt: 0, steer_approved: false, updated_at: nowIso(), }; } export function shouldReuseHarnessRunId( prompt: string, ctx: HarnessRunContext | null, command: string | null, ): boolean { if (!command) return false; if (command === "harness-new-run") return false; if (!ctx) return false; if (command === "harness-auto") { return ( (ctx.status === "active" || ctx.status === "aborted") && shouldReuseHarnessRunIdForAuto(ctx) ); } if (command === "harness-plan") { return ctx.status === "active" || ctx.status === "aborted"; } if (ctx.status === "active") return true; return Boolean(ctx.run_id); } const HARNESS_PHASE_ORDER: HarnessPhase[] = [ "plan", "execute", "evaluate", "adversary", "merge", ]; export interface HarnessPolicyState { phase: HarnessPhase; approvedPlan: boolean; planId: string | null; aborted: boolean; } export function inferHarnessPhaseFromTurn( entries: unknown[], ): HarnessPhase | null { const turn = getLatestHarnessTurn(entries); if (!turn) return null; return HARNESS_COMMAND_PHASE[turn.command] ?? null; } /** Prefer session `harness-turn`; fall back to raw slash in visible prompt only. */ export function inferHarnessPhase( entries: unknown[], userPrompt?: string, ): HarnessPhase { const fromTurn = inferHarnessPhaseFromTurn(entries); if (fromTurn) return fromTurn; if (userPrompt) { const parsed = parseHarnessSlashInput(userVisiblePromptSlice(userPrompt)); if (parsed && HARNESS_COMMAND_PHASE[parsed.command]) { return HARNESS_COMMAND_PHASE[parsed.command]; } } return "execute"; } /** @deprecated Use inferHarnessPhase(entries, prompt) — substring matching causes false plan phase. */ export function inferHarnessPhaseFromPrompt(prompt: string): HarnessPhase { const p = userVisiblePromptSlice(prompt).toLowerCase(); const parsed = parseHarnessSlashInput(userVisiblePromptSlice(prompt)); if (parsed && HARNESS_COMMAND_PHASE[parsed.command]) { return HARNESS_COMMAND_PHASE[parsed.command]; } if (p.startsWith("/harness-plan") || p.startsWith("/harness-auto")) { return "plan"; } if (p.startsWith("/harness-run") || p.startsWith("/harness-steer")) { return "execute"; } if (p.startsWith("/harness-eval") || p.startsWith("/harness-review")) { return "evaluate"; } if (p.startsWith("/harness-critic")) return "adversary"; if (p.includes("merge gate") || p.includes("policy decision")) return "merge"; return "execute"; } export function isValidHarnessPhaseTransition( from: HarnessPhase, to: HarnessPhase, ): boolean { if (from === to) return true; if (to === "plan") return true; if (to === "execute") return true; const fromIndex = HARNESS_PHASE_ORDER.indexOf(from); const toIndex = HARNESS_PHASE_ORDER.indexOf(to); return toIndex === fromIndex + 1; } export function getLatestPolicyState(entries: unknown[]): HarnessPolicyState { const fallback: HarnessPolicyState = { phase: "plan", approvedPlan: false, planId: null, aborted: false, }; for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if ( entry.type !== "custom" || entry.customType !== "harness-policy-state" ) { continue; } const candidate = entry.data as Partial | undefined; if ( candidate && typeof candidate.phase === "string" && HARNESS_PHASE_ORDER.includes(candidate.phase as HarnessPhase) ) { return { phase: candidate.phase as HarnessPhase, approvedPlan: Boolean(candidate.approvedPlan), planId: typeof candidate.planId === "string" ? candidate.planId : null, aborted: Boolean(candidate.aborted), }; } } return fallback; } export function isHarnessBootstrapPrompt(prompt: string): boolean { const p = prompt.toLowerCase(); return ( p.includes("/harness-setup") || p.includes("harness-setup") || p.includes("full harness bootstrap") ); } export function hasHarnessAbortSignal(prompt: string): boolean { const slice = userVisiblePromptSlice(prompt); for (const line of slice.split("\n")) { const parsed = parseHarnessSlashInput(line.trim()); if (parsed?.command === "harness-abort") return true; } return false; } /** Slash command line for AGT prompt defense — not expanded prompt template bodies. */ export function harnessSlashCommandLineForPolicy( prompt: string, entries?: unknown[], ): string | null { const slice = userVisiblePromptSlice(prompt); for (const line of slice.split("\n")) { const trimmed = line.trim(); const parsed = parseHarnessSlashInput(trimmed); if (parsed) return trimmed; } if (entries?.length) { const turn = getLatestHarnessTurn(entries); if (turn?.command) { return `/${turn.command}${turn.args ? ` ${turn.args}` : ""}`.trim(); } } return null; } function sessionHasHarnessPolicyState(entries: unknown[]): boolean { return entries.some((raw) => { const entry = raw as SessionEntryLike; return ( entry.type === "custom" && entry.customType === "harness-policy-state" ); }); } function policyStateForTransition( entries: unknown[], activeCtx?: HarnessRunContext | null, ): HarnessPolicyState { const state = getLatestPolicyState(entries); if (sessionHasHarnessPolicyState(entries)) return state; const runCtx = activeCtx ?? getLatestRunContext(entries); if (!runCtx?.run_id) return state; const boot = policyBootstrapFromRunContext(runCtx); return { phase: boot.phase, approvedPlan: boot.approvedPlan, planId: boot.planId, aborted: state.aborted, }; } /** Bootstrap policy phase from disk when session has no policy/run entries yet. */ export async function policyStateFromDiskIfNeeded( entries: unknown[], projectRoot: string, ): Promise { if (sessionHasHarnessPolicyState(entries) || getLatestRunContext(entries)) { return null; } const pointer = await loadProjectActiveRun(projectRoot); if (!pointer || isStaleActiveRunPointer(pointer, projectRoot)) return null; const disk = await loadRunContextFromDisk(pointer.run_id, projectRoot); if (!disk) return null; const boot = policyBootstrapFromRunContext(disk); return { phase: boot.phase, approvedPlan: boot.approvedPlan, planId: boot.planId, aborted: false, }; } /** Mirrors policy-gate phase checks so run-context does not inject on blocked turns. */ export function getPolicyTransitionBlock( userPrompt: string, entries: unknown[], activeCtx?: HarnessRunContext | null, diskPolicy?: HarnessPolicyState | null, ): { blocked: boolean; message?: string } { if ( isHarnessBootstrapPrompt(userPrompt) || hasHarnessAbortSignal(userPrompt) ) { return { blocked: false }; } const state = diskPolicy ?? policyStateForTransition(entries, activeCtx); const nextPhase = inferHarnessPhase(entries, userPrompt); if (!isValidHarnessPhaseTransition(state.phase, nextPhase)) { return { blocked: true, message: [ `Policy gate blocked invalid phase transition: ${state.phase} -> ${nextPhase}.`, "Run /harness-plan first or continue in the current phase.", ].join("\n"), }; } if (nextPhase === "execute" && !state.approvedPlan) { const runCtx = activeCtx ?? getLatestRunContext(entries); if ( !runCtx?.plan_ready && !hasApprovedPlanSignalFromUserPrompt(userPrompt) ) { return { blocked: true, message: "Policy gate blocked execute: no approved plan in active run context. Run /harness-plan first.", }; } } return { blocked: false }; } export function isAmendPlanAllowed( ctx: HarnessRunContext | null, prompt: string, driftGateActive: boolean, ): boolean { if (!ctx || ctx.status === "aborted") return true; if (ctx.last_outcome === "needs_clarification") return true; if (isDriftReplanPrompt(prompt)) return true; if (driftGateActive) return true; return false; } export function isNewTaskPlanBlocked( ctx: HarnessRunContext, prompt: string, ): boolean { if (ctx.status !== "active") return false; if (isAmendPlanAllowed(ctx, prompt, false)) return false; const cmd = parseHarnessSlashInput(userVisiblePromptSlice(prompt)); if (cmd?.command !== "harness-plan") return false; const taskMatch = prompt.match(/"([^"]+)"/); if (!taskMatch || !ctx.task_summary) return true; const newTask = taskMatch[1].trim().toLowerCase(); const prior = ctx.task_summary.trim().toLowerCase(); if (newTask === prior) return false; return newTask.length > 0 && prior.length > 0; } export async function readReviewOutcomeFromRun( runId: string, projectRoot: string, ): Promise { try { const path = join( harnessRunsRoot(projectRoot), runId, "artifacts", "review-outcome.yaml", ); return (await readYamlFile(path, "review-outcome")) as ReviewOutcomeLike; } catch { return null; } } export async function resolveRemediationClassForRun( runId: string, projectRoot: string, ): Promise { const review = await readReviewOutcomeFromRun(runId, projectRoot); if (review?.remediation_class) { return review.remediation_class as RemediationClass; } const evalV = await readEvalVerdictFromRun(runId, projectRoot); return remediationClassFromEvalVerdict(evalV); } export async function ensureReviewOutcomeFromEval( runId: string, projectRoot: string, opts?: { steerAttempt?: number; inlineRepairAttempted?: boolean }, ): Promise { const existing = await readReviewOutcomeFromRun(runId, projectRoot); if (existing?.remediation_class) return existing; const evalV = await readEvalVerdictFromRun(runId, projectRoot); if (!evalV?.status) return null; const adversary = await readAdversaryReportFromRun(runId, projectRoot); const benchmark = await readBenchmarkLogFromRun(runId, projectRoot); const synthesized = synthesizeReviewOutcome({ runId, eval: evalV, adversary, benchmark, steerAttempt: opts?.steerAttempt, inlineRepairAttempted: opts?.inlineRepairAttempted, }); if (!synthesized) return null; const outPath = join( harnessRunsRoot(projectRoot), runId, "artifacts", "review-outcome.yaml", ); await writeYamlFile(outPath, synthesized); const steerState = await readSteerStateFromRun(runId, projectRoot); const { ensureRepairBriefOnDisk } = await import("./harness-repair-brief.js"); await ensureRepairBriefOnDisk({ runId, projectRoot, steerAttempt: steerState?.attempt ?? opts?.steerAttempt ?? 0, }); return synthesized; } /** Align next_recommended_command with on-disk review/eval routing after /harness-review. */ export async function reconcileReviewRouting( projectRoot: string, ctx: HarnessRunContext, ): Promise { const evalV = await readEvalVerdictFromRun(ctx.run_id, projectRoot); const reviewStep = ctx.last_completed_step === "review" || ctx.last_completed_step === "adversary" || Boolean(evalV?.status); if (!reviewStep) return ctx; let working = { ...ctx }; if ( evalV?.status && working.last_completed_step === "execute" && String(working.last_outcome ?? "").toLowerCase() === "completed" ) { working = { ...working, last_completed_step: "review", last_outcome: evalV.status, phase: "evaluate", }; } await ensureReviewOutcomeFromEval(working.run_id, projectRoot); const remediation = await resolveRemediationClassForRun( working.run_id, projectRoot, ); if (!remediation) return working; const adversary = await readAdversaryReportFromRun( working.run_id, projectRoot, ); const burst = steerBurstAllowed( evalV, adversary, working.inline_repair_attempted, ); const steerState = await readSteerStateFromRun(working.run_id, projectRoot); const next = nextStepAfterOutcome({ phase: working.phase, lastCompletedStep: working.last_completed_step, lastOutcome: working.last_outcome, evalStatus: working.last_outcome, remediationClass: remediation, steerAttempt: steerState?.attempt ?? working.steer_attempt ?? 0, steerMaxAttempts: working.steer_max_attempts ?? steerMaxAttemptsFromEnv(), reviewComplete: true, aborted: working.status === "aborted", burstAllowed: burst, }); return { ...working, next_recommended_command: next, updated_at: nowIso(), }; } function nextStepForEvaluateLikePhase(input: { adversaryComplete?: boolean; remediation: string; evalStatus: string; steerAttempt: number; steerMax: number; burstAllowed?: boolean; }): string { const effectiveMax = effectiveSteerMaxAttempts( input.steerMax, input.burstAllowed === true, ); if (input.remediation === "implementation_gap") { if (input.steerAttempt < effectiveMax) { return input.burstAllowed ? "/harness-steer --burst" : "/harness-steer"; } return "/harness-plan (mode: revise) or /harness-abort"; } if (input.remediation === "pass") { if (input.adversaryComplete) return "/harness-policy-status"; return "/harness-review"; } if (input.evalStatus === "pass" && input.adversaryComplete) { return "/harness-policy-status"; } if (input.evalStatus === "pass" && !input.adversaryComplete) { return "/harness-review"; } if (input.remediation === "rollback") return "/harness-incident"; if (input.remediation === "plan_gap") return "/harness-plan (mode: revise)"; if (input.remediation === "inconclusive" && input.evalStatus === "fail") { if (input.steerAttempt < effectiveMax) return "/harness-steer"; return "/harness-plan (mode: revise) or /harness-abort"; } if (input.evalStatus === "fail") { if (input.remediation === "plan_gap") { return "/harness-plan (mode: revise)"; } if (input.remediation === "inconclusive") { if (input.steerAttempt < effectiveMax) return "/harness-steer"; return "/harness-plan (mode: revise) or /harness-abort"; } return "/harness-plan (mode: revise) or /harness-incident"; } if (input.adversaryComplete) return "/harness-policy-status"; return "/harness-review"; } export function nextStepAfterOutcome(input: { phase: HarnessPhase; planStatus?: string | null; executionStatus?: string | null; evalStatus?: string | null; lastCompletedStep?: string | null; lastOutcome?: string | null; policyDecision?: string | null; aborted?: boolean; adversaryComplete?: boolean; remediationClass?: string | null; steerAttempt?: number; steerMaxAttempts?: number; reviewComplete?: boolean; burstAllowed?: boolean; }): string { if (input.aborted) { return '/harness-plan ""'; } const plan = (input.planStatus ?? "").toLowerCase(); if (plan === "needs_clarification") { return "Reply with answers or run /harness-plan with updates"; } const lastOutcome = (input.lastOutcome ?? "").toLowerCase(); if (input.phase === "plan" && lastOutcome === "pending_approval") { return "Continue /harness-plan: finish Review Gate (harness_debate_round_status → debate lanes → harness_debate_consensus), then approve_plan"; } const lastStep = (input.lastCompletedStep ?? "").toLowerCase(); const exec = (input.executionStatus ?? "").toLowerCase(); const evalSt = (input.evalStatus ?? "").toLowerCase(); const remediation = (input.remediationClass ?? "").toLowerCase(); const steerAttempt = input.steerAttempt ?? 0; const steerMax = input.steerMaxAttempts ?? steerMaxAttemptsFromEnv(); const executionResolved = exec || (lastStep === "execute" ? lastOutcome : ""); const executeFinished = executionResolved === "completed" || (lastStep === "execute" && input.phase === "evaluate") || lastStep === "steer"; if ( (executionResolved === "blocked" || executionResolved === "scope_drift") && !input.reviewComplete && lastStep !== "review" ) { return "/harness-review"; } if (input.phase === "plan" && plan === "ready") { return "/harness-run"; } if (executeFinished && !input.reviewComplete && lastStep !== "review") { return "/harness-review"; } if (input.phase === "execute" && lastStep === "steer") { return "/harness-review"; } if (input.phase === "execute" && !executeFinished) { return "/harness-run-status"; } if (input.phase === "evaluate" || input.phase === "adversary") { return nextStepForEvaluateLikePhase({ adversaryComplete: input.adversaryComplete, remediation, evalStatus: evalSt, steerAttempt, steerMax, burstAllowed: input.burstAllowed, }); } if (input.phase === "merge") return "/harness-policy-status"; return "/harness-run-status"; } /** Read executor handoff artifact written by harness/running/executor submit pipeline. */ /** After /harness-run agent turn — do not mark completed without executor evidence. */ export function resolveHarnessRunPostAgentState( execStatus: string | null, planReady: boolean, ): Pick< HarnessRunContext, "last_completed_step" | "last_outcome" | "phase" | "next_recommended_command" > { if (!execStatus) { return { last_completed_step: "plan", last_outcome: planReady ? "ready" : null, phase: "plan", next_recommended_command: "/harness-run", }; } const normalized = execStatus.toLowerCase(); const completed = normalized === "completed"; return { last_completed_step: "execute", last_outcome: execStatus, phase: completed ? "evaluate" : "execute", next_recommended_command: completed ? "/harness-review" : "/harness-run", }; } function executeCompletionMatchesHandoff( ctx: HarnessRunContext, executionStatus: string, ): boolean { if (ctx.last_completed_step !== "execute") return false; const norm = executionStatus.toLowerCase(); const outcome = String(ctx.last_outcome ?? "").toLowerCase(); if (norm === "completed") return outcome === "completed"; return outcome === norm; } /** Sync plan_ready + executor handoff vs session/disk run-context (bidirectional). */ export async function reconcileStaleExecuteCompletion( projectRoot: string, ctx: HarnessRunContext, entries?: unknown[], ): Promise { let synced = await syncPlanReadyFromDisk(projectRoot, ctx, entries); const falselyCompleted = synced.last_completed_step === "execute" && String(synced.last_outcome ?? "").toLowerCase() === "completed"; const handoff = await readExecutorHandoffFromRun(synced.run_id, projectRoot); if (falselyCompleted && !handoff?.execution_status) { return { ...synced, ...resolveHarnessRunPostAgentState(null, synced.plan_ready), }; } const postExecuteProgress = synced.last_completed_step === "review" || synced.last_completed_step === "adversary" || synced.last_completed_step === "steer"; if ( handoff?.execution_status && !postExecuteProgress && !executeCompletionMatchesHandoff(synced, handoff.execution_status) ) { const runPost = resolveHarnessRunPostAgentState( handoff.execution_status, synced.plan_ready, ); synced = { ...synced, ...runPost }; } return synced; } /** Reconcile disk artifacts and recompute next_recommended_command for status UI. */ export async function refreshRunContextProgress( projectRoot: string, ctx: HarnessRunContext, entries: unknown[] = [], ): Promise { let synced = await reconcileStaleExecuteCompletion(projectRoot, ctx, entries); synced = await reconcileReviewRouting(projectRoot, synced); const statuses = await resolveCompletionStatuses( entries, synced.run_id, projectRoot, ); const reviewComplete = synced.last_completed_step === "review" || synced.last_completed_step === "adversary"; const remediationClass = await resolveRemediationClassForRun( synced.run_id, projectRoot, ); synced.next_recommended_command = nextStepAfterOutcome({ phase: synced.phase, planStatus: synced.plan_ready ? "ready" : statuses.planStatus, lastCompletedStep: synced.last_completed_step, lastOutcome: synced.last_outcome, executionStatus: statuses.executionStatus, evalStatus: statuses.evalStatus, adversaryComplete: statuses.adversaryComplete, aborted: synced.status === "aborted", remediationClass, steerAttempt: synced.steer_attempt ?? 0, steerMaxAttempts: synced.steer_max_attempts ?? steerMaxAttemptsFromEnv(), reviewComplete, }); synced.updated_at = nowIso(); return synced; } export async function blockingHarnessAutoCommandReason( command: string, activeCtx: HarnessRunContext | null, args: string, userPrompt: string, ): Promise { if (command !== "harness-auto") return null; const task = extractTaskSummaryFromHarnessInput(args, userPrompt); if (!task) { return 'Usage: /harness-auto "" [--quick] [--risk low|med|high]'; } if ( activeCtx?.status === "active" && activeCtx.owner_pi_session_id && activeCtx.task_summary && harnessAutoTasksDiffer(activeCtx, task) ) { return "Active harness run is for a different task. Run /harness-abort or /harness-new-run before /harness-auto with a new task."; } return null; } function extractTaskSummaryFromHarnessInput( args: string, prompt?: string, ): string | null { const fromArgs = args.match(/"([^"]+)"/); if (fromArgs?.[1]) return fromArgs[1]; if (args.trim() && !args.trim().startsWith("--")) { return args.trim().slice(0, 200); } if (prompt) { const quoted = prompt.match(/"([^"]+)"/); if (quoted?.[1]) return quoted[1]; } return null; } export async function blockingRunCommandReason( command: string, activeCtx: HarnessRunContext, projectRoot: string, entries?: unknown[], ): Promise { if (command !== "harness-run") return null; if (entries && isHarnessAutoSession(entries)) return null; if (!activeCtx.plan_ready) return "Plan not ready. Run /harness-plan first."; const handoff = await readExecutorHandoffFromRun( activeCtx.run_id, projectRoot, ); const executeDone = activeCtx.last_completed_step === "execute" && String(activeCtx.last_outcome ?? "").toLowerCase() === "completed"; if (executeDone || handoff?.execution_status?.toLowerCase() === "completed") { if (handoff?.execution_status === "completed" || executeDone) { return "Execute already completed for this run. Next: /harness-review (same session), or /harness-abort to replan."; } } return null; } export async function blockingReviewCommandReason( command: string, activeCtx: HarnessRunContext, projectRoot: string, ): Promise { if (!["harness-review", "harness-eval", "harness-critic"].includes(command)) { return null; } const handoff = await readExecutorHandoffFromRun( activeCtx.run_id, projectRoot, ); const execOutcome = String(activeCtx.last_outcome ?? "").toLowerCase(); const executeFinished = activeCtx.last_completed_step === "execute" && (execOutcome === "completed" || execOutcome === "scope_drift" || execOutcome === "blocked"); const handoffStarted = Boolean(handoff?.execution_status); if (!executeFinished && !handoffStarted) { return "Execute not finished. Run /harness-run first."; } return null; } export async function blockingSteerCommandReason( command: string, activeCtx: HarnessRunContext, projectRoot: string, ): Promise { if (command !== "harness-steer") return null; await ensureReviewOutcomeFromEval(activeCtx.run_id, projectRoot); const remediation = await resolveRemediationClassForRun( activeCtx.run_id, projectRoot, ); const evalV = await readEvalVerdictFromRun(activeCtx.run_id, projectRoot); if (!remediation && !evalV?.status) { return "Run /harness-review first (no eval-verdict or review-outcome on disk)."; } if (remediation !== "implementation_gap") { const next = remediation != null ? recommendedNextForRemediation(remediation) : "/harness-plan (mode: revise)"; return `Steer applies only for implementation_gap (resolved: ${remediation ?? "unknown"}). Next: ${next}`; } const briefPath = join( harnessRunsRoot(projectRoot), activeCtx.run_id, "artifacts", "repair-brief.yaml", ); try { await readYamlFile(briefPath, "repair-brief"); } catch { return "Run /harness-review first (artifacts/repair-brief.yaml missing)."; } const steerState = await readSteerStateFromRun(activeCtx.run_id, projectRoot); const attempt = steerState?.attempt ?? activeCtx.steer_attempt ?? 0; const max = activeCtx.steer_max_attempts ?? steerMaxAttemptsFromEnv(); const adversary = await readAdversaryReportFromRun( activeCtx.run_id, projectRoot, ); const burst = steerBurstAllowed( evalV, adversary, activeCtx.inline_repair_attempted, ); const effectiveMax = effectiveSteerMaxAttempts(max, burst); if (attempt >= effectiveMax) { return `Steer attempt cap reached (${effectiveMax}${burst ? ` incl. burst` : ""}). Use /harness-plan (mode: revise) or /harness-abort.`; } return null; } export async function readExecutorHandoffFromRun( runId: string, projectRoot: string, ): Promise<{ execution_status?: string; next_command?: string; } | null> { try { const path = join( harnessRunsRoot(projectRoot), runId, "handoff", "executor-summary.yaml", ); const doc = (await readYamlFile(path, "executor-handoff")) as { execution_status?: string; next_command?: string; }; return doc; } catch { return null; } } export function extractCompletionStatuses(entries: unknown[]): { planStatus: string | null; executionStatus: string | null; evalStatus: string | null; } { let planStatus: string | null = null; let executionStatus: string | null = null; let evalStatus: string | null = null; for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom") continue; if (entry.customType === "harness-plan-packet") { const d = entry.data as { plan_status?: string }; if (!planStatus && typeof d?.plan_status === "string") { planStatus = d.plan_status; } } if (entry.customType === "harness-step-handoff") { const d = entry.data as { plan_status?: string; execution_status?: string; eval_status?: string; }; if (!planStatus && typeof d?.plan_status === "string") planStatus = d.plan_status; if (!executionStatus && typeof d?.execution_status === "string") executionStatus = d.execution_status; if (!evalStatus && typeof d?.eval_status === "string") evalStatus = d.eval_status; } } return { planStatus, executionStatus, evalStatus }; } export function getLatestPolicyPhase(entries: unknown[]): HarnessPhase | null { for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom" || entry.customType !== "harness-policy-state") continue; const phase = (entry.data as { phase?: string })?.phase; if ( phase === "plan" || phase === "execute" || phase === "evaluate" || phase === "adversary" || phase === "merge" ) { return phase; } } return null; } export function driftGateActive(entries: unknown[]): boolean { for (let i = entries.length - 1; i >= 0; i--) { const entry = entries[i] as SessionEntryLike; if (entry.type !== "custom" || entry.customType !== "harness-drift-report") continue; const score = Number( (entry.data as { drift_score?: number })?.drift_score ?? 0, ); const ack = Boolean( (entry.data as { user_acknowledged?: boolean })?.user_acknowledged, ); if ( score >= Number(process.env.HARNESS_DRIFT_THRESHOLD ?? "0.65") && !ack ) { return true; } } return false; } export function phaseTraceFileName(phase: HarnessPhase): string { return `trace-${phase}.json`; } /** Collect plan approvals from a session entry list (e.g. subagent in-memory session). */ export function extractPlanApprovalsFromEntries( entries: unknown[], ): PlanUserApproval[] { const out: PlanUserApproval[] = []; for (let i = 0; i < entries.length; i++) { const entry = entries[i] as SessionEntryLike & { message?: { role?: string; toolName?: string; details?: unknown; content?: { type?: string; text?: string }[]; }; }; if (entry.type !== "message" || entry.message?.role !== "toolResult") { continue; } const fromTool = parsePlanApprovalFromMessage(entry.message); if (fromTool) out.push(fromTool); } return out; } /** True inside `pi --mode json` harness subagent subprocesses. */ export function isHarnessSubprocess(): boolean { return process.env.PI_HARNESS_SUBPROCESS === "1"; } export function harnessSubprocessRunId(): string | null { const runId = process.env.HARNESS_RUN_ID?.trim(); return runId || null; } /** Load approved run context for a harness subagent subprocess (env + disk). */ export async function loadRunContextForSubprocess( projectRoot: string, ): Promise { if (!isHarnessSubprocess()) return null; const runId = harnessSubprocessRunId(); if (!runId) return null; const disk = await loadRunContextFromDisk(runId, projectRoot); if (disk) return disk; const pointer = await loadProjectActiveRun(projectRoot); if (!pointer || pointer.run_id !== runId) return null; return { schema_version: "1.0.0", run_id: pointer.run_id, pi_session_id: "", project_root: projectRoot, phase: pointer.phase, plan_id: pointer.plan_id, plan_packet_path: canonicalPlanPath(pointer.run_id, projectRoot), plan_ready: pointer.plan_ready, task_summary: null, status: "active", last_completed_step: null, last_outcome: null, next_recommended_command: null, owner_pi_session_id: pointer.owner_pi_session_id, updated_at: pointer.updated_at, }; } export interface HarnessPolicyBootstrap { phase: HarnessPhase; approvedPlan: boolean; planId: string | null; } /** Map disk run context + subprocess agent id to policy-gate phase flags. */ export function policyBootstrapFromRunContext( runCtx: HarnessRunContext, ): HarnessPolicyBootstrap { const agentId = process.env.HARNESS_AGENT_ID?.trim() ?? ""; let phase = runCtx.phase; if (agentId.includes("executor")) phase = "execute"; else if (agentId.includes("evaluator")) phase = "evaluate"; else if (agentId.includes("adversary") || agentId.includes("tie-breaker")) { phase = "adversary"; } else if (agentId.startsWith("harness/planning/")) phase = "plan"; return { phase, approvedPlan: runCtx.plan_ready, planId: runCtx.plan_id, }; }