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.
Resources
1Install
npx skillscat add osdlc/osdlc/task-review Install via the SkillsCat registry.
$sdlc:task-review
Usage:
$sdlc:task-review— triage every unfinished task.$sdlc:task-review --since YYYY-MM-DD— only triage tasks last reviewed before that date, or
whosecreated:falls after it.$sdlc:task-review --tag <name>— only triage tasks whose frontmattertags: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 useYYYY-MM-DD-<slug>.md; closed
tasks may still use the olderNNNN-<slug>.md. Both shapes are valid.Full state/frontmatter schema in
docs/planning/tasks/README.md. States considered
"unfinished": anything that does NOT start withclosed/.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:scripts/sdlc entities validate docs/planning/tasks/*.mdValidator failures are signal — usually a
closed-shape task missingresolution_date/resolution_commit, or a state 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}/lib/model/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}/lib/model/entities/task/implementation-ready.md. Sub-agents apply that
contract during the spec-completeness portion of their investigation (item 5 below). They surface
gaps viadefinition_gap:but do NOT stampreadiness_verified_at:— that field is owned
exclusively by$sdlc:task-ensure-ready(called immediately before work starts).
1. Survey
ls docs/planning/tasks/*.mdand read frontmatter from each.- Build the unfinished list. Apply
--sinceand--tagfilters if provided. - If zero unfinished tasks, print
NO UNFINISHED TASKS — backlog is clean.and exit.
Print a one-line preview: Found <N> unfinished tasks across <states…>. Triaging in parallel.
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-onlyshould pass through as "do not edit files."Full investigation contract (each agent does ALL of these for its one task):
Read the task body end-to-end. Not just frontmatter. Read every section: Goal,
Today/Current state, Proposed, Approach, Areas, 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.Sanity-check
## Todayand## Areas, but do not gate on either resolving.
As of v7,## Todayis free-form prose (no table shape, no path-existence
check) and## Areasis an advisory list of packages/directories/modules
the task expects to touch — an Area never has to name an exact existing
path. Neither section is resolved against the working tree by any op.
Read them by eye: does the described current state still match reality,
and do the named areas still make sense given what you find in item 3
(recent history) and item 4 (related tasks)? If something has clearly
moved or no longer exists, that's spec drift (item 6's "still relevant,
but spec drifted" outcome) — not a readiness disqualifier, since Areas
was never a resolution gate. Stale path CITATIONS anywhere in the body
(backticked, extension-bearing tokens) are still caught mechanically by
thepathsclaim resolver in item 5.
Don't cap the verification; the point is to confirm the task's entire
factual basis, not get a quick signal.Cross-check against recent history.
git log --oneline -20andgit 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.
Read referenced related tasks. If the task's frontmatter
related:or body contains[[other-task]]wikilinks or0NNN-foo.mdreferences, briefly check those target files'
current state — they may have moved the ground under this task.Evaluate spec completeness against the implementation-ready contract. Apply the contract
from${CLAUDE_PLUGIN_ROOT}/lib/model/entities/task/implementation-ready.md. Read required
body sections and frontmatter by eye. Decide the MECHANICAL disqualifiers with the same ops$sdlc:task-ensure-readyruns as its gate, so this skill's gaps match the gate that runs
before work starts:Touchpoint resolution — N/A since v7:
## Todayand## Areasare no longer resolved
against the working tree (item 2 is a by-eye sanity check, not a disqualifier source).Placeholder-phrase disqualifiers — shell out to the task noun's scanner op:
scripts/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## Areascell) under the phraseempty-table-cell. Its output needs no further filtering.Claim-resolver disqualifiers — shell out to the task noun's check-claims op:
scripts/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). Eachseverity: "disqualifier"finding is a gap — relay itsmessage
(andline, 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) andquantifiers(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.Decide one of (the 5-way):
- Already shipped — described "Today" state is gone and proposed code is in place. Set
state: closed/done. Addcompletion_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. Setstate: closed/obsoletedwithcompletion_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 state, etc. Updaterelevance_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 Areas). The task stays unfinished but is flagged as
not-yet-workable.
- Already shipped — described "Today" state is gone and proposed code is in place. Set
Update frontmatter (skip in
--read-onlymode):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-definewill 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.
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> State: <new state 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 itKeep 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.
3. Synthesize ranking
When all sub-agents return, build a single ranked report:
Tier 1 — high-impact, do soon
High-impact tasks (bug fixes, unblockers) that are also open/ready / in-progress and
well-defined. These are the actionable picks.
Tier 2 — quick wins to bundle
Small-complexity items that are worth picking up alongside the Tier 1 work, especially if they touch
overlapping files.
Tier 3 — defer
Medium/low impact OR blocked by another task OR spec still incomplete.
Tier 4 — rewrite or drop
Stubs, abandoned drafts, or things now-obsolete that should be deleted or rewritten. Recommend a
concrete action for each.
Newly closed during this run
List every task whose state was changed to closed/done or closed/obsoleted by sub-agents, with
the one-line completion_note:.
Definition gaps
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.
Final recommendation
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.
4. Optional: ask before mutating closures
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).
5. Render the report, then summarize
First the durable artifact, then the conversation:
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:scripts/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.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.
Notes
- 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/docsrewriting: 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, proposeclosed/doneonly after asking the user. - Schema-contracted reports. See
${CLAUDE_PLUGIN_ROOT}/conventions/skill-reports.md— this
skill's kind slug istask-review.--read-onlysuppresses 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.