Skip to content

/sdlc:capability-map

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

Map out the capability graph for one scope of the project: survey the code (or the product’s capability-map table), propose new Capability entities where the map has gaps, enrich existing ones with locations, grain, audience and related links, and report retire candidates. Runs sdlc capability coverage and sdlc capability graph read-only, dispatches one read-only sub-agent per candidate, renders one batched plan for approval, then writes through sdlc capability create / sdlc capability update and opens a PR. Invoke when capabilities are missing, ungraded, unanchored, or when the System page’s capability view looks empty for an area. Usage: /sdlc:capability-map <dir|C-NNNN|areas> [--dry-run] [--auto].

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

Usage:

  • /sdlc:capability-map <dir> — survey the packages and crates under a directory and reconcile the capability corpus against them.
  • /sdlc:capability-map C-NNNN — re-map one capability’s subtree: enrich its members and survey the units they anchor.
  • /sdlc:capability-map areas — seed the top level: every row of the product entity’s capability-map table with no entity file becomes a system root.
  • --dry-run — stop after the batched plan. Never creates a worktree, branch, or file.
  • --auto — skip the confirmation prompt and run every non-skip verdict.

A scope is required. With no scope, print ERROR reason="scope required: <dir>, C-NNNN, or areas" and stop. A whole-project sweep produces a plan too large to review; run one directory at a time.

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

  • The read-only substrate is two ops. sdlc capability coverage --scope <scope> joins the corpus against the manifest walk’s packages and crates: units (with the capabilities anchored to each), unanchored_units, unanchored_capabilities, anchor_rot, and a per-capability attachments count (inbound wikilinks plus resolved locations). sdlc capability graph is the corpus as a tree with related edges and a warning per dangling parent_key.
  • Writes go through sdlc capability create and sdlc capability update. Their --help is the contract. Never hand-roll capability frontmatter.
  • The rubric is ${CLAUDE_PLUGIN_ROOT}/conventions/capability-rubric.md, shared with /sdlc:capability-review; 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.
  • Links between capabilities are untyped related wikilinks. Containment is parent_key only; children are derived.
  • Retirement is report-only. A zero-attachment capability lands in the plan’s retire table; closing it is a separate human decision.

Parse <scope>, --dry-run, --auto. Classify the scope: a path that exists is a dir; a string matching C-NNNN is a subtree; the literal areas is a seed.

Pre-flight with the report-only probe:

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

<stem> is the scope’s last path segment, the capability id in lower case, or areas. 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 areas. Keep the warnings array from graph as the run’s baseline; Step 7d compares against it.

For seed, run coverage with no --scope and read the product entity’s capability-map table (docs/planning/products/PR-*.md, the section headed “the capability map”). Read definition.md (see Project context).

Build the candidate list:

ScopeCandidates
direach id in unanchored_units; each id in unanchored_capabilities; each capability in capabilities whose kind is null
subtreeeach capability in capabilities; each id in unanchored_units
seedeach table row whose “Capability entity” cell is not shipped and names no [[C-…]], unless an existing capability title already matches the area name

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

Skip under --dry-run. Otherwise:

git worktree add .sdlc/worktrees/capability-map-<stem> -b chore/capability-map-<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 candidate (single message, parallel)

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

For every candidate, 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 one candidate it owns: for a unit, its id, dir, kind, ecosystem; for a capability, its file path and coverage row; for a seed row, the table row and the matching “Areas in detail” row verbatim.
  • 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 parent or a related target is chosen from what exists.
  • The verdict contract below.

Each sub-agent, in order:

  1. Read the unit (README, manifest, entry points, skill files) or the capability file.
  2. Decide the verdict:
    • new — the unit or row names an ability no capability describes.
    • enrich — an existing capability describes it; supply the missing locations, kind, audience, parent, related.
    • fold — the unit belongs under an existing capability’s locations rather than being its own.
    • skip — scaffolding, a fixture, a build artifact, or already covered.
  3. Grade with the rubric: anchors decide the grain, the surface test decides the audience, the state table decides the state. Low confidence sets need_human_review.
  4. Return a strict JSON object, no surrounding prose:
{
"candidate": "<id or path verbatim>",
"verdict": "new | enrich | fold | skip",
"target": "<C-NNNN for enrich/fold, else null>",
"title": "<for new>",
"kind": "<a grain from definition.md, or null>",
"kind_confidence": "high | low",
"audience": "user | system",
"parent": "<C-NNNN or null>",
"locations": ["<five-form Location>", "..."],
"related": ["[[C-NNNN]]", "..."],
"body": {
"Summary": ["<first bullet, ends with ^summary>", "<second bullet>"],
"Statement": "<one or two paragraphs>",
"What it provides": ["<deliverable>", "..."],
"Underlying implementation": ["<realizing path or skill>", "..."]
},
"reason": "<one sentence>"
}

Omit body keys the evidence does not support. Keep each response under ~400 words.

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

Merge into one list. A sub-agent that returned non-JSON becomes a skip with reason malformed verdict, listed in the plan.

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

  • Dedupe. Two new verdicts with the same title, or with overlapping locations, merge into one. A new whose title or locations match an existing capability becomes enrich of it.
  • Tree. Every parent is an existing id or a new in this plan. A seed row is a root (parent null, kind system, title the area name verbatim). No product-level root above the areas.
  • Grain. A new or enrich with kind_confidence low carries need_human_review: true.
  • Audience. audience: user with no user-facing location is flagged in the plan row.
  • Retire candidates. Every capability in scope, other than open/planned ones and this plan’s new entries, whose attachments is zero after the planned enrichment is listed for the reader. Nothing is closed.
  • Rot. Every anchor_rot row in scope becomes part of the owning capability’s enrich (drop or replace the location), or is listed under skip with the reason.

Render one markdown plan:

# Capability map plan — scope <scope>
<N> candidates → <A> new, <B> enrich, <F> fold, <S> skip, <R> retire candidates.
## New (<A>)
- `<title>` — kind <kind>[?] · audience <audience> · parent <C-NNNN|root>
- Locations: <n> · Related: <n> · Review: <yes|no>
## Enrich (<B>)
- C-NNNN <title> — sets: <kind|audience|parent_key|locations(+n)|related(+n)>
## Fold (<F>)
- <unit id> → C-NNNN (adds <location>)
## Retire candidates (<R>)
- C-NNNN <title> — attachments 0
## Skip (<S>)
- <candidate> — <reason>

Under --dry-run, stop here and print the plan. Under --auto, proceed to Step 7. Otherwise ask via AskUserQuestion:

  • Run the plan as shown — proceed with every non-skip verdict.

  • Walk one-by-one first — one follow-up question per new/enrich/fold item (apply, drop, change verdict), then re-render and ask again.

  • Cancel — tear down and stop:

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

From inside the worktree, in this order: roots before children, so a child’s --parent-key names a minted id.

${CLAUDE_PLUGIN_ROOT}/cli/sdlc capability create \
--project-root <worktree-absolute-path> \
--title "<title>" \
[--kind <grain>] \
--audience <user|system> \
[--parent-key "[[C-NNNN]]"] \
[--locations <location>]... \
[--related "[[C-NNNN]]"]... \
[--tags <tag>]... \
[--need-human-review] \
--provenance "sdlc:capability-map <scope>" \
--created-at <ISO 8601 UTC timestamp>

The slug positional is omitted: create derives it from --title. Pass --need-human-review when the verdict set it. --provenance and --created-at mark the file as authored by this run; one timestamp (date -u +%Y-%m-%dT%H:%M:%SZ) serves every create in the plan. Capture the printed path and the minted id; later children reference it, and Step 8 stages exactly these paths. To preview the slug and id first, or check a collision, call sdlc capability preview-id "<title>".

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

The positional is the file basename without .md (id plus slug, as capability graph lists it); a bare id resolves only for unslugged files. --set replaces a list field whole: pass the merged locations (existing minus rot plus new), not only the additions. Merge related by resolved id, not by string: the corpus spells links slugged ([[C-V44L-priority-ordering-sorters]]) and verdicts spell them bare ([[C-V44L]]); when a target is already present, keep the file’s spelling and do not add a second entry. A fold is an update adding the unit’s directory to locations.

7c. Fill body sections (applies under —auto too)

Section titled “7c. Fill body sections (applies under —auto too)”

Each created file carries <...> placeholders. For every created path, Edit each section the verdict’s body supplied: replace the placeholder block, keep the ## <Heading> line, keep the first Summary bullet’s ^summary tag. Delete every optional section the verdict left unfilled (Inputs, Outputs, Hook points, Underlying implementation, Notes); no placeholder survives to the commit. The only sections that may be added are the optional Contained sub-features and Lifecycle map: a root with children in this plan gets Contained sub-features, listing them as wikilinks, in place of Inputs / Outputs.

For every created or 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 (a dangling parent, an unresolved related) is a defect in the plan: 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 Steps 7a and 7b, 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 <created path>... <updated path>... <generated page>...
${CLAUDE_PLUGIN_ROOT}/cli/sdlc commit create --message - <<'EOF'
docs(capabilities): map <scope>: <A> new, <B> enriched
Via /sdlc:capability-map.
New:
- C-NNNN — <title> (<kind>)
Enriched:
- C-NNNN — <fields set>
EOF
git push -u origin chore/capability-map-<stem>
gh pr create --title "Capability map: <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 map for `<scope>`: <A> new, <B> enriched, <F> folded, <S> skipped.
<R> retire candidates listed below; none closed.
## New
- C-NNNN — <title> · <kind> · <audience> · parent <C-NNNN|root>
## Enriched
- C-NNNN — <fields>
## Retire candidates (not closed)
- C-NNNN — <title>, attachments 0
## Needs human review
- C-NNNN — <why the grain was uncertain>
## Skipped
- <candidate> — <reason>
## How this was generated
`/sdlc:capability-map <scope>`: `sdlc capability coverage` and `sdlc capability graph`
read-only, one read-only sub-agent per candidate in a single message, in-session
reconciliation, one batched plan, then `sdlc capability create` / `update`, `entities validate`
on every file, and `capability graph` with no new warnings.

Print CAPABILITY-MAP-DONE pr=<url> new=<A> enriched=<B> retire_candidates=<R> and tell the user the PR URL, the new ids and titles, the enriched ids, 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.
  • Steps 1, 2, 4, 5 and 6 are read-only; the first write is Step 3’s worktree, after the candidate list is non-empty, and the first entity write follows plan approval (or --auto).
  • Every created or updated file passes sdlc entities validate, and sdlc capability graph in the worktree reports no warning beyond the Step 2 baseline.
  • Every new carries kind or need_human_review: true; every low-confidence grain carries need_human_review: true.
  • No capability is closed or deleted; retire candidates appear only in the plan and PR body.
  • One commit contains exactly the touched capability files; the branch is pushed; the PR URL is reported with the CAPABILITY-MAP-DONE marker.
  • capability coverage or capability graph fails — stop, print stderr verbatim as ERROR reason="...". Nothing else runs.
  • Sub-agent returned non-JSON — treat as skip with reason malformed verdict; the batch continues.
  • capability create / 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 parent or related target in the plan was wrong. Fix with capability update and re-run the check; do not commit with a new warning.
  • No entity writes until Step 7. Step 3 creates only the worktree; sub-agents read; the orchestrator writes entities only after approval.
  • Parallelism comes from one message. Step 4 dispatches every Agent call in a single response.
  • One rubric. ${CLAUDE_PLUGIN_ROOT}/conventions/capability-rubric.md is shared with /sdlc:capability-review; a decision this skill makes is the one review would make.
  • Untyped links. related is the only capability-to-capability link this skill writes; a typed Relation entity is not activated by this skill.
  • Scope, not sweep. A directory at a time keeps the plan reviewable. Run areas first on a corpus whose top level is prose; later runs hang code under those roots.
  • Retirement is a report. Attachments of zero go in a table; the reader decides.
  • Committing model-generated messages. See ${CLAUDE_PLUGIN_ROOT}/conventions/commit-messages.md.
  • Branch naming. chore/capability-map-<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.