Skip to content

/sdlc:capability-review

Generated from solutions/ontological/plugin/plugins/sdlc/skills/capability-review/capability-review.md.

Review existing Capability entities for accuracy against the code they anchor: do the locations still realize the Statement, do kind, audience and state match the shared rubric, do parent_key and related still resolve, does the body describe what the code does. Runs sdlc capability coverage and sdlc capability graph read-only, dispatches one read-only sub-agent per capability, and prints a report. With --fix it also corrects frontmatter through sdlc capability update and stamps last_reviewed; with --rewrite it also rewrites drifted body sections; both open a PR. Invoke after a mapping run, after a refactor that moved packages, or when the System page’s capability view looks wrong. Usage: /sdlc:capability-review <dir|C-NNNN|all> [--fix] [--rewrite] [--auto].

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

Usage:

  • /sdlc:capability-review <dir> — review every capability whose locations touch a directory.
  • /sdlc:capability-review C-NNNN — review one capability and its subtree.
  • /sdlc:capability-review all — review the whole corpus.
  • --fix — write frontmatter corrections (kind, audience, state, parent_key, locations, related, need_human_review) and stamp last_reviewed on every reviewed capability, on a branch with a PR.
  • --rewrite — --fix plus a rewrite of every body section the review found drifted.
  • --body — judge the body sections too. Without it a review checks locations and frontmatter only. --rewrite implies it.
  • --auto — skip the confirmation prompt. Only meaningful with --fix or --rewrite.

Without --fix or --rewrite the skill is a report: it prints the review table and writes nothing, not even a worktree. A scope is required. With no scope, print ERROR reason="scope required: <dir>, C-NNNN, or all" and stop.

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

  • The read-only substrate is two ops. sdlc capability coverage --scope <scope> lists the capabilities in scope with attachments (inbound wikilinks plus resolved locations) and anchor_rot. sdlc capability graph is the corpus as a tree with a warning per dangling parent_key or unresolved related.
  • Writes go through sdlc capability update. Its --help is the contract. Never hand-roll capability frontmatter.
  • The rubric is ${CLAUDE_PLUGIN_ROOT}/conventions/capability-rubric.md, shared with /sdlc:capability-map; it points at the grain table, audience rubric, locations grammar and body shape in ${CLAUDE_PLUGIN_ROOT}/lib/model/entities/capability/definition.md. Read both once per run and point sub-agents at them; do not restate them.
  • Retirement is report-only. A stale capability with zero attachments lands in the retire table; closing it is a separate human decision.

Parse <scope>, --fix, --rewrite, --body, --auto. --rewrite implies --fix and --body. Classify the scope: a path that exists is a dir; a string matching C-NNNN is a subtree; the literal all is the corpus. <stem> is the scope’s last path segment, the capability id in lower case, or all.

In report mode (neither flag) skip the pre-flight. Otherwise probe:

${CLAUDE_PLUGIN_ROOT}/cli/sdlc project preflight-worktree capability-review-<stem> \
--branch chore/capability-review-<stem> \
--project-root .

If dirty is true, ask via AskUserQuestion whether to proceed (the worktree branches from main, so uncommitted changes stay behind) or stop; default stop, showing the porcelain lines. If anyCollision is true, stop and ask; a prior run may be unfinished.

${CLAUDE_PLUGIN_ROOT}/cli/sdlc capability coverage [--scope <scope>] --project-root . --output json
${CLAUDE_PLUGIN_ROOT}/cli/sdlc capability graph --project-root . --output json

Omit --scope for all. Keep the warnings array from graph as the run’s baseline; Step 7d compares against it. Read the rubric and definition.md (see Project context).

The candidates are the capabilities in coverage.capabilities: for dir, those whose locations touch the directory; for subtree, the capability and every descendant; for all, the corpus. Candidates without locations are reviewed too: a planned capability is checked for realizing code the same way map surveys it.

If the list is empty, print CAPABILITY-REVIEW-NOOP scope=<scope> and stop.

Skip in report mode. Otherwise:

git worktree add .sdlc/worktrees/capability-review-<stem> -b chore/capability-review-<stem> main

Use absolute paths under the worktree for every write from here on. Do not cd the parent session into it.

4. Dispatch one sub-agent per group (single message, parallel)

Section titled “4. Dispatch one sub-agent per group (single message, parallel)”

Group the candidates by parent so each capability sits in exactly one group: the group keyed by its parent when that parent is in scope, otherwise the group keyed by itself. Sort each group’s members by capability id and split it into chunks of at most five, so the same scope groups the same way on every run. A review reads a file and its anchors, so one agent per capability is more agents than the work needs. For every group, dispatch one sub-agent in a single message so they run concurrently. Use the general-purpose subagent type with the sonnet model; these are well-briefed reads. Sub-agents are read-only: they read files and run git log; they never write.

Each sub-agent prompt carries:

  • The project root (absolute).
  • The capabilities it owns: for each, its file path and its coverage row (attachments, rot entries).
  • The paths to the rubric and definition.md, and the instruction to apply them.
  • The current tree from capability graph (ids, titles, parents), so a proposed parent or related target is chosen from what exists.
  • Whether to judge body sections (--body or --rewrite) and whether to draft replacement text (--rewrite only; a report needs the finding, not the prose).
  • The verdict contract below.

Each sub-agent, for each capability in its group, in order:

  1. Read the capability file. Open every locations entry; for a symbol form, grep the symbol in that file. Read enough of each location (README, manifest, entry points) to judge whether it delivers the Statement.
  2. Judge each check against the rubric: locations (resolve and realize the Statement), kind, audience, state, parent_key, related, and, when the prompt asked for body checks, each body section (Summary, Statement, What it provides, Underlying implementation).
  3. Decide the verdict:
    • accurate — every check holds.
    • drift — a field or section disagrees with the code; the capability is still provided.
    • stale — the ability is no longer provided: locations gone, package deleted, or the Statement describes something the code no longer does.
  4. Return a strict JSON array, one object per capability in the group, no surrounding prose:
[{
"candidate": "C-NNNN",
"verdict": "accurate | drift | stale",
"checks": {
"locations": { "ok": true, "found": "<what the code shows>", "proposed": ["<location>"] },
"kind": { "ok": false, "found": "module", "proposed": "component" },
"audience": { "ok": true },
"state": { "ok": false, "found": "open/planned", "proposed": "open/verified" },
"parent_key": { "ok": true },
"related": { "ok": true, "proposed": ["[[C-NNNN]]"] },
"body": {
"Summary": { "ok": true },
"Statement": { "ok": false, "found": "<one line: what is wrong>", "proposed": "<text>" }
}
},
"kind_confidence": "high | low",
"need_human_review": false,
"reason": "<one sentence naming the evidence>"
}]

A check with ok: true may omit found and proposed. checks.body is absent when body checks were not asked for. proposed for locations and related is the full replacement list. proposed body text appears only when the prompt asked for it. Keep each object under ~300 words.

Single message — every Agent call goes in one assistant turn.

Merge every array into one list. A sub-agent that returned non-JSON marks every capability in its group unreviewed with reason malformed verdict.

Apply these checks to the merged list before rendering the report:

  • Grain. A proposed kind with kind_confidence low is not written; the row keeps the stored grain and gains need_human_review: true.
  • Audience. A proposed audience follows the rubric’s surface test; a proposal without a named surface is dropped.
  • State. A proposed state follows the rubric’s table. closed/retired is never proposed or written.
  • Tree. A proposed parent_key or related target must be an existing id, else the proposal is dropped and noted.
  • Rot. Every anchor_rot row in scope is part of its capability’s locations proposal (drop or replace), or the row is stale.
  • Retire candidates. Every stale capability, and every capability whose attachments is zero, is listed for the reader. Nothing is closed.

Render one markdown report:

# Capability review — scope <scope>
<N> reviewed → <A> accurate, <D> drift, <S> stale, <U> unreviewed. <R> retire candidates.
| Capability | Verdict | Failing checks | Proposed |
|---|---|---|---|
| C-NNNN <title> | drift | kind, state | kind module→component; state planned→verified |
| C-NNNN <title> | drift | body: Statement | <one line> |
| C-NNNN <title> | stale | locations | package removed in <commit> |
## Retire candidates (<R>)
- C-NNNN <title> — <stale reason | attachments 0>
## Needs human review (<H>)
- C-NNNN — <why>

In report mode, print the report, then print CAPABILITY-REVIEW-REPORT scope=<scope> reviewed=<N> accurate=<A> drift=<D> stale=<S> and stop. Body findings appear as one line each; the report never carries replacement prose.

Under --auto, proceed to Step 7. Otherwise ask via AskUserQuestion:

  • Apply the fixes as shown — every frontmatter proposal, and under --rewrite every body proposal.

  • Walk one-by-one first — one follow-up question per drift/stale row (apply, drop, mark need_human_review), then re-render and ask again.

  • Cancel — tear down and stop:

    ${CLAUDE_PLUGIN_ROOT}/cli/sdlc project teardown-worktree capability-review-<stem> \
    --branch chore/capability-review-<stem> \
    --project-root <worktree-parent-repo-root>

From inside the worktree.

For every reviewed capability, accurate rows included:

${CLAUDE_PLUGIN_ROOT}/cli/sdlc capability update <C-NNNN-slug> \
--project-root <worktree-absolute-path> \
--set '{"last_reviewed": "<today>", "kind": "<grain>", "state": "<state>", "locations": [...], "related": [...], "need_human_review": true}'

The positional is the file basename without .md. --set carries last_reviewed for every row and only the fields the row’s accepted proposals change. It replaces a list field whole: pass the full proposed locations or related, not the additions. Merge related by resolved id, not by string: keep the file’s spelling for a target already present and never add a second entry for it. Capture the path of every updated file; Step 8 stages exactly these.

For every accepted body proposal, Edit that section: replace its content, keep the ## <Heading> line, keep the first Summary bullet’s ^summary tag, wrap prose at 100 columns. Never add or delete a section; a missing optional section is a finding for the reader, not a rewrite.

A rewritten body is model text. Set provenance on that file to sdlc:capability-review <scope> through the same capability update call as 7a, so the stamp and the rewrite land together. Files whose body was not rewritten keep their provenance.

For every updated path:

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

Then:

${CLAUDE_PLUGIN_ROOT}/cli/sdlc capability graph --project-root <worktree-absolute-path> --output json

warnings must contain nothing beyond the Step 2 baseline. A new warning is a defect in the accepted proposals: fix the offending frontmatter with capability update and re-run. Do not commit with a new warning; pre-existing corpus warnings are not this run’s to fix.

Stage only the paths captured in Step 7, plus whatever git status --porcelain docs/ shows after docs generate (it rewrites every index page; the pre-commit docs-drift check rejects a stale one). Do not git add -A or glob the directory.

${CLAUDE_PLUGIN_ROOT}/cli/sdlc docs generate --project-root <worktree-absolute-path>
git add <updated path>... <generated page>...
${CLAUDE_PLUGIN_ROOT}/cli/sdlc commit create --message - <<'EOF'
docs(capabilities): review <scope>: <N> reviewed, <F> fixed, <W> rewritten
Via /sdlc:capability-review.
Fixed:
- C-NNNN — <fields set>
Rewritten:
- C-NNNN — <sections>
EOF
git push -u origin chore/capability-review-<stem>
gh pr create --title "Capability review: <scope>" --body "<see below>"

The message reaches git through the op’s temp file (the - sentinel). See ${CLAUDE_PLUGIN_ROOT}/conventions/commit-messages.md.

PR body shape:

## Summary
Capability review for `<scope>`: <N> reviewed, <A> accurate, <D> drift, <S> stale.
<F> frontmatter fixes, <W> body rewrites, <R> retire candidates listed below; none closed.
## Fixed
- C-NNNN — <field>: <before> → <after>
## Rewritten
- C-NNNN — <section>: <one line on what changed>
## Retire candidates (not closed)
- C-NNNN — <title>, <reason>
## Needs human review
- C-NNNN — <why>
## How this was generated
`/sdlc:capability-review <scope> --fix [--rewrite]`: `sdlc capability coverage` and
`sdlc capability graph` read-only, one read-only sub-agent per capability in a single message
applying `conventions/capability-rubric.md`, one report, then `sdlc capability update`,
`entities validate` on every file, and `capability graph` with no new warnings.

Print CAPABILITY-REVIEW-DONE pr=<url> reviewed=<N> fixed=<F> rewritten=<W> retire_candidates=<R> and tell the user the PR URL, the fixed ids and fields, the rewritten ids and sections, the retire candidates, the need_human_review set, and the worktree path.

  • With no scope the skill prints the ERROR reason= line and writes nothing.
  • Without --fix or --rewrite, the skill writes nothing: no worktree, no branch, no file. Its output is the report and the CAPABILITY-REVIEW-REPORT marker.
  • Steps 2, 4, 5 and 6 are read-only; the first write is Step 3’s worktree, and the first entity write follows the confirmation (or --auto).
  • Under --fix, every reviewed capability carries today’s last_reviewed; under --rewrite, every rewritten file carries provenance: sdlc:capability-review <scope>.
  • Every updated file passes sdlc entities validate, and sdlc capability graph in the worktree reports no warning beyond the Step 2 baseline.
  • A low-confidence grain is never written; the row carries need_human_review: true.
  • No capability is closed or deleted; retire candidates appear only in the report and PR body.
  • One commit contains exactly the touched capability files; the branch is pushed; the PR URL is reported with the CAPABILITY-REVIEW-DONE marker.
  • capability coverage or capability graph fails — stop, print stderr verbatim as ERROR reason="...". Nothing else runs.
  • Sub-agent returned non-JSON — the row is unreviewed with reason malformed verdict; the batch continues, and the row is listed in the report.
  • capability update fails — stop and surface stderr. The worktree isolates the partial state; the user re-runs or cancels.
  • Validate fails after a body Edit — the Edit broke the body contract. Report the file’s findings, do not commit.
  • capability graph warns anew after writes — a proposed parent or related target was wrong. Fix with capability update and re-run the check; do not commit with a new warning.
  • A report by default. Writes need --fix or --rewrite; the report never carries replacement prose, only findings.
  • Parallelism comes from one message. Step 4 dispatches every Agent call in a single response, one per group of up to five capabilities, not one per capability.
  • One rubric. ${CLAUDE_PLUGIN_ROOT}/conventions/capability-rubric.md is shared with /sdlc:capability-map; a decision this skill makes is the one map would make.
  • Retirement is a report. Stale rows and attachments of zero go in a table; the reader decides.
  • Untyped links. related is the only capability-to-capability link this skill writes.
  • Committing model-generated messages. See ${CLAUDE_PLUGIN_ROOT}/conventions/commit-messages.md.
  • Branch naming. chore/capability-review-<stem>. See ${CLAUDE_PLUGIN_ROOT}/conventions/branch-naming.md.
  • Worktree teardown is deferred. Cleanup after the PR merges; /sdlc:project-cleanup surfaces the merged branch and its worktree.