skills/roadmap-maintain/SKILL.md
{{ 𝛀𝛀𝛀 }} Recompute and synchronise roadmap task statuses across roadmaps.json and its projections, with optional codebase reconciliation
npx skillsauth add jasonwarrenuk/goblin-mode Roadmap: MaintainInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Keep the roadmap coherent across its artefacts — .claude/roadmaps.json (machine-readable source of truth; operate on the active non-archived phase), the PHASE file it names (task list + dependency diagram), docs/reports/ROADMAP_OVERVIEW.md (prose overview) and the HTML dashboard if one exists — by recomputing statuses from the dependency graph and synchronising every artefact. Optionally, first reconcile the graph itself against the actual codebase.
Shared conventions: ~/.claude/library/references/roadmap-conventions.md. The CLI is python3 "$HOME"/.claude/library/scripts/roadmap.py.
Behaviour note. The recompute (Steps 2–7) is mechanical, not inferred. It does not guess whether a blocker still applies, does not suggest promoting tasks to "in progress" (there is no in-progress state), and does not delete completed nodes from the diagram. A task's status is a deterministic function of its
dependsOn, computed byroadmap.py recompute. The only sanctioned exception is Step 0: an opt-in, evidence-gated reconciliation against the codebase that proposesdonecalls and blocker-edge removals for you to confirm before anything is written. Outside Step 0, a gate or blocker clears only by a deliberate edit to the graph.
If $ARGUMENTS names a milestone (e.g. M3), scope your reporting to that milestone; the recompute always reads the whole graph, because dependencies cross milestones. If $ARGUMENTS says reconcile (or the user's intent is "check the roadmap against what's actually built" / "I just finished a batch, update the roadmap"), run Step 0 first.
Skip this step entirely on a plain status-sync run. Run it when asked to reconcile, or after the user reports finishing a batch of work.
Detect + load. Same guard as Step 1: roadmap.py detect (stop on exit 2/3). Read roadmaps.json.
Build the candidate set. From roadmaps.json directly, take every task with status todo, blocked, or paused. Skip done, out_of_scope, and deferred — never reconsider those. Note that roadmap.py ready --json only returns effective-todo tasks, so it under-covers this set on its own — use it purely to order the todo portion by leverage (transitiveUnblocks / isMilestoneSink), so the highest-impact tasks get checked first and you can stop early on a large roadmap; blocked/paused candidates still come from the direct JSON read and are checked because their dependency might now be satisfied in code even though they aren't yet todo.
Bound the search by recency. Get the changed-file set since the last reconciliation: if a prior run left a "last reconciled at <sha>" marker (see step 7), use git diff --name-only <sha>..HEAD; otherwise use a recent window (git log --oneline -30 and git diff --name-only HEAD~30..HEAD, or the whole history for a small/new repo). Only search within this changed-file set — never the whole tree.
Search per candidate, not in bulk. For each candidate, derive 1–3 concrete terms from its description/notes (a filename, symbol, route, component name) and Grep/Glob for them within the changed-file set. Read a file only when a search hits.
Classify each candidate, applying the evidence rule below:
done. Propose removing that specific dependsOn/gate entry (and the gate's blocks[]). When the blocker is another task, don't propose a separate unblock — proposing that blocking task done (above) is sufficient, since recompute cascades the unblock automatically once its status changes.done task whose code can no longer be found. Report only; never revert a terminal status automatically.Evidence rule (conservatism):
done, out_of_scope, or a root-seeded held paused/deferred.Confirmation gate. Present the proposal before writing anything:
Proposed done (codebase evidence):
{ID} — {description}
evidence: {file:symbol / route / test}
Proposed unblock (prerequisite satisfied — remove edge):
{ID} — remove dependency on {depId or gateId}
evidence: {…}
Unconfirmed (needs your call):
{ID} — {why uncertain}
Reverse drift (marked done, code not found):
{ID} — {what's missing}
Then ask: "Does this look right? I'll apply what you confirm and recompute." Only the approved subset proceeds. Record the current git rev-parse HEAD as this run's "last reconciled at" marker for next time (mention it in the final report; no separate file needed unless the user wants one persisted).
Hand off. Feed every approved done ID and every approved edge removal into Step 2 below, exactly as if the user had typed them. Continue with Steps 1–7 as normal (Step 1's format check still applies; Step 2 now has explicit edits to make).
Run python3 "$HOME"/.claude/library/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 .claude/roadmaps.json, the active phase's PHASE file (its path), and docs/reports/ROADMAP_OVERVIEW.md.
If the user is marking tasks done (or resetting them to todo/blocked), edit those status fields in roadmaps.json first — preserving tab indentation, field order (id, description, status, dependsOn, softDependsOn, iterative, notes, assignee), and the notes/iterative/assignee/softDependsOn values exactly. The recompute in step 3 sets every derived status; you only hand-edit terminal decisions (done, out_of_scope) and deliberate parked seeds. Never touch or infer assignee here — this step edits status only.
If the user asks to add or remove a soft (optional, best-effort) link between two nodes, edit the relevant task's softDependsOn array directly — see the conventions reference for direction and semantics. Soft edges never go through the recompute in step 3; they're pure data, picked up automatically when the diagram regenerates in step 5.
If Step 0 ran, this is also where its approved edits land: set status: done on each approved done-ID, and remove each approved dependency/gate edge from the relevant task's dependsOn (and the gate's blocks[], keeping the two in parity). Apply only what was explicitly approved — an unconfirmed or reverse-drift item from Step 0 is never written here.
Run python3 "$HOME"/.claude/library/scripts/roadmap.py recompute --render. It applies the fixed-point recompute under the precedence rule deferred > paused > blocked > todo, writes the corrected statuses back atomically, and refreshes the HTML artefact when one exists. It prints each {ID}: old -> new change; capture that list — it drives the projection sync below.
Failure modes, all surfaced by exit 1 with a message — stop and report, never work around:
dependsOn — the graph must be fixed (loops belong on the iterative flag, not a real back-edge).--reformat (it rewrites the whole file, not just statuses).The rule it applies is in the conventions reference; you never compute it by hand.
For each task whose status changed (from the script's output), update its line in the PHASE file. If the file uses status sub-sections, move the task under the matching sub-section (omit any that would be empty); otherwise leave it in place. Update the checkbox and trailing annotation:
| Status | Checkbox | Trailing annotation |
| ----------------------------------- | -------- | ---------------------------------------------------------- |
| done | - [x] | none |
| blocked | - [ ] | _(blocked — depends on {comma-separated dependsOn IDs})_ |
| paused | - [ ] | _(paused — reconvene {gateId})_ |
| deferred | - [ ] | _(deferred to a later phase)_ |
| todo with dependencies (all done) | - [ ] | _(depends on {IDs})_ |
| todo with empty dependsOn | - [ ] | none |
| out_of_scope | - [ ] | left as authored (terminal) |
Line format: - [ ] **{ID}** — {description} + annotation. Indented - Note: sub-bullets stay attached to their task and are never moved. Do not reorder tasks — they stay in milestone/category/sequence order.
Soft edges have no annotation of their own — they're carried entirely by the Mermaid diagram (step 5), never by a checkbox-line annotation.
Replace the entire fenced mermaid block under ## Dependency Diagram with the output of:
python3 "$HOME"/.claude/library/scripts/roadmap.py graph --mermaid --direction LR
Wholesale replacement — never line-edit class lists or recolour by hand. The generated block carries the canonical classDefs, every edge under the terminal milestone convention, and correct class statements, so the diagram cannot drift from the JSON. (A legacy diagram that used the open class or old hexes is fixed by this same replacement.) softDependsOn entries render as dotted edges (X -.-> Y) in this same output — never hand-add a dotted edge afterwards; it authors as data or it gets wiped by the next reconcile.
A pure status update does not change the task total, so the header count usually holds. If it changed (after an add/remove), update **N tasks across M milestones.** — get the number from python3 "$HOME"/.claude/library/scripts/roadmap.py stats. Do not rewrite the prose narrative.
Run python3 "$HOME"/.claude/library/scripts/roadmap.py validate — it must report clean. Then report each {ID}: old → new status change grouped by milestone, whether the diagram block was regenerated, whether the HTML artefact was refreshed, and the overview count if it changed. On a reconcile run, also restate the Step 0 proposal outcome (what was approved and applied, what was left unconfirmed, any reverse drift) and the commit SHA to use as next time's "last reconciled at" marker.
dependsOn and its blocks[]), never by casual judgement.done calls and blocker-edge removals, always evidence-backed and always confirmed before Step 2 writes them. It is opt-in — only runs on a reconcile request, never silently.done and out_of_scope are terminal; root-seeded parked tasks are held as authored (details in the conventions reference). Step 0 never re-opens or flips these; reverse drift is reported, not corrected automatically.tools
{{ 𝚫𝚫𝚫 }} Rebuild roadmap-system.zip, the distributable snapshot of the roadmap tooling (scripts, HTML template, conventions reference, and every roadmap-touching skill, including this one).
tools
--- name: "Suggest: Task" description: "{{ 𝚫𝚫𝚫 }} Suggest the next logical task — grounded in the roadmap's pre-vetted ready-set when one exists, codebase analysis otherwise" when_to_use: "When you don't know what to work on next and want a grounded recommendation rather than picking arbitrarily." model: haiku effort: low disable-model-invocation: true allowed-tools: ["Read", "Glob", "Grep", "Bash(python3:*)", "Bash(npm:*)", "Bash(bun:*)", "Bash(pnpm:*)", "Bash(deno:*)"] argument-hint: [named
development
{{ 𝛀𝛀𝛀 }} Convert an old simple-style roadmap (single Markdown, four statuses, <a name> anchors, roadmaps.json pointer registry) into the rich phase-array format (roadmaps.json source of truth + PHASE task list + prose overview).
data-ai
{{ ƔƔƔ }} Create a pull request to main — wordy or shiny (with screenshots), ready-for-review or draft