/* RFC-1028: Validation state types and path resolver for the .validation-state.json runtime artifact. Records per-validator status after mission.validate completes, enabling inspection via validation.state.inspect. Do not implement validation logic — this module only defines types and path resolution. Do not read or write the cache — that is handled by the pipeline executor and cache layer. RFC-1028: initial implementation — ValidationState, ValidatorState interfaces, resolveValidationStatePath, readValidationState, writeValidationState. */ import { join } from "node:path"; import type { KernelExecutionReport } from "@warpgogol/werkstatt-shared/kernel"; import { createDefaultIO, type WorkspaceIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; export interface ValidationState { missionId: string; lastValidatedAt: string; lastValidationStatus: "pass" | "fail" | "incomplete"; validatorStates: ValidatorState[]; } export interface ValidatorState { commandName: string; status: "pass" | "fail" | "skipped" | "not-run"; cached: boolean; inputHash?: string; moduleHash?: string; durationMs?: number; skippedBy?: string; diagnosticCounts?: { error: number; warning: number; info: number }; } /** * Path: missions//.validation-state.json (gitignored, runtime only). */ export function resolveValidationStatePath(workspaceRoot: string, missionId: string): string { return join(workspaceRoot, "missions", missionId, ".validation-state.json"); } export async function readValidationState( workspaceRoot: string, missionId: string, io: WorkspaceIO = createDefaultIO().io, ): Promise { const path = resolveValidationStatePath(workspaceRoot, missionId); try { const content = await io.readFile(path); return JSON.parse(content) as ValidationState; } catch { return null; } } export async function writeValidationState( workspaceRoot: string, state: ValidationState, io: WorkspaceIO = createDefaultIO().io, ): Promise { const path = resolveValidationStatePath(workspaceRoot, state.missionId); await io.mkdir(join(path, "..")); await io.writeFile(path, JSON.stringify(state, null, 2) + "\n"); } /** * RFC-1028: Build ValidatorState[] from pipeline step reports. * Maps KernelExecutionReport[] to the compact ValidatorState format * for .validation-state.json. */ export function buildValidatorStatesFromSteps(steps: KernelExecutionReport[]): ValidatorState[] { return steps.map((step) => ({ commandName: step.commandName, status: step.ok ? "pass" : "fail", cached: (step.timing as { fromCache?: boolean })?.fromCache ?? false, durationMs: step.timing?.durationMs, diagnosticCounts: step.logSummary ? { error: step.logSummary.error, warning: step.logSummary.warning, info: step.logSummary.notice, } : undefined, })); }