.agents/skills/pr-stack-workflow/SKILL.md
Use this for any change too large to review as one pull request: deciding whether to stack at all, where to cut the slices, the mechanics that keep a stack reviewable, and how to verify each branch instead of only the tip.
Branch names, commit and PR-title conventions, and how to handle bot review
threads live in git-workflow. Read that first; this skill does not restate it.
A stack costs real time, because every landing forces a base move plus a merge down the rest of the chain. Pay that only when one of these is true.
| Stack it | Ship it as one PR |
|---|---|
| You cannot describe the diff in one sentence without the word "and" | One symptom, one cause |
| It changes a default, or a shape users already have persisted, so it needs a migration decision | The behavior is new and nobody has state in it |
| Someone will ask "why" and the answer is evidence: numbers, screenshots, a competitor's UI | The bug report is the justification |
| It touches a shared component other surfaces consume | The blast radius is one screen |
| Verification is a click path, not an assertion | An existing test proves it |
Two consequences:
Keep the stack short. Every landing costs another round of retarget-and-merge, so length is a running cost you pay until the last PR is in.
Not by file, not by directory, not by frontend versus backend. A slice is right when a reviewer can state what it does, and check it, without loading any other slice. Practical shapes, in the order they should land:
main regardless of the wider change.
It is mergeable on day one, it carries no judgment, and landing it early
shrinks everything downstream.This repo's own agent-setup work is a worked example: #16390, #16391 and #16392
shipped a project-structure path guard as three PRs, landed in that order — the
measured baseline first, then the rules the guard's messages cite, then the hook
that enforces them. The later two are based on the first's branch, not on main,
and #16392's body opens by naming the merge order.
State the position and the dependency in the body, so a reviewer knows what they may ignore:
2/8, based on #16913. Base retargets to main when #16913 lands.Open the whole stack as reviewable, not drafts.
main's history.
(The one documented force-push in CONTRIBUTING.md is the CLA author-header
fix on a single PR — not a stack under review.)main, main is
merged into it, and the merge is then propagated down the rest of the chain.
Every landing, until the stack is empty. Plan for that cost instead of
meeting it on merge day.main cannot be split by cherry-picking
its commits. The reconciled state exists only in the merged tree: upstream
took some of your changes, replaced others, reverted a few. Slice the final
diff instead, then prove the union.After slicing a branch, git diff <stack-tip> <original-branch> must be empty,
or every remaining difference must be a deliberate, named decision.
Do not skip this because the slices look right. On an eight-PR split the union
delta came to three files and one added, three removed lines — and that delta was
the finding: the original branch had dropped three ESLint disable pragmas and was
carrying a latent lint failure toward main.
Keep the original branch untouched as the arbiter. When a slice's behavior is in doubt, run both and compare. Never edit the arbiter to match a slice.
The commands for slicing a merged branch and proving the union are in
references/stack-commands.md, together with the
git-to-PR walk for recovering why a surface is the way it is before you slice it.
Run the verification bar from .agents/AGENTS.md on each branch, in a
worktree checked out on that branch, and publish the result as a table of branch
by check. That file owns the commands and the checks that pass without executing;
follow it there rather than from memory. Three failure modes belong to stacking
specifically:
using shared worktree cache), which is exactly why a green lint on slice 4
can be a replay of slice 3's result. Force execution when you switch branches.knip is a required check. The fix
is to move the export into the slice that uses it, not to widen an ignore list..agents/AGENTS.md covers: drive
it yourself where the outcome is uncertain, hand over the preview URL where it
is not.