* fix(worktree): recurse into tasks/ when syncing slice artifacts back to project root (#1678)
syncWorktreeStateBack() only processed files directly in each slice
directory, silently skipping the tasks/ subdirectory. Task-level
summaries (T01-SUMMARY.md, T02-SUMMARY.md, etc.) were therefore never
copied from the worktree back to the project root before teardown,
causing data loss when the worktree was removed on milestone completion.
Fix: detect the tasks/ directory entry in the inner loop and recurse
into it, copying all .md files and appending them to the synced list.
Consistent with how syncStateToProjectRoot() already uses recursive
copy via safeCopyRecursive().
Adds regression test (case 8 in worktree-sync-milestones.test.ts)
covering slice-level and task-level summary sync.
* feat(cleanup): add ~/.gsd/projects/ orphan detection and pruning
Introduces a complete lifecycle management story for the external project
state directory (~/.gsd/projects/<hash>/). Previously these directories
accumulated indefinitely with no mechanism to identify or remove them
after a repo was deleted or moved.
Changes:
repo-identity.ts
- Write `repo-meta.json` into each external state dir on first open
(and backfill on any subsequent open if the file is missing).
- Records: version, hash (dir name), gitRoot, remoteUrl, createdAt.
- Non-fatal: metadata write failure never blocks project setup.
- Export `readRepoMeta()` and `RepoMeta` interface for consumers.
doctor-types.ts
- Add `orphaned_project_state` to DoctorIssueCode.
- Add `GLOBAL_STATE_CODES` set — codes that must never be auto-fixed
at fixLevel=task (post-task automated health checks must not delete
project state directories).
doctor-checks.ts
- Add `checkGlobalHealth()` — scans ~/.gsd/projects/, reads repo-meta.json
from each dir, reports info-severity issue for any whose gitRoot is gone.
- Auto-fixable with --fix; skipped entirely at fixLevel=task.
doctor.ts
- Import and call `checkGlobalHealth` after `checkRuntimeHealth`.
- Gate on `GLOBAL_STATE_CODES` in `shouldFix` at task fixLevel.
commands-maintenance.ts
- Add `handleCleanupProjects(args, ctx)` — interactive audit command.
- Categorises dirs as active / orphaned / unknown (no metadata yet).
- Without --fix: prints full report with per-dir gitRoot + remoteUrl.
- With --fix: deletes orphaned dirs, reports removed/failed counts.
commands/handlers/ops.ts
- Route `cleanup projects` and `cleanup projects --fix` to handler.
commands/catalog.ts
- Add `projects` and `projects --fix` to cleanup tab-completions.
* feat(cleanup): add metrics.json bloat detection and pruning
The metrics ledger has no TTL and grows by one entry per completed unit —
~1-2 KB/entry with no ceiling. On a busy project (50 units/day) this
reaches 4-9 MB in 90 days and continues growing indefinitely.
Changes:
metrics.ts
- Add pruneMetricsLedger(base, keepCount): trims oldest entries from the
head of the units array, keeping the newest `keepCount`. Updates both
the on-disk file and the in-memory ledger if a session is active.
doctor-types.ts
- Add "metrics_ledger_bloat" to DoctorIssueCode.
doctor-checks.ts (checkRuntimeHealth)
- Add metrics ledger bloat check after the existing integrity check.
- Threshold: 2000 units / fires as "warning".
- Fix: prune to newest 1500 entries via pruneMetricsLedger().
- Reports both the unit count and file size in MB in the issue message.
* fix cleanup project-state path and repo-meta refresh