Agent 方法论框架 superpowers:分支收尾技能防的是什么

2026-07-29

本文基于 superpowers 仓库 commit 44c9b2d(2026-07-27)梳理,该项目仍在持续迭代,具体行为以仓库 https://github.com/obra/superpowers 最新代码与文档为准。

一次 Agent 编码任务里最危险的动作,不是它写错了某行代码,而是它在活干完之后替你做了那个不可逆的决定——合并到哪条分支、要不要推远端、那个工作区还留不留。 代码写错有测试兜底,评审能抓回来;但一次合并到错误的基线分支、一次 git branch -D,回滚成本完全是另一个量级。superpowers 这个开源项目里有个技能专门守这一步,叫 finishing-a-development-branch,它做的事概括起来只有一句:把收尾变成一道必须由人回答的选择题,Agent 只负责把选项摆出来并执行。

一、它要防的到底是什么

先看这个技能被谁调用。skills/executing-plans/SKILL.md 里,执行计划的第三步写得很硬:所有任务完成并验证之后,宣布 I'm using the finishing-a-development-branch skill to complete this work.,然后标注 REQUIRED SUB-SKILL,强制转入 superpowers:finishing-a-development-branchskills/subagent-driven-development/SKILL.md 的 Finish 一节则在删掉本次计划的工作目录之后,以 Use superpowers:finishing-a-development-branch. 这一句结束整篇。也就是说,不管你走的是分会话执行计划的路子,还是在当前会话里派子代理逐任务实现的路子,最后都会汇到同一个出口。

这个设计的针对性很明确。一个刚跑完二十个任务、测试全绿、上下文里塞满了「我完成了」的 Agent,处在一种极强的完成偏好里——它会顺手把分支合掉,因为「显然你就是想要合并」。finishing-a-development-branch 的 Common Rationalizations 表把这条借口原样列出来了:"They obviously want it merged",对应的现实是 Integration is your human partner's decision. Present the menu and wait.

同一张表还堵了另外几个口子。"Tests passed earlier this session" 的回应是「在你即将集成的那棵树上重跑一遍,一次绿只能证明它跑过的那棵树」。"The base branch is obviously main" 的回应是「确认分叉点或者直接问,合错基线分支的代价很贵」。这些不是提示语气的建议,是写成对照表的硬规则——因为方法论文档里最容易蒸发的部分,恰恰是那些需要 Agent 克制自己的部分。

顺带说清本篇和站内两篇的分工:Agent 把活交给人的交接设计讲的是交接点该怎么设计这个通用命题,Agent 团队协作与交付讲的是团队尺度上的交付规范,两篇都是方法论层面。这一篇不重复那些原则,只看一个具体开源项目把它落到了什么粒度——具体到哪个变量在哪一步捕获、哪个词必须原样打出来才算确认。

二、五个步骤,顺序本身就是设计

skills/finishing-a-development-branch/SKILL.md 开头把核心原则写成一条流水线:Verify tests → Detect environment → Present options → Execute choice → Clean up。这个顺序不能调换,每一步都在为下一步排除掉一类事故。

第一步验证测试。 跑项目的完整测试套件(文档里举了 npm test / cargo test / pytest / go test ./...)。测试挂了就报告失败并停下,菜单只在绿色套件之后出现。这一步把「有没有资格谈集成」和「怎么集成」拆成了两个问题,避免人在看到三个诱人选项时忘了先问工作到底完没完成。

第二步探测环境。 这一步是整个技能里最工程化的部分:

GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
# Capture now, while still inside the workspace — Step 5 changes directory
# before cleanup (Step 6) needs this value
WORKTREE_PATH=$(git rev-parse --show-toplevel)

注意那条注释:现在就捕获,因为第五步会切换目录,而第六步的清理还要用这个值。这不是风格洁癖。仓库的发布说明里记着这条修复——工作区路径曾经是在清理已经切走目录之后才重新计算的,导致来源判断永远匹配不上,清理静默地什么也没做。一个「静默 no-op 的清理」在日志里毫无痕迹,只会表现为工作区越攒越多。

GIT_DIRGIT_COMMON 是否相等,决定了你在普通仓库还是在链接工作区里。这个判据同时出现在 skills/using-git-worktrees/SKILL.md 的 Step 0,那边还额外加了一道 submodule 守卫:子模块里这两个值同样不等,所以要先用 git rev-parse --show-superproject-working-tree 排除掉,否则会把子模块误判成工作区。

第三步确定基线分支。 文档的措辞是:基线分支就是这份工作分叉出来的地方,通常写在计划里、对话里,或者分支的上游。不确定就问 This branch split from <your best guess> - is that correct?

第四步呈现选项。 普通仓库和带分支名的工作区,给三个:本地合并回基线、推送并建 PR、保持原样。处于 detached HEAD 的工作区则收缩成两个,去掉合并——没有分支可合,这是 git 的事实约束而非策略选择。文档特意要求「按写的样子原样呈现」,不许自由发挥加项。

第五步执行选择。 合并这条路里有个细节值得抄:先合并、再验证合并结果的测试、最后才删东西。如果合并后的树测试挂了,停下,工作区和分支原地保留——因为什么都还没推,这次合并是本地的、可恢复的。

第六步清理工作区,只在选项一和明确确认的丢弃之后运行;选项二和选项三永远保留工作区。

三、菜单里为什么没有「丢弃」

这是我觉得最值得单独讲的一处。仓库 docs/superpowers/specs/ 下那份文件名带 2026-04-06 的工作区重构设计文档里,菜单还是四项,第四项写着 4. Discard this work。现在的 SKILL.md 里,菜单只剩三项,丢弃被移到了正文一个单独的小节,标题是 If your human partner asks to discard the work,开头第一句就限定了适用范围:这条路径只作为对「明确要求扔掉这份工作」的响应而存在。

发布说明记了改动理由:完成菜单诞生于「扔掉分支是家常便饭」的年代,把 Discard this work 摆在 Merge 旁边,等于在给「销毁已完成且测试通过的工作」打广告。菜单是一种推荐,四个并列选项里的任何一个都带着「这是合理选择之一」的暗示。

丢弃这条路保留了原来的确认仪式,而且卡得比一般的二次确认更死:

This will permanently delete:
- Branch <name>
- All commits: <commit-list>
- Worktree at <path>

Type 'discard' to confirm.

Common Rationalizations 表里配了两条对应的堵漏:"They seem done with this feature — I'll offer to discard it" 的现实是「菜单就是写好的那些,丢弃只在对方明确说出口时才发生」;"'Yeah, get rid of it' counts as confirmation" 的现实是「只有打出 discard 这个词才构成授权」。一句随口的「嗯,删了吧」不算数——这个判定标准的价值在于它不需要 Agent 揣摩语气,只需要做字符串比较。这也是人机确认设计里一个通用的取舍,展开可以看Agent 流程里的人工介入点

另外提一句:仓库 README 里那份技能概览的一行摘要仍写着 presents options (merge/PR/keep/discard),和当前 SKILL.md 的三项菜单对不上。你要照着实现,以 SKILL.md 为准。

四、清理的归属判定:什么它敢删,什么它不碰

第六步的清理不是无条件的 git worktree remove,而是先做一次来源判断。

GIT_DIR == GIT_COMMON 说明是普通仓库,没有工作区要清,直接结束。WORKTREE_PATH 位于 .worktrees/worktrees/ 之下,说明这个工作区是 superpowers 自己建的——skills/using-git-worktrees/SKILL.md 的目录选择策略正是这两个目录,两个都存在时 .worktrees 优先——它才拥有清理权:

git worktree remove "$WORKTREE_PATH"
git worktree prune  # Self-healing: clean up any stale registrations

除此之外的一切路径,归属宿主环境,原地留着;如果平台提供了退出工作区的工具就用那个。Common Rationalizations 里对应的那条是 "This other worktree looks stale — I'll clean it too"——只清 .worktrees/worktrees/ 下面的,其余都属于宿主。

这条归属规则重要到被写进了回归测试。tests/claude-code/test-worktree-path-policy.sh 会断言 finishing-a-development-branch 的 SKILL.md 里必须含有 `.worktrees/` or `worktrees/` 这段文本,同时断言两个技能文档里都不能再出现旧的全局工作区路径。用测试脚本去守一份 Markdown 文档的内容,这个做法本身就说明作者认为这条规则最容易在后续编辑中被磨掉。工作区隔离本身怎么用,可以参考worktree 并行开发实践

五、组成部分速查

组成部分它负责什么对应仓库位置你什么时候会碰到它
收尾技能本体验证测试、探测环境、出菜单、执行选择、按归属清理skills/finishing-a-development-branch/SKILL.md实现完成、测试全绿,准备集成时
计划执行技能逐任务执行写好的计划,完成后强制转入收尾技能skills/executing-plans/SKILL.md拿到一份实现计划、要在独立会话里执行时
子代理驱动开发每任务派新子代理实现 + 任务级复核 + 分支级终审,末尾同样转入收尾skills/subagent-driven-development/SKILL.md计划里任务彼此独立、想留在当前会话执行时
工作区隔离技能探测是否已在隔离工作区、征得同意后创建、跑基线测试skills/using-git-worktrees/SKILL.md开工之前,尤其是不想污染当前分支时
完成前验证技能「没跑过验证命令就不许声称通过」的铁律与门函数skills/verification-before-completion/SKILL.md任何要说「好了/通过了/修好了」的时刻
工作区路径策略测试用断言守住清理归属规则和旧全局路径的移除tests/claude-code/test-worktree-path-policy.sh你改动这两个技能文档、跑仓库测试时

六、边界与代价:它明确不管什么

这套设计的取舍相当清楚,用之前得认。

它让收尾变慢,而且是故意的。 每次做完一份工作,你都要被问一次「合并、PR,还是先放着」。对于改一行文案、修一个错别字这种改动,这是彻头彻尾的过度设计——菜单的价值来自决策的不可逆程度,改动越小价值越低。小改动直接提交,别套这一层。

它让 Agent 更啰嗦。 技能开头要求 Announce at start,把「我正在用某某技能」说出来;执行计划技能里同样有一句宣布语。这对旁观者是可观测性,对你则是每次都要读的样板话。

它明确不管代码质量。 收尾技能只跑测试套件看绿不绿,代码好不好是评审的事——那是 skills/requesting-code-review/skills/receiving-code-review/ 的领域。它也不管你的验收标准定得对不对,那需要单独一套东西,可以看Agent 验收标准怎么定

它不替你选分支策略。 基线分支是谁、要不要 rebase、squash 还是 merge commit、PR 模板怎么填,这些它一概按你仓库的既有约定走。创建 PR 那一步在文档里的措辞是「用你的代码托管平台的工具——有 CLI 就用 CLI,或者用推送后大多数平台会打印的创建链接」,并且遵循仓库自带的 PR 模板和约定,不绑定任何一家。

它管不到已经推出去的东西。 三个选项里只有本地合并这条路是可回滚的。一旦推送并建了 PR,工作区必须保留——因为 PR 的反馈意见要在那个工作区里改。Common Rationalizations 里 "The PR is up, so the worktree is clutter now" 的现实就是「PR 反馈在那个工作区里修,工作落地之前它得留着」。

它的强制力来自文档,不来自代码。 这是最需要认清的一点:所有这些规则都写在 Markdown 里,靠模型愿意读、愿意照做。没有任何机制能物理阻止一个 Agent 跳过菜单直接 git merge。那张 Common Rationalizations 表之所以存在,正是因为唯一的执行手段是在模型即将编造借口的地方,把借口和反驳预先摆在它眼前。

七、上手与避坑清单

别在收尾这一步才第一次跑完整测试。 会踩是因为你相信半小时前那次绿色;但这半小时里可能又提交了三次。技能要求在「你即将集成的那棵树」上重跑,避免的办法是把收尾前的完整套件当成固定动作,而不是在心里估算「应该还是绿的」。这条和完成前验证技能的铁律是同一件事:NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE

别让 Agent 自己猜基线分支。 会踩是因为大多数仓库确实是从 main 分出来的,猜对率很高——直到某次你是从一条发布分支切出来的。避免的办法是把基线分支写进计划文件或者开工时的第一条指令里,让它有据可查,而不是靠一次「这个分支是从 main 分出来的吧?」的追问。

别在切目录之后才去算工作区路径。 会踩是因为写清理代码时很自然的顺序是「先回到主仓库根目录,再删工作区」,路径顺手在删之前取。但那时 git rev-parse --show-toplevel 返回的已经是主仓库了。避免的办法就是文档里那条注释:探测阶段就把 WORKTREE_PATH 存下来。这个 bug 在这个项目里是真实发生过并被记录下来的。

别把「差不多是同意了」当授权。 会踩是因为对话式交互里人的表达天然模糊,模型又倾向于把模糊往「用户想推进」的方向解释。避免的办法是把授权判据设成一个字符串比较——必须打出 discard 这个词。任何需要模型揣摩意图的确认设计,在疲劳的长会话里都会松动。

别顺手清理不属于你的工作区。 会踩是因为 git worktree list 里那些看着很旧的条目确实碍眼。避免的办法是只认路径归属:.worktrees/worktrees/ 之外的一律不碰,哪怕它看起来再像垃圾。

别在没有子代理能力的环境里硬套子代理流程。 skills/executing-plans/SKILL.md 明确提示:superpowers 在有子代理的环境里效果好得多,如果有子代理就该改用 superpowers:subagent-driven-development。反过来,环境不支持时硬拆流程只会增加轮次而不增加质量。

别把这套照抄进小改动。 会踩是因为一套流程用顺手了就想全域套用。避免的办法是设一条自己的门槛:改动是否不可逆、是否有人会在你之后接着改这份工作。两个都是否,直接提交。

结尾:一份可以贴在收尾前的自检

把这套东西压缩成四个问题,够你在按下合并之前问自己一遍:这棵即将集成的树上,测试是这一刻跑绿的吗?基线分支是确认过的还是猜的?集成方式是人选的还是 Agent 替人选的?要删的那个工作区,是这套流程自己建的吗?

四个问题里有任何一个答不上来,收尾就不该继续。

想继续往下读,建议的顺序是:先看 skills/finishing-a-development-branch/SKILL.md 末尾那张 Common Rationalizations 表——它比正文更能说明设计者到底在防什么;再看 skills/verification-before-completion/SKILL.md,那是整套流程里所有「完成」声明的门;最后回到 skills/using-git-worktrees/SKILL.md,把开工的隔离和收尾的清理对着看,两边的 GIT_DIR/GIT_COMMON 判据是同一套,理解一次就够。

这个项目以 MIT 许可证开源,版权归 Jesse Vincent。你不需要整套搬进自己的工作流——那张对照表和那条打字确认的规则,单独拿走也成立。

本文属于 superpowers 方法论专题(共 30 篇,含三篇与其它开源 Agent 项目的对照)。想看把资产铺满的另一种取向,见 ECC 开源 Agent 套件专题

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。