Skip to content

Add doctor command - #22

Merged
flyingrobots merged 5 commits into
mainfrom
cycles/0038-doctor-command
Apr 10, 2026
Merged

flyingrobots merged 5 commits into
mainfrom
cycles/0038-doctor-command

Conversation

@flyingrobots

Copy link
Copy Markdown
Owner

Summary

  • add a bounded native doctor surface for METHOD in both CLI and MCP
  • report config, structure, frontmatter, git-hook, and backlog issues without requiring a healthy Workspace instance
  • close cycle 0038-doctor-command with retro and witness evidence

Notes

  • method doctor now treats missing empty backlog lane directories as acceptable instead of structural errors
  • unset core.hooksPath now falls back to default git hook inspection instead of misreporting git metadata as unavailable
  • on this repo, method doctor currently reports warn with only git-hooks-not-configured

Validation

  • npm test
  • npm run build
  • git diff --check
  • ./node_modules/.bin/tsx src/cli.ts doctor

@coderabbitai

coderabbitai Bot commented Apr 10, 2026 •

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Added method doctor command to inspect workspace health and report issues with suggested fixes.
    • Added --json flag for machine-readable diagnostic output.
    • Exposed method_doctor as an MCP tool for remote health queries.
    • Command can run even when the workspace cannot be fully loaded; returns structured status/counts.
  • Documentation

    • Added CLI, MCP, design, retro, and verification docs describing the doctor command and expected output.
  • Tests

    • Added unit and CLI tests covering doctor behavior and MCP integration.

Walkthrough

A new doctor diagnostic command and engine were added to inspect METHOD workspace health (config, structure, frontmatter, git-hooks, backlog). CLI and MCP surfaces were extended to invoke it, Zod types were added, tests and design/retro documentation were included.

Changes

Cohort / File(s) Summary
Doctor Engine & Types
src/doctor.ts, src/domain.ts
New doctor engine implementing checks (config, structure, frontmatter, git-hooks, backlog), aggregation into DoctorReport, text renderer, and corresponding Zod schemas / TS types for the doctor domain.
CLI Argument & Command Wiring
src/cli-args.ts, src/cli.ts
Added { command: 'doctor'; json?: boolean }, parseDoctorArgs, added 'doctor' topic, and wired CLI to call runDoctor and render JSON or text; exit code set to 1 on error status.
MCP Integration
src/mcp.ts
Registered method_doctor tool (input schema requires workspace) and added an early-return handler that runs the doctor and returns text + structured report.
Tests
tests/doctor.test.ts, tests/cli.test.ts, tests/mcp.test.ts
New and extended tests covering malformed .method.json, missing paths, frontmatter parsing (including CRLF EOF), orphaned backlog items, git-hook diagnostics, CLI flag errors, and MCP/CLI parity.
Documentation & Guides
README.md, docs/CLI.md, docs/MCP.md, docs/design/0038-doctor-command/doctor-command.md
Added doctor command to README/CLI/MCP docs and a detailed design doc specifying scoped behavior, output contract, playback questions, non-goals, and accessibility/localization constraints.
Retro & Verification Records
docs/method/retro/0038-doctor-command/..., docs/method/backlog/inbox/PROCESS_doctor-command.md
Added retro and verification witness (test/drift/manual checks), removed older inbox doc; records cycle outcome and verification details (17 test files, 189 passing tests).

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Agent
    participant CLI as CLI Handler
    participant Doctor as Doctor Engine
    participant Config as Config Parser
    participant FS as File System
    participant Git as Git Subprocess

    User->>CLI: method doctor [--json]
    CLI->>Doctor: runDoctor(root)

    Doctor->>Config: Read & parse .method.json
    alt config parse ok
        Config-->>Doctor: config + schema ok
    else parse failed
        Config-->>Doctor: emit config-parse-failed
    end

    Doctor->>FS: Verify required repo structure
    FS-->>Doctor: missing-directory / type mismatches

    Doctor->>FS: Traverse packet/backlog files, validate YAML frontmatter
    FS-->>Doctor: missing/unterminated/invalid frontmatter

    Doctor->>Git: Inspect core.hooksPath & hook files
    Git-->>Doctor: git-hooks-not-configured / missing hooks / unavailable

    Doctor->>FS: Scan backlog lanes for orphaned items
    FS-->>Doctor: orphaned-backlog-item warnings

    Doctor->>Doctor: Aggregate checks & issues → DoctorReport

    alt --json flag
        Doctor-->>CLI: DoctorReport (JSON)
        CLI-->>User: JSON.stringify(report)
    else
        Doctor-->>CLI: DoctorReport
        CLI->>Doctor: renderDoctorText(report)
        Doctor-->>CLI: Formatted text
        CLI-->>User: Human-readable output
    end

    CLI-->>User: exit 1 if any error, else 0 (ok/warn)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🩺 A ruthless doctor reads the repo's chart,
finds broken JSON and frontmatter torn apart,
points to missing lanes and hooks that failed to bind,
prescribes a checklist, terse and clearly lined,
fixes in text so humans don't lose their mind.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add doctor command' directly and clearly summarizes the main change: introducing a new 'doctor' command to the METHOD tool.
Description check ✅ Passed The description is directly related to the changeset, detailing the doctor command's scope, behavior, and validation steps performed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cycles/0038-doctor-command

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 094c0464bc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/doctor.ts
Comment thread src/doctor.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/cli-args.ts`:
- Line 180: The unknown-option throw in src/cli-args.ts should include doctor
usage guidance: update the throw at the Unknown option branch so the MethodError
includes usage('doctor') (either by passing usage('doctor') as additional
context/argument to MethodError or appending its output to the error message)
where the current throw new MethodError(`Unknown option: ${value}`) occurs;
reference the MethodError constructor and the usage function to add the doctor
usage text.

In `@src/doctor.ts`:
- Around line 82-131: The JSON parse-failure branch in inspectConfig currently
returns DEFAULT_PATHS which causes downstream bogus missing-file/directory
issues for repos with custom paths; change the catch block to return a
non-default empty/neutral paths value (e.g., an empty array or null-equivalent
your code expects) instead of DEFAULT_PATHS so you don't invent structure
errors, leaving the single createIssue('config-parse-failed', ...) intact;
update references to DEFAULT_PATHS in the catch return and ensure the rest of
the code that consumes InspectConfig (callers of inspectConfig) correctly
handles the empty/neutral paths value to avoid further false positives.
- Around line 203-228: The frontmatter detection currently only accepts
LF-delimited blocks and fails on CRLF or EOF-terminated closing delimiters;
update the logic that checks raw, end, and yamlBlock to (1) accept either
'---\n' or '---\r\n' as the opening delimiter (compute startOffset accordingly
instead of hardcoding 4), and (2) search for a closing delimiter using a
tolerant pattern like /\r?\n---(?:\r?\n|$)/ (or equivalent string search) so it
matches CRLF or LF and also a terminating '---' at EOF; then slice the YAML with
the computed startOffset and the found closing index to populate yamlBlock.
Reference: raw, end, yamlBlock, createIssue.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6586aec5-995f-47ee-bdd6-44c48ccfc676

📥 Commits

Reviewing files that changed from the base of the PR and between fcc50a4 and 094c046.

📒 Files selected for processing (16)
  • ARCHITECTURE.md
  • README.md
  • docs/CLI.md
  • docs/MCP.md
  • docs/design/0038-doctor-command/doctor-command.md
  • docs/method/backlog/inbox/PROCESS_doctor-command.md
  • docs/method/retro/0038-doctor-command/doctor-command.md
  • docs/method/retro/0038-doctor-command/witness/verification.md
  • src/cli-args.ts
  • src/cli.ts
  • src/doctor.ts
  • src/domain.ts
  • src/mcp.ts
  • tests/cli.test.ts
  • tests/doctor.test.ts
  • tests/mcp.test.ts
💤 Files with no reviewable changes (1)
  • docs/method/backlog/inbox/PROCESS_doctor-command.md

Comment thread src/cli-args.ts Outdated
Comment thread src/doctor.ts
Comment thread src/doctor.ts Outdated
@flyingrobots

Copy link
Copy Markdown
Owner Author

Resolved the current PR #22 review round in two commits:

Issue Commit
Require real directories for required directory paths 6143522
Require real files for required file paths 6143522
Skip path-based checks when malformed JSON makes repo paths unknowable 6143522
Accept CRLF and EOF-terminated frontmatter blocks 6143522
Show method doctor usage for invalid doctor flags fe88ce9

Validation after the fixes:

  • npm test
  • npm run build
  • git diff --check

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/doctor.ts`:
- Around line 448-465: The helper hasInstalledHook currently assumes the
provided path is a directory and will throw when given a regular file; update
hasInstalledHook(directory: string) to first return false (or throw a clear
error) if !isDirectoryPath(directory) so it does not call readdirSync on
non-directories, and then keep the existing file-check logic
(readdirSync/resolve/statSync/mode test) for actual directories; also update the
caller inspectGitHooks() to detect isDirectoryPath(directory) === false and emit
a specific "hooks path is not a directory" diagnostic instead of letting the
broad catch map the error to git-hooks-unavailable, ensuring you reference the
hasInstalledHook and inspectGitHooks functions when making these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a106e8b5-b3ec-4b5e-b615-bb2719f9aa57

📥 Commits

Reviewing files that changed from the base of the PR and between 094c046 and fe88ce9.

📒 Files selected for processing (4)
  • src/cli-args.ts
  • src/doctor.ts
  • tests/cli.test.ts
  • tests/doctor.test.ts

Comment thread src/doctor.ts
@flyingrobots

Copy link
Copy Markdown
Owner Author

Additional fix for the final hook-path review thread:

Issue Commit
Diagnose configured/default git hooks paths that exist as files 91916b6

Validation after the fix:

  • npm test
  • npm run build
  • git diff --check

@flyingrobots
flyingrobots merged commit d884f3c into main Apr 10, 2026
2 of 3 checks passed
@flyingrobots
flyingrobots deleted the cycles/0038-doctor-command branch April 10, 2026 05:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/doctor.ts`:
- Around line 24-39: runDoctor currently builds issues only for checks that ran
(structure/frontmatter/backlog) but still calls summarizeCheck for every id in
DOCTOR_CHECKS, producing "ok" for checks that were skipped when
configInspection.paths === null; update runDoctor to detect which checks
actually ran (use configInspection.paths to determine that) and call
summarizeCheck with either a filtered list of only-run check ids or pass an
explicit flag/metadata to summarizeCheck indicating a skipped check so it can
return a skipped status/message; reference runDoctor, DOCTOR_CHECKS,
summarizeCheck, and configInspection.paths and ensure the final checks array
reflects skipped checks explicitly instead of reporting them as healthy.
- Around line 113-120: The current logic feeds {} into PathsSchema.safeParse
when the top-level JSON is non-object (arrays/primitives), causing DEFAULT_PATHS
to be applied and spawning bogus findings; change the branch so you only call
PathsSchema.safeParse when rawConfig is a plain object and actually has a
'paths' property (i.e., typeof rawConfig === 'object' && rawConfig !== null &&
!Array.isArray(rawConfig) && 'paths' in rawConfig), otherwise set paths: null;
keep using parsedPaths.success ? parsedPaths.data : null when you do parse so
that fallback DEFAULT_PATHS is only produced from a real config object (refer to
rawConfig, parsedPaths, and PathsSchema to locate the code).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1c68a212-c036-41fe-a6cc-bc5140c75228

📥 Commits

Reviewing files that changed from the base of the PR and between fe88ce9 and 91916b6.

📒 Files selected for processing (2)
  • src/doctor.ts
  • tests/doctor.test.ts

Comment thread src/doctor.ts
Comment on lines +24 to +39
export function runDoctor(root: string): DoctorReport {
const configInspection = inspectConfig(root);
const structureIssues = configInspection.paths === null ? [] : inspectStructure(root, configInspection.paths);
const frontmatterIssues = configInspection.paths === null ? [] : inspectFrontmatter(root, configInspection.paths);
const gitHookIssues = inspectGitHooks(root);
const backlogIssues = configInspection.paths === null ? [] : inspectBacklog(root, configInspection.paths);

const issues = [
...configInspection.issues,
...structureIssues,
...frontmatterIssues,
...gitHookIssues,
...backlogIssues,
];

const checks = DOCTOR_CHECKS.map((id) => summarizeCheck(id, issues));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't report skipped path checks as healthy.

When paths is null, Lines 26-29 correctly skip structure, frontmatter, and backlog, but Line 39 still summarizes those checks as ok / No issues found. That is a false green report for checks that never ran. Surface them as explicitly skipped, or at least with a non-ok status/message, instead of folding them into the healthy path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/doctor.ts` around lines 24 - 39, runDoctor currently builds issues only
for checks that ran (structure/frontmatter/backlog) but still calls
summarizeCheck for every id in DOCTOR_CHECKS, producing "ok" for checks that
were skipped when configInspection.paths === null; update runDoctor to detect
which checks actually ran (use configInspection.paths to determine that) and
call summarizeCheck with either a filtered list of only-run check ids or pass an
explicit flag/metadata to summarizeCheck indicating a skipped check so it can
return a skipped status/message; reference runDoctor, DOCTOR_CHECKS,
summarizeCheck, and configInspection.paths and ensure the final checks array
reflects skipped checks explicitly instead of reporting them as healthy.

Comment thread src/doctor.ts
Comment on lines +113 to +120
const parsedPaths = PathsSchema.safeParse(
rawConfig !== null && typeof rawConfig === 'object'
? ('paths' in rawConfig ? (rawConfig as { paths?: unknown }).paths : {})
: {},
);

return {
paths: parsedPaths.success ? parsedPaths.data : null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Non-object .method.json still invents default-path findings.

If the file parses as JSON but the top level is not an object ([], "x", 42), this branch feeds {} into PathsSchema.safeParse() and recovers DEFAULT_PATHS. On a repo that actually uses custom paths, that recreates the bogus missing-directory / missing-file noise you already avoided for JSON parse failures. Only recover fallback paths from a real config object; otherwise keep paths: null and skip the path-based checks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/doctor.ts` around lines 113 - 120, The current logic feeds {} into
PathsSchema.safeParse when the top-level JSON is non-object (arrays/primitives),
causing DEFAULT_PATHS to be applied and spawning bogus findings; change the
branch so you only call PathsSchema.safeParse when rawConfig is a plain object
and actually has a 'paths' property (i.e., typeof rawConfig === 'object' &&
rawConfig !== null && !Array.isArray(rawConfig) && 'paths' in rawConfig),
otherwise set paths: null; keep using parsedPaths.success ? parsedPaths.data :
null when you do parse so that fallback DEFAULT_PATHS is only produced from a
real config object (refer to rawConfig, parsedPaths, and PathsSchema to locate
the code).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant