Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ src/
cli-renderer.ts
cli.ts
config.ts
doctor.ts
domain.ts
drift.ts
errors.ts
Expand Down Expand Up @@ -104,12 +105,13 @@ Two-way sync between the local backlog and GitHub Issues:
## Testing

<!-- generate:test-summary -->
16 test files in `tests/` using Vitest:
17 test files in `tests/` using Vitest:

- `api.test.ts`
- `cli.test.ts`
- `config.test.ts`
- `docs.test.ts`
- `doctor.test.ts`
- `domain.test.ts`
- `drift.test.ts`
- `exec.test.ts`
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ If you can answer these questions by reading the repo, you do not need
a standup:

- What is actively open in this workspace? -> `method status`
- Is this workspace structurally healthy? -> `method doctor`
- What is under review? -> `method review-state`
- What is committed? -> each design doc names its sponsors and hill
- What is next? -> `ls docs/method/backlog/asap/`
Expand Down Expand Up @@ -480,14 +481,14 @@ Run during development:
npm run method -- status
```

Core commands: `method init`, `method inbox`, `method pull`,
`method close`, `method drift`, `method status`, `method mcp`,
`method sync github`, `method sync ship`.
Core commands: `method init`, `method doctor`, `method inbox`,
`method pull`, `method close`, `method drift`, `method status`,
`method mcp`, `method sync github`, `method sync ship`.

See `docs/CLI.md` for the full command reference with flags and
examples. See `docs/MCP.md` for the MCP tool reference
(`method_status`, `method_inbox`, `method_pull`, `method_drift`,
`method_close`, `method_sync_ship`, `method_sync_github`,
(`method_doctor`, `method_status`, `method_inbox`, `method_pull`,
`method_drift`, `method_close`, `method_sync_ship`, `method_sync_github`,
`method_capture_witness`).

Repo-local CI currently uses GitHub Actions as a host adapter through
Expand Down
4 changes: 4 additions & 0 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ operations. Run `method help` for a quick summary or

Scaffold a METHOD workspace in the given directory.

### `method doctor [--json]`

Inspect METHOD workspace health without mutating it.

### `method inbox <idea> [--legend CODE] [--title TITLE]`

Capture a raw idea in docs/method/backlog/inbox/.
Expand Down
4 changes: 4 additions & 0 deletions docs/MCP.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ workspace-agnostic — a single instance can serve multiple projects.
## Tools

<!-- generate:mcp-tools -->
### `method_doctor`

Inspect METHOD workspace health and report concrete problems with suggested fixes, even when the workspace is partially broken.

### `method_review_state`

Get PR review / merge-readiness state for the current branch or an explicit PR. `pr` and `currentBranch` are mutually exclusive; when `pr` is omitted, current-branch resolution is the default behavior.
Expand Down
84 changes: 84 additions & 0 deletions docs/design/0038-doctor-command/doctor-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: "Doctor Command"
legend: "PROCESS"
cycle: "0038-doctor-command"
source_backlog: "docs/method/backlog/inbox/PROCESS_doctor-command.md"
---

# Doctor Command

Source backlog item: `docs/method/backlog/inbox/PROCESS_doctor-command.md`
Legend: PROCESS

## Sponsors

- Human: Backlog operator
- Agent: Implementation agent

## Hill

Land a bounded `doctor` surface that inspects METHOD workspace health
without requiring a fully healthy `Workspace` instance, reports concrete
problems with suggested fixes, and is callable from both the CLI and
MCP.

## Playback Questions

### Human

- [ ] Does `method doctor --json` return a bounded workspace health
report without throwing on malformed `.method.json`?
- [ ] Does `method doctor` surface missing required paths and malformed
frontmatter with fix suggestions?

### Agent

- [ ] Does `method_doctor` return the same doctor report contract under
`structuredContent.result`?
- [ ] Does `runDoctor()` report orphaned backlog items and git-hook
diagnostics without requiring a healthy `Workspace` instance?

## Accessibility and Assistive Reading

- Linear truth / reduced-complexity posture: the text output must be a
short check list plus issue list, so operators can read it top to
bottom without reconstructing hidden state.
- Non-visual or alternate-reading expectations: each issue must carry a
direct fix suggestion instead of relying on color or layout cues.

## Localization and Directionality

- Locale / wording / formatting assumptions: command output remains
ASCII-first and stable for script consumption; file paths stay repo-
relative where possible.
- Logical direction / layout assumptions: no UI layout work is in scope;
this cycle is about deterministic text output and structured results.

## Agent Inspectability and Explainability

- What must be explicit and deterministic for agents: the check names,
issue severities, and suggested fixes must be stable enough for MCP
callers to branch on.
- What must be attributable, evidenced, or governed: each reported issue
must identify the path or subsystem being diagnosed so agents can act
without guessing.

## Non-goals

- [ ] Building the broader `method validate` command family in this
cycle.
- [ ] Auto-repairing broken workspaces; `doctor` diagnoses and suggests,
but does not mutate.
- [ ] Enforcing repo-level legend coverage rules beyond bounded orphaned
backlog detection.

## Backlog Context

Add a method doctor command that checks workspace health: required
directories and core files exist, METHOD packet frontmatter is present
and parseable, `.method.json` parses cleanly, git hooks are configured,
and backlog cards have not escaped the recognized lane structure.

This should stay bounded. It is not the future `validate` family, and
it should not require a healthy `Workspace` object before it can tell
you why the workspace is unhealthy.
9 changes: 0 additions & 9 deletions docs/method/backlog/inbox/PROCESS_doctor-command.md

This file was deleted.

52 changes: 52 additions & 0 deletions docs/method/retro/0038-doctor-command/doctor-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: "Doctor Command"
cycle: "0038-doctor-command"
design_doc: "docs/design/0038-doctor-command/doctor-command.md"
outcome: hill-met
drift_check: yes
---

# Doctor Command Retro

## Summary

This cycle landed a native `doctor` surface for METHOD that works even
when the workspace cannot be loaded through the normal `Workspace`
constructor path. The new engine inspects configuration parsing,
required repo structure, packet frontmatter, git hook setup, and
backlog lane shape, then reports bounded issues with direct fix
suggestions for both humans and agents.

The late cleanup in this cycle mattered. The first pass treated missing
empty backlog lane directories as hard structural failures and treated
an unset `core.hooksPath` as "git metadata unavailable." The final
behavior is stricter in the right places instead: missing empty lane
directories in a clean clone are acceptable, while a real repo with no
configured hooks reports a truthful warning instead of a false error.

## Playback Witness

- [Verification Witness](./witness/verification.md)
- Close-time verification captured the full `npm test` pass and a direct
drift check for `0038-doctor-command`.

## Drift

- None recorded after aligning the final agent playback question string
to its exact test description.

## New Debt

- None recorded.

## Cool Ideas

- None recorded.

## Backlog Maintenance

- Backlog maintenance deferred; the cycle hill was about bounded
diagnosis, not repo-wide triage or queue grooming.
- [ ] Inbox processed
- [x] Priorities reviewed
- [ ] Dead work buried or merged
41 changes: 41 additions & 0 deletions docs/method/retro/0038-doctor-command/witness/verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: "Verification Witness for Cycle 38"
---

# Verification Witness for Cycle 38

This witness proves that `Doctor Command` now carries the required
behavior and adheres to the repo invariants.

## Test Results

```text

> @flyingrobots/method@1.0.0 test
> vitest run --config vitest.config.ts


RUN v4.1.2 .


Test Files 17 passed (17)
Tests 189 passed (189)
Start at 18:48:01
Duration 1.40s (transform 1.35s, setup 0ms, import 4.66s, tests 2.79s, environment 1ms)


```

## Drift Results

```text
No playback-question drift found.
Scanned 1 active cycle, 4 playback questions, 202 test descriptions.
Search basis: exact normalized match in tests/**/*.test.* and tests/**/*.spec.* descriptions.

```

## Manual Verification

- [x] `method doctor` reports this repo as `warn`, not `error`, with only the truthful `git-hooks-not-configured` warning.
- [x] Automated capture completed successfully.
27 changes: 26 additions & 1 deletion src/cli-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { MethodError } from './errors.js';
export type ParsedCommand =
| { command: 'help'; topic?: string }
| { command: 'init'; path: string }
| { command: 'doctor'; json?: boolean }
| { command: 'inbox'; idea: string; legend?: string; title?: string }
| { command: 'pull'; item: string }
| { command: 'close'; cycle?: string; driftCheck?: 'yes' | 'no'; outcome: Outcome }
Expand Down Expand Up @@ -34,6 +35,8 @@ export function parseCliArgs(argv: readonly string[]): ParsedCommand {
switch (command) {
case 'init':
return parseInitArgs(rest);
case 'doctor':
return parseDoctorArgs(rest);
case 'inbox':
return parseInboxArgs(rest);
case 'pull':
Expand Down Expand Up @@ -80,12 +83,19 @@ export function parseCliArgs(argv: readonly string[]): ParsedCommand {
}
}

export const CLI_TOPICS = ['init', 'inbox', 'pull', 'close', 'status', 'drift', 'review-state', 'mcp', 'sync'] as const;
export const CLI_TOPICS = ['init', 'doctor', 'inbox', 'pull', 'close', 'status', 'drift', 'review-state', 'mcp', 'sync'] as const;

export function usage(topic?: string): string {
if (topic === 'init') {
return 'Usage: method init [path]\n\nScaffold a METHOD workspace in the given directory.';
}
if (topic === 'doctor') {
return [
'Usage: method doctor [--json]',
'',
'Inspect METHOD workspace health without mutating it.',
].join('\n');
}
if (topic === 'inbox') {
return 'Usage: method inbox <idea> [--legend CODE] [--title TITLE]\n\nCapture a raw idea in docs/method/backlog/inbox/.';
}
Expand Down Expand Up @@ -137,6 +147,7 @@ export function usage(topic?: string): string {
'',
'Commands:',
' init [path] Scaffold a METHOD workspace.',
' doctor [--json] Inspect workspace health and suggest fixes.',
' inbox <idea> Capture a raw idea in inbox/.',
' pull <item> Promote a backlog item into a cycle.',
' close [cycle] Write a retro for an active cycle.',
Expand All @@ -158,6 +169,20 @@ function parseInitArgs(args: readonly string[]): ParsedCommand {
return { command: 'init', path: args[0] ?? '.' };
}

function parseDoctorArgs(args: readonly string[]): ParsedCommand {
let json = false;

for (const value of args) {
if (value === '--json') {
json = true;
continue;
}
throw new MethodError(`Unknown option: ${value}\n\n${usage('doctor')}`);
}

return { command: 'doctor', json };
}

function parseInboxArgs(args: readonly string[]): ParsedCommand {
let legend: string | undefined;
let title: string | undefined;
Expand Down
Loading
Loading