Skip to content

/gsd doctor

/gsd doctor scans your .gsd/ directory for structural integrity issues — missing directories, stale locks, orphaned worktrees, corrupt git state, environment misconfigurations, missing API keys, database constraint violations, and more. It operates in four modes:

  • Doctor (default) — Scans and reports issues. Read-only, changes nothing.
  • Fix — Scans and auto-repairs what it can. Removes stale locks, creates missing directories, sanitizes delimiter characters, cleans up git state.
  • Heal — Scans, auto-repairs fixable issues, then dispatches remaining errors to the LLM for interactive investigation and resolution.
  • Audit — Full scan with all warnings included, up to 50 issues displayed, no fixes applied.

Doctor checks a large set of distinct issue codes spanning seven domains (project/runtime, git/worktrees, environment, provider, engine/database, milestone structure, slice structure, task structure). Each issue has a severity (error, warning, info) and a fixability flag. Fix mode only touches issues marked as fixable — it won’t rewrite plan content or make judgment calls.

During auto-mode, a proactive healing layer runs lightweight checks before each unit dispatch, tracks health trends over time (green → yellow → red), and can escalate to LLM-assisted healing after 5 consecutive units with unresolved errors.

/gsd doctor # Report issues (default scope: active slice)
/gsd doctor M001 # Report issues for milestone M001
/gsd doctor M001/S02 # Report issues for a specific slice
/gsd doctor fix # Auto-repair fixable issues
/gsd doctor fix M001 # Auto-repair within milestone M001
/gsd doctor heal # Fix what's fixable, dispatch the rest to LLM
/gsd doctor audit # Full audit — all issues, all scopes, warnings included

Flags:

FlagDescription
--fixEnable fix mode without changing the default doctor subcommand (equivalent to /gsd doctor fix)
--buildRun npm run build and report failures as env_build (slow, opt-in)
--testRun npm test and report failures as env_test (slow, opt-in)
--dry-runShow what fix mode would change without applying any changes
--jsonOutput the doctor report as raw JSON instead of human-readable text

The scope argument is optional. Without it, doctor auto-selects the active slice (if one exists), then the active milestone, then the first incomplete milestone.

Doctor loads the full project state via deriveState() and scans the milestone registry. If no scope is provided, it selects the narrowest active context — the active slice if one exists, otherwise the active milestone, otherwise the first incomplete milestone in the registry. Audit mode always uses the explicitly requested scope without auto-selection (and defaults to full-project scan when none is given).

The scanner runs seven independent check functions:

  • checkGitHealth — Orphaned auto-worktrees, stale milestone branches, corrupt merge/rebase state, tracked runtime files, legacy slice branches, missing integration branches, orphaned worktree directories, stale uncommitted changes (auto-snapshot fix), and worktree lifecycle (merged, stale, dirty, unpushed). Git checks are skipped entirely if the project is not a git repo. Worktree and branch checks are also skipped when the project’s git.isolation preference is set to "none".
  • checkRuntimeHealth — Stale crash locks, stranded lock directories, stale parallel sessions, orphaned completed-unit keys, stale hook state, macOS numbered .gsd collision variants, activity log bloat, STATE.md drift, gitignore drift, failed migration artifacts, broken .gsd symlinks, metrics ledger integrity and bloat, large planning file detection, snapshot ref bloat.
  • checkGlobalHealth — Cross-project check that scans ~/.gsd/projects/ for orphaned project state directories whose git root no longer exists on disk.
  • checkEnvironmentHealth — Node version vs. engines requirement, node_modules staleness, missing .env files, port conflicts on common dev ports, disk space, Docker availability, package manager, TypeScript/Python/Rust/Go tool presence, and git remote reachability. The --build and --test flags add opt-in slow checks that run your build/test scripts.
  • checkEngineHealth — When the GSD database is available, runs DB constraint checks: orphaned tasks (task references a non-existent slice), orphaned slices (slice references a non-existent milestone), tasks marked done in the DB without summaries, and duplicate entity IDs. Also detects projection drift — when the event log is newer than rendered markdown files — and re-renders stale projections automatically as a fix rather than reporting an issue. Emits db_unavailable if the database file exists on disk but cannot be loaded.
  • runProviderChecks — When an active milestone exists, checks required LLM provider API keys (based on configured model preferences), remote questions channel token (Slack/Discord/Telegram), and optional tool integrations (Brave, Tavily, Jina, Context7). Fast — reads auth.json and env vars only, no network I/O.
  • Inline structural checks — Preference validation, requirement auditing, and all milestone/slice/task structural integrity checks (circular dependencies, orphaned directories, duplicate task IDs, missing plans, must-have verification, future timestamps) run directly in runGSDDoctor.

Checks are independent — a failure in one domain doesn’t prevent others from running.

When fix mode is enabled, fixable issues are repaired in place during the scan. Fixes include:

  • Creating missing tasks/ or slice directories
  • Removing stale crash locks (auto.lock) where the owning process is dead
  • Removing stranded lock directories left by hard crashes
  • Cleaning up stale parallel session status files
  • Removing orphaned completed-unit keys from completed-units.json
  • Clearing stale hook state from hook-state.json
  • Removing macOS numbered .gsd collision variants (e.g. .gsd 2, .gsd 3)
  • Removing orphaned worktrees for completed milestones
  • Deleting stale milestone branches
  • Aborting corrupt git merge/rebase state
  • Removing tracked runtime files from git index
  • Removing orphaned worktree directories that aren’t registered with git
  • Creating gsd snapshot commits for stale uncommitted changes
  • Pruning activity logs (7-day retention)
  • Pruning metrics ledger to newest 1500 entries when over 2000
  • Pruning snapshot refs to newest 5 per label when over 50
  • Regenerating STATE.md from current disk state
  • Ensuring .gitignore has required patterns
  • Recovering failed external state migrations (.gsd.migrating)
  • Sanitizing delimiter characters (em dash, en dash) from milestone titles in roadmaps
  • Updating recorded integration branch to fallback when original no longer exists
  • Re-rendering stale markdown projections when the event log is newer

Fix levels: When doctor runs automatically from auto-mode post-unit hooks, it uses a restricted fixLevel: "task" that skips two protected issue codes:

  • GLOBAL_STATE_CODESorphaned_project_state and orphaned_completed_units. Removing completed-unit keys can revert project state, treating finished tasks as incomplete. Removing orphaned project state directories deletes external state. These are only fixed by explicit manual doctor runs (/gsd doctor fix).

All other fixable codes are applied immediately even at fixLevel: "task" — including delimiter sanitization (delimiter_in_title), missing directory creation, lock cleanup, and runtime file cleanup — to keep the workspace in a clean state after each task.

Heal mode first runs all fixes, then filters the remaining issues to find actionable ones — all errors. It dispatches these to the LLM as a structured list with issue codes, unit IDs, file paths, and fixability flags. The LLM receives the full doctor report as context and can use standard tools to investigate and resolve each issue. The LLM is instructed to prefer reconstructing real artifacts from existing context over leaving placeholders.

During auto-mode, doctor runs a lightweight proactive healing layer in addition to any manual invocations:

  1. Pre-dispatch health gate — Before each unit dispatch, checks for stale crash locks, corrupt merge state, missing STATE.md, missing integration branches, stale uncommitted changes (auto-snapshot), and low disk space. Attempts auto-repair of each. Blocks dispatch if critical issues cannot be resolved.
  2. Health score tracking — After each unit, records a health snapshot (error count, warning count, fixes applied, top issues). Tracks trends across the last 50 snapshots to detect degradation. Emits level-change events when health transitions between green (no errors), yellow (1+ errors or degrading trend), and red (3+ consecutive error units).
  3. Auto-heal escalation — After 5 consecutive units with unresolved errors, if the trend is not improving, escalates to LLM-assisted heal. Escalation fires at most once per auto-mode session.

After every run, doctor appends a compact JSONL entry to .gsd/doctor-history.jsonl with timestamps, error/warning counts, issue codes, fix descriptions, and a human-readable summary. This history is used by auto-mode for trend analysis and by formatHealthSummary() in the dashboard overlay.

Doctor checks a large set of distinct issue codes grouped by domain.

CodeSeverityDescriptionFixable
invalid_preferenceswarningPreference file has malformed fields (lists that aren’t arrays, invalid skill rules)No
active_requirement_missing_ownererrorRequirement is Active but has no primary owning sliceNo
blocked_requirement_missing_reasonwarningRequirement is Blocked but Notes field is emptyNo
state_file_stalewarningSTATE.md active milestone/slice/phase doesn’t match derived stateYes
state_file_missingwarningSTATE.md doesn’t exist but milestones directory doesYes
gitignore_missing_patternswarning.gitignore lacks required GSD runtime exclusion patternsYes
activity_log_bloatwarningActivity log directory exceeds 500 files or 100MBYes
stale_crash_lockerrorauto.lock exists but the owning process is deadYes
stranded_lock_directoryerror.gsd.lock/ directory exists but no live process holds the session lock — blocks new sessionsYes
stale_parallel_sessionwarningParallel session status file exists but the owning process is deadYes
orphaned_completed_unitswarningcompleted-units.json references units whose expected artifacts no longer existYes
stale_hook_stateinfohook-state.json has residual cycle counts from a previous sessionYes
numbered_gsd_variantwarningmacOS filesystem created a numbered .gsd collision variant (e.g. .gsd 2) — can cause GSD state to appear deletedYes
failed_migrationerror.gsd.migrating found — a previous external state migration failedYes
broken_symlinkerror.gsd symlink target does not exist — external state directory was deletedNo
metrics_ledger_corruptwarningmetrics.json has invalid structure or is not valid JSONNo
metrics_ledger_bloatwarningmetrics.json has over 2000 unit entriesYes
large_planning_filewarningOne or more planning .md files exceed 100KB — causes LLM context pressureNo
orphaned_project_stateinfoProject state directories in ~/.gsd/projects/ whose git root no longer existsYes
CodeSeverityDescriptionFixable
corrupt_merge_stateerrorMERGE_HEAD, SQUASH_MSG, rebase-apply, or rebase-merge state foundYes
tracked_runtime_fileswarningFiles in .gsd/activity/ or .gsd/runtime/ are tracked by gitYes
legacy_slice_branchesinfoPer-slice gsd/*/* branches found (legacy pattern, no longer used)Yes
stale_uncommitted_changeswarningUncommitted changes with no commit above the configured threshold — auto-snapshots tracked filesYes
orphaned_auto_worktreewarningWorktree exists for a completed milestoneYes
stale_milestone_branchinfomilestone/* branch exists for a completed milestoneYes
integration_branch_missingwarning/errorActive milestone’s recorded integration branch no longer exists in git (warning if fallback available, error if none)Yes (if fallback)
worktree_directory_orphanedwarningWorktree directory exists on disk but is not registered with gitYes
worktree_branch_mergedinfoGSD-managed worktree’s branch is fully merged into main — safe to removeYes
worktree_stalewarningGSD-managed worktree has had no commits in 7+ daysNo
worktree_dirtywarningStale worktree has uncommitted changesNo
worktree_unpushedwarningStale worktree has unpushed commitsNo
snapshot_ref_bloatwarningOver 50 snapshot refs under refs/gsd/snapshots/Yes
CodeSeverityDescriptionFixable
env_node_versionwarningNode.js version doesn’t meet the project’s engines.node requirementNo
env_dependencieserror/warningnode_modules missing, or lockfile is newer than node_modulesNo
env_env_filewarning.env.example exists but no .env or .env.local foundNo
env_port_conflictwarningA dev server port from package.json scripts is already in useNo
env_disk_spaceerror/warningLess than 500MB free disk space (error) or less than 2GB (warning)No
env_dockerwarningProject has Docker files but Docker is not installed or daemon is not runningNo
env_package_managerwarningProject’s packageManager field requires a tool that isn’t installedNo
env_typescriptwarningTypeScript is a dependency but tsc is not availableNo
env_pythonwarningProject has Python config but python is not installedNo
env_cargowarningProject has Cargo.toml but cargo is not installedNo
env_gowarningProject has go.mod but go is not installedNo
env_git_remotewarningGit remote origin is unreachableNo
env_builderrornpm run build exits non-zero (opt-in via --build)No
env_testwarningnpm test exits non-zero (opt-in via --test)No
CodeSeverityDescriptionFixable
provider_key_missingwarningA required LLM provider API key is not configured (based on model preferences)No
provider_key_backedoffwarningAll credentials for a required provider are currently backed off (rate limited)No

These checks only run when the GSD SQLite database is available (gsd.db).

CodeSeverityDescriptionFixable
db_unavailablewarningDatabase file exists on disk but cannot be loaded — GSD falls back to filesystem state derivationNo
db_orphaned_taskerrorTask in the database references a slice that no longer existsNo
db_orphaned_sliceerrorSlice in the database references a milestone that no longer existsNo
db_done_task_no_summarywarningTask is marked done in the database but has no summary storedNo
db_duplicate_iderrorDuplicate milestone, slice, or task ID detected in the databaseNo
CodeSeverityDescriptionFixable
delimiter_in_titlewarningMilestone title contains em dash, en dash, or slash — breaks state parsingYes (em/en dash only)
circular_slice_dependencyerrorCircular dependency detected between slices in a milestone’s roadmapNo
orphaned_slice_directorywarningA directory in slices/ is not referenced in the roadmapNo
all_slices_done_missing_milestone_validationinfoAll slices complete but VALIDATION.md is missing — milestone is in validating-milestone phaseNo
all_slices_done_missing_milestone_summarywarningAll slices complete but SUMMARY.md is missing — milestone stuck in completing-milestone phaseNo
CodeSeverityDescriptionFixable
delimiter_in_titlewarningSlice title contains em dash, en dash, or slash — breaks state parsingNo
unresolvable_dependencywarningSlice depends on an ID that is not a known slice in this roadmap — permanently blocks the sliceNo
missing_slice_direrror/warningSlice listed in roadmap has no directory (error if incomplete, warning if complete)Yes
missing_slice_planwarningSlice directory exists but has no plan file (only reported for incomplete slices)No
missing_tasks_direrror/warningSlice has a plan but no tasks/ directory (error if incomplete, warning if complete)Yes
duplicate_task_iderrorA task ID appears more than once in a slice plan — causes dispatch failuresNo
task_file_not_in_planinfoA task summary file on disk references a task ID not found in the slice planNo
blocker_discovered_no_replanwarningA task summary has blocker_discovered: true but no REPLAN.md exists for the sliceNo
stale_replan_fileinfoA REPLAN.md exists for a slice where all tasks are done — file may be staleNo
CodeSeverityDescriptionFixable
task_done_must_haves_not_verifiedwarningTask done but must-haves from task plan not mentioned in summaryNo
future_timestampwarningTask summary has a completed_at more than 24h in the futureNo
FilePurpose
.gsd/STATE.mdCurrent state for scope selection and staleness check
.gsd/REQUIREMENTS.mdRequirement audit (active/blocked status)
.gsd/preferences.mdPreference shape validation
.gsd/milestones/*/Full milestone registry scan
.gsd/auto.lockCrash lock detection
.gsd/.gsd.lock/Stranded lock directory detection
.gsd/parallel/*.status.jsonParallel session staleness detection
.gsd/completed-units.jsonOrphaned key detection
.gsd/hook-state.jsonStale hook state detection
.gsd/activity/Activity log size check
.gsd/metrics.jsonMetrics ledger integrity and bloat check
.gsd/doctor-history.jsonlDoctor run history (read for trend display)
.gsd/gsd.dbDB constraint and projection drift checks (engine domain)
.gsd/event-log.jsonlProjection drift detection (compared against roadmap mtime)
.gitignorePattern completeness check
package.jsonNode version, deps, tools, port checks
auth.jsonProvider API key presence
FilePurpose
.gsd/milestones/*/M*-ROADMAP.mdSanitized delimiter characters in milestone titles; re-rendered projections
.gsd/milestones/*/slices/*/tasks/Created when tasks/ directory is missing
.gsd/milestones/*/slices/*/Created when slice directory is missing
.gsd/STATE.mdRegenerated from disk state
.gsd/auto.lockRemoved when stale
.gsd/parallel/*.status.jsonRemoved when session is stale
.gsd/completed-units.jsonOrphaned keys removed
.gsd/hook-state.jsonCleared when auto-mode is not running
.gsd/activity/Pruned to 7-day retention
.gsd/metrics.jsonPruned to newest 1500 entries
.gsd/doctor-history.jsonlDoctor run appended after every scan
.gitignoreMissing GSD runtime patterns added

Running doctor on a project with completion drift:

> /gsd doctor
GSD doctor report.
Scope: M002/S03
Issues: 3 total · 1 error(s) · 2 warning(s) · 2 fixable
Top issue types:
- task_done_must_haves_not_verified: 1
- worktree_stale: 1
- stale_crash_lock: 1
Priority issues:
- [ERROR] M002/S03: stale auto.lock — PID 48221 is dead
- [WARN] M002/S03/T02: Task T02 has 2 must-haves but summary addresses only 1
- [WARN] M002/S03: stale worktree has had no commits in 8 days

Auto-fixing:

> /gsd doctor fix
GSD doctor report.
Scope: M002/S03
Issues: 2 total · 1 error(s) · 1 warning(s) · 1 fixable
Fixes applied:
- cleared stale auto.lock

Previewing fixes without applying them:

> /gsd doctor fix --dry-run
GSD doctor report.
Scope: M002/S03
Fixes applied:
- [dry-run] would fix: sanitized delimiter characters in M002 title
- [dry-run] would fix: created .gsd/milestones/M002/slices/S03/tasks/

Heal mode dispatching to LLM:

> /gsd doctor heal
GSD doctor heal prep.
Scope: M002/S03
Issues: 1 total · 1 error(s) · 0 warning(s) · 0 fixable
Doctor heal dispatched 1 issue(s) to the LLM.
● Investigating: db_orphaned_task for M002/S03/T04...
Checking database state via gsd_milestone_status...

Full audit across all scopes:

> /gsd doctor audit
GSD doctor audit.
Scope: (all)
Issues: 12 total · 2 error(s) · 7 warning(s) · 3 info(s) · 8 fixable
...

Running environment checks including a build:

> /gsd doctor --build
GSD doctor report.
Scope: M003/S01
Issues: 2 total · 1 error(s) · 1 warning(s) · 0 fixable
- [ERROR] environment: Build failed — npm run build exited non-zero
- [WARN] environment: TypeScript is a dependency but tsc is not available
  • /gsd forensics — Deep post-mortem investigation of auto-mode failures
  • /gsd status — View current project state
  • /gsd cleanup — Clean up stale branches and worktrees
  • /gsd prefs — Configure preferences (doctor validates these)
  • /gsd keys — Manage provider API keys (doctor checks these)