From speckit-claude
Show the recommended next feature to build based on specs/ directory and dependency graph. Scans every feature spec, derives build status from tasks.md completion, and recommends the lowest-numbered spec with all dependencies satisfied.
How this skill is triggered — by the user, by Claude, or both
Slash command
/speckit-claude:build-orderThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Any of these phrases should trigger this skill when spoken in natural language — please invoke `/build-order` when the user asks:
/build-order)Any of these phrases should trigger this skill when spoken in natural language — please invoke /build-order when the user asks:
$ARGUMENTS
Arguments are whitespace-separated tokens in any order. Three kinds are recognised:
1, 2, 3, or 4. Passed through to the scanner as --tier=<N> (or as a bare digit — generate.ts accepts both).stale — show the full stale-artifacts table (normally collapsed behind a count when >5 rows). Sets STALE_FULL=1 env var for generate.ts.text, html, or both. Controls which artifacts you show the user:
text — run generate.ts --format=md, print its markdown, do NOT open the browser.html — skip the scanner entirely, just open docs/build-order.html (trust git hooks + CI for freshness; see Step 0.5 freshness check).both (default) — run generate.ts --format=md, print its markdown, AND open docs/build-order.html.Any other token is ignored with a stderr warning. /build-order 1 text, /build-order text 1, and /build-order 1 are all valid. /build-order html skips the scanner entirely.
Output format determination follows a graceful-fallback ladder. AskUserQuestion is unverified inside skill workflows — in non-interactive contexts (CI, /loop auto-pacing, parent-skill invocation) it can deadlock or silently default. The ladder below treats interactive prompting as a best-effort last resort.
$ARGUMENTS. Tokenise on whitespace. If any token equals text, html, or both (case-insensitive), use that format — skip to Step 1. (If multiple format tokens are present, use the last one and warn about the others on stderr.)GITHUB_ACTIONS=true is set, default to both and skip to Step 1. (Other non-interactive signals are less reliable; rely primarily on this one for CI contexts.)Both (default) — text + open HTML, Text only — print markdown, no browser, HTML only — skip scanner, open the styled report. Use Both as the default selection. If the prompt succeeds, use the user's answer.both and continue. Never block. The markdown is always valuable output; worst case is the user sees both artifacts when they'd have chosen one.html)When the user picks html (either via $ARGUMENTS or the prompt), run a quick freshness check before calling open docs/build-order.html:
if [ -f docs/build-order.html ]; then
html_mtime=$(stat -f %m docs/build-order.html 2>/dev/null || stat -c %Y docs/build-order.html 2>/dev/null)
specs_newest=$(find specs -name tasks.md -exec stat -f %m {} \; 2>/dev/null | sort -nr | head -1)
[ -z "$specs_newest" ] && specs_newest=$(find specs -name tasks.md -exec stat -c %Y {} \; 2>/dev/null | sort -nr | head -1)
if [ -n "$html_mtime" ] && [ -n "$specs_newest" ] && [ "$html_mtime" -lt "$specs_newest" ]; then
echo "WARNING: docs/build-order.html may be stale (no local regen since specs/ changed); consider running in 'both' mode to refresh." >&2
fi
fi
Do NOT auto-regenerate — the CI + git hooks are the regen authority. Just warn and proceed to open the HTML.
text and both formats; skipped for html)Run the scanner (canonical mode — fetches from origin/master before scanning):
env -u BUILD_ORDER_USE_WORKING_TREE STALE_FULL=<1-if-stale-arg-else-0> npx tsx scripts/build-order/generate.ts --format=md ${tier:+--tier=$tier}
Set STALE_FULL=1 when $ARGUMENTS contains stale (shows full stale table); otherwise STALE_FULL=0.
The env -u BUILD_ORDER_USE_WORKING_TREE prefix unsets that env var so a dev with it in their shell profile still gets canonical mode from /build-order.
The scanner fetches origin/master before scanning. Spec files are still read from the working tree, so unmerged local changes to specs/ are visible. In canonical mode, the scanned-at source ref is derived from origin/master, but other metadata such as timestamps or displayed commit SHA may still reflect wall-clock execution time or the current checkout. If the fetch fails and BUILD_ORDER_USE_WORKING_TREE is not set, the scanner exits non-zero with a clear error message.
If a dev genuinely wants working-tree mode (rarely — only for debugging uncommitted local edits), set BUILD_ORDER_USE_WORKING_TREE=1 and run npx tsx scripts/build-order/generate.ts --format=md directly, NOT via /build-order. CI auto-bypasses the fetch gate via GITHUB_ACTIONS=true since CI is already on the correct ref.
Output the scanner's markdown verbatim to the user. Do NOT summarize, paraphrase, strip emoji, or cut sections. The scanner produces a complete report and the user wants to see all of it. Only add a brief sentence before or after the report if you're highlighting something specific (e.g., "Here's the current build order:" or "The blocked feature waiting on F17 is D3.").
The scanner's output includes ALL of these sections (depending on what's in the repo):
ready ∪ logicallyReady queue, sorted by priority (P1>P2>P3>untagged) → tier (1>2>3>4) → spec number → tasks.md-present. Shows title, fid, tier, priority, branch, and the Next command to run (/speckit-implement if tasks.md exists, else /speckit-plan). Before running that command, run /speckit-claim {N} to claim the spec and create your branch. Replaces the legacy "Next recommended" + "Next to plan" pair./speckit-* skill to run to advance that spec.tasks.md completion, showing progress fraction and branch status- for untagged). Sorted by priority → tier → spec number.Status: Superseded. Collapsed bullet list with the fm note (what they were consolidated into). Excluded from dependency resolution and waves.Status: Built in frontmatter contradicts an incomplete tasks.md. Non-fatal at scan time; CI lint (.github/workflows/build-order-lint.yml) fails the build if this section is non-empty./build-order is the single unified entry point. Same content as /migration-backlog summary: next-actionable migrations, deferred entries with triggers, externally-blocked, and the full planned list. Reads scripts/lib/migration-backlog-data.ts via scripts/migration-backlog.ts.Every table row includes a Branch column with one of:
- (no branch anywhere)Pass the emoji icons through as-is. Modern terminals render them. Do not substitute ASCII.
html and both formats; skipped for text)The committed docs/build-order.html is regenerated by CI on every push to master and by local git hooks on pull/checkout/commit; popping it after each /build-order run keeps the styled view in sync with the markdown the user just saw. Use the cross-platform helper:
bash .githooks/_open-html.sh && bash -c '. .githooks/_open-html.sh && open_build_order_html docs/build-order.html'
Or directly: open docs/build-order.html (macOS) / xdg-open docs/build-order.html (Linux) / explorer.exe docs/build-order.html (WSL). If popping fails (no display, headless SSH), don't retry — the markdown the user saw is the authoritative report.
For html format: after the Step 0.5 freshness check warns (or not), run the open command and that's it — no scanner, no markdown to the user.
If the user asks follow-up questions about a specific feature's dependencies, you can read the corresponding specs/NNN-*/spec.md directly — don't re-run the scanner for single-feature questions.
For a styled HTML view with a mermaid dependency graph: docs/build-order.html is the browser-viewable report, regenerated by local git hooks (git config core.hooksPath .githooks) on pull, checkout, and commit, and by /build-order itself. CI does NOT auto-commit it (GitHub blocks the bot from bypassing required PR review); the file lands in master through normal human PRs that touch specs/migrations/scanner. CI surfaces drift via job summary so a regen-and-commit is added to the next relevant PR. Same source data as the markdown, richer presentation.
Frontmatter-first resolution ladder. The scanner checks **Status**: in each
spec's header and falls back to tasks.md completion only when the frontmatter
doesn't declare a terminal status:
Status: Superseded … → SUPERSEDED (own section, excluded from deps/waves)Status: Complete … → BUILT (fm note captured)Status: Built (partial — …) → IN PROGRESS (parenthetical captured as note)Status: Built → BUILTtasks.md missing → NOT PLANNEDtasks.md all [x] → BUILTtasks.md some [x] → IN PROGRESStasks.md all [ ] / empty → NOT BUILT / NO TASKSDrift warnings. If frontmatter says Status: Built AND tasks.md still has
unchecked items, the spec is flagged in a dedicated ## Drift warnings section.
The frontmatter signal wins (spec is classified BUILT), but the warning surfaces
the inconsistency so the spec author can resolve it. CI lint fails on drift.
Depends On sentinels. Three states for the **Depends On**: line matter:
None / N/A / - — explicit no-deps. Unbuilt specs land in "Logically ready" — plannable immediately.This distinction makes "I haven't decided" different from "I've decided: no deps." Both are valid states but route differently.
Why frontmatter first. tasks.md-only was fragile: 14 specs shipped and had
their Status updated in frontmatter but never had a tasks.md authored, and the
scanner silently misclassified them as "not planned" and leaked them into Wave 1
parallelism suggestions. Frontmatter is the author's declared truth; tasks.md is
the in-flight-work signal. Trust the declared truth, warn on divergence.
The scanner reads two lines from each spec's frontmatter and merges them into a single dependency set:
**Depends On**: F## Feature IDs — feature-level dependencies resolved against other specs in the specs/ directory**Required Migrations**: NEW-### migration tags — schema-level dependencies resolved against the /migration-backlog registry at scripts/lib/migration-backlog-data.tsA spec is classified as:
Cross-reference to /migration-backlog: at startup, the scanner shells out to npx tsx scripts/migration-backlog-dump.ts once to get a TSV snapshot of every migration tag and its current status (via alias resolution for implemented_by entries). A migration is considered built if its status is built, built (schema), or built (reduced). Deferred, retracted, and planned tags count as not-built. If the dump helper fails (missing tsx, registry corrupt), the scanner falls back to F##-only dependency resolution with a stderr warning — the feature-level classification still works, just without the migration-dep overlay.
Example frontmatter:
**Feature ID**: F15
**Depends On**: F10 Deal Memory, F14 Calibration
**Required Migrations**: NEW-065, NEW-079
A spec with that frontmatter is ready only when F10, F14, NEW-065, and NEW-079 are all built. If NEW-065 is still planned, the scanner puts F15 in the blocked table with Waiting on: F14,NEW-065.
Why both fields instead of one: feature deps and migration deps have different granularity and different sources of truth. Feature deps live in the same spec directory tree; migration deps live in a machine-validated registry with a CI lint. Keeping them as separate frontmatter lines makes the declaration explicit and lets the scanner dispatch on the tag prefix without ambiguity.
Up Next and the Proceed queue both draw from the unified union of ready ∪ logicallyReady and sort by a fully locked deterministic key:
**Priority**: P1 in spec frontmatter. Priority is the primary key. A P1 Tier-2 Logically-Ready spec WINS over a P2 Tier-1 Ready spec — because priority trumps tier.tasks.md (Ready) wins over the one without (Logically Ready). This is the first-movers tiebreak: implement beats plan when everything else is equal.The same key is used for the single Up Next pick and the full Proceed ordering. Same repo state → same recommendation every time.
The legacy "Next recommended" + "Next to plan" sections are removed from the rendered report, but BuildOrderModel.nextRecommended and nextToPlan are preserved in the model for downstream consumers that read the JSON output (deploy-monitor skill, external scripts).
Specs may declare a rough size estimate in their frontmatter:
**Estimated effort**: M
The size token is one of XS / S / M / L / XL. The scanner maps to hours via a single constant in scripts/build-order/types.ts. The mapping is tunable in one place:
| Size | Hours | Rough dev time |
|---|---|---|
| XS | 4h | half day |
| S | 8h | 1 day |
| M | 16h | 2 days |
| L | 24h | 3 days |
| XL | 40h | 1 week (scale cap) |
The XL cap at one week is deliberate. Specs that exceed one week of dev time should be split into multiple smaller specs, or represented as an epic in a task tracker where child work fits the XL-and-smaller scale. The scanner does not model multi-week epics.
Specs without the field are counted in specsMissingEstimate and render as — in the HTML. The summary aggregates across only the specs that have an estimate — totals are honest about incomplete data.
The HTML surfaces effort in three places:
X hours built · Y hours remaining · Z hours total · N of 53 specs estimatedTuning the scale is one-line: edit EFFORT_SIZE_HOURS in scripts/build-order/types.ts. No per-spec edits needed.
Forward-compat format: **Estimated effort**: M (~16h) is also accepted — the parenthetical is informational only; the scanner uses the bucket. Authors who want to override the bucket value should edit EFFORT_SIZE_HOURS instead.
The scanner runs git fetch origin --quiet at startup to update remote refs. After the main report, it compares each spec's local tasks.md against origin/master:specs/NNN/tasks.md. If origin/master has a more complete tasks.md (more [x] items, or a tasks.md that doesn't exist locally), the scanner flags it in a Stale local data section with a git pull prompt.
This closes the multi-dev visibility gap: if another developer merges a completed feature to master, every dev running /build-order sees the stale warning without needing to remember to pull first.
The fetch adds ~1-2 seconds. If the machine is offline or has no remote configured, the fetch fails silently and the remote check is skipped.
/speckit-implement work — it reads the same tasks.md the implement skill is updating, so the output will lag behind reality.specs/ on the current checkout, so the output is branch-relative.Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub gratefuljinx77/speckit-claude --plugin speckit-claude