import { createHash } from "node:crypto"; import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; import { join, resolve } from "node:path"; import { readSegmentedJsonlFileSync } from "../storage/durable-history.ts"; import { readWorkItem } from "../work/read.ts"; import { GOVERNOR_PLAN_SCHEMA_VERSION, type GovernorPlanEvent, type GovernorPlanEventType, type GovernorPlanHistory, type GovernorPlanProposal, type GovernorPlanRecord, type GovernorPlanRequest, type GovernorPlanReviewFinding, type GovernorPlanReviewReceipt, type GovernorPlanReviewReviewer, type GovernorPlanReviewRound, type GovernorPlanReviewSummary, type GovernorPlanReviewVerdict, type GovernorReplanningPolicy, MAX_GOVERNOR_PLAN_REVIEWERS, MAX_GOVERNOR_PLAN_REVISION_ROUNDS, } from "./plan-types.ts"; const PLAN_ID = /^plan-[0-9]{4}-[a-f0-9]{8}$/; const GOAL_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/; const WORK_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/; const APPROVAL_ID = /^apr-[a-f0-9]{20}$/; const PLAN_KEY = /^[a-z][a-z0-9-]{0,31}$/; const TEMPLATE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/; const MAX_PLANS = 100; const MAX_RECORD_BYTES = 256 * 1024; const EVENT_TYPES = new Set([ "plan.awaiting_approval", "plan.resumed", "plan.reviewed", "plan.proposed", "plan.applied", "plan.completed", "plan.rejected", "plan.retry_requested", "plan.attention", "plan.failed", "plan.reopened", ]); export function readGovernorPlans( coordRootRaw: string, goalId: string, originalRootWorkId: string, ): GovernorPlanHistory { assertId(goalId, GOAL_ID, "governor id"); assertId(originalRootWorkId, WORK_ID, "work id"); const coordRoot = resolve(coordRootRaw); const root = plansRoot(coordRoot, goalId); if (!existsSync(root)) { return { plans: [], active_root_work_id: originalRootWorkId, generation: 0, applied_work_ids: [], materialized_work_ids: [], milestones_completed: 0, completed: false, }; } const entries = readdirSync(root, { withFileTypes: true }) .filter((entry) => entry.isDirectory() && PLAN_ID.test(entry.name)) .map((entry) => entry.name); if (entries.length > MAX_PLANS) throw new Error(`governor plan history exceeds ${MAX_PLANS}`); const plans = entries .map((planId) => readGovernorPlan(coordRootRaw, goalId, planId)) .sort((left, right) => left.request.sequence - right.request.sequence); const sequences = new Set(); for (const plan of plans) { if (sequences.has(plan.request.sequence)) { throw new Error(`governor ${goalId} has duplicate plan sequence ${plan.request.sequence}`); } sequences.add(plan.request.sequence); } let activeRoot = originalRootWorkId; let generation = 0; let milestonesCompleted = 0; let completed = false; const appliedWork = new Set(); const materializedWork = new Set(); for (const plan of plans) { for (const spec of plan.proposal?.work ?? []) { const workId = `${plan.request.id}-${spec.key}`; if (existsSync(join(coordRoot, ".harnery", "work", workId, "intent.json"))) { materializedWork.add(workId); } } if (plan.status !== "applied" || !plan.root_work_id) continue; activeRoot = plan.root_work_id; generation++; if ( plan.proposal?.milestone && readWorkItem(coordRoot, plan.root_work_id).projection.state === "succeeded" ) { milestonesCompleted++; } for (const workId of plan.work_ids) appliedWork.add(workId); } const completedPlans = plans.filter((plan) => plan.status === "completed"); if ( completedPlans.length > 1 || (completedPlans.length === 1 && completedPlans[0] !== plans.at(-1)) ) { throw new Error(`governor ${goalId} has invalid completion history`); } completed = completedPlans.length === 1; return { plans, active_root_work_id: activeRoot, generation, applied_work_ids: [...appliedWork], materialized_work_ids: [...materializedWork], milestones_completed: milestonesCompleted, completed, latest: plans.at(-1), }; } export function readGovernorPlan( coordRootRaw: string, goalId: string, planId: string, ): GovernorPlanRecord { assertId(goalId, GOAL_ID, "governor id"); assertId(planId, PLAN_ID, "governor plan id"); const coordRoot = resolve(coordRootRaw); const dir = planDir(coordRoot, goalId, planId); const request = readJson(join(dir, "request.json"), "plan request"); validateRequest(request, goalId, planId); const proposalPath = join(dir, "proposal.json"); const proposal = existsSync(proposalPath) ? readJson(proposalPath, "plan proposal") : undefined; if (proposal) validateProposalEnvelope(proposal, planId); const review = readGovernorPlanReviewReceipt(coordRootRaw, goalId, planId); const events = readEvents(join(dir, "events.jsonl"), planId); const derived = deriveStatus(coordRoot, events, proposal); return { request, proposal, review: review ? summarizeReview(review) : undefined, events, ...derived, }; } export function readGovernorPlanReviewReceipt( coordRootRaw: string, goalId: string, planId: string, ): GovernorPlanReviewReceipt | undefined { assertId(goalId, GOAL_ID, "governor id"); assertId(planId, PLAN_ID, "governor plan id"); const coordRoot = resolve(coordRootRaw); const path = join(planDir(coordRoot, goalId, planId), "review.json"); if (!existsSync(path)) return undefined; const receipt = readJson(path, "plan review receipt"); validateReviewReceipt(receipt, planId); const reviewPolicy = readFrozenReviewPolicy(coordRoot, goalId); if (reviewPolicy) validateReviewReceiptMatchesPolicy(receipt, planId, reviewPolicy); return receipt; } function deriveStatus( coordRoot: string, events: GovernorPlanEvent[], proposal: GovernorPlanProposal | undefined, ): Pick< GovernorPlanRecord, "status" | "approval_id" | "root_work_id" | "work_ids" | "reason" | "class" > { const latest = events.at(-1); const completed = [...events].reverse().find((event) => event.event === "plan.completed"); if (completed) { // ADR 0050: an operator finding on work beneath a completed mission reopens // the mission by APPENDING, never by rewriting. The accepted completion stays // in the log verbatim; a later `plan.reopened` supersedes it, which is what // drops `plans.completed` and lets the projection dispatch again. const reopened = [...events] .reverse() .find((event) => event.event === "plan.reopened" && event.seq > completed.seq); if (reopened) { return { status: "reopened", work_ids: [], reason: reopened.reason }; } return { status: "completed", work_ids: [], reason: completed.reason }; } const applied = [...events].reverse().find((event) => event.event === "plan.applied"); if (applied) { return { status: "applied", root_work_id: applied.root_work_id, work_ids: applied.work_ids ?? [], reason: applied.reason, }; } if (latest?.event === "plan.rejected") { return { status: "rejected", work_ids: [], reason: latest.reason }; } if (latest?.event === "plan.retry_requested") { return { status: "retry_requested", work_ids: [], reason: latest.reason }; } if (latest?.event === "plan.attention") { return { status: "attention", work_ids: [], reason: latest.reason }; } if (latest?.event === "plan.failed") { // ADR 0046: carry the planner run's failure class so the projection can stop // an environment failure and bound consecutive upstream ones, rather than // replanning an unchanged environment to budget exhaustion. return { status: "failed", work_ids: [], reason: latest.reason, class: latest.class }; } const parked = [...events].reverse().find((event) => event.event === "plan.awaiting_approval"); const resumedAfterPark = parked !== undefined && events.some((event) => event.event === "plan.resumed" && event.seq > parked.seq); if (parked?.approval_id && !resumedAfterPark) { return { status: approvalIsResolved(coordRoot, parked.approval_id) ? "resumable" : "awaiting_approval", approval_id: parked.approval_id, work_ids: [], reason: parked.reason, }; } if (proposal) return { status: "proposed", work_ids: [], reason: proposal.rationale }; return { status: "interrupted", work_ids: [], reason: latest?.reason }; } function readEvents(path: string, planId: string): GovernorPlanEvent[] { return readSegmentedJsonlFileSync(path, { max_record_bytes: MAX_RECORD_BYTES, max_records: 1_000_000, }).map((event, index) => { validateEvent(event, planId, index + 1); return event; }); } function validateRequest(request: GovernorPlanRequest, goalId: string, planId: string): void { if ( request.schema_version !== GOVERNOR_PLAN_SCHEMA_VERSION || request.id !== planId || request.goal_id !== goalId || !Number.isSafeInteger(request.sequence) || request.sequence < 1 || request.sequence > MAX_PLANS || (request.trigger !== undefined && !["initial", "recovery", "milestone"].includes(request.trigger)) || typeof request.trigger_fingerprint !== "string" || request.trigger_fingerprint.length < 1 || request.trigger_fingerprint.length > 64_000 || !WORK_ID.test(request.prior_root_work_id) || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/.test(request.workflow_run_id) || !validTimestamp(request.created_at) ) { throw new Error(`governor plan request ${planId} has an unsupported schema`); } } function validateProposalEnvelope(proposal: GovernorPlanProposal, planId: string): void { if ( proposal.schema_version !== GOVERNOR_PLAN_SCHEMA_VERSION || proposal.plan_id !== planId || !["apply", "complete", "attention"].includes(proposal.decision) || typeof proposal.rationale !== "string" || proposal.rationale.length < 1 || proposal.rationale.length > 2_000 || !Array.isArray(proposal.work) || proposal.work.length > 25 || typeof proposal.root !== "string" || proposal.root.length > 32 || !validTimestamp(proposal.proposed_at) ) { throw new Error(`governor plan proposal ${planId} has an unsupported schema`); } if (proposal.milestone !== undefined) { const milestone = proposal.milestone; if ( !Number.isSafeInteger(milestone.sequence) || milestone.sequence < 1 || milestone.sequence > 20 || typeof milestone.title !== "string" || milestone.title.length < 1 || milestone.title.length > 200 || typeof milestone.objective !== "string" || milestone.objective.length < 1 || milestone.objective.length > 4_000 || !Array.isArray(milestone.acceptance) || milestone.acceptance.length < 1 || milestone.acceptance.length > 50 || milestone.acceptance.some( (criterion) => typeof criterion !== "string" || criterion.length < 1 || criterion.length > 500, ) ) { throw new Error(`governor plan proposal ${planId} milestone is invalid`); } } if ( (proposal.decision === "attention" || proposal.decision === "complete") && (proposal.root !== "" || proposal.work.length > 0 || proposal.milestone !== undefined) ) { throw new Error(`governor plan proposal ${planId} terminal decision contains work`); } for (const [index, spec] of proposal.work.entries()) { if ( !spec || typeof spec !== "object" || !PLAN_KEY.test(spec.key) || typeof spec.title !== "string" || spec.title.length < 1 || spec.title.length > 200 || typeof spec.objective !== "string" || spec.objective.length < 1 || spec.objective.length > 4_000 || !Array.isArray(spec.acceptance) || spec.acceptance.length > 50 || spec.acceptance.some((value) => typeof value !== "string" || value.length > 500) || !Array.isArray(spec.dependencies) || spec.dependencies.length > 50 || spec.dependencies.some((value) => typeof value !== "string" || value.length > 100) || !TEMPLATE_ID.test(spec.template) ) { throw new Error(`governor plan proposal ${planId} work[${index}] is invalid`); } } } function validateReviewReceipt(receipt: GovernorPlanReviewReceipt, planId: string): void { if ( !receipt || typeof receipt !== "object" || receipt.schema_version !== GOVERNOR_PLAN_SCHEMA_VERSION || receipt.plan_id !== planId || !["passed", "revision_exhausted", "attention", "failed"].includes(receipt.status) || !/^[a-f0-9]{64}$/.test(receipt.candidate_sha256) || !Array.isArray(receipt.rounds) || receipt.rounds.length < 1 || receipt.rounds.length > MAX_GOVERNOR_PLAN_REVISION_ROUNDS + 1 ) { throw new Error(`governor plan review ${planId} has an unsupported schema`); } validateProposalEnvelope(receipt.final_candidate, planId); if (receipt.candidate_sha256 !== candidateDigest(receipt.final_candidate)) { throw new Error(`governor plan review ${planId} has invalid candidate digest`); } receipt.rounds.forEach((round, index) => { validateReviewRound(round, planId, index + 1); }); const finalRound = receipt.rounds.at(-1); if (finalRound?.candidate_sha256 !== receipt.candidate_sha256) { throw new Error(`governor plan review ${planId} final round does not match its candidate`); } if (receipt.status === "passed" && finalRound?.outcome !== "approved") { throw new Error(`governor plan review ${planId} passed without approval`); } if (receipt.status === "attention" && finalRound?.outcome === "approved") { throw new Error(`governor plan review ${planId} attention conflicts with approval`); } } function readFrozenReviewPolicy( coordRoot: string, goalId: string, ): GovernorReplanningPolicy["review"] | undefined { const path = join(coordRoot, ".harnery", "governors", goalId, "intent.json"); if (!existsSync(path)) return undefined; const intent = readJson<{ replanning?: GovernorReplanningPolicy }>(path, "governor intent"); return intent.replanning?.review; } function validateReviewReceiptMatchesPolicy( receipt: GovernorPlanReviewReceipt, planId: string, review: NonNullable, ): void { if (receipt.rounds.length > review.max_revision_rounds + 1) { throw new Error(`governor plan review ${planId} exceeds the frozen review policy`); } for (const round of receipt.rounds) { if (round.reviewers.length !== review.reviewer_specialists.length) { throw new Error(`governor plan review ${planId} does not match the frozen review policy`); } round.reviewers.forEach((reviewer, index) => { if (reviewer.specialist !== review.reviewer_specialists[index]) { throw new Error(`governor plan review ${planId} does not match the frozen review policy`); } }); const outcome = aggregateReviewers(round.reviewers); if (round.outcome !== outcome) { throw new Error(`governor plan review ${planId} has an invalid reviewer outcome`); } } const finalRound = receipt.rounds.at(-1)!; if (receipt.status === "passed" && finalRound.outcome !== "approved") { throw new Error(`governor plan review ${planId} passed without policy approval`); } if (receipt.status === "revision_exhausted" && finalRound.round <= review.max_revision_rounds) { throw new Error(`governor plan review ${planId} exhausted before the frozen review limit`); } } function aggregateReviewers( reviewers: readonly GovernorPlanReviewReviewer[], ): GovernorPlanReviewRound["outcome"] { if (reviewers.length < 1) return "failed"; if (reviewers.some((reviewer) => reviewer.verdict === "attention")) return "attention"; if ( reviewers.some( (reviewer) => reviewer.verdict === "revise" || reviewer.findings.some((finding) => finding.severity === "blocking"), ) ) { return "revise"; } return "approved"; } function candidateDigest(candidate: GovernorPlanProposal): string { return createHash("sha256") .update(JSON.stringify(canonicalCandidate(candidate))) .digest("hex"); } function canonicalCandidate(candidate: GovernorPlanProposal): unknown { return { schema_version: candidate.schema_version, plan_id: candidate.plan_id, decision: candidate.decision, rationale: candidate.rationale, root: candidate.root, work: candidate.work, milestone: candidate.milestone, }; } function validateReviewRound( round: GovernorPlanReviewRound, planId: string, sequence: number, ): void { if ( !round || typeof round !== "object" || round.round !== sequence || !/^[a-f0-9]{64}$/.test(round.candidate_sha256) || !Array.isArray(round.reviewers) || round.reviewers.length < 1 || round.reviewers.length > MAX_GOVERNOR_PLAN_REVIEWERS || !["approved", "revise", "attention", "failed"].includes(round.outcome) || (round.revision_workflow_run_id !== undefined && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/.test(round.revision_workflow_run_id)) ) { throw new Error(`governor plan review ${planId} round ${sequence} is invalid`); } round.reviewers.forEach((reviewer, index) => { validateReviewReviewer(reviewer, planId, sequence, index); }); } function validateReviewReviewer( reviewer: GovernorPlanReviewReviewer, planId: string, round: number, index: number, ): void { if ( !reviewer || typeof reviewer !== "object" || typeof reviewer.specialist !== "string" || reviewer.specialist.length < 1 || reviewer.specialist.length > 100 || !["approve", "revise", "attention"].includes(reviewer.verdict as GovernorPlanReviewVerdict) || typeof reviewer.rationale !== "string" || reviewer.rationale.length < 1 || reviewer.rationale.length > 2_000 || !Array.isArray(reviewer.findings) || reviewer.findings.length > 50 ) { throw new Error(`governor plan review ${planId} round ${round} reviewer ${index} is invalid`); } reviewer.findings.forEach((finding, findingIndex) => { validateReviewFinding(finding, planId, round, index, findingIndex); }); } function validateReviewFinding( finding: GovernorPlanReviewFinding, planId: string, round: number, reviewerIndex: number, findingIndex: number, ): void { if ( !finding || typeof finding !== "object" || typeof finding.code !== "string" || !/^[a-z][a-z0-9._-]{0,99}$/.test(finding.code) || !["blocking", "advisory"].includes(finding.severity) || typeof finding.summary !== "string" || finding.summary.length < 1 || finding.summary.length > 1_000 || typeof finding.recommendation !== "string" || finding.recommendation.length < 1 || finding.recommendation.length > 1_000 ) { throw new Error( `governor plan review ${planId} round ${round} reviewer ${reviewerIndex} finding ${findingIndex} is invalid`, ); } } function summarizeReview(receipt: GovernorPlanReviewReceipt): GovernorPlanReviewSummary { const findings = receipt.rounds.flatMap((round) => round.reviewers.flatMap((reviewer) => reviewer.findings), ); return { status: receipt.status, candidate_sha256: receipt.candidate_sha256, rounds: receipt.rounds.length, blocking_findings: findings.filter((finding) => finding.severity === "blocking").length, advisory_findings: findings.filter((finding) => finding.severity === "advisory").length, }; } function validateEvent(event: GovernorPlanEvent, planId: string, sequence: number): void { if ( event.schema_version !== GOVERNOR_PLAN_SCHEMA_VERSION || event.plan_id !== planId || event.seq !== sequence || !EVENT_TYPES.has(event.event) || !validTimestamp(event.ts) || typeof event.actor !== "string" || event.actor.length < 1 || event.actor.length > 200 || typeof event.reason !== "string" || event.reason.length < 1 || event.reason.length > 2_000 ) { throw new Error(`governor plan ${planId} event ${sequence} has an unsupported schema`); } if (event.approval_id !== undefined && !APPROVAL_ID.test(event.approval_id)) { throw new Error(`governor plan ${planId} event ${sequence} has an invalid approval id`); } if (event.root_work_id !== undefined && !WORK_ID.test(event.root_work_id)) { throw new Error(`governor plan ${planId} event ${sequence} has an invalid root work id`); } if (event.work_ids !== undefined) { if (!Array.isArray(event.work_ids) || event.work_ids.some((id) => !WORK_ID.test(id))) { throw new Error(`governor plan ${planId} event ${sequence} has invalid work ids`); } } if ( event.class !== undefined && event.class !== "environment" && event.class !== "upstream" && event.class !== "decision" ) { throw new Error(`governor plan ${planId} event ${sequence} has an unknown failure class`); } } function approvalIsResolved(coordRoot: string, approvalId: string): boolean { const path = join(coordRoot, ".harnery", "approvals", approvalId, "decision.json"); if (!existsSync(path)) return false; try { const value = readJson>(path, "workflow approval decision"); return value.approval_id === approvalId && ["allow", "deny"].includes(String(value.verdict)); } catch { return false; } } function readJson(path: string, label: string): T { if (!existsSync(path)) throw new Error(`${label} does not exist at ${path}`); const size = statSync(path).size; if (size <= 0 || size > MAX_RECORD_BYTES) throw new Error(`${label} has invalid size ${size}`); try { return JSON.parse(readFileSync(path, "utf8")) as T; } catch (error) { throw new Error(`cannot parse ${label} at ${path}: ${(error as Error).message}`); } } function plansRoot(coordRoot: string, goalId: string): string { return join(coordRoot, ".harnery", "governors", goalId, "plans"); } function planDir(coordRoot: string, goalId: string, planId: string): string { return join(plansRoot(coordRoot, goalId), planId); } function assertId(value: string, pattern: RegExp, label: string): void { if (!pattern.test(value)) throw new Error(`invalid ${label} ${JSON.stringify(value)}`); } function validTimestamp(value: unknown): value is string { return typeof value === "string" && value.length <= 40 && Number.isFinite(Date.parse(value)); }