Imported from mtrense/skills (
domain-driven/skills/task-refine/SKILL.md). Install upstream withnpx skills add mtrense/skills --skill task-refine. Copyright stays with the author.
Task Refine — Draft → Todo
You turn a draft task into a ready-to-implement todo. A todo is a task whose goal and success criteria you and the human both understand, that is small enough to land in one implementation pass, that fits the domain, and whose dependencies are wired. Getting there is an interview, not a rewrite.
Step 1 — Pick the task
- If an id was given, refine that one.
- Otherwise, run
bash <skills-root>/task-status/tasks.sh by-status draftand take the lowest id. If there are no drafts, say so and stop.
Step 2 — Assess (subagent)
Spawn the task-analyzer subagent (subagent_type: task-analyzer) with the task file path and the project root. It reads the task, the relevant bounded-contexts/<context>.md (and context-map.md for relationships), domain-model.md, and the architecture guidelines (the crisp <architecture-home>/<topic>.md summaries plus the architecture/decisions.md index by default, or under the directory named in the project's .workflow-overrides/architecture-path file), then returns a structured assessment:
- Completeness — what the spec is missing (unclear outcome, no success criteria, hidden ambiguity).
- Domain-compliance — does the task fit a bounded context? Does it use that context's ubiquitous language correctly? Does it leak across a boundary in a way the relationship pattern forbids? Is it phrased as an outcome, or has it slipped into premature implementation detail?
- Size — does this look like one implementation pass, or several? If several, a suggested split, with each piece named by its outcome/slug (never an id suffix like
0001a— ids for children don't exist until Step 4 mints them). - Complexity — how hard the pass is to get right, as
low/medium/highwith a one-line justification. Reasoning difficulty, not volume — orthogonal to Size: a broad-but-mechanical change islow, a small-but-tricky invariant ishigh. - Dependencies — other tasks (by id) or unbuilt prerequisites this needs.
- Interfaces — the interface surfaces the task touches or must introduce (HTTP/REST endpoints, gRPC services, message topics/event schemas, and in-process contracts: interfaces, traits, protocols), each tagged as defined (new/changed) or consumed (existing).
- Implementation plan — a proposed ordered sequence of steps and the concrete files to be touched (existing vs new), scouted from the codebase.
- Decisions — choices the human must make, and which existing ADRs (from the index) already bear on this task.
- Exemplars — the exemplars (from
exemplars/exemplars.md, when the project keeps them) bearing on this task:normativeones the acceptance criteria should cite (and which make natural first test fixtures),illustrativeones as non-binding context, and any conflict between the task and a normative exemplar. - Dossiers — the dossiers (from
dossiers/dossiers.md, when the project keeps them) bearing on this task: the specific claims that constrain it (with confidence), acceptance criteria leaning on alow-confidence claim, open unknowns the task would trip over, and an under-researched flag when the task hinges on external facts no dossier covers.
The subagent reads the corpus so you don't — you receive only its report.
Step 3 — Interview the human (Socratic, one question at a time)
Work through the assessment with the human, one question at a time, reflecting back what you hear. Drive to:
- A clear outcome and concrete acceptance criteria you could verify.
- Domain fit. If the analyzer flagged a domain conflict, surface it plainly: a task that violates the domain means either the task has the wrong shape / is too implementation-oriented (reshape it) or the map itself is wrong (note it and tell the human to revisit
/domain-modelor/context-mapping— do not silently "fix" the task around a broken map). - The right context for the task's frontmatter.
- The interfaces the task touches. Confirm the analyzer's list with the human and settle, for each, whether the task defines the contract (a new/changed endpoint, service, topic, event schema, interface/trait/protocol) or merely consumes an existing one. Note the contract at the granularity known now — route + method + shape, service + method, topic + payload, trait/protocol + signatures — without designing the implementation. If the task defines a contract that crosses a bounded-context boundary, it is often the published language of that relationship; if that surfaces a real decision, treat it under Step 5 (offer an ADR).
- The complexity estimate. State the analyzer's
low/medium/highverdict with its one-line justification and let the human correct it — this rarely needs a question of its own unless the human disagrees or the interview reshaped the task (a split, a scope cut, or a plan correction can change the difficulty; re-settle it when it does). Keep it what it measures: how hard the pass is to get right, not how much typing it is — and keep it abstract (a property of the task, never a model or agent choice; the routing that consumes it is/task-cycle's business). - The implementation plan. Walk the analyzer's proposed steps and file list with the human, correct anything its scout got wrong (files that don't exist, a seam it missed), and settle on an ordered plan the implementer can start from. Keep it a plan — steps and files, not code. If refining the plan reveals the task is bigger than one pass after all, go back to Step 4 and split.
Capture what you scope out. Refinement narrows: to make the task land in one pass you will often cut functionality out of it — an edge case deferred, a follow-on capability pushed to "later", a nice-to-have set aside. Anything cut this way must not evaporate. For each piece of scoped-out functionality, create a new draft task so it stays on the backlog: mint an id (tasks.sh next-id, one at a time) and write tasks/NNNN-slug.md in status: draft with a short body. That body records the problem, not the anticipated solution — state the need or gap that was deferred (what's missing, for whom, why it matters), and resist writing down the fix that came up in conversation: deciding how is the job of the later /task-refine pass that takes this draft, and a pre-baked solution would anchor it. Do record what is already known and binding: prerequisites (this task's id when the deferred piece builds on it, other tasks, facts that must be settled first) and boundaries the interview established (what's explicitly out of its scope, constraints already agreed). Do not interview on these drafts now — a draft is a placeholder, not a ready task. This is different from a split (Step 4): a split divides all of an oversized task's work among todo children and tombstones the original; scoping-out keeps this task and merely spins off the bits you deliberately dropped as fresh drafts. If the human isn't sure whether a cut piece is worth keeping, ask — but default to capturing it.
Step 4 — Split if too big
If the task can't land in one implementation pass, propose a split and get the human's agreement on the pieces. When you present the proposed pieces, name each by its outcome / slug (or "child 1 / child 2") — never as 0007a / 0007b or any suffix of the original id. That suffix wrongly implies the original id survives the split; it does not (see below). The a/b labels also tend to leak straight into the minted filenames, so keep them out of the conversation entirely.
Never derive child ids by suffixing the original (no 0007a / 0007b, no 0007-1). A split produces genuinely new, top-level tasks: the original becomes an inert split tombstone and each child is minted a fresh sequential id via tasks.sh next-id. This is not a style preference: a file named 0007a-slug.md does not match the loader's NNNN-slug.md glob, so it is invisible to every tasks.sh command — it would never schedule, never count, never check. (The check-dag you run at the end of this step flags any such malformed filename, but the point is not to create one.) Then, as two passes:
Pass A — create children and tombstone the original.
- For each child, mint an id (
tasks.sh next-id, one at a time) and write a newtasks/NNNN-slug.mdinstatus: todowith its own outcome/criteria and any dependency ordering among the children. - Rewrite the original task to a tombstone: set
status: splitandsplit_into: ["NNNN", ...]listing the children; leave its body as the record of what it was. A tombstone is inert — it schedules nothing.
Pass B — rewire dependents. Run
bash <skills-root>/task-status/tasks.sh dependents <original-id> to find every task that had depends_on: [<original>]. For each, with the human decide which child (or children) it should now depend on — do not blindly repoint to all children — and edit its depends_on accordingly. Leaving a dependent pointing at the split tombstone is a bug (check-dag will flag it as dangling).
Step 5 — Decisions & ADRs
For each genuine decision surfaced: if it is significant and expensive to reverse, offer to record it via Skill(adr) (never auto-create). For every ADR — newly recorded or pre-existing — that constrains the task, add its number to the task's related_adrs frontmatter so the implementer inherits it. Add any strategic docs the implementer needs (e.g. the context file — and, for integration work, the bounded-contexts/<external-system>.md of each external context the task touches; context: itself always stays an owned context) to related_documents — including each bearing normative exemplar's directory (exemplars/<slug>/), so the implementer inherits the binding sample; where one applies, phrase an acceptance criterion against it concretely (e.g. "parses exemplars/pipeline-config/ without error") and note in the plan that it is the first test fixture. An illustrative exemplar may be mentioned in ## Notes as shape guidance but never cited in acceptance criteria — if the task really needs it binding, the route is a /spec-sharpener promotion first, and a conflict between the task and a normative exemplar is resolved like an ADR conflict (fix the task, or sharpen/revise the exemplar) — never ignored.
Likewise add each bearing dossier (dossiers/<slug>.md) to related_documents so the implementer inherits the facts. If the analyzer flagged the task under-researched — its outcome hinges on external facts (a regulation, an undocumented API, a market rule) no dossier covers — surface that to the human and offer a /dossier run on the subject before finalizing; this is an offer, never a hard gate (the human may accept the risk and proceed). If an acceptance criterion leans on a low-confidence claim, say so — the human decides whether to firm the fact up (an accretion pass on the dossier) or proceed on it.
Step 6 — Finalize to todo
For each resulting task (the single refined task, or each split child), set status: todo, context: <slug>, complexity: <low|medium|high> (the estimate the interview settled — each split child gets its own, from the analyzer's per-child estimates), and wire depends_on to the ids the interview identified. The task body must have exactly these sections, in this order and at these heading levels:
## Outcome— what is true when the task is done (the shared goal).### Why this matters— the value the outcome delivers.### Acceptance criteria— concrete, verifiable checks.
## Implementation plan— ordered steps + files to be touched (see below).### Interfaces— the interface surfaces touched (see below).
## Notes— what the refinement settled (decisions, scoped-out drafts, domain fit, anything the implementer should know).## Closing— the post-implementation record; leave its subsections as placeholders —/task-cyclefills them at implementation.### Manual testing— placeholder;/task-cyclefills it.### Deviations from plan— placeholder;/task-cyclefills it.
Fill sections 1–3 and their subsections; leave ## Closing and its subsections as their placeholders.
In ### Interfaces, record the interface surfaces the task touches, one per line, each tagged define or consume — e.g. - define — POST /orders (HTTP): {…} → 201, - define — trait OrderRepository: save, findById, - consume — gRPC PaymentService.Authorize. This is the contract the implementer must honor, not the implementation. If the task genuinely touches no interface, write - None. so the absence is explicit rather than an oversight.
In ## Implementation plan, record the ordered steps the implementer will follow (TDD-friendly: the test to write, then the change to make it pass) and a Files list naming each file to be touched with its path and a phrase on the change, marking new files as such — e.g. - src/orders/api.rs (edit) — add POST /orders handler, - src/orders/repository.rs (new) — OrderRepository trait + impl. This is the plan settled in the interview, kept at plan granularity — enough for /task-cycle to start without re-deriving the layout, not line-level code. Note any file the analyzer marked tentative so the implementer confirms it.
Then guard the graph: run bash <skills-root>/task-status/tasks.sh check-dag. If it reports a cycle or a dangling reference, you introduced it — fix the offending depends_on before finishing. Do not leave the backlog with a failing check-dag.
When you are done
Report: which task(s) are now todo, any tombstoned split, any new draft tasks you spun off for scoped-out functionality, the dependencies wired, and any ADRs recorded or attached. If drafts remain, mention that /task-refine can take the next one. Do not implement anything — /task-cycle does that.