Phase 2 of approved feature #1817. Wires the pure `rebuildCore` transition
(Phase 1, #1827) to the `gsd state rebuild` CLI subcommand per ADR-1817
§5 (heavy/manual counterpart to lightweight, auto-triggered `state sync`).
Source changes:
- src/state.cts: implement cmdStateRebuild. Locks via
readModifyWriteStateMd (real path) or read-only (dry-run). Wires
phaseInventoryProvider to a real .planning/phases/ disk scan (same
canonical source buildStateFrontmatter uses). --dry-run emits a
structured preview without writing. --verbose tees the audit-log
entries to stderr (treated as data-only per ADR-1577).
- src/state-command-router.cts: register cmdStateRebuild in the
StateModule interface + add the rebuild handler with --dry-run and
--verbose flag parsing.
- src/command-aliases.cts: register the state.rebuild canonical +
'state rebuild' alias + mutation=true (so the manifest covers the
new subcommand for SDK parity / dispatch hub tests).
Tests (tests/state-rebuild-cli.test.cjs, 5 cases):
- state rebuild with no flags reconciles drifted body + drops orphan
table rows + appends audit log (end-to-end #1, #2, audit log).
- state rebuild --dry-run computes the diff, writes nothing (criterion #5).
- state rebuild --verbose tees the log; audit-log section still written.
- Running rebuild twice on the just-rebuilt file is byte-identical
(criterion #6 end-to-end).
- Missing STATE.md produces a clean 'STATE.md not found' message, no
stack trace (CONTRIBUTING QA matrix).
Verified locally:
- node --test tests/state-rebuild-cli.test.cjs → 5/5 pass
- node --test tests/state-rebuild.test.cjs → 18/18 pass (Phase 1 regression)
- node --test tests/state-transition.test.cjs → 85/85 pass (ADR-1769 regression)
Docs (docs/COMMANDS.md): document `state rebuild [--dry-run] [--verbose]`
with the canonical-command block format used by `state sync` /
`state prune`.
Changeset (.changeset/1817-state-rebuild.md): type=Added, user-facing
description of the new subcommand (closes#1817 epic on merge).