Skip to content

/sdlc:task-review

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

Triage every unfinished task in docs/planning/tasks/: verify each is still relevant against the current codebase, check that the spec is complete enough to act on, update frontmatter (impact, complexity, last_reviewed, plus closed/done when code shows the work shipped), and produce a ranked recommendation of the next 1-2 tasks to tackle and why.

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

Usage:

  • /sdlc:task-review — triage every unfinished task.
  • /sdlc:task-review --since YYYY-MM-DD — only triage tasks last reviewed before that date, or whose created: falls after it.
  • /sdlc:task-review --tag <name> — only triage tasks whose frontmatter tags: list contains <name>.
  • /sdlc:task-review --read-only — produce the ranked report without modifying any files.

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

  • Task documents live in docs/planning/tasks/. Active tasks use YYYY-MM-DD-<slug>.md; closed tasks may still use the older NNNN-<slug>.md. Both shapes are valid.

  • Full status/frontmatter schema in docs/planning/tasks/README.md. Statuses considered “unfinished”: anything that does NOT start with closed/.

  • Frontmatter is validated by the sdlc plugin’s schema. Before the synthesis step (and any time you’ve just updated a batch of frontmatter), run the validator over the corpus:

    ${CLAUDE_PLUGIN_ROOT}cli/sdlc entities validate docs/planning/tasks/*.md

    Validator failures are signal — usually a closed-shape task missing resolution_date/resolution_commit, or a status that’s drifted from the canonical enum. Surface these in the synthesized report under “Schema violations” alongside the other gaps, and have the per-task sub-agent fix them as part of its frontmatter update. Schema lives at ${CLAUDE_PLUGIN_ROOT}/entities/task/schema.ts.

  • This skill keeps the backlog honest: it triages and re-ranks unfinished tasks. Starting a task is /sdlc:task-work’s job.

  • The authoritative definition of “implementation-ready” lives at ${CLAUDE_PLUGIN_ROOT}/entities/task/implementation-ready.md. Sub-agents apply that contract during the spec-completeness portion of their investigation (item 5 below). They surface gaps via definition_gap: but do NOT stamp readiness_verified_at: — that field is owned exclusively by /sdlc:task-ensure-ready (called immediately before work starts).

  1. ls docs/planning/tasks/*.md and read frontmatter from each.
  2. Build the unfinished list. Apply --since and --tag filters if provided.
  3. If zero unfinished tasks, print NO UNFINISHED TASKS — backlog is clean. and exit.

Print a one-line preview: Found <N> unfinished tasks across <statuses…>. Triaging in parallel.

2. Dispatch one sub-agent per task (parallel)

Section titled “2. Dispatch one sub-agent per task (parallel)”

Spawn one sub-agent per unfinished task — each task gets its own dedicated investigator. Send all the Agent calls in a single message so they run concurrently. For large backlogs (>20 tasks), still spawn one agent per task.

Each sub-agent prompt must include:

  • Task file: the single absolute path to the task this agent owns.

  • Codebase root: the project working directory.

  • Mutation flag: --read-only should pass through as “do not edit files.”

  • Full investigation contract (each agent does ALL of these for its one task):

    1. Read the task body end-to-end. Not just frontmatter. Read every section: Goal, Today/Current state, Proposed, Approach, Files to touch, Acceptance criteria, Out of scope, Dependencies, Discovery context, etc. The agent should be able to explain in its own words what the task is asking for.

    2. Verify every touchpoint row. In v3 (schema_version: 3), the task’s ## Today and ## Files to touch sections are | Location | ... | markdown tables using a five-form Location grammar (file, file+symbol, file+line, directory, glob). Shell out to the task noun’s resolver op, which decomposes each row and resolves its cited Location against the working tree in one call:

      ${CLAUDE_PLUGIN_ROOT}cli/sdlc task resolve-touchpoints <task-file>

      The op emits a single JSON object { rows: [...] }. Each row carries section (today / files_to_touch), the raw location cell, the parsed decomposition ({form, path, symbol, line}, null when the parser rejected the cell), the change kind it resolved AS (new / modify / delete; ## Today rows resolve as modify), and the verdict resolved: true|false with a machine reason token (file-exists, file-missing, symbol-found, symbol-missing, directory-exists, glob-matches, glob-no-match, new, parse-error: …). The op decides existence under the five-form grammar so the agent does not re-spell the loop: form: file / form: line check file existence (line is a hint); form: symbol requires file-exists AND the symbol token present; form: directory checks the directory; form: glob expands to ≥1 match; kind: new rows fire no check.

    Treat any resolved: false row as material drift: the cited Location no longer holds. Read the reason and narrate what shifted in the decision below (the op reports existence only — drift narration is yours). Don’t cap the verification; the point is to confirm the task’s entire factual basis, not get a quick signal.

    Rows the parser rejected surface as resolved: false, reason: "parse-error: …" (no filesystem probe) — relay the message. For tasks still on the v2 (or earlier) bulleted-shape, the resolver emits no rows for that section; confirm by running task parse-touchpoints <task-file> (it reports the section kind: bulleted-legacy), surface the gap, and recommend re-running /sdlc:entities-migrate to convert the shape — do NOT hand-fix the body.

    1. Cross-check against recent history. git log --oneline -20 and git log --all --oneline | command grep <task-slug> to find any commits or branches that already touch this task’s surface. Look for:

      • Merged PRs that match the task slug.
      • Commits whose message mentions the same symbol/file the task is rewriting.
      • In-flight branches task/<slug> that may already exist.
    2. Read referenced related tasks. If the task’s frontmatter related: or body contains [[other-task]] wikilinks or 0NNN-foo.md references, briefly check those target files’ current status — they may have moved the ground under this task.

    3. Evaluate spec completeness against the implementation-ready contract. Apply the contract from ${CLAUDE_PLUGIN_ROOT}/entities/task/implementation-ready.md. Read required body sections and frontmatter by eye. Decide the MECHANICAL disqualifiers with the same ops /sdlc:task-ensure-ready runs as its gate, so this skill’s gaps match the gate that runs before work starts:

      • Touchpoint resolution — already covered by resolve-touchpoints in item 2: every resolved: false row (including parse-error: … rows and a bulleted-legacy section) is a disqualifier.

      • Placeholder-phrase disqualifiers — shell out to the task noun’s scanner op:

        ${CLAUDE_PLUGIN_ROOT}cli/sdlc task scan-placeholders <task-file>

        It emits a JSON array of matches ({"section", "phrase", "line", "snippet"}); exit 0 with [] means none. Each match is a disqualifier — name the section and the matched phrase in the gap. The scanner already skips fenced code and inline-code spans, inspects only the required body sections, and flags unfilled <...>/empty table cells (e.g. an empty ## Files to touch cell) under the phrase empty-table-cell. Its output needs no further filtering.

      • Claim-resolver disqualifiers — shell out to the task noun’s check-claims op:

        ${CLAUDE_PLUGIN_ROOT}cli/sdlc task check-claims <task-file>

        It emits a JSON object keyed by resolver name; each value is a list of findings ({"line", "severity", "message"}). Every resolver always appears as a key (empty array when it found nothing). Each severity: "disqualifier" finding is a gap — relay its message (and line, if helpful). severity: "warning" findings are informational only. Today’s registry: paths (a cited path moved — basename matches an existing file under a different parent) and quantifiers (an AC asserts over a universal class without pinning the set).

    Report each requirement and disqualifier as pass/fail in your return; a triggered op finding is a fail. Separately, note Dependencies completeness — Dependencies isn’t part of the contract but is relevant to ranking. If any contract check fails, the spec has a gap.

    1. Decide one of (the 5-way):

      • Already shipped — described “Today” state is gone and proposed code is in place. Set status: closed/done. Add completion_note: citing real evidence — commit hash, PR number, or specific file change. Do not auto-close on inference: if the evidence isn’t unambiguous, surface to the orchestrator for confirmation rather than closing.
      • Obsoleted — the file/feature no longer exists or the surrounding architecture was rewritten such that the task can never apply. Set status: closed/obsoleted with completion_note: explaining what changed and where the equivalent concern lives now (if anywhere).
      • Still relevant, accurate — task spec matches current code. Refresh relevance_note: only if anything material shifted; otherwise leave it alone.
      • Still relevant, but spec drifted — paths/line numbers/symbol names moved, scope shrunk or grew, dependencies changed status, etc. Update relevance_note: with a precise diff of what shifted. Do NOT rewrite the body.
      • Spec incomplete — one or more of the completeness items is missing or thin. Add definition_gap: listing what’s missing (e.g. definition_gap: missing AC and Files-to-touch). The task stays unfinished but is flagged as not-yet-workable.
    2. Update frontmatter (skip in --read-only mode):

      • impact: <high|medium|low> — high: addresses a bug, unlocks other work, removes a class of bugs; medium: meaningful quality improvement; low: nice-to-have / cosmetic / tiny scope. Overwrite any prior value if your fresh assessment disagrees.
      • complexity: <small|medium|large> — small: <1 day; medium: 1-3 days; large: multi-day or multi-PR.
      • last_reviewed: <today UTC>.
      • definition_gap: <one paragraph> — set if and only if the implementation-ready contract from item 5 detected gaps. Be specific enough that the user knows what to fix; this is what /sdlc:task-define will pick up later. Clear the field if a previously-flagged task now passes.
      • Do NOT set, clear, or otherwise touch readiness_verified_at: — that field is owned exclusively by /sdlc:task-ensure-ready.
      • Body content is never modified.
    3. Note dependencies and unblock relationships for the synthesis step. Be explicit: “this task must land before X because ”, or “this task is now unblocked because Y shipped”.

  • Return format (per agent, structured for easy aggregation):

    Task: <filename>
    Status: <new status if changed, else unchanged>
    Impact: <high|medium|low>
    Complexity: <small|medium|large>
    Decision: <shipped|obsoleted|relevant-accurate|relevant-drifted|incomplete>
    Definition gap: <list, or none>
    Spec completeness: <which sections present/missing>
    Key findings: <2-4 bullets, including any code citations that informed the decision>
    Dependencies: <list>
    Confidence in decision: <high|medium|low>; if not high, what would clarify it

    Keep each agent’s report under 300 words; favor structure over prose.

Each agent has narrow scope (one task) — go deep on that scope rather than skim.

When all sub-agents return, build a single ranked report:

High-impact tasks (bug fixes, unblockers) that are also open/ready / in-progress and well-defined. These are the actionable picks.

Small-complexity items that are worth picking up alongside the Tier 1 work, especially if they touch overlapping files.

Medium/low impact OR blocked by another task OR spec still incomplete.

Stubs, abandoned drafts, or things now-obsolete that should be deleted or rewritten. Recommend a concrete action for each.

List every task whose status was changed to closed/done or closed/obsoleted by sub-agents, with the one-line completion_note:.

List every task that picked up a definition_gap: field — these are tasks the user should flesh out before /sdlc:task-work can pick them up. Suggest /sdlc:task-define <basename> as the interactive way to resolve each.

1-2 specific picks for the next session. State the why in terms of dependency ordering (does it unblock others?), risk class (does it close a bug?), and effort (small/medium/large). If there are no good Tier-1 picks, say so plainly rather than padding.

If sub-agents proposed marking a task closed/done based on inference (no obvious commit hash, no merged PR reference), surface those to the user via AskUserQuestion before the close is finalised. Auto-close only when the evidence is unambiguous (e.g. described file deleted upstream, or commit message explicitly names the task).

First the durable artifact, then the conversation:

  1. Assemble the report payload per the contract in ${CLAUDE_PLUGIN_ROOT}lib/model/entities/task/reports/review/schema.ts (meta header, filters, one row per reviewed task with tier/decision/findings, newly closed, schema violations, final recommendation). Read the schema — its .describe() annotations are the authoring doc; counts and tallies are derived by the template, so the payload carries none. Write the JSON to a temp file, then follow the render recipe in ${CLAUDE_PLUGIN_ROOT}conventions/skill-reports.md:

    ${CLAUDE_PLUGIN_ROOT}cli/sdlc report render task-review <payload.json>

    On a contract violation the op exits non-zero with the Zod issues — fix the payload and re-run; never hand-write the HTML.

  2. Link, then talk. Print a clickable file:// link to the rendered HTML report (plus the JSON sidecar path), then print the synthesized ranking inline.

Stop here. Do not start any task.

  • One agent per task: always. Even for a 3-task backlog, spawn 3 agents, each going deep on its one task. Do NOT batch multiple tasks into one agent.
  • Send all agents in one message: dispatch every Agent call in a single response so they run in parallel.
  • No lib/docs rewriting: edit only frontmatter and synthesis output; never edit code or rewrite task bodies.
  • Definition gaps are signal, not blockers: a task with definition_gap: is still in the backlog; it just isn’t workable yet. Surface them so the user can fix them, but don’t mark them closed.
  • Don’t invent dates: when summarising “shipped” tasks, reference real commits / PRs found via git log. If you can’t find evidence, propose closed/done only after asking the user.
  • Schema-contracted reports. See ${CLAUDE_PLUGIN_ROOT}conventions/skill-reports.md — this skill’s kind slug is task-review. --read-only suppresses frontmatter edits, NOT the report: .sdlc/reports/ is gitignored runtime output, not a project mutation.
  • When in doubt about a closure, ask via AskUserQuestion — mis-closing a task is worse than one extra question.