adopt native GitHub stack workflow
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
2026-07-26-incremental-pr-base-retargeting.md: e2097ac4c32a926c8c0271df19dbc9796d0ed19d
|
||||
2026-07-26-incremental-pr-base-retargeting.zh.md: a6c94b66732b6c037fee1b0726b31ecb6f3b48c5
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-26-incremental-pr-base-retargeting.md
|
||||
2026-07-26-incremental-pr-base-retargeting.md: b2e644d99877b4214b5a6edb2775b3962e6b7da2
|
||||
2026-07-26-incremental-pr-base-retargeting.zh.md: 5014fef644f9907c4d16a9a2a3767d6f55633688
|
||||
|
||||
@@ -10,9 +10,9 @@ A PR base can advance while its current tip is being merged into the PR branch.
|
||||
|
||||
## Decision
|
||||
|
||||
Each observed base tip gets its own merge checkpoint. If the base advances during the work, finish and validate the merge already in progress, commit it, and push it when the task authorizes a push. Only then fetch and merge the newer base in a separate merge commit. Never abandon, amend, rebase, or otherwise rewrite the earlier work.
|
||||
When merge-forward is chosen, each observed base tip gets its own merge checkpoint. If the base advances during the work, finish and validate the merge already in progress, commit it, and push it when the task authorizes a push. Only then fetch and merge the newer base in a separate merge commit. Do not abandon or rewrite a checkpoint within that merge-forward sequence.
|
||||
|
||||
The root [AGENTS.md](../../../../AGENTS.md) states the standing order. The [stacked-PR landing skill](../../../skills/dsh-merging-stacked-prs/SKILL.md) applies it while retargeting dependent PRs, and the [stack review guide](../../../../docs/cookbook/responding-to-pr-review-on-a-stack.md) owns merging fixes down a stack.
|
||||
The [native-stack and optional-rebase decision](2026-08-02-native-github-stacks-and-optional-rebases.md) also permits a lease-protected rebase for standalone or stacked PRs, including after review. This note owns the merge-forward path only. The [stacked-PR landing skill](../../../skills/dsh-merging-stacked-prs/SKILL.md) selects either history under the root [AGENTS.md](../../../../AGENTS.md), and the [stack review guide](../../../../docs/cookbook/responding-to-pr-review-on-a-stack.md) owns propagating fixes through dependent layers.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ Status: implemented
|
||||
|
||||
## 决策
|
||||
|
||||
每次观察到的 base 分支顶端提交都保留为独立的合并检查点。如果处理期间 base 分支继续前移,先完成并验证正在进行的合并,再将其提交;任务授权推送时,还要完成推送。完成这些步骤后,才能拉取较新的 base,并通过单独的合并提交将其合入。绝不放弃先前工作,也不通过 amend、rebase 或其他方式重写它。
|
||||
选择 merge-forward 时,每次观察到的 base 分支顶端提交都保留为独立的合并检查点。如果处理期间 base 分支继续前移,先完成并验证正在进行的合并,再将其提交;任务授权推送时,还要完成推送。完成这些步骤后,才能拉取较新的 base,并通过单独的合并提交将其合入。在这条 merge-forward 序列中,不得放弃或重写任何检查点。
|
||||
|
||||
根 [AGENTS.md](../../../../AGENTS.md) 规定了这项常设指令。[堆叠 PR 落地 skill(技能)](../../../skills/dsh-merging-stacked-prs/SKILL.md)在调整依赖 PR 的 base 时执行这一规则,[堆叠评审指南](../../../../docs/cookbook/responding-to-pr-review-on-a-stack.md)则负责说明如何将修复沿堆叠向下合并。
|
||||
[原生堆叠与可选 rebase 决策](2026-08-02-native-github-stacks-and-optional-rebases.md)也允许独立或堆叠 PR 使用受 lease 保护的 rebase,评审后同样如此。本文只负责 merge-forward 路径。[堆叠 PR 落地 skill(技能)](../../../skills/dsh-merging-stacked-prs/SKILL.md)根据根 [AGENTS.md](../../../../AGENTS.md) 选择其中一种历史更新方式,[堆叠评审指南](../../../../docs/cookbook/responding-to-pr-review-on-a-stack.md)则负责说明如何在依赖层之间传播修复。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-02-native-github-stacks-and-optional-rebases.md
|
||||
2026-08-02-native-github-stacks-and-optional-rebases.md: a349ed18a27ab006384310e4318f057dbf8873b1
|
||||
2026-08-02-native-github-stacks-and-optional-rebases.zh.md: 0205eb475bfe951f8382d61bf19df988027afb13
|
||||
@@ -0,0 +1,43 @@
|
||||
# Agent Note: Native GitHub stacks and optional PR rebases
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-08-02-native-github-stacks-and-optional-rebases.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
A dependent PR chain represented only by base branches has no official stack identity. Landing it requires manually merging one PR at a time, preserving intermediate branches, retargeting every child, and reconstructing whether the chain survived. GitHub's native stacked-PR feature instead carries the order, applies trunk rules and CI to every layer, and owns bottom-up merges and retargeting.
|
||||
|
||||
A blanket prohibition on rewriting reviewed branches also excludes the native `gh stack` synchronization workflow, whose cascading rebase updates each active layer and publishes it with lease protection. Applying that prohibition only outside stacks would give standalone and stacked PRs inconsistent history choices.
|
||||
|
||||
## Decision
|
||||
|
||||
Every same-repository chain of two or more dependent PRs uses GitHub's official stack object before landing. Live `PullRequest.stack` and `stackEntry.position` fields are authoritative. An unstacked chain whose PRs have one author is linked automatically in bottom-to-top order with `gh stack link`; mixed or unavailable authors require user confirmation. Missing native support and cross-fork chains hard-stop. Existing membership in conflicting stacks or an official order that disagrees with the branch topology requires user direction before any stack is dissolved or rebuilt.
|
||||
|
||||
"Land the stack" merges the complete official stack through `gh stack merge <stack-number> --yes --merge`. A partial landing requires an explicit boundary PR and merges the bottom prefix through that PR. The workflow never falls back to per-PR `gh pr merge` and manual retargeting. A direct native merge is all-or-nothing; a merge queue may process the selected PRs in separate groups, so every selected PR must independently reach `MERGED` before the landing is complete.
|
||||
|
||||
Merge-forward and rebase are both allowed refresh histories for standalone and officially stacked PRs, including after review. A remote history rewrite uses an exact lease or the lease-protected `gh stack` push path and aborts if the remote moved; raw `--force` is forbidden. The [incremental base-retargeting decision](2026-07-26-incremental-pr-base-retargeting.md) remains the owner of the merge-forward option.
|
||||
|
||||
Relevant checks normally run before publication. `gh stack sync` is the explicit exception because it fetches, cascade-rebases, and pushes as one operation: every rewritten layer is validated immediately afterward, and no affected PR merges until that evidence passes. After any rewritten push, current heads, unresolved review threads, approvals, mergeability, and checks are re-audited because earlier commit OIDs and inline anchors may be outdated.
|
||||
|
||||
## Verification
|
||||
|
||||
The [stack landing skill](../../../skills/dsh-merging-stacked-prs/SKILL.md) verifies native support, same-repository branches, live authors, official membership and order, merge range, and final merged state. The [stack review guide](../../../../docs/cookbook/responding-to-pr-review-on-a-stack.md) keeps fixes on their introducing layer and covers both propagation histories. The [pre-push workflow](../../../skills/dsh-pre-push-checks/SKILL.md) owns lease protection and immediate post-sync evidence.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep branch chains as the only stack representation.** This preserves the manual procedure but gives GitHub no stack object through which to show order, enforce trunk rules across every layer, or merge a range atomically.
|
||||
|
||||
**Adopt native stacks while forbidding their rebase commands after review.** This keeps commit OIDs stable but disables the official synchronization path when a stack is under active review and leaves standalone PRs under a different policy.
|
||||
|
||||
**Require rebase for every PR refresh.** A linear history is useful, but merge checkpoints remain a valid choice when preserving completed conflict resolution and its recovery point matters more than compact history.
|
||||
|
||||
**Automatically dissolve conflicting stacks.** This would make local branch inference override shared GitHub metadata and could disturb PRs or authors outside the requested chain; merged and queued entries cannot always be removed.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Reviewers and automation receive GitHub's stack map, stack-wide rules, CI, and native merge state.
|
||||
- A same-author legacy chain becomes official without an extra prompt, while mixed ownership and conflicting metadata retain a human decision boundary.
|
||||
- Rebases can invalidate commit hashes, approvals, or comment anchors after review, so every rewritten push carries a live review and check audit.
|
||||
- `gh stack sync` can briefly publish code whose local evidence is pending; the affected PRs remain blocked from merging until immediate post-sync validation passes.
|
||||
- Merge-forward remains available and preserves completed checkpoints, at the cost of additional merge commits.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Agent Note: GitHub 原生堆叠与可选 PR rebase
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-08-02-native-github-stacks-and-optional-rebases.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
仅以 base 分支表示的依赖 PR(Pull Request)链没有官方的堆叠身份。要让它落地,就必须逐个手动合并 PR、保留中间分支、调整每个子 PR 的 base,并重新查证这条链是否仍然完整。GitHub 原生的堆叠 PR 功能则会承载顺序,对每一层应用 trunk 规则和 CI,并负责自底向上的合并与 base 调整。
|
||||
|
||||
一概禁止改写已评审分支,也会排除原生的 `gh stack` 同步工作流:该工作流通过级联 rebase 更新每个活跃层,并在 lease 保护下发布。如果只在堆叠之外实施这项禁令,就会让独立 PR 和堆叠 PR 面临不一致的历史选择。
|
||||
|
||||
## 决策
|
||||
|
||||
同一仓库内由两个或更多个相互依赖的 PR 组成的每条链,在落地前都必须使用 GitHub 的官方 stack 对象。以实时 `PullRequest.stack` 和 `stackEntry.position` 字段为权威依据。对于尚未形成官方堆叠且所有 PR 作者相同的链,系统使用 `gh stack link` 按自底向上的顺序自动关联;作者不一或作者信息不可用时,必须取得用户确认。缺少原生支持或跨 fork 的链会使流程硬性停止。如果现有成员属于相互冲突的堆叠,或者官方顺序与分支拓扑不一致,则在解散或重建任何堆叠之前都必须取得用户指示。
|
||||
|
||||
「落地堆叠」通过 `gh stack merge <stack-number> --yes --merge` 合并整个官方堆叠。部分落地需要明确指定边界 PR,并合并从底部到该 PR 的前缀。工作流绝不回退到逐个执行 `gh pr merge` 和手动调整 base。原生直接合并要么全部成功,要么全部不合并;合并队列可能分组处理所选 PR,因此只有每个所选 PR 都分别达到 `MERGED`,落地才算完成。
|
||||
|
||||
merge-forward 和 rebase 都可以作为独立 PR 与官方堆叠 PR 的历史刷新方式,包括评审后。改写远端历史时,必须使用精确 lease 或受 lease 保护的 `gh stack` 推送路径;如果远端已经前移,操作必须中止。禁止直接使用 `--force`。[增量更新 base 的决策](2026-07-26-incremental-pr-base-retargeting.md)仍负责 merge-forward 选项。
|
||||
|
||||
相关检查通常在发布前运行。`gh stack sync` 是明确的例外,因为它在一次操作中完成获取、级联 rebase 和推送:随后立即验证每个已改写的层;这些验证通过前,不得合并任何受影响的 PR。每次改写推送后,都要重新审计当前 head、未解决的评审线程、批准状态、可合并性和检查结果,因为先前的 commit OID 和内联锚点可能已经过时。
|
||||
|
||||
## 验证
|
||||
|
||||
[堆叠落地 skill(技能)](../../../skills/dsh-merging-stacked-prs/SKILL.md)验证原生支持、同仓库分支、实时作者信息、官方成员关系与顺序、合并范围以及最终合并状态。[堆叠评审指南](../../../../docs/cookbook/responding-to-pr-review-on-a-stack.md)让修复留在引入问题的层,并涵盖两种用于传播修复的历史策略。[推送前工作流](../../../skills/dsh-pre-push-checks/SKILL.md)负责 lease 保护和同步后立即验证所得的证据。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
**仅以分支链表示堆叠。** 这种做法保留手动流程,但 GitHub 没有 stack 对象可用于展示顺序、对每一层执行 trunk 规则或以原子操作合并整个范围。
|
||||
|
||||
**采用原生堆叠,但禁止在评审后使用其 rebase 命令。** 这会保持 commit OID 稳定,但也会在堆叠正在接受评审时禁用官方同步路径,并让独立 PR 遵循不同的政策。
|
||||
|
||||
**要求每次刷新 PR 都使用 rebase。** 线性历史很有价值,但当保存已经完成的冲突解决及其恢复点比紧凑历史更重要时,合并检查点仍然是有效选择。
|
||||
|
||||
**自动解散相互冲突的堆叠。** 这会让本地分支推断凌驾于共享的 GitHub 元数据之上,并可能干扰所请求链之外的 PR 或作者;已经合并或进入队列的条目不一定都能移除。
|
||||
|
||||
## 后果
|
||||
|
||||
- 评审者和自动化会获得 GitHub 的堆叠图、覆盖整个堆叠的规则、CI 和原生合并状态。
|
||||
- 同一作者的遗留链无需额外询问即可成为官方堆叠;链由多名作者共同拥有或元数据发生冲突时,仍保留人工决策边界。
|
||||
- 评审后,rebase 可能使 commit hash、批准状态或评论锚点失效,因此每次改写推送后都要对实时评审状态和检查结果进行审计。
|
||||
- `gh stack sync` 可能短暂发布本地验证仍待完成的代码;受影响的 PR 在同步后立即验证通过前仍禁止合并。
|
||||
- merge-forward 仍然可用,并以增加合并提交为代价保留已完成的检查点。
|
||||
@@ -1,53 +1,127 @@
|
||||
---
|
||||
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.
|
||||
description: Use when landing a stack of dependent GitHub PRs (A ← B ← C, where each bases on the one below) onto master, merging a PR whose base is another open PR's branch, or whenever a request mentions "stacked PRs", "PR stack", "dependent PRs", or merging several related PRs in sequence. Requires every same-repository dependency chain to use GitHub's official stacked-PR feature before landing so GitHub owns stack-wide rules, CI, ordering, retargeting, and merge state.
|
||||
---
|
||||
|
||||
# Merging a stacked PR chain
|
||||
# Landing an official GitHub PR stack
|
||||
|
||||
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.
|
||||
Land dependent PRs through GitHub's native stack object and `gh stack merge`. Do not reproduce stack semantics by merging and retargeting individual PRs with `gh pr merge` and `gh pr edit`. The root [AGENTS.md](../../../AGENTS.md) owns the allowed merge-forward and rebase histories; the [stack review guide](../../../docs/cookbook/responding-to-pr-review-on-a-stack.md) owns review-fix propagation.
|
||||
|
||||
## The hazard this prevents
|
||||
## Require native stack support
|
||||
|
||||
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.**
|
||||
Run `gh stack --version` before changing GitHub state. Hard-stop if the official extension or server-side stack feature is unavailable; do not fall back to the legacy manual landing procedure. GitHub stacks require every head branch to live in the same repository, so hard-stop on a cross-fork chain.
|
||||
|
||||
## The procedure
|
||||
Use a clean dedicated worktree. Fetch current PR metadata and exact head OIDs rather than trusting branch names or an earlier report:
|
||||
|
||||
Given `A ← B ← C` landing on `master`:
|
||||
```sh
|
||||
gh pr view <pr> --json number,author,baseRefName,baseRefOid,headRefName,headRefOid,isCrossRepository,state,isDraft,reviewDecision,mergeStateStatus,statusCheckRollup
|
||||
```
|
||||
|
||||
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.
|
||||
Query `PullRequest.stack` and `stackEntry.position` for at least one PR in each apparent chain; this official GitHub object, not base-branch inference alone, is the stack-membership authority. Paginate `entries` when `size` exceeds the returned page:
|
||||
|
||||
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 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.
|
||||
- If `origin/master` moves during that work, finish and push the in-progress merge, then fetch and merge the newer tip in a separate commit. Never abandon or rewrite the earlier work ([rationale](../../notes/implemented/process/2026-07-26-incremental-pr-base-retargeting.md)).
|
||||
- `gh pr merge B --merge` — still no `--delete-branch` (PR C bases on branch B).
|
||||
```sh
|
||||
gh api graphql -F owner=<owner> -F name=<repo> -F number=<pr> -f query='
|
||||
query($owner: String!, $name: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
pullRequest(number: $number) {
|
||||
number
|
||||
author { login }
|
||||
baseRefName
|
||||
headRefName
|
||||
stackEntry { position }
|
||||
stack {
|
||||
number
|
||||
baseRefName
|
||||
size
|
||||
entries(first: 100) {
|
||||
nodes {
|
||||
position
|
||||
pullRequest { number author { login } baseRefName headRefName state isDraft }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
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`.
|
||||
Establish the expected bottom-to-top order from the live PR bases: the bottom targets the trunk, and each higher PR targets the head branch immediately below it.
|
||||
|
||||
4. **Only after every PR (A, B, C) is merged, delete the branches** — local and remote, for all of A, B, C.
|
||||
## Link missing stack members
|
||||
|
||||
## Why "merge new master into the dependent before merging it"
|
||||
First compare any existing stack entries with the expected chain. One existing stack may contain an order-preserving subset of the requested chain; multiple stack numbers, an unexpected entry, or a conflicting order requires user direction before any mutation.
|
||||
|
||||
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.
|
||||
When any dependent PR is not yet in that official stack:
|
||||
|
||||
## Verify before deleting anything
|
||||
1. Compare every `author.login` exactly.
|
||||
2. If all authors match, link the chain automatically in bottom-to-top order:
|
||||
|
||||
Before deleting a branch, ask GitHub directly whether any open PR still bases on it:
|
||||
```sh
|
||||
gh stack link --base <trunk> <bottom-pr> <next-pr> ... <top-pr>
|
||||
```
|
||||
|
||||
3. If authors differ or any author is unavailable, ask the user whether to link before changing GitHub state.
|
||||
4. Re-query GraphQL and require one stack number, the expected trunk, the complete PR set, and the expected positions and base chain.
|
||||
|
||||
Never dissolve, reorder, or rebuild an existing stack automatically; `gh stack link` is additive and merged or queued entries cannot be unstacked.
|
||||
|
||||
## Refresh only when needed
|
||||
|
||||
Do not rewrite branches merely because a refresh mechanism exists. When the live merge state or repository rules require an updated trunk, choose either allowed history:
|
||||
|
||||
- **Native cascading rebase:** check out the remote stack with `gh stack checkout <pr-or-stack>` when it is not tracked locally, then run `gh stack sync`. The command may rebase and lease-protected force-push every active layer before local validation. Immediately inspect the rewritten scope, run the relevant checks for every affected layer, and do not merge or claim readiness until they pass. If sync detects a rebase conflict, use `gh stack rebase`, resolve and validate it, then publish with `gh stack push`. If checkout or sync reports divergent local and remote stack compositions, cancel and ask rather than deleting or recreating the remote stack automatically.
|
||||
- **Incremental merge-forward:** merge the trunk into the bottom affected branch, then propagate each updated parent into its child in bottom-to-top order and push normally. If the base advances during an in-progress merge, preserve that checkpoint before merging the newer tip as specified by the [incremental-retargeting note](../../notes/implemented/process/2026-07-26-incremental-pr-base-retargeting.md).
|
||||
|
||||
Any history rewrite is allowed after review, but it invalidates commit-OID assumptions. Re-fetch exact heads and re-audit unresolved review threads, approvals, mergeability, and checks after the push. Never use raw `--force` or overwrite a concurrently advanced remote head.
|
||||
|
||||
## Preflight the merge range
|
||||
|
||||
Re-query the official stack immediately before merging. Require every selected PR to be open, non-draft, in the expected order, and compliant with the repository's review and check requirements. Treat each PR's state independently; a ready top layer does not prove its dependencies are ready.
|
||||
|
||||
"Land the stack" selects the whole stack. A partial landing requires an explicit boundary PR and includes every layer from the bottom through that boundary.
|
||||
|
||||
## Merge through the stack API
|
||||
|
||||
Merge the whole stack by its official stack number:
|
||||
|
||||
```sh
|
||||
gh stack merge <stack-number> --yes --merge
|
||||
```
|
||||
|
||||
For an explicitly requested partial landing, merge through the boundary PR:
|
||||
|
||||
```sh
|
||||
gh stack merge <boundary-pr> --yes --merge
|
||||
```
|
||||
|
||||
Do not pass `--delete-branch`, manually retarget dependents, or issue per-PR merge commands. GitHub merges the selected range bottom-up and retargets/rebases any remaining upper layers. A direct stack merge is all-or-nothing; when the trunk uses a merge queue, GitHub queues the selected range together but may land it in separate groups.
|
||||
|
||||
Do not bypass merge requirements. If the native merge reports a blocker, inspect and resolve that blocker through the owning PR or stop and report it; never fall back to `gh pr merge`.
|
||||
|
||||
## Verify the landed state
|
||||
|
||||
Wait for every selected PR to report `MERGED`; a queued request is not a completed landing:
|
||||
|
||||
```sh
|
||||
gh pr view <pr> --json number,state,mergedAt,mergeCommit,baseRefName,headRefName
|
||||
```
|
||||
|
||||
For a partial landing, re-query the official stack and verify that every remaining PR is still linked in the expected order and targets the stack trunk or the layer below it. Re-check current heads, review state, and CI because GitHub may have rebased the remaining layers.
|
||||
|
||||
Delete branches only in a separate final pass after the corresponding PRs report `MERGED`. Before deleting each branch, require GitHub to report no open PR still using it as a base:
|
||||
|
||||
```sh
|
||||
gh pr list --state open --base <branch> --json number --jq length
|
||||
```
|
||||
|
||||
Anything other than `0` means open PRs still base on `<branch>` 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`.
|
||||
Anything other than `0` blocks deletion.
|
||||
|
||||
## Longer chains
|
||||
## Checklist
|
||||
|
||||
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`; wait until `gh pr view <n> --json state` shows `MERGED`.
|
||||
- [ ] For each dependent: `gh pr edit <n> --base master` → fetch and merge `origin/master` into the branch (resolve conflicts there, push) → `gh pr merge <n> --merge`, no `--delete-branch`; again wait for `MERGED`.
|
||||
- [ ] Before each branch delete: `gh pr list --state open --base <branch> --json number --jq length` prints `0`.
|
||||
- [ ] Delete all branches (local + remote) only as a final pass.
|
||||
- [ ] Native `gh stack` support is available; every PR branch is in the same repository.
|
||||
- [ ] Live PR bases and exact heads establish one bottom-to-top dependency chain.
|
||||
- [ ] GraphQL reports one official stack with the expected trunk, entries, and order; an eligible same-author unstacked chain was linked automatically.
|
||||
- [ ] Any rewritten layers passed relevant validation, and review threads, approvals, mergeability, and checks were re-audited afterward.
|
||||
- [ ] The whole stack, or an explicitly bounded prefix, was submitted through `gh stack merge --yes --merge`.
|
||||
- [ ] Every selected PR reports `MERGED`; any remaining upper layers still form the expected official stack.
|
||||
- [ ] Branch deletion happened only after merged-state and zero-dependent verification.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
name: dsh-pre-push-checks
|
||||
description: Use before pushing, force-pushing, marking ready for review, or claiming checks pass on a deepseek-harness branch to select the smallest tests and checks that cover the outgoing diff without reflexively running the full repository suite.
|
||||
description: Use before pushing, force-pushing, marking ready for review, or claiming checks pass on a deepseek-harness branch, and immediately after gh stack sync publishes rewritten branches, to select the smallest tests and checks that cover the outgoing or just-published diff without reflexively running the full repository suite.
|
||||
---
|
||||
|
||||
# DSH Pre-Push Checks
|
||||
|
||||
Use this skill to run relevant local evidence once before a `deepseek-harness` push. Git hooks are intentionally narrow: pre-commit fixes staged lint, checks staged whitespace, and guards vendored-source metadata; pre-push runs only the incremental repository typecheck. CI owns exhaustive coverage and the platform matrix.
|
||||
Use this skill to run relevant local evidence once before a `deepseek-harness` push. The sole ordering exception is `gh stack sync`, which may publish a cascading rebase before the rewritten layers can be validated; validate them immediately afterward and do not merge until the evidence passes. Git hooks are intentionally narrow: pre-commit fixes staged lint, checks staged whitespace, and guards vendored-source metadata; pre-push runs only the incremental repository typecheck. CI owns exhaustive coverage and the platform matrix.
|
||||
|
||||
## Inspect the outgoing change
|
||||
|
||||
@@ -63,9 +63,26 @@ pnpm exec vitest related packages/<group>/<package>/src/<changed>.ts \
|
||||
|
||||
Run the complete local approximation only when the user explicitly requests it, while diagnosing a CI failure, or when the change spans the repository so broadly that no narrower set is credible. Use the current workflow and package scripts as the inventory; do not recreate the removed `check:pre-push` aggregate.
|
||||
|
||||
## Protect history-rewriting pushes
|
||||
|
||||
Rebase is allowed for standalone and stacked PR branches, including after review. Before a standalone history rewrite, fetch the current remote branch and record its exact OID; publish with `--force-with-lease=<branch>:<observed-oid>` so a concurrent update aborts the push. `gh stack push` and `gh stack sync` supply lease protection for their managed branches. Raw `--force` is never allowed.
|
||||
|
||||
After any rewritten push, fetch the live heads again and re-audit unresolved review threads, approvals, mergeability, and checks. Commit hashes and inline-comment anchors from before the rewrite are not current evidence.
|
||||
|
||||
### Post-sync validation
|
||||
|
||||
`gh stack sync` fetches, cascade-rebases, and pushes as one operation, so it cannot place local validation between rewrite and publication. Before running it, require a clean worktree and record the official stack order and exact remote heads. After it returns:
|
||||
|
||||
1. Re-query every branch head and the official GitHub stack order.
|
||||
2. Inspect the changed scope of every rewritten layer against its live PR base.
|
||||
3. Run the relevant evidence selected by this skill for each affected layer.
|
||||
4. Keep every PR unmerged and report validation as pending until all selected checks pass.
|
||||
|
||||
If post-sync evidence fails, leave the lease-protected published heads in place, repair the failure, validate the repair, and publish the correction. Do not claim the sync made the stack ready merely because the command succeeded.
|
||||
|
||||
## Handle failures
|
||||
|
||||
If a relevant check fails, stop and fix or explain the blocker. Do not push and hope CI differs.
|
||||
If a relevant check fails before an ordinary push, stop and fix or explain the blocker. Do not push and hope CI differs. For the post-sync exception, block the merge and follow the repair procedure above.
|
||||
|
||||
If a failure looks environment-specific, prove it:
|
||||
|
||||
@@ -76,9 +93,11 @@ If a failure looks environment-specific, prove it:
|
||||
|
||||
## Push procedure
|
||||
|
||||
For ordinary and standalone rebase pushes:
|
||||
|
||||
1. Run the selected relevant checks once.
|
||||
2. Commit normally and inspect any files changed by the pre-commit fixer before continuing.
|
||||
3. Push normally so the incremental typecheck hook runs.
|
||||
3. Push normally, or use the exact lease for an authorized rewritten branch, so the incremental typecheck hook runs.
|
||||
4. Verify the remote ref matches local `HEAD`.
|
||||
|
||||
```sh
|
||||
@@ -92,3 +111,5 @@ gh pr checks
|
||||
```
|
||||
|
||||
Report pending checks as pending. Inspect failures before attributing them to the branch or the environment.
|
||||
|
||||
For `gh stack sync`, use the post-sync validation sequence instead of pretending the ordinary order was possible.
|
||||
|
||||
Reference in New Issue
Block a user