Summary
Teach the SDD flow about inter-feature dependencies, and let the existing pr-stack skill turn those dependencies into stacked PRs automatically — no new stack tooling, just SDD-driven orchestration.
Declare that one feature depends on another — Spec Kit and stacked PRs do the rest.
The problem
Spec Kit walks users through one feature at a time. But real features often build on each other: Feature B needs code from Feature A. Today that means:
- Wait for Feature A to be reviewed and merged before starting Feature B (blocks parallel work).
- Start B anyway without visibility into A''s code, inventing placeholders that get thrown away.
- Combine both into one giant PR that reviewers can''t follow.
Real example — issues logged on github/spec-kit:
- #4210 — Add deterministic contribution IDs and stack lookup IDs for resolved artifacts
- #4212 — Add
specify artifact command exposing composition stacks as JSON
#4212 depends on #4210. Running each in its own Copilot session today, the #4212 session branched off main and had no visibility into #4210''s in-flight work.
What already exists
- Stacked pull requests — GitHub feature that lets PR B target PR A before A is merged; reviews happen in parallel; upstream changes auto-rebase; merging the top lands the whole stack.
pr-stack skill — already ships in the Copilot app; wraps gh-stack for creating/managing stacks.
- Spec Kit +
spec-kit-copilot — handle intra-feature dependency ordering (phases, [P] markers, Dependencies & Execution Order in tasks.md) but have no first-class notion of feature-to-feature dependencies.
Gap
Spec Kit has no way to say "Feature #4212 depends on Feature #4210" such that any command acts on it. The spec.md "Assumptions" section is prose, not machine-readable, and assumes the dependency is already merged.
Proposal
Extend the existing spec-kit-copilot SDD skills (primarily speckit-workflow-step / speckit-workflow) to be stacks-aware. Delegate all Git/PR mechanics to the existing pr-stack skill. No new skill, no new user-visible commands in the MVP, no changes to core spec-kit.
Dependency declaration (any of these, unified internally)
Read whichever is available, in priority order:
- Explicit prompt at kickoff — "stack on #4210."
- GitHub sub-issue / tracked-by links on the issue.
Depends on: #NNNN line in the issue body.
dependencies: frontmatter in spec.md (persists after issue is closed).
All resolve to {feature_id, upstream_prs, upstream_branches}.
Hooks into each SDD phase
| Phase |
New behavior when a dependency exists |
| Session kickoff |
Detect deps → if unmerged upstream PR exists, prompt to stack → call pr-stack to check out on top of upstream branch. |
/specify |
Load upstream spec.md as context. Write structured ## Dependencies into new spec. |
/plan |
Load upstream plan.md and contracts. Do not re-plan interfaces upstream owns. |
/tasks |
Skip tasks upstream delivers; tag consuming tasks with [upstream:#NNNN]. |
/analyze |
Cross-check assumptions against upstream contracts. Flag drift. |
/implement |
Runs against a working tree that already includes upstream code — real APIs, no stubs. |
| PR creation |
Call pr-stack submit → PR targets upstream branch; stack map included in body. |
| Upstream changes |
Auto-call pr-stack restack → rerun /analyze to flag invalidated assumptions. |
SDD canvas changes
- Stack lane — features in a stack render as connected columns in dependency order.
- Layer badge on each card: "Layer 2 of 3, base: #4210".
- Merge-readiness indicator per layer.
- Base preview — clicking the base opens the upstream feature''s canvas in a side panel.
Worked example — #4210 → #4212
- #4212 issue body says
Depends on: #4210.
- Session opens on #4212. Plugin detects the dep, calls
pr-stack to base the branch on #4210''s branch.
/specify runs with #4210''s spec as context.
/plan and /tasks reference #4210''s ContributionId API directly.
/implement writes real code against real APIs.
pr-stack submit opens PR #4212 targeting #4210''s branch.
- #4210 reviewer renames
ContributionId → ArtifactRef; force-pushes.
- Plugin auto-triggers
pr-stack restack on #4212 → reruns /analyze → flags affected tasks → /implement patches them.
- #4210 merges. #4212 auto-retargets to
main. Merge queue lands it.
Delivery plan
MVP (small)
- Extend
speckit-workflow-step to read deps from issue body + spec.md frontmatter.
- Auto-call
pr-stack add at kickoff, pr-stack submit at end of /implement.
- Write
dependencies: into spec.md.
Phase 2
- Upstream context injection into
/specify, /plan, /tasks, /analyze.
- Auto
pr-stack restack + /analyze rerun on upstream change.
Phase 3
- SDD canvas stack lane.
- Sub-issue / tracked-by detection.
- Content-based inference fallback in
/analyze.
- Optional
/speckit.stack escape hatch command if users request explicit control.
Non-goals
- No changes to core
spec-kit templates or commands.
- No reimplementation of stack mechanics —
pr-stack owns that.
- No new user-visible skill or command surface in the MVP.
- No non-GitHub host support (stacked PRs are GitHub-only).
Framing
Spec Kit already handles dependencies inside a feature. This makes it aware of dependencies between features — and lets stacked PRs carry that awareness all the way through review and merge.
Summary
Teach the SDD flow about inter-feature dependencies, and let the existing
pr-stackskill turn those dependencies into stacked PRs automatically — no new stack tooling, just SDD-driven orchestration.The problem
Spec Kit walks users through one feature at a time. But real features often build on each other: Feature B needs code from Feature A. Today that means:
Real example — issues logged on
github/spec-kit:specify artifactcommand exposing composition stacks as JSON#4212 depends on #4210. Running each in its own Copilot session today, the #4212 session branched off
mainand had no visibility into #4210''s in-flight work.What already exists
pr-stackskill — already ships in the Copilot app; wrapsgh-stackfor creating/managing stacks.spec-kit-copilot— handle intra-feature dependency ordering (phases,[P]markers,Dependencies & Execution Orderintasks.md) but have no first-class notion of feature-to-feature dependencies.Gap
Spec Kit has no way to say "Feature #4212 depends on Feature #4210" such that any command acts on it. The
spec.md"Assumptions" section is prose, not machine-readable, and assumes the dependency is already merged.Proposal
Extend the existing
spec-kit-copilotSDD skills (primarilyspeckit-workflow-step/speckit-workflow) to be stacks-aware. Delegate all Git/PR mechanics to the existingpr-stackskill. No new skill, no new user-visible commands in the MVP, no changes to core spec-kit.Dependency declaration (any of these, unified internally)
Read whichever is available, in priority order:
Depends on: #NNNNline in the issue body.dependencies:frontmatter inspec.md(persists after issue is closed).All resolve to
{feature_id, upstream_prs, upstream_branches}.Hooks into each SDD phase
pr-stackto check out on top of upstream branch./specifyspec.mdas context. Write structured## Dependenciesinto new spec./planplan.mdand contracts. Do not re-plan interfaces upstream owns./tasks[upstream:#NNNN]./analyze/implementpr-stack submit→ PR targets upstream branch; stack map included in body.pr-stack restack→ rerun/analyzeto flag invalidated assumptions.SDD canvas changes
Worked example — #4210 → #4212
Depends on: #4210.pr-stackto base the branch on #4210''s branch./specifyruns with #4210''s spec as context./planand/tasksreference #4210''sContributionIdAPI directly./implementwrites real code against real APIs.pr-stack submitopens PR #4212 targeting #4210''s branch.ContributionId→ArtifactRef; force-pushes.pr-stack restackon #4212 → reruns/analyze→ flags affected tasks →/implementpatches them.main. Merge queue lands it.Delivery plan
MVP (small)
speckit-workflow-stepto read deps from issue body +spec.mdfrontmatter.pr-stack addat kickoff,pr-stack submitat end of/implement.dependencies:intospec.md.Phase 2
/specify,/plan,/tasks,/analyze.pr-stack restack+/analyzererun on upstream change.Phase 3
/analyze./speckit.stackescape hatch command if users request explicit control.Non-goals
spec-kittemplates or commands.pr-stackowns that.Framing