0.54.0 · core 2026-07-14
Reliability overhaul — implements plan stelow-reliability.md
Added
- TypeBox runtime validation for
WorkflowandTrackingData(extensions/stelow/schemas.ts) — Replaces compile-time-only validation. Schemas enforcenamelength,currentPhase ≥ 0,statusenum, nestedScope/ScopeRecord/ScopeTaskshapes. Failures throwWorkflowValidationErrorwith full JSON Pointer path. RespectsSTELOW_VALIDATE=0escape hatch (consistent with per-scope toggle). - Advisory file lock for
writeTrackingandwriteGlobalTracking(extensions/stelow/file-lock.ts) — Prevents the read-modify-write race that atomic write alone does not cover. O_EXCL atomic create + exponential backoff + stale-lock TTL reclamation (default 10s). POSIX-portable (mac + linux). safeHookwrapper for all 5 extension hooks (extensions/stelow/safe-hook.ts) — Extracted to its own module. A throw inside any hook is logged with the hook name and returnsundefined(Pi's "allow" signal) instead of breaking the user input stream or silently swallowing errors. Sibling hooks continue to execute./sw-recovercommand + orphan workflow detection (extensions/stelow/commands.ts,extensions/stelow/doctor.ts) — Detects workflow directories on disk (.stelow/{date}/{dirHash}/specs/spec-product_*.md) that have no matching entry instelow.json. Operator-driven recovery: walks orphans, prompts per-directory, writes minimal paused-status entry. New alias/stelow-recoverand--allflag for non-interactive recovery./sw-status --json(extensions/stelow/commands.ts) — Emits structured JSON snapshot of the active workflow (name, dirHash, status, currentPhase, phases[], scopes[], scopeProgress) for tool/agent consumption. Human-formatted output remains the default.- Stryker mutation testing infrastructure (
stryker.config.json) — Configured to mutatesafe-hook.ts,file-lock.ts,schemas.ts. Run withnpm run test:mutation. Initial run: 100% score onsafe-hook.ts; 63% overall with 49 surviving mutants documented as future work. - Atomic file writes (
extensions/stelow/state.ts:writeJson) — All state writes go throughwriteJson(path, data)which uses a tmp-file +renameSyncpattern.renameis atomic on the same filesystem, so a crash mid-write leaves the previous file intact. - Defensive
isTrackingShapetype guard (extensions/stelow/state.ts) —readTrackingnow returnsnullfor malformed shapes (e.g.[]array root, missingworkflowsfield) instead of crashing downstream consumers insyncScopesIfNeeded.
Changed
extensions/stelow/commands.ts:cmdStatus— Added--jsonflag for machine-readable output. Default output unchanged.extensions/stelow/doctor.ts:DoctorReport— Addedorphans: OrphanWorkflow[]field.formatDoctorReportrenders orphan count and directory paths.- CI matrix (
.github/workflows/ci.yml) — All 3 jobs (typecheck, lint, test) now run onubuntu-latest+macos-latest. extensions/stelow/state.ts:renameWorkflowreturn type — Now returns{ ok: true } | { ok: false; error: string }for explicit success/failure signalling. Callers updated.tests/integration/property-based.test.ts— Property 2 (renameWorkflow preserves dirHash) now filtersdirHashto^[a-z0-9-]+$to prevent invalid-name edge cases. Property 6 (stages-guard) added with 4 sub-properties (determinism, block enforcement, safe fallback, non-blocked allow).tests/integration/property-based.test.ts— Removedprocess.env.HOMEmanipulation inbeforeEachto avoid races with parallel vitest files. Property 3 now uses uniquedirHashvalues per iteration.
Fixed
- cmdRecover partial-recovery data loss (
extensions/stelow/commands.ts) — Previously, if a later orphan produced an invalid synthetic entry,writeTrackingwould throw at the end and silently lose all in-memory pushes (the file stayed at its old state). Now each synthetic entry is validated individually; invalid ones are skipped with a warning so prior successful recoveries in the same run are preserved. - readTracking crashed on array root (
extensions/stelow/state.ts) —readJsononly validatedJSON.parsesuccess, not shape. A user writing[](array) or a single object tostelow.jsonwould causesyncScopesIfNeededto throwdata.workflows is not iterable. Now caught by the newisTrackingShapeguard.
Tests
- +143 tests, 1037 total passing (3 pre-existing architectural-invariants failures on
mainunchanged). - 8 new test files:
atomic-write.test.ts,concurrency.test.ts,corrupt-tracking-recovery.test.ts,file-lock.test.ts,hook-error-containment.test.ts,hook-order.test.ts,orphan-workflow-recovery.test.ts,property-based.test.ts,safe-hook-integration.test.ts,schemas.test.ts,subagent-acceptance-contract.test.ts,sw-status-json.test.ts. - Property-based testing: 5 invariant classes using
fast-check(write/read round-trip, renameWorkflow preserves dirHash, add/remove global index balance, parseSpecTechScopes idempotence, stages-guard permission check). - Concurrency stress: 5 tests firing 10/20/50 parallel
writeTrackingcalls viaPromise.allto validate atomic write + lock combination.
Dependencies
- Added
typebox@^1.1.38(moved from peerDep to direct — now used at runtime). - Added
fast-check@^4.9.0(devDep — property-based testing). - Added
@stryker-mutator/core@^9.6.1,@stryker-mutator/typescript-checker,@stryker-mutator/vitest-runner(devDeps — mutation testing).
Plan coverage
Implements 10 of 10 plan items from stelow-reliability.md:
- G1A (hook try/catch), G2A (runtime validation), G3A (lock file), G3B (atomic write), G4A+C (doctor recovery), G5A (/sw-status JSON), G6A (property tests), G6C (mutation testing), G7A (concurrency tests), G8 (subagent acceptance contract), G9A (hook order), G10A (CI matrix).