Update Tasks · git:20261027.3c941d9 · 2026-10-27 · sha256 13688f57e43e4efa
Update Tasks git:20261027.3c941d9A
Immutable. This exact content never changes and is served at /api/v1/blob/13688f57e43e4efa.
---
name: "Roadmap: Add Task"
description: "Add a well-formed task, or a reviewed batch of them, to a rich-format roadmap: ID assignment, dependency wiring in both directions, graph integrity"
when_to_use: "Whenever the user wants to add a task, feature or work item to a roadmap, even phrased as 'add this to the roadmap', 'put this in the plan' or 'track this as a task'. For a batch of half-formed ideas, run roadmap:create-interview first and feed its approved proposal here as one batch."
model: opus
effort: medium
metadata:
glyph: ᛟ
family: roadmap
disable-model-invocation: false # invocable by Claude so "add this to the roadmap" loads this skill instead of hand-editing the JSON; Step 7's approval gate still applies
allowed-tools: ["Read", "Glob", "Grep", "Edit", "Bash(python3:*)"]
arguments: ["mode", "description"]
argument-hint: "[t|c|m] [description]"
---
# Roadmap Task Adder
Adds a well-formed task to an existing rich-format roadmap. The job is not appending a line; it is placing the task correctly in the dependency graph, wiring its relationships in **both** artefacts (`.claude/roadmaps.json` and the PHASE file it names), and leaving the roadmap coherent.
Shared conventions: `${CLAUDE_PLUGIN_ROOT}/references/roadmap-conventions.md`. The CLI is `python3 "${CLAUDE_PLUGIN_ROOT}"/scripts/roadmap.py`.
## Batch mode
When the input is a multi-task proposal (typically `roadmap:create-interview`'s approved output), run Steps 1–6 for the batch as a whole rather than once per task: assign every ID up front, wire all edges including those between the new tasks themselves, and run the Step 5 integrity checks across the combined graph. Steps 7–9 stay singular: one consolidated proposal, one approval, one write pass, one validate. Never loop the full skill per task; fifteen approval gates for one already-reviewed proposal is ceremony, not safety.
---
## Step 0: Resolve the mode
`$mode` is `t`, `c` or `m` (aliases below), first token of `$ARGUMENTS`, case-insensitive. Remainder is `$description`.
| Token | Aliases | Meaning |
|-------|------------------------------------|---------|
| `t` | `task`, `tasks` | One or more tasks, independence unknown: treat as unrelated unless `$description` says otherwise |
| `c` | `chain`, `sequence`, `set` | At least two tasks with a natural dependency order the user asserts: wire the chain in the order given |
| `m` | `mile`, `ms`, `miles`, `milestone` | Create at least one new milestone; `$description` may also carry a seed task |
Resolution depends on how this skill was reached:
- **Explicit `/roadmap:update-tasks` invocation with no arguments or with a first token matching none of the aliases**: hard stop. Print `Usage: /roadmap:update-tasks t|c|m "<description>"` and run nothing. Never guess a mode for an explicit invocation with no recognisable token: "improve the CSS" alone is exactly this case.
- **Model-invoked from natural language** ("add this to the roadmap", with no mode token because there's no slash command to carry one): infer the mode from the request shape and say the inference aloud before proceeding: one item → `t`; "X then Y" / "after" / explicit sequential phrasing → `c`; "new milestone" / "new phase of work" → `m`. This is the only path where mode is inferred rather than typed.
- **`m` with a milestone description but no seed task**: not a stop; ask for the seed task via AskUserQuestion, then continue.
`t` and `c` both proceed into Steps 1–9 below, unchanged in step numbering; `c` additionally parses `$description` for its asserted order (the word "then", commas, "after") and wires each task's `dependsOn` to the one before it in that order, then runs the rest identically to `t`. `m` proceeds into "Milestone mode (`m`)" below before continuing into Step 3.
---
## Step 1: Locate the roadmap and check the format
Run `python3 "${CLAUDE_PLUGIN_ROOT}"/scripts/roadmap.py detect`. Exit **3** = old simple format: **stop and tell the user to run `roadmap:migrate` first**. Exit **2** = could not locate/parse: ask the user for the path. Only proceed on exit 0.
Read the full `roadmaps.json` and the active phase's PHASE file before adding: you need the existing task graph, milestone IDs, gates and categories.
---
## Step 2: Understand what to add
Extract: **description**; **milestone** (which milestone; ask if unclear); **tier** (read from the milestone name's trailing `(Secondary)`/`(Tertiary)` suffix, Core when there is none; see the conventions reference, Tiers. A follow-up to a task in one tier often belongs in a later tier, and that is where the tier gate in Step 4 applies); **category** (2–3 letter prefix, reuse an existing one in that milestone where it fits); **dependencies** (what must be done first; what it unblocks); **assignee** (ask the user directly; never infer it from the description, the git author, the category owner or who is running this skill; leave it unset if the user doesn't say). Ask before proceeding if any is ambiguous: a badly placed task is worse than a delayed one.
**In milestone mode (`m`), do this instead of picking an existing milestone**: see "Milestone mode" below, then continue into Step 3 for the seed task(s).
---
## Milestone mode (`m`)
No existing skill appends a milestone to a *live* phase (`roadmap:create` builds a whole phase from scratch, including its milestones, in one pass; `roadmap:migrate` upgrades an old-format file wholesale). This is that missing procedure. Run it in place of the ordinary "pick an existing milestone" half of Step 2, then continue into Step 3 onward for the seed task(s) that populate the new milestone.
1. **Assign the milestone ID**: `M{max existing milestone number + 1}`. Never reuse a milestone ID, mirroring the task-ID rule below. This numbering rule and the milestone field-order rule both live in `${CLAUDE_PLUGIN_ROOT}/references/roadmap-conventions.md`; read it there rather than re-deriving it.
2. **Milestone object shape**: exactly `{id, name, goal, tasks: []}`; no other fields. Ask the user for `goal` (one sentence) if they haven't given one.
3. **Milestone-to-milestone gating**: milestones have no `dependsOn` field of their own; a "this milestone can't start until X finishes" gate is expressed on the seed task(s) inside it instead. If the user says the new milestone is gated on other milestones, give the seed task `dependsOn: ["M{a}", "M{b}", ...]`; it resolves once every non-`softMilestone` member of each is `done` or `out_of_scope`.
4. **Flag mid-sequence insertion rather than deciding it silently.** `M{N}` numbering is loosely coupled to task-ID category prefixes elsewhere in a roadmap (a category can span several milestones), and nothing documents what happens if a milestone is ever inserted between existing ones rather than appended at the end. Appending (this procedure) is unambiguous; if the user ever wants to insert mid-sequence, say plainly that this is an open question rather than picking a convention on the spot.
5. **Childless-milestone check**, parallel to Step 5's childless-task check below: a milestone with only one seed task is fine on its own. Flag it only if that task's own nature obviously implies more should follow soon.
6. Proceed into Step 3 for the seed task(s), exactly as for any other new task, with the milestone now resolved to the ID just assigned.
---
## Step 3: Assign a task ID
`{MilestoneNum}{Category}.{Seq}`: find the highest sequence in that category and use `next = highest + 1`. Sub-tasks: alpha suffix (`2TI.15a`). **Never reuse an ID**, even a removed one.
---
## Step 4: Identify dependencies
**Incoming** (`dependsOn`): tasks this new task requires. An entry may be a **task ID**, a **milestone ID** (`M1`: resolves done only when all its tasks are done), or an **external gate ID** (from `externalGates`). If a gate is an incoming dependency, the gate's `blocks[]` must gain this task ID (parity).
**Tier gate** (when the target milestone is in tier 1 or above): a tier reaches a lower tier by milestone, never by task (conventions reference, Tiers). Before wiring anything from a lower tier, read what the milestone's existing tasks carry as lower-tier entries (`["M7", "M8", "M9", "M10", "M11"]`, or `["M1", "M6"]`; with no siblings yet, the gate the tier's other milestones carry): that list is the gate, and the new task copies it. Then, for every lower-tier **task** the user named as a prerequisite: if the gate already covers it (its milestone is in the list, or a listed entry reaches it), it does not go in `dependsOn`; record it in `notes` as `Extends {ID}` instead, and say which sibling the gate was copied from. When the gate does not cover it, the task edge is still wrong; the prerequisite's **milestone** is what joins the gate. By default the task keeps its tier: add that milestone to the copied list (`["M7", "M8", "M9"]` where the siblings carry `["M7", "M8"]`), note `Extends {ID}` as usual and flag in Step 7 that this task's gate now differs from its siblings'. The one exception is a task the user confirms is meant to escape its tier (wanted soon, so it waits on no lower-tier milestone): then the task edge stays, it carries no gate at all and `notes` says so. One prerequisite neither branch covers: a `softMilestone` member of a lower-tier milestone. A soft member never gates its milestone (conventions reference, Soft milestone members), so `M10` in the list does not reach `10SE.7` when that task is soft, and adding M10 to the gate would not reach it either. The task edge is the only thing that waits on it, so it stays in `dependsOn` alongside the copied gate, and `validate` accepts that shape. The one way the gate does reach a soft member is through a hard member of its milestone that depends on it (M10 cannot finish before that hard member, which waits on `10SE.7`): then the gate covers it after all, so record `Extends 10SE.7` and leave the edge out. `validate` (Step 9) refuses a covered lower-tier task edge, so make the choice here; Step 9 only reports it.
**Outgoing**: existing tasks that this new task should now block; add the new ID to their `dependsOn` (and mirror any gate parity). Completing this task may change those tasks' computed status (the recompute handles that). If one of them is claimed (it has a `started` date), say so before adding the edge: the new dependency blocks work someone has already started.
**Soft (`softDependsOn`):** an optional, best-effort link worth drawing but not worth blocking on; ask if the relationship is a real dependency or a soft one before defaulting to `dependsOn`. Renders dotted, imposes no status, no cycle constraint (see conventions reference for direction).
---
## Step 5: Graph integrity checks (before writing)
**Orphan check.** A task with no dependency edges in or out is orphaned. Warn (`"This task has no connections to the existing graph. Intentional?"`), suggest the most plausible connection, and proceed on the user's call. Some tasks genuinely stand alone.
**Childless check.** If nothing depends on the new task but its nature clearly unlocks future work, create a placeholder child in the appropriate milestone: `- [ ] **{NewID}**: {unlocked capability} _(blocked: depends on {NewTaskID})_`, `status: "blocked"`, with the dependency edge. Skip placeholders for obviously terminal tasks (deploy, final release notes). Tell the user what placeholder was created and why. In milestone mode (`m`), also run the childless-*milestone* check from that section above.
---
## Step 6: Compute the new task's status (mechanical)
The mechanical status rule applies (see conventions reference): empty `dependsOn` → `todo`; any non-`done` dependency → at least `blocked`, escalating under `deferred > paused > blocked > todo` — this covers both a dependency on a gate that `imposes: paused`/`deferred` and a dependency on a task that is itself already `paused`/`deferred`. `softDependsOn` never feeds this rule. After wiring, confirm the new task's status and any downstream changes with `python3 "${CLAUDE_PLUGIN_ROOT}"/scripts/roadmap.py recompute --check` (preview, no write).
---
## Step 7: Prepare the proposal (do not edit yet)
In milestone mode (`m`), lead with the milestone block, then the seed task block below it:
```text
New milestone: {ID}, {Name}
Goal: {goal}
Gated on: {milestone IDs, or "nothing"}
```
Task block (always; the only block in `t`/`c` modes):
```text
New task: {ID}, {description}
Milestone: {N} ({Milestone Name}) Status: {todo|blocked|paused|deferred}
Assignee: {name, or "unassigned"}
Dependencies (in): {IDs / milestone / gate, or "none"}
Tier gate: {lower-tier milestone IDs copied, and the sibling they came from, e.g. "M7, M8, M9, M10, M11 (from 13SE.1)"; "M7, M8 (from 13SE.1) + M9 for 9AB.2: differs from siblings" when a prerequisite's milestone was added; "n/a: core tier"; or "none: escapes the tier"}
Lower-tier work noted, not wired: {IDs moved into notes as "Extends {ID}", or "none"}
Dependencies (out): {task IDs this gets added to, or "none"}
Placeholder child: {ID and description, or "none"}
Graph changes:
+ roadmaps.json: new milestone object (m mode only); new task object; edits to {existing tasks' dependsOn}; gate blocks[] updates
+ diagram: regenerated from the JSON (node {ID}; edges {list})
```
Then ask: *"Does this look right? I'll write to the roadmap on your say-so."*
---
## Step 8: Write to both artefacts (once approved)
0. **`m` mode only, before anything else**: insert the new milestone object (`{id, name, goal, tasks: []}`) into the phase's `milestones[]` array, after the last existing milestone.
1. **`roadmaps.json`**: insert the task object in its milestone's `tasks[]` (field order per the conventions reference; tabs; British spelling). Include `assignee`/`softDependsOn`/`softMilestone` only when the user gave one; omit them entirely otherwise, exactly like `notes`. Update any existing tasks' `dependsOn`. Update any gate's `blocks[]` for parity. Add the placeholder task if any.
2. **PHASE file**: add the task line under its milestone with the status annotation (`_(blocked: depends on {IDs})_` etc.); update any existing task lines whose dependency clause changed; add the placeholder line. **`m` mode only**: first emit a full new milestone section (`## Milestone {N}: {Name}`, blank line, `**Goal:** {goal}`, blank line) before the task line, matching `roadmap:create` Step 7's shape. This is new work into an existing milestone; the section doesn't exist yet the way it does for a milestone `roadmap:create` built from scratch.
3. **Mermaid diagram**: replace the entire fenced `mermaid` block with the output of `python3 "${CLAUDE_PLUGIN_ROOT}"/scripts/roadmap.py graph --mermaid --direction LR`. Never hand-edit edges, sinks or class lines; the generator recomputes milestone sinks (including any former sink displaced by the new task) and the canonical colours.
4. **`ROADMAP_OVERVIEW.md`**: the task total (and, in `m` mode, the milestone total) changed, so update `**N tasks across M milestones.**`; pull *both* numbers from `python3 "${CLAUDE_PLUGIN_ROOT}"/scripts/roadmap.py stats --json` (`total` and `milestonesTotal`), never just the task count.
---
## Step 9: Validate and confirm
Run `python3 "${CLAUDE_PLUGIN_ROOT}"/scripts/roadmap.py validate`; it must report clean (parity, acyclicity, status recompute, no lower-tier task edge the task already requires by milestone). A cross-tier discrepancy here means Step 4's tier gate was skipped: drop the edge and move the ID into `notes`, never the reverse. If an HTML dashboard exists (`docs/artefacts/roadmap-*.html`), refresh it with `roadmap.py render`. Then report: task added (and milestone, in `m` mode); status; edges added; existing tasks modified; placeholder created; any orphan warning. If the new task's computed status is `blocked` on a dependency that looks like it should already be `done` in reality, say so explicitly rather than reporting `blocked` as if the wiring itself is uncertain; the roadmap can lag real-world completion.
---
## Conventions
- Task line: `- [ ] **{ID}**: {description}` + annotation. Completed: `- [x] **{ID}**: {description}`.
- Milestone section: `## Milestone {N}: {Name}` + `**Goal:** {goal}`, matching `roadmap:create` Step 7.
- roadmaps.json is the source of truth; the PHASE file and overview are projections.
- Everything else (statuses, colours, graph/edge rules, formatting, milestone-ID assignment and milestone field order): `${CLAUDE_PLUGIN_ROOT}/references/roadmap-conventions.md`.
<raw-arguments value="$ARGUMENTS" />