# 04. Resumable run-state persistence meta: id: pygienium-04 feature: pygienium priority: P1 depends_on: [pygienium-01] tags: [infrastructure, state] objective: - Build `src/hygiene-state.ts`: a resumable state file (`pygienium/run-state.json`) tracking one run per invocation with per-check phase status, so interrupted runs can resume and `/pygienium-status` can report progress. deliverables: - `src/hygiene-state.ts` exporting `initRun`, `latestRun`, `latestResumableRun`, `readRunState`, `applyPhaseStatus`, `markRunStatus`, `tallyPhases` - State schema: `{ runs: [{ run_id, mode, status, checks: { : { status, attempt, last_error, artifacts, ... } } }] }` - Idempotent writes (read-modify-write with safe merge); file absent / unparseable handled gracefully steps: - Port piolium's `audit-state.ts` read/write helpers, renaming audit→run, phase→check - Implement `initRun(cwd, { mode })` returning a fresh run state record - `applyPhaseStatus(cwd, run, checkName, patch)` merges per-check status - `latestResumableRun` returns a run whose status is in_progress or failed (not complete) tests: - Unit: init -> apply phase complete -> markRunStatus complete reads back correctly - Unit: unparseable file returns `parseError` without throwing acceptance_criteria: - `readRunState(cwd)` returns `{ exists, parseError?, state? }` - applyPhaseStatus persists and is readable by a subsequent read - Resumable selection picks in_progress > failed, ignores complete validation: - Run a check, kill mid-flight, inspect `pygienium/run-state.json` notes: - Keep schema forward-compatible-by-addition only within this build; no legacy migrations - This file is the source of truth for `/pygienium-status` and `/pygienium-resume`