stack-on-pr

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Stack the frontier a PR unlocks

堆叠某个 PR 解除的 frontier

Turn the tickets an open PR unblocks into draft PRs stacked on that PR's branch, then hand each to Copilot — so work a still-open PR blocks can start now, against the contract that PR already defines (its API, its schema, its types).
This is
delegate-frontier
with two deltas: the frontier is computed as if the PR were already merged, and each new PR is based on the PR's head branch instead of the default branch. The PR body shape and the Copilot handoff are unchanged — follow them live from
../delegate-frontier/SKILL.md
.
将开放 PR 解除的工单转化为堆叠在该 PR 分支上的草稿 PR,然后交给 Copilot——这样,被仍开放 PR 阻塞的工作现在就能基于该 PR 已定义的契约(其 API、schema 和 types)启动。
这是
delegate-frontier
的变体,有两项调整:frontier 按该 PR 仿佛已经合并的情形计算,每个新 PR 都基于该 PR 的 head 分支,而不是默认分支。PR 正文结构和 Copilot 交接方式保持不变——实时遵循
../delegate-frontier/SKILL.md
。

Steps

步骤

1. Resolve the tracker, the PR, and its spec

1. 解析 tracker、PR 及其 spec

The tracker is the upstream org repo from
git remote -v
(not a personal fork). The PR is the user's argument (number or URL). Read the PR's
Closes #<ticket>
to get the base ticket(s) it will close, that ticket's
## Parent
to get the spec, and the PR's head branch — the branch the new PRs stack on.
Done when: you have the tracker
owner/repo
, the PR number and its head branch, the base ticket(s) the PR closes, and the spec issue number.
工单系统(tracker)是从
git remote -v
获取的上游组织仓库(而不是个人 fork)。PR 是用户参数(编号或 URL)。读取 PR 的
Closes #<ticket>
,获取它将关闭的基础工单;读取该工单的
## Parent
以获取 spec;并读取 PR 的 head 分支——新 PR 将堆叠其上的分支。
完成条件: 已获取 tracker 的
owner/repo
、PR 编号及其 head 分支、PR 将关闭的基础工单,以及 spec issue 编号。

2. Compute the frontier the PR unlocks

2. 计算 PR 解除的 frontier

Over the spec's open tickets, compute the frontier exactly as
delegate-frontier
step 2 does — but count the PR's base ticket(s) as already closed. The result is the tickets that only this PR still blocks. Drop any ticket that already has an open PR that
Closes
it — whether that PR is stacked on this PR's head branch or targets the default branch — so a re-run never re-opens one.
Done when: you have the tickets unblocked solely by this PR and with no existing open PR that
Closes
them, and can name why every excluded ticket was held — another open blocker, or an existing PR.
按照
delegate-frontier
步骤 2 完全相同的方式,对 spec 中的开放工单计算 frontier——但将 PR 的基础工单视为已经关闭。结果应是仅由此 PR 解除阻塞的工单。丢弃任何已有开放 PR 通过
Closes
关闭的工单——无论该 PR 是堆叠在此 PR 的 head 分支上,还是以默认分支为目标——从而确保重新运行时不会重复创建。
完成条件: 已获取仅由此 PR 解除阻塞且不存在已有开放 PR 通过
Closes
关闭的工单,并能说明每个被排除工单被暂缓处理的原因——另一个仍开放的 blocker,或已有 PR。

3. Open a draft PR per ticket, stacked on the PR

3. 为每个工单打开堆叠草稿 PR

For each unlocked ticket, first re-check that no open PR already
Closes
it (guarding against a re-run or a PR opened since step 2); if one exists, skip the ticket. Otherwise open a draft PR exactly as
delegate-frontier
step 3 — same repo-convention title, template body, and labels — with one change: branch off the PR's head branch and set the new PR's base to that head branch. The PR then stacks and shows only its incremental diff on top of the base PR.
Done when: every unlocked ticket has exactly one labelled draft PR based on the PR's head branch, its title passing the repo's convention, body linking
Closes #<ticket>
with acceptance criteria and every required template section — and no ticket that already had a PR got a second one.
对于每个已解除阻塞的工单,先重新检查是否已有开放 PR 通过
Closes
关闭它(防止重新运行,或防止步骤 2 之后新打开了 PR);如果存在,则跳过该工单。否则,完全按照
delegate-frontier
步骤 3 打开草稿 PR——使用相同的仓库约定标题、模板正文和 labels——仅做一处调整:从 PR 的 head 分支切出分支,并将新 PR 的 base 设置为该 head 分支。这样,PR 就形成堆叠,并且相对于基础 PR 仅显示其增量 diff。
完成条件: 每个已解除阻塞的工单都恰好对应一个带 labels 的草稿 PR,基于该 PR 的 head 分支;其标题符合仓库约定,正文链接
Closes #<ticket>
并包含验收标准和所有必需的模板章节;任何已有 PR 的工单都不会获得第二个 PR。

4. Hand each PR to Copilot

4. 将每个 PR 交给 Copilot

Exactly as
delegate-frontier
step 4.
Done when: every stacked PR carries one
@copilot
comment per
delegate-frontier
step 4.
完全按照
delegate-frontier
步骤 4 执行。
完成条件: 每个堆叠 PR 都按照
delegate-frontier
步骤 4 包含一条
@copilot
评论。

Reference

参考

Frontier-on-merge. The pick is
delegate-frontier
's frontier rule with the PR's base ticket(s) forced closed: a downstream ticket qualifies when every
## Blocked by
issue is closed once the PR's ticket is counted as closed. Any ticket with another still-open blocker waits for a later run.
Stacking base. The new PR's base is the PR's head branch, not the default branch. When the base PR merges and its branch is deleted, GitHub auto-retargets the stacked PR to the base PR's base (the default branch) and its diff settles to the ticket's own changes; rebase the stacked branch afterward if the retarget leaves conflicts.
Idempotent re-runs. The dedup key is an open PR that
Closes #<ticket>
, checked both when computing the frontier (step 2) and again immediately before opening (step 3). Re-running the skill on the same PR therefore only fills gaps — tickets that still have no PR — and never opens a second PR for a ticket already delegated.
合并时的 frontier。 采用
delegate-frontier
的 frontier 规则,并强制将 PR 的基础工单视为已关闭:当 PR 的工单被计为已关闭后,下游工单的每个
## Blocked by
issue 都已关闭时,该下游工单才符合条件。任何仍有另一个开放 blocker 的工单都要等待后续运行。
堆叠基分支。 新 PR 的 base 是 PR 的 head 分支,而不是默认分支。当基础 PR 合并且其分支被删除时,GitHub 会自动将堆叠 PR 的目标重新指向基础 PR 的 base(默认分支),其 diff 最终只剩该工单自身的变更;如果重新定向后出现冲突,请 rebase 堆叠分支。
幂等重新运行。 去重键是开放 PR 是否通过
Closes #<ticket>
关闭工单;计算 frontier(步骤 2)时检查一次,打开 PR 前(步骤 3)再立即检查一次。因此,对同一个 PR 重新运行此 skill 时,只会填补空缺——仅为尚无 PR 的工单创建 PR——绝不会为已经委派的工单打开第二个 PR。