/*
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,
}));
}