ship-it

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Ship It

Ship It

Input: a PR number or URL, or nothing — then use the current branch's PR (
gh pr view
with no argument resolves it). If the current branch has no PR yet, push it and open one with
gh pr create
(maintainer branch only), then continue. Goal: the PR merged with every code-review finding fixed and CI green.
输入:PR 编号或 URL,或者不提供任何输入——此时使用当前 branch 的 PR(
gh pr view
不带参数即可解析)。如果当前 branch 还没有 PR,则先 push 该分支,再使用
gh pr create
创建一个(仅限 maintainer branch),然后继续。目标:合并 PR,修复所有 code-review 发现项,并让 CI 通过。

0b. A figure in the description, when the change has a shape

0b. 当变更具有可直观呈现的形态时,在描述中添加图示

If the PR changes how something flows — a request path, a background job, what a feature does step by step — a picture explains it faster than the diff does. Use the
figure
skill: write a JSON spec, render one animated SVG, and put it at the top of the PR description. It plays in the description with no upload and no click.
Reference it by commit SHA, not branch name, or the image dies when the branch is deleted at merge:
https://raw.githubusercontent.com/<owner>/<repo>/<sha>/<path>.svg
. If the SVG is not part of the change itself, don't commit it to the repo — render it, commit it on the PR branch only if it belongs there, and otherwise keep the description's image pinned to the commit that carried it.
Skip it for a fix with no shape: a one-line guard, a dependency bump, a typo.
如果 PR 改变了某项内容的流转方式——请求路径、后台作业、功能的逐步执行过程——那么图示比 diff 更快地解释清楚。使用
figure
skill:编写 JSON spec,渲染一张动画 SVG,并放在 PR 描述顶部。它会直接在描述中播放,无需上传,也无需点击。
必须通过 commit SHA 引用,而不能使用 branch name,否则合并时删除 branch 后,图片就会失效:
https://raw.githubusercontent.com/<owner>/<repo>/<sha>/<path>.svg
。如果 SVG 本身不属于此次变更,不要将其提交到仓库——先渲染;只有当它确实属于此次变更时,才提交到 PR branch;否则,让描述中的图片继续固定到承载该 SVG 的 commit 上。
对于没有可展示形态的修复,跳过此步骤:一行 guard、依赖版本升级、拼写错误。

0. Blocker rule (applies at every step)

0. Blocker 规则(适用于每一步)

A blocker is a finding whose fix would go against the PR's goal — the code-review principles say the thing the PR sets out to do is wrong (e.g. the PR's whole point is a raw-SQL handler, a new advisory lock, a tuple-returning API, a test that asserts via SQL, dropping bank scoping), or fixing it would mean rewriting/removing the feature rather than polishing it.
On a blocker: stop immediately. Don't push, don't merge, don't partially fix. Tell the user what the PR is trying to do, which principle it conflicts with (quote the
code-review
section), and the options you see — then ask for confirmation with AskUserQuestion. Resume only on an explicit answer.
Everything else (must fix, should fix, nits) is not a blocker: fix it all.
blocker 是指这样一类审查发现:如果修复它,就会违背 PR 的目标——code-review 原则认为 PR 试图实现的事情本身就是错误的(例如,PR 的全部目的就是 raw-SQL handler、新 advisory lock、返回 tuple 的 API、通过 SQL 进行断言的测试,或取消 bank scoping),或者修复它意味着重写或删除该功能,而不是对其进行打磨。
遇到 blocker:立即停止。不要 push,不要 merge,也不要只修复一部分。向用户说明 PR 想做什么、它与哪条原则冲突(引用
code-review
部分),并列出可选方案——然后通过 AskUserQuestion 请求确认。只有收到明确答复后才能继续。
其他所有项(must fix、should fix、nits)都不属于 blocker:全部修复。

1. Load the PR

1. 加载 PR

Read the PR — title, body, linked issue, diff — so you know its goal; the blocker rule depends on it. If it's
CONFLICTING
, rebase onto
origin/main
first: a conflicting PR gets zero CI runs, silently.
Work out whether the author is a maintainer (write/maintain/admin on the repo, e.g.
nicoloboschi
).
读取 PR——标题、正文、关联 issue 和 diff——以了解其目标;blocker 规则依赖于这些信息。如果状态为
CONFLICTING
,先将 branch rebase 到
origin/main
:处于冲突状态的 PR 不会触发任何 CI run,而且不会给出提示。
判断作者是否为 maintainer(在仓库中拥有 write/maintain/admin 权限,例如
nicoloboschi
)。

2. Check out the PR branch

2. 检出 PR branch

Work in the current worktree — don't create a new worktree or clone. Before switching, note its state (current branch/commit and any uncommitted changes) so you can restore it in step 6; set uncommitted work aside with a WIP commit or a uniquely tagged stash (never a bare
git stash
— the stash stack is shared across worktrees). Then check out the PR's branch (skip if you're already on it). On current-branch input, ask the user if uncommitted work isn't obviously part of the PR.
  • Maintainer PR: fixes are committed and pushed to this branch — the PR's own head. Never open a separate fix PR.
  • External (fork) PR: push to the fork branch too if maintainers can modify it; otherwise stop and ask the user how to proceed (follow-up PR vs. ask the contributor).
Before every push, make sure the author hasn't pushed meanwhile. If they have, read their new commits and rebase yours onto their head — never force-push over work you haven't read.
在 current worktree 中工作——不要创建新的 worktree 或 clone。切换前,记录其状态(当前 branch/commit 以及所有未提交更改),以便在步骤 6 恢复;使用 WIP commit 或带唯一标签的 stash 暂存未提交工作(绝不要使用普通的
git stash
——stash 栈在不同 worktree 间共享)。然后切换到 PR 的 branch(如果已经在该 branch 上则跳过)。当输入指向当前 branch 时,如果未提交工作显然不属于该 PR,请询问用户。
  • **Maintainer PR:**修复会提交并 push 到这个 branch——即 PR 自己的 head。绝不要另开 fix PR。
  • **外部(fork)PR:**如果 maintainers 可以修改 fork branch,也将修复 push 到该 branch;否则停止并询问用户应如何继续(另开后续 PR,还是请贡献者处理)。
每次 push 前,都要确认作者在此期间没有 push。如果作者已经 push,请读取其新 commits,并将你的 commits rebase 到其 head 上——绝不要 force-push 覆盖你尚未阅读的改动。

3. Review

3. 审查

Read and follow the repo's code-review skill at its absolute path,
<repo>/.claude/skills/code-review/SKILL.md
(
<repo>
=
git rev-parse --show-toplevel
), against the PR's changes (base =
origin/<baseRefName>
). Collect every finding: must fix, should fix, and nits.
Classify each against step 0. Any blocker → stop and ask.
按绝对路径
<repo>/.claude/skills/code-review/SKILL.md
(
<repo>
=
git rev-parse --show-toplevel
)读取并遵循仓库的 code-review skill,并针对 PR 的变更进行审查(base =
origin/<baseRefName>
)。收集每一项发现:must fix、should fix 和 nits。
依据步骤 0 对每项发现进行分类。任何 blocker → 停止并询问用户。

4. Apply ALL fixes

4. 应用所有修复

  • Fix every finding, not just the must-fixes. Stay in the PR's scope — don't refactor neighbouring code the review didn't flag.
  • Run
    ./scripts/hooks/lint.sh
    and the tests covering the touched code (see CLAUDE.md for commands). Regenerate OpenAPI/clients/docs-skill if the change requires it.
  • Loop until clean. One review pass is never enough: fixes introduce new findings, and the review only sees what the last pass changed. So repeat — review → fix everything → review again — until a full
    code-review
    pass returns zero findings of any severity (must fix, should fix, nits). Don't stop at "only nits left", don't stop because the last pass found fewer things, and don't declare done on a pass you didn't actually re-run. The only early exit is a blocker (step 0) → stop and ask.
  • Commit with a message that lists what was fixed, then push to the PR branch.
Don't post a review comment on a maintainer's PR — findings go in the chat reply and the commit message. On an external PR, one short comment summarising the fixes you pushed is fine.
  • 修复每一项发现,而不只是 must-fix。保持改动在 PR 范围内——不要重构审查未标记的相邻代码。
  • 运行
    ./scripts/hooks/lint.sh
    ,并运行覆盖已修改代码的测试(命令见 CLAUDE.md)。如果变更需要,重新生成 OpenAPI、clients 和 docs-skill。
  • **循环直到干净。**一轮 review 永远不够:修复会引入新的发现,而 review 只能看到上一轮实际改动的部分。因此重复执行——review → 修复全部问题 → 再次 review——直到完整的一轮
    code-review
    对任何严重级别(must fix、should fix、nits)都返回零发现。不要在“只剩 nits”时停止,不要因为最后一轮发现变少就停止,也不要把未实际重新运行的一轮 review 当作完成。唯一的提前退出条件是 blocker(步骤 0)→ 停止并询问用户。
  • 使用列明修复内容的 message 进行 commit,然后 push 到 PR branch。
不要在 maintainer 的 PR 上发布 review comment——发现项应放在聊天回复和 commit message 中。对于外部 PR,可以发布一条简短 comment,概述你 push 的修复。

5. Wait for CI green

5. 等待 CI 通过

The repo has no required status checks:
gh pr merge --auto
merges immediately, and
gh pr checks
reports only the checks registered so far (a lone early Strix check reads as "green"). Gate on the CI workflow run for the pushed head SHA:
bash
SHA=$(gh pr view <N> --json headRefOid --jq .headRefOid)
gh run list --branch <headRefName> --workflow CI --json databaseId,status,conclusion,headSha \
  | jq --arg s "$SHA" 'map(select(.headSha==$s)) | .[0] // empty'
No run yet → keep waiting (give up and report after ~15 min with no run). Once
completed
, judge per job (
gh run view <id> --json jobs
): zero
failure
. If the PR touches the API/engine,
test-api (1..3/3)
,
Core LLM tests
, and the LLM acceptance matrix must be
success
, not
skipped
; for changes outside those paths the change filter skips them legitimately. The oracle-client jobs are known-flaky and may be
cancelled
— ignore those.
Fork PRs skip
test-api
and the LLM jobs
(no secrets). Run the full suite on an upstream branch before merging — merging to main runs no CI at all:
bash
git push origin HEAD:ci/pr-<N>-verify
gh workflow run CI --ref ci/pr-<N>-verify   # exactly ONCE — a second dispatch cancels the first
A failure caused by the PR/fixes → fix, push, back to step 5. A known flake (check the job log isn't touching the PR's code) →
gh run rerun <id> --failed
once the run has completed. If CI still fails for reasons you can't attribute, stop and report — don't merge red.
仓库没有必需的 status checks:
gh pr merge --auto
会立即合并,而
gh pr checks
只报告目前已注册的 checks(单独一个早期 Strix check 会显示为“green”)。必须以针对所 push 的 head SHA 运行的 CI workflow run 作为门禁:
bash
SHA=$(gh pr view <N> --json headRefOid --jq .headRefOid)
gh run list --branch <headRefName> --workflow CI --json databaseId,status,conclusion,headSha \
  | jq --arg s "$SHA" 'map(select(.headSha==$s)) | .[0] // empty'
尚未出现 run → 继续等待(如果约 15 分钟仍无 run,则放弃并报告)。run 完成后,按 job 逐一判断(
gh run view <id> --json jobs
):不得出现
failure
。如果 PR 触及 API/engine,
test-api (1..3/3)
、
Core LLM tests
和 LLM acceptance matrix 必须为
success
,不能为
skipped
;对于这些路径之外的变更,变更过滤器跳过它们属于正常情况。oracle-client jobs 已知可能 flaky,也可能会被
cancelled
——忽略这些 jobs。
Fork PR 会跳过
test-api
和 LLM jobs
(因为没有 secrets)。合并前在 upstream branch 上运行完整测试套件——合并到 main 不会运行任何 CI:
bash
git push origin HEAD:ci/pr-<N>-verify
gh workflow run CI --ref ci/pr-<N>-verify   # exactly ONCE — a second dispatch cancels the first
如果 failure 是由 PR 或修复导致的 → 修复、push,然后返回步骤 5。如果是已知 flake(检查 job log 未涉及 PR 代码)→ 在 run 完成后使用
gh run rerun <id> --failed
重跑一次。如果 CI 仍因无法归因的原因失败,停止并报告——不要在 CI 失败时合并。

6. Restore the worktree, then merge

6. 恢复 worktree,然后合并

First restore the worktree to how you found it: switch back to the original branch/commit and re-apply any work you set aside (undo the WIP commit / apply-then-drop your tagged stash). Do the same if you stop early on a blocker or failure. Restoring first also keeps
--delete-branch
from trying to check out
main
, which another worktree may hold.
bash
gh pr merge <N> --squash --delete-branch
git push origin --delete ci/pr-<N>-verify   # if you created it
Report: PR link, the findings fixed (one line each), anything deliberately left, and the CI run that gated the merge.
先将 worktree 恢复到发现它时的状态:切回原始 branch/commit,并重新应用此前暂存的工作(撤销 WIP commit,或应用后删除带标签的 stash)。如果因 blocker 或 failure 提前停止,也执行同样操作。先恢复还有助于避免
--delete-branch
尝试 checkout
main
,因为另一个 worktree 可能正持有该 branch。
bash
gh pr merge <N> --squash --delete-branch
git push origin --delete ci/pr-<N>-verify   # if you created it
报告:PR 链接、已修复的发现(每项一行)、有意保留的内容,以及作为合并门禁的 CI run。

Note on
gh pr edit

关于
gh pr edit
的说明

It fails on this repo — a GraphQL "Projects (classic)" deprecation aborts it and nothing changes, silently. To edit a description use the REST API instead:
bash
gh api -X PATCH repos/<owner>/<repo>/pulls/<N> -F body=@body.md
它在此仓库上会失败——GraphQL 的 “Projects (classic)” 弃用错误会中止操作,而且不会发生任何更改,不会给出提示。要编辑描述,请改用 REST API:
bash
gh api -X PATCH repos/<owner>/<repo>/pulls/<N> -F body=@body.md