Make SF auto-mode impossible to loop from broken docs, missing artifacts, stale runtime state, or ambiguous background-unit completion. `ROADMAP.md` must become a rendered human artifact, not executable dispatch state.
### Track 1 - Canonical State And No-Doc-Loop Dispatch
- [ ] Add `getCanonicalMilestonePlan(basePath, milestoneId)` as the only dispatch-facing milestone plan accessor.
- [ ] Prefer DB slice rows when DB is available and populated.
- [ ] Add `.sf/milestones/Mxxx/Mxxx-ROADMAP.json` as the structured fallback projection.
- [ ] Treat `ROADMAP.md` as rendered display only.
- [ ] Keep Markdown parsing only for import, migration, doctor repair, and parser tests.
- [ ] Move parallel research, single-slice research, prior-slice guard, UAT/validation dispatch, and prompt slice enumeration to canonical plan state.
- [ ] Add generated marker/hash metadata to `ROADMAP.md`.
- [ ] Stop dispatch when DB/projection/Markdown disagree.
- [ ] Add `/sf doctor --fix` support to re-render generated roadmap artifacts from canonical state.
### Track 2 - Unit Runtime FSM
- [ ] Introduce durable unit runtime state under `.sf/runtime/units/*.json`.
- [ ] Model unit states: `queued`, `claimed`, `running`, `progress`, `completed`, `failed`, `blocked`, `cancelled`, `stale`, `runaway-recovered`, `notified`.
- [ ] Prevent redispatch for terminal units with `notifiedAt`.
- [ ] Allow retry only when status is retryable and retry budget remains.
- [ ] Require explicit reset to rerun failed synthetic units like `parallel-research`.
### Track 3 - Progress And Liveness
- [ ] Separate heartbeat, progress, and output growth.
- [ ] Treat silent-but-running as valid only when heartbeat is fresh.
- [ ] Add watchdog classifiers: dead PID, expired lease, no heartbeat, no output growth, permission prompt, interactive prompt, runaway recovery.
- [ ] Extend `sf headless --output-format json query` with active unit, status, elapsed time, retry count, watchdog reason, last progress time, and output path.
- [ ] Later: render TUI/footer status rows from the same runtime model.