import type { ScenarioSlice } from "./types.js"; /** * Checkpoint/resume for the algorithm. Two layers of durability already * exist without a journal: finished slices live in git (the slice * commit), and within a slice the working tree itself persists — the * checklist builders reconstruct covered/uncovered state by inspecting * what actually exists. What is LOST when a run dies is the algorithm's * memory of which phases already ran and what they produced (e.g. that * the red-check was already observed — re-running it against a * half-built slice would wrongly report a guardrail violation — or which * contract draft the fan-out was working against). * * The journal records exactly that: per slice, each completed phase and * its result. On restart, completed slices are skipped entirely and the * in-flight slice resumes after its last recorded phase. */ /** The phase boundaries of one slice, in execution order. The *-commit * phases are the trunk's mid-slice CHECKPOINTS — journaled like any other * phase so a resumed run never re-commits or half-commits. */ export type SlicePhase = "outer-red" | "specification" | "wip-spec-commit" | "contract" | "contract-commit" | "fan-out" | "join" | "join-commit" | "real-api" | "real-api-commit" | "driver-dry-run" | "smoke"; export interface RunCheckpoint { /** Slices fully finished: committed and slice pipeline green. */ completedSlices: string[]; /** * The slices this run planned to build, in order — recorded the moment * planning finishes so a resumed run rebuilds the EXACT plan instead of * re-assessing. Re-assessment on resume is unsafe: a half-built slice's * acceptance test passes against the fake, so coverage assessment would * mark it "done" and drop it before its real-API wiring, @wip removal and * trunk commit ever run — silently shipping an unfinished slice. Absent * only in legacy checkpoints written before plan persistence existed. */ plannedSlices?: ScenarioSlice[]; /** The slice that was mid-flight when the run died, if any. */ inFlightSlice?: { name: string; /** Each recorded phase, with the result it returned (null for void). */ phaseResults: Partial>; }; } export interface RunJournal { /** The last durably recorded state for this feature run, or null. */ load(featureFilePath: string): Promise; /** * Persist the run's slice plan (its record of intent) so a death anywhere * after planning — even before the first phase records — resumes this exact * plan rather than re-assessing. Called once, right after planning. */ recordPlan(featureFilePath: string, slices: readonly ScenarioSlice[]): Promise; recordPhaseResult(slice: ScenarioSlice, phase: SlicePhase, result: unknown): Promise; /** The slice is committed and its pipeline green — never re-entered. */ recordSliceCompleted(slice: ScenarioSlice): Promise; /** All slices done and handed off; the journal can be cleared. */ recordRunFinished(featureFilePath: string): Promise; } /** * Wraps a slice's phases: an already-recorded phase is skipped and its * recorded result returned; a fresh phase runs and is recorded before * the algorithm moves on. Intra-phase progress needs no journal — on * re-entry, the checklist builders and "needs" checks read the working * tree, so only unfinished work is redone. */ export declare function slicePhaseTracker(journal: RunJournal, slice: ScenarioSlice, checkpoint: RunCheckpoint | null): { phase(name: SlicePhase, run: () => Promise): Promise; }; export type SlicePhaseTracker = ReturnType;