version-bump
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVersion Bump — version a project at land time
Version Bump — 在 land 时为项目确定版本
This repository's own versioning procedure. declares it as the
pre-merge step, so the land command runs it before merging. Tagging and the
GitHub release are not part of it; does that on
merge. When invoked directly, run this skill before the land command,
against the version of you intend to land onto. When a land command
invokes this skill, return to it after step 8.
AGENTS.mdrelease-on-merge.ymlmain这是仓库自身的版本管理流程。 将其声明为合并前步骤,因此 land command 会在合并前运行它。打标签和创建 GitHub release 不属于此流程; 会在合并后完成这些操作。直接调用时,应在 land command 之前运行此 skill,并针对你准备合入的 版本执行。当 land command 调用此 skill 时,完成第 8 步后返回该命令。
AGENTS.mdrelease-on-merge.ymlmainPrecondition — explicit land intent
前置条件——明确的 land 意图
Why: the bump is computed against the base branch's tip at this moment; a
bump made before the land goes stale when another PR merges, and the pre-merge
guard then denies the merge.
This skill fires only on explicit land intent, meaning one of:
- The user asked to land: "ship it", "land the PR", "land this".
- The user asked for the bump itself: "bump the version", "version this PR".
- A land command is already in flight and reached the project's pre-merge steps.
Never infer land intent. None of the following is a cue to bump:
- The work is finished, the review passed, or CI is green.
- A draft PR is about to be opened, or was just opened. A drafted PR carries
no version: the bullet goes under and nothing else moves.
## [Unreleased] - The invariant script exited 1. That exit states a precondition for merging, expected for a runtime PR's whole review lifetime, never a request to bump now.
With no land intent, stop and say so. Report that the branch will need a
bump before it can merge, and wait for the user. Do not bump "to be helpful".
**原因:**版本递增是根据基础分支当前的 tip 计算的;如果在 land 之前完成版本递增,而此时另一个 PR 合并,那么之前的递增结果就会过时,随后合并前门禁会拒绝合并。
此 skill 仅在存在明确 land 意图时触发,即满足以下任一条件:
- 用户要求 land:“发布吧”、“land 这个 PR”、“执行 land”。
- 用户要求执行版本递增:“递增版本”、“为此 PR 确定版本”。
- land command 已在执行,并已进入项目的合并前步骤。
绝不能推断 land 意图。 以下情况均不是执行版本递增的信号:
- 工作已完成、评审已通过或 CI 全绿。
- 即将创建或刚创建了 draft PR。draft PR 不携带版本:相关条目应放在 下,除此之外不移动任何内容。
## [Unreleased] - invariant script 以退出码 1 结束。这表示一项合并前置条件,在 runtime PR 的整个评审周期内都属于预期情况,绝不表示现在要求执行版本递增。
若没有 land 意图,停止并说明情况。报告该分支在合并前仍需进行版本递增,然后等待用户。不要为了“顺手帮忙”而递增版本。
Steps
Steps
0. Runtime-vs-dev gate — does this PR warrant a bump at all?
0. runtime 与 dev 门禁——这个 PR 是否确实需要版本递增?
Run this before everything else. Most steps below only apply if it says yes.
The quick look is orientation only — it never decides the exit:
. The decision comes from the
invariant run, the same invocation contract the pre-merge guard enforces:
git diff "origin/<default>...HEAD" --name-only- Resolve the default branch by asking GitHub (), falling back to the local
gh repo viewref. Never a guessedorigin/HEAD. If both fail, stop: no verdict.main - must succeed. Never degrade to a stale base for a verdict.
git fetch origin <default> - The fetched tip must be an ancestor of the branch tip. If the branch is behind, stop: rebase onto
origin/<default>and re-enter step 0.origin/<default> - Run the script with = the local branch tip and
HEAD_SHA= the fetchedBASE_SHAtip — never a pre-computed merge-base, and never a hand-rolled two-dot diff.origin/<default>
bash
DEFAULT=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null)
DEFAULT=${DEFAULT:-$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')}
[ -n "$DEFAULT" ] || { echo "cannot resolve the default branch — no verdict"; exit 1; }
git fetch origin "$DEFAULT" || { echo "fetch failed — no verdict"; exit 1; }
git merge-base --is-ancestor "refs/remotes/origin/$DEFAULT" HEAD \
|| { echo "behind base — rebase onto origin/$DEFAULT, re-enter step 0"; exit 1; }
HEAD_SHA=$(git rev-parse HEAD) BASE_SHA=$(git rev-parse "refs/remotes/origin/$DEFAULT") \
.claude/scripts/version-bump-required.shRead the outcome by exact output match — the signal rule, default-deny:
- Exit 0, stdout starting → dev-only and final. DO NOT BUMP. Skip every step below. Leave the version untouched, do not cut the changelog, and land with the plain conventional title (
OK: runtime_changed=false bumped=false). On a re-entry whose PR title still carries a stale<type>: <subject>prefix from an earlier bump, strip it now (vX.Y.Z) — the title backstop never strips a stale prefix, and this is the one step-8 action a no-bump exit still owes. Then go straight to the land command. This exit requires that OK line — the quick look alone never authorizes it.gh pr edit --title - Exit 0, stdout starting → already bumped (a recovery re-entry). Never re-bump — proceed to the land command.
OK: runtime_changed=true bumped=true - Exit 1, verdict containing → bump warranted. It is actionable here only because the land-intent precondition already passed; read outside a land it states a merge precondition and nothing more.
cannot merge until version-bump runs at land time- On a branch with no commit: continue to step 1. Every other exit-1 verdict in this list stops.
chore(version) - On a branch already carrying a commit: the bump went stale (a rebase moved the fork point). Stop — drop the bump commit, undo the changelog cut, reset the title, and re-enter step 0.
chore(version)
- On a branch with no
- Exit 1, verdict containing → wrongful bump. Stop — drop the commit, undo the cut, and re-run step 0.
must land with no bump - Anything else — a non-semver version, a merge-base or diff failure, or unrecognized output — stops in both directions. Surface the message verbatim. A hard script error never means "keep going".
This is a hard gate, not a judgment call.
在执行其他所有操作之前运行此步骤。下方大多数步骤仅在结果为 yes 时适用。
快速查看仅用于初步定位——绝不能据此决定退出结果:
。最终结论来自 invariant run,其调用契约与合并前门禁强制执行的契约完全相同:
git diff "origin/<default>...HEAD" --name-only- 通过 GitHub 查询默认分支(),失败时回退到本地
gh repo viewref。绝不能猜测默认分支为origin/HEAD。如果两者都失败,则停止:无法给出结论。main - 必须成功。绝不能基于陈旧的 base 给出结论。
git fetch origin <default> - 获取到的 tip 必须是分支 tip 的祖先。如果分支落后,则停止:将分支 rebase 到
origin/<default>上,然后重新进入步骤 0。origin/<default> - 运行脚本时,使用本地分支 tip,
HEAD_SHA使用获取到的BASE_SHAtip——绝不能使用预先计算的 merge-base,也绝不能使用自行构造的 two-dot diff。origin/<default>
bash
DEFAULT=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null)
DEFAULT=${DEFAULT:-$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')}
[ -n "$DEFAULT" ] || { echo "cannot resolve the default branch — no verdict"; exit 1; }
git fetch origin "$DEFAULT" || { echo "fetch failed — no verdict"; exit 1; }
git merge-base --is-ancestor "refs/remotes/origin/$DEFAULT" HEAD \
|| { echo "behind base — rebase onto origin/$DEFAULT, re-enter step 0"; exit 1; }
HEAD_SHA=$(git rev-parse HEAD) BASE_SHA=$(git rev-parse "refs/remotes/origin/$DEFAULT") \
.claude/scripts/version-bump-required.sh根据精确输出匹配读取结果——信号规则,默认拒绝:
- 退出码 0,stdout 以 开头 → 仅涉及开发内容,且流程至此结束。不要递增版本。 跳过下方每一步。保持版本不变,不要切割 changelog,并使用普通 conventional title(
OK: runtime_changed=false bumped=false)执行 land。如果重新进入此流程时,PR 标题仍带有早先递增遗留的过时<type>: <subject>前缀,此时应将其移除(vX.Y.Z)——title backstop 从不移除过时前缀,而这是无需递增的退出路径仍需完成的唯一一项第 8 步操作。随后直接进入 land command。该退出结果必须包含那行 OK——仅凭快速查看绝不能授权跳过递增。gh pr edit --title - 退出码 0,stdout 以 开头 → 已经完成过递增(属于恢复流程的重新进入)。绝不能再次递增——直接进入 land command。
OK: runtime_changed=true bumped=true - 退出码 1,结论中包含 → 需要执行版本递增。只有在 land 意图前置条件已经通过后,该结论才在此处可执行;如果不在 land 流程中阅读,它仅表示一项合并前置条件,不应触发其他操作。
cannot merge until version-bump runs at land time- 如果分支上没有 提交:继续进入步骤 1。此列表中的其他任何 exit 1 结论都应停止。
chore(version) - 如果分支上已经包含 提交:之前的递增结果已过时(rebase 移动了 fork point)。停止——丢弃递增提交,撤销 changelog 切割,重置标题,然后重新进入步骤 0。
chore(version)
- 如果分支上没有
- 退出码 1,结论中包含 → 错误地执行了递增。停止——丢弃提交,撤销切割,然后重新运行步骤 0。
must land with no bump - 其他任何情况——非 SemVer 版本、merge-base 或 diff 失败、无法识别的输出——无论结论朝哪个方向都应停止。逐字呈现其消息。脚本硬错误绝不表示“继续执行”。
这是硬门禁,不是主观判断。
1. Decide the bump level
1. 确定递增级别
Reached only when step 0 said a bump is warranted; the only question is how
big the bump is.
bash
undefined仅在步骤 0 判定需要递增时进入此步骤;唯一需要回答的问题是递增幅度有多大。
bash
undefinedRe-derive the base: shell variables do not persist from step 0's call.
Re-derive the base: shell variables do not persist from step 0's call.
DEFAULT=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null)
DEFAULT=${DEFAULT:-$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')}
: "${DEFAULT:?cannot resolve the default branch}"
git log "origin/$DEFAULT..HEAD" --oneline
git diff "origin/$DEFAULT...HEAD" --stat
[SemVer 2.0.0](https://semver.org/spec/v2.0.0.html), quoted verbatim. Items 6,
7, and 8 each carry an `x > 0` precondition:
| Item | Normative rule | Scope |
|------|----------------|-------|
| 6 | PATCH "MUST be incremented if only backward compatible bug fixes are introduced. A bug fix is defined as an internal change that fixes incorrect behavior." | `x.y.Z \| x > 0` |
| 7 | MINOR "MUST be incremented if new, backward compatible functionality is introduced to the public API." | `x.Y.z \| x > 0` |
| 8 | MAJOR "MUST be incremented if any backward incompatible changes are introduced to the public API." | `X.y.z \| X > 0` |
If the version starts `0.`, none of those three binds. Item 4 governs: "Major
version zero (`0.y.z`) is for initial development. Anything MAY change at any
time. The public API SHOULD NOT be considered stable." Item 5: "Version 1.0.0
defines the public API." The spec assigns no level pre-1.0, so the decision
below uses the same observable-change rule before and after 1.0.0.
Ask these in order. **The first yes wins.** Judge the *change*, never the
commit subject:
1. **Can a plugin user observe the difference?** → **minor**. Anything that
changes what they type, what they get back, or what the project does on
their behalf is observable: a command's name or arguments
(`argument-hint`), documented behavior, whether a step prompts them, an
artifact's format or frontmatter schema, an agent's model, or tool access.
New and changed capability both land here.
2. **Otherwise** → **patch**: internal-only *and* backward compatible — prose
that clarifies without changing an instruction, a comment, restructuring
that preserves behavior. Item 6 requires both qualifiers, not just a `fix:`
subject. Expect patch to be rare for projects that ship instructions a
model reads. Do not widen patch to make the cadence feel familiar.
**`major` is unreachable while the version starts `0.`** A breaking change
pre-1.0 is a **minor**, not a major. If a change looks like it warrants major,
that is a signal to **ask whether it is time to declare 1.0.0** — a deliberate
decision, never a side effect of this skill.
State the chosen level and which question decided it. A level that needed a
judgment call means the observability question was not actually answered —
answer it rather than asking the user.DEFAULT=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null)
DEFAULT=${DEFAULT:-$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')}
: "${DEFAULT:?cannot resolve the default branch}"
git log "origin/$DEFAULT..HEAD" --oneline
git diff "origin/$DEFAULT...HEAD" --stat
以下引用 [SemVer 2.0.0](https://semver.org/spec/v2.0.0.html) 的规范内容。第 6、7、8 项分别带有 `x > 0` 前置条件:
| 项目 | 规范规则 | 适用范围 |
|------|----------------|-------|
| 6 | PATCH“如果只引入向后兼容的 bug 修复,MUST 递增。bug 修复定义为修复错误行为的内部变更。” | `x.y.Z \| x > 0` |
| 7 | MINOR“如果向公共 API 引入新的向后兼容功能,MUST 递增。” | `x.Y.z \| x > 0` |
| 8 | MAJOR“如果对公共 API 引入任何向后不兼容的变更,MUST 递增。” | `X.y.z \| X > 0` |
如果版本以 `0.` 开头,上述三项规则均不适用。第 4 项规定:“主版本零(`0.y.z`)用于初始开发。任何内容都 MAY 随时更改。公共 API SHOULD NOT 被视为稳定。”第 5 项规定:“`1.0.0` 定义公共 API。”该规范未为 1.0.0 之前指定级别,因此下方判定在 1.0.0 前后采用相同的可观察变更规则。
按顺序询问以下问题。**第一个 yes 决定结果。** 判断依据始终是*变更本身*,绝不是提交主题:
1. **插件用户能否观察到差异?** → **minor**。任何改变用户输入内容、返回内容或项目代表其执行行为的变化,都是可观察的:命令的名称或参数(`argument-hint`)、文档所描述的行为、某个步骤是否会提示用户、artifact 的格式或 frontmatter schema、Agent 的模型或工具访问权限。新增能力和变更能力均归入此处。
2. **否则** → **patch**:仅涉及内部且向后兼容——澄清文案但不改变指令、添加注释,或进行保持行为的重构。第 6 项要求同时满足两个条件,而不能仅凭 `fix:` 主题作出判断。对于发布供模型读取的指令的项目,patch 应当很少使用。不要为了让发布节奏显得熟悉而扩大 patch 的适用范围。
**版本以 `0.` 开头时,`major` 不可达。** 1.0.0 之前的破坏性变更是 **minor**,而不是 major。如果某项变更看起来需要 major,这应是一个信号:询问是否已到宣布 `1.0.0` 的时候——这必须是一项有意决策,绝不能成为此 skill 的副作用。
说明所选级别,以及由哪个问题决定。如果某个级别仍需要主观判断,说明可观察性问题实际上没有得到回答;应回答该问题,而不是询问用户。2. Compute the next version
2. 计算下一个版本
bash
bash .claude/scripts/next-version.sh <level>bash
bash .claude/scripts/next-version.sh <level>3. Bump every version string
3. 递增所有版本字符串
The version lives in every file below:
| File | Occurrences |
|---|---|
| 1 ( |
| 2 ( |
| 1 ( |
| 1 ( |
| 1 ( |
| 1 ( |
Each host shows the version in its own manifest: Codex reads
in preference to the Claude manifest; Antigravity
reads the root , which sits at the root rather than in a directory
of its own because that host resolves and as siblings of
its manifest. A stale one makes the same release look like two different
versions depending on the host.
.codex-plugin/plugin.jsonplugin.jsonskills/agents/Edit every file, then prove it:
bash
grep -rn '"version"' package.json plugin.json .claude-plugin/plugin.json \
.claude-plugin/marketplace.json .codex-plugin/plugin.json .cursor-plugin/plugin.jsonEvery line must show the new version. None may still show the old one.
版本出现在下列每一个文件中:
| 文件 | 出现次数 |
|---|---|
| 1( |
| 2( |
| 1( |
| 1( |
| 1( |
仓库根目录的 | 1( |
每个 host 都在自己的 manifest 中显示版本:Codex 会优先读取 ,而不是 Claude manifest;Antigravity 读取仓库根目录的 。后者之所以位于根目录而不是独立目录中,是因为该 host 会将 和 解析为其 manifest 的同级目录。任何一个版本未同步,都会让同一次 release 在不同 host 上看起来像是两个不同版本。
.codex-plugin/plugin.jsonplugin.jsonskills/agents/编辑每个文件,然后进行验证:
bash
grep -rn '"version"' package.json plugin.json .claude-plugin/plugin.json \
.claude-plugin/marketplace.json .codex-plugin/plugin.json .cursor-plugin/plugin.json每一行都必须显示新版本。不得有任何一行仍显示旧版本。
4. Cut the changelog section
4. 切割 changelog 章节
In (Keep a Changelog format, entry style per
):
CHANGELOG.mdskills/team-pr/references/changelog.md- Move the entire body into a new
[Unreleased](today's date) section inserted directly below## [X.Y.Z] - YYYY-MM-DD. Leave## [Unreleased]in place, now empty again.## [Unreleased] - Re-point the link-reference footer:
- compare base →
[Unreleased]vX.Y.Z...HEAD - Add
[X.Y.Z]: https://github.com/<owner>/<repo>/compare/v<prev>...vX.Y.Z
This section becomes the GitHub release notes verbatim — write it for a reader
deciding if the upgrade is worth it. Any links must be absolute URLs:
relative paths (e.g. ) render as dead links on the release
page.
docs/versioning.mdEmpty . A PR that reached this step changed runtime files; an
empty on it means nobody wrote the user-facing bullet. Derive
at least one bullet from the PR's runtime commits
(///, in that entry style). Never write an empty
section ( errors on empty release notes).
[Unreleased][Unreleased]feat:fix:perf:security:release-on-merge.ymlEmpty and no runtime change is not this case: that PR must
have stopped at step 0, with no bump and no changelog cut. Do not invent a
bullet to justify a bump that step 0 already declined. Go back and land plain.
[Unreleased]在 中(采用 Keep a Changelog 格式,条目样式遵循 ):
CHANGELOG.mdskills/team-pr/references/changelog.md- 将整个 正文移入一个新的
[Unreleased](今天的日期)章节,并直接插入在## [X.Y.Z] - YYYY-MM-DD下方。保留## [Unreleased],但将其内容重新置空。## [Unreleased] - 重新指向链接引用页脚:
- 将 的 compare base 指向
[Unreleased]vX.Y.Z...HEAD - 添加
[X.Y.Z]: https://github.com/<owner>/<repo>/compare/v<prev>...vX.Y.Z
- 将
该章节将逐字成为 GitHub release notes——应面向正在判断升级是否值得的读者来编写。任何链接都必须是绝对 URL:相对路径(例如 )在 release 页面中会呈现为失效链接。
docs/versioning.md空的 。 到达此步骤的 PR 改动了 runtime 文件;此时 为空,说明没有人编写面向用户的条目。至少根据该 PR 的 runtime 提交推导出一条条目(///,并遵循相应条目样式)。绝不编写空章节( 会在 release notes 为空时报错)。
[Unreleased][Unreleased]feat:fix:perf:security:release-on-merge.yml[Unreleased]5. Land-time consistency assertion
5. Land-time 一致性断言
Run it after the changelog cut (so the dated section exists) and before
committing:
bash
bash .claude/scripts/check-version-consistency.sh
node -e "['.claude-plugin/plugin.json','.claude-plugin/marketplace.json','.codex-plugin/plugin.json','.cursor-plugin/plugin.json','.agents/plugins/marketplace.json','plugin.json','package.json'].forEach(f=>JSON.parse(require('fs').readFileSync(f)));console.log('JSON OK')"The released-section and footer-compare-link invariants hold only after the
cut, so assert them inline here:
bash
V=$(jq -r .version .claude-plugin/plugin.json)
OWNER_REPO=$(gh repo view --json nameWithOwner --jq .nameWithOwner)
ESC=$(sed 's/\./\\./g' <<<"$V")
grep -qE "^## \[$ESC\] - [0-9]{4}-[0-9]{2}-[0-9]{2}$" CHANGELOG.md \
|| { echo "::error::no '## [$V] - YYYY-MM-DD' section — the cut did not land"; exit 1; }
grep -qE "^\[$ESC\]: https://" CHANGELOG.md \
|| { echo "::error::no footer compare link for $V"; exit 1; }
grep -q "\[Unreleased\]: https://github.com/$OWNER_REPO/compare/v$V...HEAD" CHANGELOG.md \
|| { echo "::error::[Unreleased] footer does not compare from v$V"; exit 1; }
echo "OK: land-time consistency holds"If any check fails, stop before committing and fix the cut.
在切割 changelog 之后(这样带日期的章节才会存在)且提交之前运行:
bash
bash .claude/scripts/check-version-consistency.sh
node -e "['.claude-plugin/plugin.json','.claude-plugin/marketplace.json','.codex-plugin/plugin.json','.cursor-plugin/plugin.json','.agents/plugins/marketplace.json','plugin.json','package.json'].forEach(f=>JSON.parse(require('fs').readFileSync(f)));console.log('JSON OK')"发布章节和页脚 compare 链接的不变量只有在切割完成后才成立,因此在此处内联断言:
bash
V=$(jq -r .version .claude-plugin/plugin.json)
OWNER_REPO=$(gh repo view --json nameWithOwner --jq .nameWithOwner)
ESC=$(sed 's/\./\\./g' <<<"$V")
grep -qE "^## \[$ESC\] - [0-9]{4}-[0-9]{2}-[0-9]{2}$" CHANGELOG.md \
|| { echo "::error::no '## [$V] - YYYY-MM-DD' section — the cut did not land"; exit 1; }
grep -qE "^\[$ESC\]: https://" CHANGELOG.md \
|| { echo "::error::no footer compare link for $V"; exit 1; }
grep -q "\[Unreleased\]: https://github.com/$OWNER_REPO/compare/v$V...HEAD" CHANGELOG.md \
|| { echo "::error::[Unreleased] footer does not compare from v$V"; exit 1; }
echo "OK: land-time consistency holds"如果任何检查失败,必须在提交前停止并修复切割结果。
6. Commit
6. 提交
Commit the bump as its own commit in the PR branch:
bash
git add .claude-plugin/plugin.json .claude-plugin/marketplace.json \
.codex-plugin/plugin.json .cursor-plugin/plugin.json plugin.json package.json \
CHANGELOG.md
git commit -m "chore(version): X.Y.Z"在 PR 分支中将版本递增作为独立提交:
bash
git add .claude-plugin/plugin.json .claude-plugin/marketplace.json \
.codex-plugin/plugin.json .cursor-plugin/plugin.json plugin.json package.json \
CHANGELOG.md
git commit -m "chore(version): X.Y.Z"7. Assert the bump invariant
7. 断言递增不变量
Re-run the invariant run from step 0 — the head is now the branch tip carrying
the commit. Require exit 0 with stdout starting
.
chore(version)OK: runtime_changed=true bumped=trueThis runs before any remote change (the title edit in step 8 is remote).
On any other outcome, stop: drop the commit, undo the
changelog cut, and land plain — nothing has left the machine, so the recovery
is purely local.
chore(version)重新运行步骤 0 中的 invariant run——此时 HEAD 是包含 提交的分支 tip。要求退出码为 0,且 stdout 以 开头。
chore(version)OK: runtime_changed=true bumped=true此检查在任何远程变更之前运行(第 8 步中的标题编辑属于远程变更)。如果出现任何其他结果,则停止:丢弃 提交,撤销 changelog 切割,并按普通方式 land——此时尚未有任何内容离开本机,因此恢复操作完全可以在本地完成。
chore(version)8. Title the PR
8. 设置 PR 标题
vX.Y.Z <type>: <subject>v1.4.0 feat: add a design review gategh pr edit --titlePR title syncA re-entry path that runs after a title already exists must reset it: the
stale-bump recovery re-titles with the recomputed version, and a re-entry that
ends at "no bump" strips the prefix explicitly — the title backstop
never strips a stale prefix.
vX.Y.ZReturn to the land command to push, wait for CI, and squash-merge.
vX.Y.Z <type>: <subject>v1.4.0 feat: add a design review gategh pr edit --titlePR title sync如果重新进入流程时标题已经存在,则必须重置标题:递增结果过期的恢复流程应使用重新计算的版本重设标题;以“无需递增”结束的重新进入流程则应显式移除 前缀——title backstop 从不移除过时前缀。
vX.Y.Z返回 land command,继续推送、等待 CI,并执行 squash-merge。