Skip to content

/sdlc:milestone-review

Generated from solutions/ontological/skills/milestone-review/SKILL.md.

Given a milestone id (M-NNNN) or milestone file, assess its real status against its declared frontmatter status: walk its member tasks and related items to derive a grounded status and the concrete gaps / remaining work, then scan unclosed tasks and backlog items for related-but-unlinked work — bugs or gaps reported elsewhere. Reports read-only by default; corrects frontmatter drift on —fix or after asking. Use when asked to review or audit a milestone, check whether a milestone’s status is accurate, or find what’s left before closing it.

  • Read
  • Edit
  • Bash
  • Glob
  • Grep
  • Agent
  • AskUserQuestion

Usage:

  • /sdlc:milestone-review <M-NNNN | path> — assess one milestone; report, then ask whether to apply any drift corrections.
  • /sdlc:milestone-review <...> --fix — apply the safe drift corrections without asking.
  • /sdlc:milestone-review <...> --read-only — report only; never mutate and never prompt.

References:

  • ${CLAUDE_PLUGIN_ROOT}entities/milestone/definition.md — status enum, membership derivation, and the frontmatter tasks: ↔ body ## Deliverables sync rule. Read once per project — it governs every skill that touches milestone files.

Project context (don’t re-derive every run):

  • Milestones live in docs/planning/milestones/ as M<NNNN>-<slug>.md. Status enum: open/{draft,planned,active}, closed/{done,partial,superseded,abandoned}.

  • Member tasks are the frontmatter tasks: list of [[T-NNNN]] wikilinks; the body ## Deliverables section mirrors them. Tasks carry no milestone back-link — membership is one-directional, so real membership is derived by reading each member plus scanning for unlinked relatives.

  • Tasks live in docs/planning/tasks/; “unfinished” = a status that does NOT start with closed/. Backlog lives in docs/planning/backlog/ as B<NNNN> files — freeform ideas that may point at a milestone via related: or body text.

  • Validate after any frontmatter edit; gate on the exit code, don’t pipe (see Notes):

    ${CLAUDE_PLUGIN_ROOT}cli/sdlc entities validate <path>

If the argument is a path, use it. If it is an id, glob docs/planning/milestones/M<NNNN>*.md. On no match or an ambiguous match, print ERROR reason="..." and stop. Read the frontmatter (status, tasks, related, version, completion_note) and the body (Goal, Success criteria, Deliverables).

For each [[T-NNNN]] in tasks:, read the target task’s frontmatter and classify its status: done (closed/done), other-closed (any other closed/*), in-progress (in-progress[/blocked]), ready (open/ready), planning (planning/*), or missing (no file / broken wikilink). Note blocked tasks. Trust the member statuses here — deep “did this actually ship” verification is /sdlc:task-review’s job; only flag a done task that the body plainly contradicts.

Map member reality to the milestone status the evidence supports:

  • none started or all planning/*, body still placeholder → open/draft.
  • committed, tasks queued, none started → open/planned.
  • ≥1 member in-progress, OR mixed (some closed, some open) → open/active.
  • all members closed/done AND success criteria met → closed/done candidate (needs version + completion_note; recommend, never auto-flip).
  • some members closed non-done with the rest done → closed/partial candidate.

Compare grounded vs declared and set a drift verdict. Declared closed/* while ≥1 member is still open is drift (premature close) → recommend reopening to open/active.

Grep docs/planning/tasks/ and docs/planning/backlog/ for the milestone id and its slug keywords. Read the frontmatter of any hit not already in tasks:, and surface two buckets — this is where bugs or gaps reported elsewhere show up:

  • Unclosed tasks that reference or clearly belong to the milestone but aren’t members.
  • Backlog items that reference the milestone or its theme.

Rank by relatedness, keep it to the top handful, and state plainly if you dropped weaker matches.

Print inline, in this order:

  • Header: <id> — <title>, then Declared: <status> · Grounded: <status> · Drift: <yes|no>.
  • Member roster: one line per member task with its classified status; call out missing/blocked.
  • Gaps / what remains: unmet success criteria plus every open member task.
  • Related-but-unlinked candidates: the tasks and backlog items from step 4, each with a one-line why-related.

If --read-only, skip to step 7 — no mutation, no prompt. Otherwise assemble the candidate corrections:

  • status → the grounded open/* value. Do NOT auto-close: closing needs version + completion_note, so only propose it and ask the user for those values.
  • last_reviewed → today (UTC); optional relevance_note → one line on what shifted.
  • Append high-confidence unlinked member tasks to BOTH frontmatter tasks: and the body ## Deliverables list (keep the two in sync). Backlog items are NOT tasks — never add them to tasks:; recommend /sdlc:backlog-triage to promote them.

If --fix, apply the safe corrections directly. Otherwise, after the report, send one AskUserQuestion (multiSelect) listing the proposed corrections and apply only the selected ones via Edit.

If anything was edited, run entities validate <path> and gate on its exit code (no pipe); fix and re-run on failure. Then emit the terminal marker:

  • MILESTONE-REVIEW-OK: <id> declared=<s> grounded=<s> drift=<yes|no> — no edits.
  • MILESTONE-REVIEW-FIXED: <id> status=<old>→<new> tasks+=<n> — edits applied.
  • ERROR reason="..." — unrecoverable failure.
  • Read-only by default: mutation happens only under --fix or an explicit AskUserQuestion selection; --read-only forces a pure report.
  • Never auto-closes a milestone — closed/done requires version + completion_note (schema- enforced). Surface the close as a recommendation and let the user supply those.
  • Membership is one-directional: tasks carry no milestone back-link, so this skill reads every member and scans for unlinked relatives to ground the real state.
  • Frontmatter ↔ Deliverables sync: appending a member task updates both tasks: and the body ## Deliverables bullet — see ${CLAUDE_PLUGIN_ROOT}entities/milestone/definition.md.
  • Backlog items are signal, not members: surface related ones and point at /sdlc:backlog-triage; don’t fold them into tasks:.
  • Don’t pipe commands you gate on: entities validate returns a pipeline’s last exit code, so a piped | tail masks a failure — see ${CLAUDE_PLUGIN_ROOT}skills/CLAUDE.md.