coop / guides / stacked PRs

Stacked PRs, flown from the cockpit

GitHub's stacked pull requests break a large change into a chain of small, individually-reviewable PRs — each layer targeting the one below it. coop supports them end to end: the built-in feature-implement workflow can finish a plan as a ready-made stack, and the status bar reads any stack — coop-emitted or hand-built — as a live position chip with a full ladder one click away.

01 · Emit a stack from a plan

The finish line is a knob on the built-in feature-implement workflow. The default ("branch") is exactly the classic behavior: one integrated branch, nothing pushed. The two stacked modes chain the plan's workstreams instead — one layer per workstream, in dependency order — and open them as pull requests after review approves.

// one-off, per run:
/workflow-run feature-implement args={"plan":"plans/my-feature.md","finishLine":"draft-stack"}

// or set a repo default in .coop/workflow-config.json:
{ "finishLine": "draft-stack" }   // "branch" (default) · "draft-stack" · "stack"

Layer branches are namespaced stack/<feature>/<i>-<workstream>, and the run never touches branches it didn't create. If anything fails mid-emission, the result reports exactly which layers landed — pushed, PR'd, or neither — so recovery is a table, not an archaeology dig.

02 · Read the chip

the coop status bar with the stack chip reading PR #2 · 2/2≡ · open · ✓ mergeable

the status bar — the chip anchors your current branch's PR and shows its position in the stack

The k/n≡ fragment is your layer's position, counted from the trunk. Hover (or focus) the chip for the plain-words version — "stacked PRs — layer 2 of 2, landing on main." When attention is needed somewhere, the chip compresses to keep the flag visible:

StateChip
Healthy, in a stackPR #2 · 2/2≡ · open · ✓ mergeable
A sibling layer needs someonePR #102 · 2/3≡ · ⚑ #101
Several layers blockedPR #102 · 2/3≡ · ⚑ #101 (+1⚑)
Your layer: changes requestedPR #102 · 2/3≡ · ⚑ changes
Your layer: failing checksPR #102 · 2/3≡ · ⚑ checks ✕
Your layer: merge conflictPR #102 · 2/3≡ · ✕ conflicts
Your layer merged (pre-retarget)PR #102 · 2/3≡ · merged

The chip details at most one blockage — yours outranks a sibling's — and the (+N⚑) overflow guarantees a second blocked layer is never silent. Screen readers hear the whole thing in words, not glyphs.

03 · The ladder

the stack ladder overlay: layer 2 tagged YOU with a teal node, layer 1 below on the rail cord, and a main ground row

click the chip (or Tab + Enter) — the ladder, top of stack down to the trunk

your layer (YOU) merged needs someone open

The cursor opens on your row — Enter opens that PR on GitHub. ↑↓ walk the layers, Home/End jump the rails, Esc closes. Every layer shows its state in words next to the node, so nothing rides on color alone. The ladder shrinks honestly as layers merge and GitHub re-stacks what's above them.

04 · When the ladder goes flat

coop never guesses a ladder. If the chain can't be resolved with certainty, the chip falls back to the ordinary flat PR view — and tells you why, in the chip tooltip and a quiet line in the overlay:

CauseNote
Default branch unknown (no origin/HEAD)couldn't resolve the repo's default branch
A layer isn't in this session's seta layer in the chain isn't in this session's set
Two PRs target the same basetwo PRs target the same base
The base chain loopsthe base chain loops

05 · The safety model

06 · Good to know