一键导入
roadmap-to-workflow
Fan an approved `.task/roadmap/<slug>.md` out to a dynamic Workflow — parallel planning, serialized implementation, dependency-ordered waves.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Fan an approved `.task/roadmap/<slug>.md` out to a dynamic Workflow — parallel planning, serialized implementation, dependency-ordered waves.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Capture a multi-task initiative into `.task/roadmap/<slug>.md` — a phase-grouped backlog of ready-to-pick-up items.
Capture load-bearing technical decisions into a standalone `.task/spec/<slug>.md` — Decision/Rationale/Constrains sections cited via `Spec:`.
Interrogate a plan or decision one question at a time before capture, keeping a decision-plus-rationale ledger, then route to the right capture skill.
Capture the chat into `.task/task/<slug>.md` with `## Description` plus `## Plan` (Goal/Touches/Logic) — the deepest one-task capture.
Capture the current chat discussion into a task, no implementation plan
Self-audit this skills repo against CLAUDE.md invariants, the v3 artifact contract, and README/CLAUDE.md/docs sync via three parallel read-only subagents. Local meta-skill — independent of the /task:* pipeline.
| name | roadmap-to-workflow |
| description | Fan an approved `.task/roadmap/<slug>.md` out to a dynamic Workflow — parallel planning, serialized implementation, dependency-ordered waves. |
| disable-model-invocation | true |
| user-invocable | true |
Drive an approved roadmap through a dynamic Workflow. This skill collects the roadmap's unchecked items, sorts them into dependency-ordered waves, then authors and invokes a dynamic Workflow (the Workflow tool) that, within each wave, plans all items in parallel and then implements them one at a time in the shared working tree, ticking off the roadmap as items land. It does not hand-roll that fan-out itself. If the Workflow tool isn't available, it falls back to running items serially by hand, in the same dependency order (Step 2).
Per-item model control. Each roadmap item may carry a **Model:** hint (haiku | sonnet | opus); the Workflow passes it to that item's implement agent as opts.model.
Per-item execution is a two-agent split by default — opus plans, the item's model implements (Step 2).
This skill is the opt-in for the Workflow tool — reading it and following the Steps is the authorization; there is no magic keyword and no separate confirmation.
Input: $ARGUMENTS — optional. A single positional <roadmap-slug> (or path) to skip the roadmap picker. No flags — item scope is chosen interactively (Step 0).
Format contract: docs/contract.md § Roadmap file format is the single source of truth for item grammar (### - [ ] N., **Dependencies:**, **Model:**); docs/contract.md § task.md format for the artifact each item's plan agent writes.
roadmap-to-workflow is not an intake skill — it never runs setup itself (a roadmap can't exist without config, so an absent config means something upstream is broken).
echo "$CLAUDE_PLUGIN_ROOT" # note this absolute path — bake it as PLUGIN_ROOT in the Step 2 script
source "${CLAUDE_PLUGIN_ROOT}/skills/_lib/resolve-ws.sh" # sourcing runs find_ai_dir → sets AI_DIR
[[ -f "$AI_DIR/config/config.md" ]] || echo "config.md not found"
bash "${CLAUDE_PLUGIN_ROOT}/skills/validate/validate.sh" all
config.md not found (the guard above echoes it; validate.sh all also exits 2 with the same message) → hard-stop redirect (do not bootstrap here):
The project isn't set up yet. Capture something first with
/task:to-task,/task:to-plan, or/task:to-roadmap— those set the project up inline.
validate.sh → validate.sh all checks every artifact, so an error may sit on a task or roadmap unrelated to this run. A validation error on the roadmap you are about to run stops the run — report it and do not proceed. Errors on other artifacts are surfaced but do not block. (WARN lines never set a non-zero exit; they are informational only.)If $ARGUMENTS gives a positional <roadmap-slug>/path, resolve it and skip the picker. Otherwise list the available roadmaps with progress (uses the kept roadmap.sh helpers):
source "${CLAUDE_PLUGIN_ROOT}/skills/_lib/roadmap.sh" # resolve_artifact_path, roadmap_progress_counts
shopt -s nullglob
for f in "$AI_DIR"/roadmap/*.md; do
counts=$(roadmap_progress_counts "$f")
total=$(awk -F': ' '/^total/{print $2}' <<<"$counts")
done_n=$(awk -F': ' '/^done/{print $2}' <<<"$counts")
printf '%s\t%s/%s\t%s\n' "$(basename "$f" .md)" "$done_n" "$total" "$f"
done
/task:to-roadmap."done == total > 0).AskUserQuestion (convention (c)), one chip per roadmap labelled <slug> (<done>/<total>); sort partial roadmaps first, complete ones last with a (complete) suffix, and refuse to proceed on a complete pick.Read the roadmap's Spec: <slug> header lines, if any — collect the referenced .task/spec/<slug>.md paths and pass them to every item's plan agent (Step 2) as fixed technical-decision anchors.
No flags — always ask interactively unless there's nothing to ask. When the chosen roadmap has more than one unchecked item, present a single AskUserQuestion (convention (c)) — "How much of <slug> should this run cover?":
AskUserQuestion free-text ("Other") option, e.g. 1,3-5,8; validate each number exists and is unchecked.One unchecked item → skip the question, run it. Zero unchecked → stop: "all items in <slug> are already done — pick another roadmap, or capture new work with /task:to-roadmap."
Read the resolved roadmap. For each unchecked (### - [ ] N.) item in the chosen scope, capture N, title, **Dependencies:**, and **Model:** (default sonnet when absent or off-list). This prints one N<TAB>deps<TAB>model<TAB>title line per unchecked item:
ROADMAP=$(resolve_artifact_path roadmap "<slug-or-path>") # roadmap.sh, sourced in Step 0
awk '
function flush() { if (pend) { print n "\t" deps "\t" (model==""?"sonnet":model) "\t" title; pend=0 } }
/^### - \[[ x~>-]\] [0-9]+\. / {
flush()
if ($0 ~ /^### - \[ \] /) { # unchecked item — start capturing
n=$0; sub(/^### - \[ \] /,"",n); sub(/\..*/,"",n)
title=$0; sub(/^### - \[ \] [0-9]+\. /,"",title)
model=""; deps=""; pend=1
}
next
}
/^\*\*Dependencies:\*\*/ && pend { deps=$0; sub(/^\*\*Dependencies:\*\* */,"",deps); gsub(/[ \t]/,"",deps); if (deps=="—"||deps=="-") deps="" }
/^\*\*Model:\*\*/ && pend { model=$0; sub(/^\*\*Model:\*\* */,"",model); gsub(/[ \t]/,"",model); if (model!="haiku" && model!="sonnet" && model!="opus") model="" }
END { flush() }
' "$ROADMAP"
Filter that list to the Step 0 scope. Then topologically sort into waves, computed by you (not by bash — the item set is small and this is reasoning, not parsing):
Dependencies are empty, or whose dependencies are all already checked ([x]/[~]/[>]/[-]) in the roadmap file — i.e. nothing left in this run blocks it.to-roadmap's cyclic-deps check is report-only, so a cycle can reach this skill.)The result is a waves: Item[][] structure — bake it into the Workflow script's literal in Step 2.
Author a dynamic Workflow from the computed waves and invoke it via the Workflow tool. Within each wave, plan agents run in parallel (parallel()) — safe because each writes only its own .task/task/<item-slug>.md, never the working tree — and then implement agents run strictly one at a time in the shared working tree, so wave-mates never collide on it. A barrier separates waves so a later wave never starts before every dependency it needs has landed; each implement therefore sees its already-landed wave-mates' commits, and /verify runs against the integrated state.
Each item runs as two agents, not one. Context passes between them via the on-disk .task/task/<item-slug>.md artifact — the first agent writes it, the second reads it fresh from disk — so there is no chat-context transfer to engineer:
const slug = "<roadmap-slug>";
const PLUGIN_ROOT = "<absolute value of $CLAUDE_PLUGIN_ROOT>"; // bake the LITERAL path
// the JS sandbox can't expand env vars, and a relative "skills/…" path is resolved
// against the agent's cwd, not this repo — Read needs the absolute plugin path
// (echo it in Step 0).
const waves = [ // from Step 1 — dependency order
[ { n: 1, title: "…", model: "sonnet" }, { n: 2, title: "…", model: "haiku" } ],
[ { n: 3, title: "…", model: "opus" } ],
// …
];
// PLAN on a strong model — writes .task/task/<item-slug>.md (see prompt below).
// Safe to run in parallel across a wave: a plan agent writes only its own task
// file, never the working tree. Opus is the planner floor (the default shape);
// scale reasoning effort down for lightweight items so a tiny `haiku` item
// doesn't pay a full deep-reasoning pass. Returns the digest line.
async function runPlan(n, title, model, w) {
const plan = await agent(
`Read ${PLUGIN_ROOT}/skills/to-plan/SKILL.md and run it NON-INTERACTIVELY for roadmap item
${slug}#${n} ("${title}"). Draft .task/task/<item-slug>.md (Description +
## Plan, + ## Tests if the config Testing Policy calls for it), stamping
the header with "Roadmap: ${slug}" and "Source item: #${n}", plus a
"Spec: <spec-slug>" line for each spec the item cites (via its
"### Spec references → <spec-slug> §N" entries or the roadmap's own
"Spec:" headers). Read each referenced .task/spec/<spec-slug>.md first as
a fixed technical anchor. Auto-accept every confirmation; make constructive
assumptions; never block on a prompt. Do NOT implement or commit.
Last non-empty line MUST be exactly:
OK #${n} <item-slug> planned (on success)
FAIL #${n} <item-slug> <what failed> (on failure)`,
{ model: "opus", effort: model === "haiku" ? "low" : "medium",
phase: `Wave ${w} · Item #${n}` }
);
return plan.trim().split("\n").filter(Boolean).pop();
}
// IMPLEMENT + VERIFY + REVIEW + COMMIT on the item's own model, reading the task
// file fresh from disk (no chat carries over from the plan agent). Run one at a
// time within a wave — this is the sole mutator of the shared working tree, so
// each implement sees its already-landed wave-mates' commits. Returns the digest.
async function runImplement(n, itemSlug, model, w) {
const r = await agent(
`Implement .task/task/${itemSlug}.md. Follow its ## Execution block
exactly: implement the ## Plan (or ## Description if no Plan), run
/verify end-to-end, run /code-review on the diff and apply fixes only
within the files named in Touches (report the rest), then commit per
.task/config/config.md → Commit Format. Do NOT tick the roadmap
checkbox yourself — the driver does that after this call returns OK.
Make constructive assumptions; never block on a prompt.
Last non-empty line MUST be exactly:
OK #${n} ${itemSlug} <one-line summary> (on success)
FAIL #${n} ${itemSlug} <what failed> (on failure)`,
{ model, phase: `Wave ${w} · Item #${n}` }
);
return r.trim().split("\n").filter(Boolean).pop();
}
for (const [w, items] of waves.entries()) {
// 1) PLAN the whole wave in parallel — plan agents only write .task/, never the
// working tree, so there is no collision. A single plan FAIL stops the run
// before any implement of this wave starts (plans are cheap to rerun).
const plans = await parallel(
items.map(({ n, title, model }) => () => runPlan(n, title, model, w + 1))
);
for (const [i, status] of plans.entries()) {
console.log(status); // per-item plan digest
if (status.startsWith("FAIL"))
return `roadmap-to-workflow stopped in wave ${w + 1} (planning), item #${items[i].n}: ${status}`;
}
// 2) IMPLEMENT the wave STRICTLY ONE AT A TIME — the shared tree has a single
// writer, so wave-mates never collide and each implement builds on the last.
for (const [i, { n, model }] of items.entries()) {
const itemSlug = plans[i].split(" ")[2]; // <item-slug>, echoed by the plan agent
const status = await runImplement(n, itemSlug, model, w + 1);
console.log(status); // per-item digest, printed as it lands
if (status.startsWith("FAIL"))
return `roadmap-to-workflow stopped in wave ${w + 1}, item #${n}: ${status}`;
// AUTO-MARK is the DRIVER's job, done here right after the implement returns
// OK — never inside the per-item agents, so parallel plan agents never race
// on the roadmap file. There is NO markRoadmapItemDone() helper — flip item
// N's checkbox with an anchored, macOS-portable awk rewrite (no GNU-only
// `sed -i`, no roadmap.sh helper). Match ONLY `^### - \[ \] N\. ` so a `> `
// blockquote line or a substring number is never touched:
//
// awk -v n="${n}" '
// $0 ~ ("^### - \\[ \\] " n "\\. ") { sub(/\[ \]/, "[x]") } { print }
// ' "$ROADMAP" > "$ROADMAP.tmp" && mv "$ROADMAP.tmp" "$ROADMAP"
//
// Run that against the roadmap file as the single driver-side write for N.
}
// Barrier: do not start wave w+2 until every item in wave w+1 above is marked.
}
return "roadmap-to-workflow: all items shipped.";
Graceful fallback: if the Workflow tool isn't available in this environment, run the items one at a time by hand, respecting the same wave order: for each item, run to-plan for that roadmap item (writes .task/task/<item-slug>.md) and take the exact written path from to-plan's own Step 8 output digest — do not reconstruct <item-slug> from the item title, since to-plan may disambiguate the slug on a collision (its Step 2a.5). Then in a plain session say implement <that path> and tell it not to tick the roadmap checkbox itself (despite its ## Execution block), then — as the driver — manually tick that item's checkbox in .task/roadmap/<slug>.md before moving to the next. This keeps the auto-mark the driver's job, exactly as in the Workflow path.
OK|FAIL #N <item-slug> <summary>), printed as each wave lands.→ Done. Roadmap complete — \.task/roadmap/.md` fully checked; review the landed commits with `git log`.`FAIL → surface the failing digest, then → Next: fix the item, then rerun \/task:roadmap-to-workflow` — completed items stay checked, only the unchecked remainder reruns.`config.md — this skill hard-stops and redirects; only to-task / to-plan / to-roadmap are intake-capable.Dependencies allow.OK, to avoid parallel writers racing on the roadmap file.