From bb4cc0ff601b6717b3b1ef42752ceb589c3410a5 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 6 Jul 2026 00:35:54 +0800 Subject: [PATCH 1/6] docs: add the dsh-merging-stacked-prs skill Commit the stacked-PR landing procedure as a repo skill so every collaborator's agent discovers it: merge bottom-up, retarget and refresh each dependent, and delete no branch until the whole stack has landed (deleting a base branch auto-closes the open PR that bases on it). The responding-to-pr-review-on-a-stack cookbook guide's dependent-check step links the skill as the full landing procedure. --- .../skills/dsh-merging-stacked-prs/SKILL.md | 52 +++++++++++++++++++ .../responding-to-pr-review-on-a-stack.md | 2 +- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 .agents/skills/dsh-merging-stacked-prs/SKILL.md diff --git a/.agents/skills/dsh-merging-stacked-prs/SKILL.md b/.agents/skills/dsh-merging-stacked-prs/SKILL.md new file mode 100644 index 0000000000..b72fda7bd3 --- /dev/null +++ b/.agents/skills/dsh-merging-stacked-prs/SKILL.md @@ -0,0 +1,52 @@ +--- +name: dsh-merging-stacked-prs +description: Use when landing a stack of dependent GitHub PRs (A ← B ← C, where each bases on the one below) onto master — merging more than one PR in a chain, merging a PR whose base is another open PR's branch, or whenever a request mentions "stacked PRs", "PR stack", "dependent PRs", "base branch", or merging several related PRs in sequence. Critical because deleting a base branch mid-chain auto-closes the open PR that bases on it — get the order wrong and you silently close unmerged work. +--- + +# Merging a stacked PR chain + +This skill is the landing procedure for a dependent PR stack. The standing orders it rests on — merge commits only (`gh pr merge --merge`), never rewrite a pushed branch — live in the root [AGENTS.md](../../../AGENTS.md) § Conventions; the discipline for handling review comments across a stack before it lands is the [responding-to-pr-review-on-a-stack](../../../docs/cookbook/responding-to-pr-review-on-a-stack.md) cookbook guide. + +## The hazard this prevents + +On GitHub, **deleting a PR's base branch auto-closes that PR.** In a stack `A ← B ← C` (B bases on A, C bases on B), branch A is the base of PR B, and branch B is the base of PR C. So if you merge A with `--delete-branch`, GitHub closes PR B before it's merged — silently destroying the chain. The whole procedure below exists to avoid that: **merge one at a time, retarget each dependent as you go, and delete nothing until every PR has landed.** + +## The procedure + +Given `A ← B ← C` landing on `master`: + +1. **Merge PR A into master, keeping its branch.** `gh pr merge A --merge` — no `--delete-branch`. Branch A must survive because PR B still bases on it. + +2. **Retarget PR B, refresh it, then merge it — keeping its branch.** + - `gh pr edit B --base master` (now that A is in master, B's base becomes master). + - Merge the new master *into* branch B (check out B, `git merge master`, resolve any conflicts here, push). This makes B current and surfaces conflicts in the working branch where they can be tested — not as a surprise at the GitHub merge. + - `gh pr merge B --merge` — still no `--delete-branch` (PR C bases on branch B). + +3. **Retarget PR C, refresh it, then merge it — keeping its branch.** Same steps: `gh pr edit C --base master`, merge new master into branch C and resolve conflicts there, then `gh pr merge C --merge` without `--delete-branch`. + +4. **Only after every PR (A, B, C) is merged, delete the branches** — local and remote, for all of A, B, C. + +## Why "merge new master into the dependent before merging it" + +Each retarget step merges the freshly-updated master back into the dependent branch *before* merging the PR. This keeps each PR's diff clean (it only shows that PR's own changes, not the parent's) and forces conflicts to surface in the working branch, where you can build and test the resolution — instead of letting GitHub attempt a blind merge that may conflict or quietly mis-resolve. + +## Verify before deleting anything + +Before *any* branch delete, confirm nothing still depends on it: + +```sh +gh pr list --json number,baseRefName +``` + +If any open PR's `baseRefName` is a branch you're about to delete, **do not delete it** — that PR will auto-close. Default to merging without `--delete-branch` throughout, and do the deletions as a separate final pass once the list shows no open dependents. + +## Longer chains + +The pattern extends to any depth. For `A ← B ← C ← D ← …`, walk the stack from the bottom up: merge the lowest, then for each next link retarget to master, merge master into it, merge the PR — always without deleting — and only sweep up all the branches at the very end. The invariant never changes: **a branch may be deleted only when no open PR bases on it.** + +## Quick checklist + +- [ ] Merge bottom PR first, `--merge`, no `--delete-branch`. +- [ ] For each dependent: `gh pr edit --base master` → merge master into the branch (resolve conflicts there) → `gh pr merge --merge`, no `--delete-branch`. +- [ ] Run `gh pr list --json number,baseRefName` to confirm no open dependents remain. +- [ ] Delete all branches (local + remote) only as a final pass. diff --git a/docs/cookbook/responding-to-pr-review-on-a-stack.md b/docs/cookbook/responding-to-pr-review-on-a-stack.md index 7995918a94..4bb9f014fe 100644 --- a/docs/cookbook/responding-to-pr-review-on-a-stack.md +++ b/docs/cookbook/responding-to-pr-review-on-a-stack.md @@ -15,7 +15,7 @@ A wave of review comments lands across several PRs in a dependent stack (`A ← 2. Map each accepted finding to its originating PR, fix it there, then merge down the chain in order. 3. Delegated fixes are trust-but-verify: a sub-agent's report describes intent, not necessarily what landed. Re-run the gates yourself on the actual tree, and for a regression guard, prove it FAILS on the unfixed code (introduce the regression, watch red, revert) — a guard that passes both ways guards nothing. A sub-agent that reframes a problem as already-handled is a signal to dig in personally. 4. Reply in the review thread (`gh api repos/{owner}/{repo}/pulls/{pr}/comments/{id}/replies`), not as a top-level comment, stating the fix and the commit that carries it. -5. Before merging the stack, check dependents: deleting a PR's base branch auto-closes the dependent PR — `gh pr list --json number,baseRefName` first, and merge without `--delete-branch` where a child still bases on the branch. +5. Before merging the stack, check dependents: deleting a PR's base branch auto-closes the dependent PR — `gh pr list --json number,baseRefName` first, and merge without `--delete-branch` where a child still bases on the branch. The full landing procedure is the [dsh-merging-stacked-prs](../../.agents/skills/dsh-merging-stacked-prs/SKILL.md) skill. ## Verify From dd44402871b68353af61503bbc9f38f318f8df19 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 6 Jul 2026 00:46:22 +0800 Subject: [PATCH 2/6] docs: fix review findings in the stacked-PR merge skill Codex review caught three procedure defects: the dependent check used bare 'gh pr list', whose default pagination can hide an open dependent and make branch deletion look safe (now --state open --limit 1000); the refresh step merged local master, which 'gh pr merge' leaves stale (now fetch and merge origin/master); and the quick checklist omitted pushing the dependent after the local merge. --- .agents/skills/dsh-merging-stacked-prs/SKILL.md | 14 +++++++------- .../cookbook/responding-to-pr-review-on-a-stack.md | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.agents/skills/dsh-merging-stacked-prs/SKILL.md b/.agents/skills/dsh-merging-stacked-prs/SKILL.md index b72fda7bd3..6a054c366b 100644 --- a/.agents/skills/dsh-merging-stacked-prs/SKILL.md +++ b/.agents/skills/dsh-merging-stacked-prs/SKILL.md @@ -19,10 +19,10 @@ Given `A ← B ← C` landing on `master`: 2. **Retarget PR B, refresh it, then merge it — keeping its branch.** - `gh pr edit B --base master` (now that A is in master, B's base becomes master). - - Merge the new master *into* branch B (check out B, `git merge master`, resolve any conflicts here, push). This makes B current and surfaces conflicts in the working branch where they can be tested — not as a surprise at the GitHub merge. + - Merge the new master *into* branch B: check out B, `git fetch origin`, `git merge origin/master` — merge `origin/master`, not local `master`, because `gh pr merge` updated only GitHub and the local branch is stale — resolve any conflicts here, and push. This makes B current and surfaces conflicts in the working branch where they can be tested — not as a surprise at the GitHub merge. - `gh pr merge B --merge` — still no `--delete-branch` (PR C bases on branch B). -3. **Retarget PR C, refresh it, then merge it — keeping its branch.** Same steps: `gh pr edit C --base master`, merge new master into branch C and resolve conflicts there, then `gh pr merge C --merge` without `--delete-branch`. +3. **Retarget PR C, refresh it, then merge it — keeping its branch.** Same steps: `gh pr edit C --base master`, fetch and merge `origin/master` into branch C, resolve conflicts there and push, then `gh pr merge C --merge` without `--delete-branch`. 4. **Only after every PR (A, B, C) is merged, delete the branches** — local and remote, for all of A, B, C. @@ -35,18 +35,18 @@ Each retarget step merges the freshly-updated master back into the dependent bra Before *any* branch delete, confirm nothing still depends on it: ```sh -gh pr list --json number,baseRefName +gh pr list --state open --limit 1000 --json number,baseRefName ``` -If any open PR's `baseRefName` is a branch you're about to delete, **do not delete it** — that PR will auto-close. Default to merging without `--delete-branch` throughout, and do the deletions as a separate final pass once the list shows no open dependents. +The explicit `--limit` matters: without it the list is paginated, and a dependent past the first page would make deletion look safe when it is not. If any open PR's `baseRefName` is a branch you're about to delete, **do not delete it** — that PR will auto-close. Default to merging without `--delete-branch` throughout, and do the deletions as a separate final pass once the list shows no open dependents. ## Longer chains -The pattern extends to any depth. For `A ← B ← C ← D ← …`, walk the stack from the bottom up: merge the lowest, then for each next link retarget to master, merge master into it, merge the PR — always without deleting — and only sweep up all the branches at the very end. The invariant never changes: **a branch may be deleted only when no open PR bases on it.** +The pattern extends to any depth. For `A ← B ← C ← D ← …`, walk the stack from the bottom up: merge the lowest, then for each next link retarget to master, fetch and merge `origin/master` into it, merge the PR — always without deleting — and only sweep up all the branches at the very end. The invariant never changes: **a branch may be deleted only when no open PR bases on it.** ## Quick checklist - [ ] Merge bottom PR first, `--merge`, no `--delete-branch`. -- [ ] For each dependent: `gh pr edit --base master` → merge master into the branch (resolve conflicts there) → `gh pr merge --merge`, no `--delete-branch`. -- [ ] Run `gh pr list --json number,baseRefName` to confirm no open dependents remain. +- [ ] For each dependent: `gh pr edit --base master` → fetch and merge `origin/master` into the branch (resolve conflicts there, push) → `gh pr merge --merge`, no `--delete-branch`. +- [ ] Run `gh pr list --state open --limit 1000 --json number,baseRefName` to confirm no open dependents remain. - [ ] Delete all branches (local + remote) only as a final pass. diff --git a/docs/cookbook/responding-to-pr-review-on-a-stack.md b/docs/cookbook/responding-to-pr-review-on-a-stack.md index 4bb9f014fe..0dd161513d 100644 --- a/docs/cookbook/responding-to-pr-review-on-a-stack.md +++ b/docs/cookbook/responding-to-pr-review-on-a-stack.md @@ -15,7 +15,7 @@ A wave of review comments lands across several PRs in a dependent stack (`A ← 2. Map each accepted finding to its originating PR, fix it there, then merge down the chain in order. 3. Delegated fixes are trust-but-verify: a sub-agent's report describes intent, not necessarily what landed. Re-run the gates yourself on the actual tree, and for a regression guard, prove it FAILS on the unfixed code (introduce the regression, watch red, revert) — a guard that passes both ways guards nothing. A sub-agent that reframes a problem as already-handled is a signal to dig in personally. 4. Reply in the review thread (`gh api repos/{owner}/{repo}/pulls/{pr}/comments/{id}/replies`), not as a top-level comment, stating the fix and the commit that carries it. -5. Before merging the stack, check dependents: deleting a PR's base branch auto-closes the dependent PR — `gh pr list --json number,baseRefName` first, and merge without `--delete-branch` where a child still bases on the branch. The full landing procedure is the [dsh-merging-stacked-prs](../../.agents/skills/dsh-merging-stacked-prs/SKILL.md) skill. +5. Before merging the stack, check dependents: deleting a PR's base branch auto-closes the dependent PR — `gh pr list --state open --limit 1000 --json number,baseRefName` first (the explicit `--limit` defeats pagination), and merge without `--delete-branch` where a child still bases on the branch. The full landing procedure is the [dsh-merging-stacked-prs](../../.agents/skills/dsh-merging-stacked-prs/SKILL.md) skill. ## Verify From 5caceee380b962af051821b95258037d2c4830ab Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 6 Jul 2026 00:52:06 +0800 Subject: [PATCH 3/6] Move ACP snapshot replay doc into package --- docs/graph-atlas.md | 2 +- .../process/2026-07-03-documentation-graph-atlas.md | 2 +- {docs => packages/ui}/acp/snapshot-replay.md | 0 scripts/gen-doc-graphs.ts | 6 +++--- 4 files changed, 5 insertions(+), 5 deletions(-) rename {docs => packages/ui}/acp/snapshot-replay.md (100%) diff --git a/docs/graph-atlas.md b/docs/graph-atlas.md index 9fb678d3d7..92953d9fe7 100644 --- a/docs/graph-atlas.md +++ b/docs/graph-atlas.md @@ -18,7 +18,7 @@ The process decision behind this index is recorded in [the documentation graph R | [event producer/consumer matrix](event-producer-consumer.md) | `hybrid generated` | | [agent turn and step lifecycle](agent-lifecycle.md) | `curated` | | [tool execution pipeline](tool-execution-pipeline.md) | `curated` | -| [ACP snapshot replay](acp/snapshot-replay.md) | `curated` | +| [ACP snapshot replay](../packages/ui/acp/snapshot-replay.md) | `curated` | Regenerate with `pnpm run gen-doc-graphs`; verify freshness with `pnpm run verify-doc-graphs`. diff --git a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md index 0cf93a8f6e..11b14a4902 100644 --- a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md +++ b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md @@ -39,7 +39,7 @@ The first index links ten relationship surfaces. Package topology and tool-packa | [event producer/consumer matrix](../../../event-producer-consumer.md) | hybrid generated | Cordis event declarations, AST-scanned `ctx.on/emit/parallel/serial/waterfall` sites, and explicit dynamic dispatch overrides | | [agent turn and step lifecycle](../../../agent-lifecycle.md) | curated | architecture.md loop lifecycle, Cordis catalog links, and session event semantics | | [tool execution pipeline](../../../tool-execution-pipeline.md) | curated | tool pipeline semantics and the `tools/execute` waterfall | -| [ACP snapshot replay](../../../acp/snapshot-replay.md) | curated | snapshot harness behavior | +| [ACP snapshot replay](../../../../packages/ui/acp/snapshot-replay.md) | curated | snapshot harness behavior | ### Why generators own the docs diff --git a/docs/acp/snapshot-replay.md b/packages/ui/acp/snapshot-replay.md similarity index 100% rename from docs/acp/snapshot-replay.md rename to packages/ui/acp/snapshot-replay.md diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts index 183f8ca62a..4f90b79e8e 100644 --- a/scripts/gen-doc-graphs.ts +++ b/scripts/gen-doc-graphs.ts @@ -695,7 +695,7 @@ function renderDocs(): GraphDoc[] { { rel: 'docs/event-producer-consumer.md', content: renderEventRelations(pkgs) }, { rel: 'docs/agent-lifecycle.md', content: renderLifecycle() }, { rel: 'docs/tool-execution-pipeline.md', content: renderToolPipeline() }, - { rel: 'docs/acp/snapshot-replay.md', content: renderSnapshotReplay() }, + { rel: 'packages/ui/acp/snapshot-replay.md', content: renderSnapshotReplay() }, ] docs.unshift({ rel: 'docs/graph-atlas.md', content: renderIndex(docs) }) return docs @@ -710,7 +710,7 @@ function renderIndex(docs: GraphDoc[]): string { 'docs/event-producer-consumer.md': 'event producer/consumer matrix', 'docs/agent-lifecycle.md': 'agent turn and step lifecycle', 'docs/tool-execution-pipeline.md': 'tool execution pipeline', - 'docs/acp/snapshot-replay.md': 'ACP snapshot replay', + 'packages/ui/acp/snapshot-replay.md': 'ACP snapshot replay', } const modes: Record = { 'docs/capability-seams.md': 'hybrid generated', @@ -720,7 +720,7 @@ function renderIndex(docs: GraphDoc[]): string { 'docs/event-producer-consumer.md': 'hybrid generated', 'docs/agent-lifecycle.md': 'curated', 'docs/tool-execution-pipeline.md': 'curated', - 'docs/acp/snapshot-replay.md': 'curated', + 'packages/ui/acp/snapshot-replay.md': 'curated', } const rows = [ '| [module dependency graph](module-graph.md) | `generated` |', From 1c05c41f47352e5ad7bf31ed12416d59cbd42e70 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 6 Jul 2026 00:55:09 +0800 Subject: [PATCH 4/6] docs: wait for MERGED before advancing the stack Second review round: 'gh pr merge' can return after merely enabling auto-merge when required checks are pending or a merge queue is active, so retargeting the next PR could run against a master that does not yet contain the parent. The procedure and checklist now require 'gh pr view --json state' to report MERGED after every merge. --- .agents/skills/dsh-merging-stacked-prs/SKILL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.agents/skills/dsh-merging-stacked-prs/SKILL.md b/.agents/skills/dsh-merging-stacked-prs/SKILL.md index 6a054c366b..f9e6077712 100644 --- a/.agents/skills/dsh-merging-stacked-prs/SKILL.md +++ b/.agents/skills/dsh-merging-stacked-prs/SKILL.md @@ -15,7 +15,7 @@ On GitHub, **deleting a PR's base branch auto-closes that PR.** In a stack `A Given `A ← B ← C` landing on `master`: -1. **Merge PR A into master, keeping its branch.** `gh pr merge A --merge` — no `--delete-branch`. Branch A must survive because PR B still bases on it. +1. **Merge PR A into master, keeping its branch.** `gh pr merge A --merge` — no `--delete-branch`. Branch A must survive because PR B still bases on it. Before touching the next link, confirm the merge actually landed: with required checks pending or a merge queue, `gh pr merge` may only enable auto-merge and return early, so wait until `gh pr view A --json state` reports `MERGED`. This applies after every merge in the stack. 2. **Retarget PR B, refresh it, then merge it — keeping its branch.** - `gh pr edit B --base master` (now that A is in master, B's base becomes master). @@ -46,7 +46,7 @@ The pattern extends to any depth. For `A ← B ← C ← D ← …`, walk the st ## Quick checklist -- [ ] Merge bottom PR first, `--merge`, no `--delete-branch`. -- [ ] For each dependent: `gh pr edit --base master` → fetch and merge `origin/master` into the branch (resolve conflicts there, push) → `gh pr merge --merge`, no `--delete-branch`. +- [ ] Merge bottom PR first, `--merge`, no `--delete-branch`; wait until `gh pr view --json state` shows `MERGED`. +- [ ] For each dependent: `gh pr edit --base master` → fetch and merge `origin/master` into the branch (resolve conflicts there, push) → `gh pr merge --merge`, no `--delete-branch`; again wait for `MERGED`. - [ ] Run `gh pr list --state open --limit 1000 --json number,baseRefName` to confirm no open dependents remain. - [ ] Delete all branches (local + remote) only as a final pass. From 3b145bff5c2163c69e5c41ed5cd9cb37e36af397 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 6 Jul 2026 01:15:32 +0800 Subject: [PATCH 5/6] docs: check dependents per-branch with --base, not a capped sweep Review comment on the verify step: dumping every open PR under --limit 1000 wastes agent context and re-truncates past 1000. Ask the exact question server-side instead: gh pr list --state open --base --json number --jq length, whose output is a single count that is exact at any number of open PRs. Skill, checklist, and the cookbook dependent-check line all use the per-branch form. --- .agents/skills/dsh-merging-stacked-prs/SKILL.md | 8 ++++---- docs/cookbook/responding-to-pr-review-on-a-stack.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.agents/skills/dsh-merging-stacked-prs/SKILL.md b/.agents/skills/dsh-merging-stacked-prs/SKILL.md index f9e6077712..0218c5720c 100644 --- a/.agents/skills/dsh-merging-stacked-prs/SKILL.md +++ b/.agents/skills/dsh-merging-stacked-prs/SKILL.md @@ -32,13 +32,13 @@ Each retarget step merges the freshly-updated master back into the dependent bra ## Verify before deleting anything -Before *any* branch delete, confirm nothing still depends on it: +Before deleting a branch, ask GitHub directly whether any open PR still bases on it: ```sh -gh pr list --state open --limit 1000 --json number,baseRefName +gh pr list --state open --base --json number --jq length ``` -The explicit `--limit` matters: without it the list is paginated, and a dependent past the first page would make deletion look safe when it is not. If any open PR's `baseRefName` is a branch you're about to delete, **do not delete it** — that PR will auto-close. Default to merging without `--delete-branch` throughout, and do the deletions as a separate final pass once the list shows no open dependents. +Anything other than `0` means deleting `` auto-closes that many open PRs — do not delete it. The `--base` filter is applied server-side, so the answer is exact no matter how many PRs are open, and the output is a single number. Default to merging without `--delete-branch` throughout, and do the deletions as a separate final pass once every branch you're about to delete reports `0`. ## Longer chains @@ -48,5 +48,5 @@ The pattern extends to any depth. For `A ← B ← C ← D ← …`, walk the st - [ ] Merge bottom PR first, `--merge`, no `--delete-branch`; wait until `gh pr view --json state` shows `MERGED`. - [ ] For each dependent: `gh pr edit --base master` → fetch and merge `origin/master` into the branch (resolve conflicts there, push) → `gh pr merge --merge`, no `--delete-branch`; again wait for `MERGED`. -- [ ] Run `gh pr list --state open --limit 1000 --json number,baseRefName` to confirm no open dependents remain. +- [ ] Before each branch delete: `gh pr list --state open --base --json number --jq length` prints `0`. - [ ] Delete all branches (local + remote) only as a final pass. diff --git a/docs/cookbook/responding-to-pr-review-on-a-stack.md b/docs/cookbook/responding-to-pr-review-on-a-stack.md index 0dd161513d..4eb7dc482d 100644 --- a/docs/cookbook/responding-to-pr-review-on-a-stack.md +++ b/docs/cookbook/responding-to-pr-review-on-a-stack.md @@ -15,7 +15,7 @@ A wave of review comments lands across several PRs in a dependent stack (`A ← 2. Map each accepted finding to its originating PR, fix it there, then merge down the chain in order. 3. Delegated fixes are trust-but-verify: a sub-agent's report describes intent, not necessarily what landed. Re-run the gates yourself on the actual tree, and for a regression guard, prove it FAILS on the unfixed code (introduce the regression, watch red, revert) — a guard that passes both ways guards nothing. A sub-agent that reframes a problem as already-handled is a signal to dig in personally. 4. Reply in the review thread (`gh api repos/{owner}/{repo}/pulls/{pr}/comments/{id}/replies`), not as a top-level comment, stating the fix and the commit that carries it. -5. Before merging the stack, check dependents: deleting a PR's base branch auto-closes the dependent PR — `gh pr list --state open --limit 1000 --json number,baseRefName` first (the explicit `--limit` defeats pagination), and merge without `--delete-branch` where a child still bases on the branch. The full landing procedure is the [dsh-merging-stacked-prs](../../.agents/skills/dsh-merging-stacked-prs/SKILL.md) skill. +5. Before merging the stack, check dependents: deleting a PR's base branch auto-closes the dependent PR — check each branch with `gh pr list --state open --base --json number --jq length` (non-zero = open dependents), and merge without `--delete-branch` where a child still bases on the branch. The full landing procedure is the [dsh-merging-stacked-prs](../../.agents/skills/dsh-merging-stacked-prs/SKILL.md) skill. ## Verify From 89e7005cfd0118c9a370efb1798a2a009b16da00 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 6 Jul 2026 01:19:46 +0800 Subject: [PATCH 6/6] docs: state the dependent check's contract as zero-versus-non-zero Verified against gh: --base filters server-side and exactly, so any open dependent guarantees a non-zero print, but --jq length counts the fetched page and gh's default --limit is 30 (--limit 5 prints 5 when the true count is 20). The printed number is min(actual, limit), so the skill no longer claims it as the auto-close count; only 0 clears a delete. --- .agents/skills/dsh-merging-stacked-prs/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.agents/skills/dsh-merging-stacked-prs/SKILL.md b/.agents/skills/dsh-merging-stacked-prs/SKILL.md index 0218c5720c..dc1d2ec265 100644 --- a/.agents/skills/dsh-merging-stacked-prs/SKILL.md +++ b/.agents/skills/dsh-merging-stacked-prs/SKILL.md @@ -38,7 +38,7 @@ Before deleting a branch, ask GitHub directly whether any open PR still bases on gh pr list --state open --base --json number --jq length ``` -Anything other than `0` means deleting `` auto-closes that many open PRs — do not delete it. The `--base` filter is applied server-side, so the answer is exact no matter how many PRs are open, and the output is a single number. Default to merging without `--delete-branch` throughout, and do the deletions as a separate final pass once every branch you're about to delete reports `0`. +Anything other than `0` means open PRs still base on `` and deleting it would auto-close them — do not delete it. The `--base` filter is applied server-side, so zero-versus-non-zero is exact no matter how many PRs are open; the printed number itself saturates at `gh`'s `--limit` (default 30), which never matters here because only `0` clears a delete. Default to merging without `--delete-branch` throughout, and do the deletions as a separate final pass once every branch you're about to delete reports `0`. ## Longer chains