| name | worktree-isolation |
| description | 开工前先在独立 git worktree 里再改代码——**绝不在 main(主检出/main 分支)上开发**,任何迭代/功能/哪怕一行修改都先建分支+worktree(并发只是让这条更刚性,单人单线同样适用);worktree 集成回目标分支后立即清理 worktree+本地分支+远端分支(让位见收尾节)。Never develop on the main checkout/branch — always create a dedicated worktree first (concurrency only makes it stricter, not a precondition); also covers teardown/cleanup when a worktree is integrated. 触发:凡要动代码,动手前先过本技能 Step 0(由 owner 在其实现阶段调用,本技能不抢交付入口)、并行做两个版本、改 ccl-skills 等共享仓库、被隔离闸 deny、worktree 干完要清理时。Skip when:交付级的重新开发/推倒重来/清除代码重来("之前那版不要了,重新开发")先回 product-rd-workflow 重新分类+重建计划——本技能只管 worktree 机制,不替代交付重入决定;product-rd 重入后仍按本技能 Step 0 先建 worktree 再动手,绝不在 main 上重写。 |
Worktree Isolation(编辑隔离)
核心心法
主检出(main 那个工作树)是干净的基线/集成点,不是开发现场。每个迭代/任务在自己的 worktree 里做。并行的两个迭代 = 两个 worktree,互不碰主检出 → 天生不会 clobber。
「天生不会 clobber」只覆盖 git 工作树 / 索引 / 分支——不覆盖仓外的共享运行时状态。 独立 worktree 让并行 lane 互不碰彼此的 tracked 文件和 index,但不隔离仓外共享面,大致三类:①共享服务/端口——同一本机的测试库 / 消息队列 / 缓存服务、监听端口(如 :3000)、docker compose 工程名与卷;②共享文件状态——共享的 .env、跨 worktree 共享的可变 node_modules / .venv / build 输出 / 生成物目录、固定 /tmp 路径;③主机全局配置与凭据——$HOME 下的工具配置与 auth profile、KUBECONFIG / gcloud·kubectl 当前 context / cloud·registry 凭据、~/.npmrc、浏览器/设备 profile(切错 context 会让另一条 lane 的 migration/deploy/install 打到错误目标——是权限/目标级 clobber,不止数据)。两个 worktree 同时跑 pytest / migration / dev server / deploy 会在这些面互踩——一方的 migration 或 fixture 清库毁掉另一方、端口占用失败、缓存串写、或打到错误集群。所以并行前先隔离:每条 lane 独立的 DB/schema/namespace、不同端口、独立 compose project 与卷、每 worktree 自己的可变依赖安装/构建/输出目录、独立的 KUBECONFIG/配置 profile 并显式传 context 而非依赖全局当前值(并发安全的内容寻址/只读缓存可共享,不必强拆——如 pnpm store、Go module cache;cargo 仅指依赖下载缓存,不含 $CARGO_HOME 的 config/凭据/bin/registry index/target;只拆会被并发写坏的可变面);隔离不了的共享面就把那部分工作串行(呼应 multi-agent-delegation 的「共享 state / migration / 生成物就串行或留本地」)。单人单线顺序跑通常不触发,但共享库残留脏数据或残留的全局 context 仍可能跨 lane 串——按需重置。
绝不在 main 上开发:main(主检出 / main 分支)永远是干净基线/集成点,不是开发现场——任何迭代/功能、哪怕一行修改,都先建分支 + worktree 再改,不论单人单线还是并发、不论是不是技能仓库(worktree 很便宜,没有例外)。并发只是让这条更刚性,不是它的前提。
绝不依赖 ambient cwd(机械纪律,与上条并列):多 worktree 下 shell 的 cwd 可能在两次工具调用之间被 harness 静默重置(常见回显 Shell cwd was reset to <某路径>;被重置的是 cd 出来的 shell cwd——宿主原生 EnterWorktree/--worktree 设的持久工作上下文不受此影响,对它 git -C 是双保险)。所以凡必须落到某个特定检出的操作都不靠"当前恰好 cd 在哪":git 变更(add/commit/merge/branch 等)一律显式 git -C "<abs-worktree-path>" …(-C 等价于在该目录里起 git,pathspec 也按 -C 目录解析,故配绝对文件路径);文件写入用绝对路径(宿主原生 Write/Edit 本就要求绝对路径,自动满足);确实需要工作目录的命令(在 worktree 内跑 pytest/build/dev server,或本技能自己的 cwd 相关操作——Step 0 的 worktree-status.sh、收尾「在主检出里跑」的 git worktree remove/prune、worktree-sweep.sh)用单条 cd <abs> && <cmd> 在调用当刻设好工作目录,绝不假设它存活到下一次工具调用。为什么"cd 前先确认 cwd"不够:确认之后、下次调用之前 cwd 仍可能被重置,裸 git commit/相对路径就落到 cwd 当时指向的检出——多 worktree 下常是主检出,把提交落到错的分支,到 ff-only 合并才暴露(即收尾节「合并方向」条的"站错分支就会合进 B、信息却写着 C")。命令若落到非 git 目录或空索引会显式报错,真正危险的是 add+commit 都静默落进主检出那种。诱因不止重置——删除已用 worktree 后路径复用同样让 cwd 失效(见收尾节「已删 worktree 的路径从此作废」),两者都是本条实例。
Step 0:开工先自检(每次实现任务的第一步,agent 自己做,不等人安排)
先跑只读 preflight/status/lane inventory:
bash skills/worktree-isolation/scripts/worktree-status.sh --slug <task-slug>
- 结果
SAFE 且默认/基线分支已确证时才能编辑;UNSAFE 时不要编辑,先按脚本打印的 git worktree add -b ... 建功能 worktree,再进入新路径。若脚本提示 default/base branch not confirmed,先显式传 --base <ref> 或按下面的手工检查复核默认分支。
- 需要机器可读 gate 时加
--json;需要固定基线时加 --base <ref>;脚本只读,不会创建、删除、checkout 或改 git 状态。
- 这个工具借鉴 OMO worktrees 的 preflight、lane inventory 和 cleanup safety;本仓侧只采用这些能力。
.work/worktrees/<slug> 是新 lane 的默认/推荐路径(每条 lane 的本地 metadata 约定放在 .work/lanes/<slug>.json),不是硬性安全条件:已存在的、不在 .work/worktrees 下的独立 worktree 只要满足硬安全条件(独立 worktree、命名功能分支、非 submodule、非 detached、干净树、基线已确证)仍算 SAFE,status 脚本只会对它打一条非阻断 warning 提示新 lane 用 .work/worktrees。不引入 .slim/worktrees/ 目录约定,也不强制 .slim/worktrees.json。
.work/ 是本地工作目录和本地忽略目录(应被 .gitignore 忽略)。当前 status 脚本只读:只打印/JSON 暴露建议路径、metadata 路径和 metadata snippet,不写入 metadata;真正建 worktree 仍由你复制脚本打印的 git worktree add -b ... 命令完成。
脚本不可用时,按下面的手工检查兜底:
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)
SUPER=$(git rev-parse --show-superproject-working-tree 2>/dev/null)
BRANCH=$(git symbolic-ref --quiet --short HEAD 2>/dev/null)
并行两个迭代(agent 驱动,人不参与)
主检出 = 干净基线(没人在里面开发)
迭代 A → 会话A:Step 0 → worktree-A(branch iterA) → 在里面干
迭代 B → 会话B/后台:Step 0 或 bgIsolation → worktree-B(branch iterB) → 在里面干
后台/二级会话由 harness worktree.bgIsolation 默认隔离(自动逼进 worktree)。
被隔离闸 deny 时
Claude Code 端有 PreToolUse 硬闸:直接改共享/并行主检出会被 deny,deny 文案带 git worktree add 命令。照着建 worktree 再改即可,不用找人。
合并/落地前:确认"要落地的对象"已含全部预期改动(别落下 worktree 未提交 / tip 未推送的改动)
要落地的对象(push 的 remote 分支 / MR 的 remote head / 被合并的 tip)必须已含所有本次要落地的改动。两个常见静默漏项,尤其分支 checkout 在独立 worktree、不在当前检出里时:
- worktree 里未提交的改动:push/合并落地的是 commit 过的 tip,不含那个 worktree 工作区/暂存区里未提交的改动。
- 本地 tip 未推送:本地 HEAD 领先
origin/<branch>,remote/MR head 还是旧的——合并那个 MR 会漏掉已 commit 但没 push 的改动。
漏了本该进的改动 = 落成残缺版本(本会话踩过:MR 已落地,worktree 里未提交的改动没进那次 MR,事后清理才发现)。这跟下面"落后"是两条独立轴(那条是分支落后于目标、旧快照盖回目标修复),也不同于"收尾清理前验干净"(那是集成之后,太晚,漏的已落地)。
push / 建 MR / 合并之前自证落地对象完整。下面是给 agent 读输出用的诊断命令、不是自动闸——真正要保证的是"这次实际要合的 ref(MR head SHA / 你 push 的目标)= 你预期的 tip",别只信 @{u}(它只是本地配置的上游代理,可能不是这次的落地对象):
git worktree list
git -C "$wt" status --porcelain=v1 -b
git -C "$wt" rev-parse --verify @{u} >/dev/null 2>&1 || echo "无上游 → 先 push/set-upstream 再谈落地"
git -C "$wt" rev-list --left-right --count @{u}...HEAD 2>/dev/null
git rev-parse --verify <branch>@{u} >/dev/null 2>&1 || echo "无上游 → 先 push/set-upstream 再谈落地"
git rev-list --left-right --count <branch>@{u}...<branch> 2>/dev/null
git fetch -q origin <landing-ref> && [ "$(git rev-parse FETCH_HEAD)" = "<你的预期 tip 的 SHA>" ] || echo "落地对象 ≠ 预期 tip:先补齐再合"
(status 只是脏树闸:子模块 WIP、被 ignore 的生成物它不显示;落地对象里若含生成物,另按预期产物清单 / 重跑生成校验确认。)
处置:
- 未提交、且属于这次落地 → 先 commit 再 push/合并;但只
git add 明确属于本次落地的路径(先看 status/diff 名单),别 -A 一把梭把别人的 WIP / secret 顺带 commit。
- 本地领先上游 → 必须先 push 到这次实际落地的 ref,让 MR head 等于你的预期 tip(平台 update-branch 只把目标并进分支、不会带上你未推送的本地 commit,别拿它替代 push)。
- 无关的独立 WIP → 留着不动,这次落地对它自己的目标范围仍完整(无关 WIP 不算残缺)。
- 归属不清 → 按 product-rd 并发隔离规则留 pending,别替它 commit,也别默认算进这次落地。
合并回目标分支前:落后就先更新到目标分支(防 stale 分支静默回退目标已修复的内容)
并行/长活的 worktree 分支从某个旧基线分出去后,目标分支(main 等)往往又前进了(别的迭代合进来了)。这时直接合并这个落后的分支有个静默 data-loss 坑。注意 git 的实际行为:3-way 合并(含 git merge --squash)以 merge-base/目标/分支三方内容做合并——分支没碰过的文件保留目标版本;两侧改了不同区域的同一文件能各自合上;只有当分支的 diff 覆盖/改回了目标分支刚修复的那一块内容(典型是分支整文件重写/重新生成/格式化了一个旧快照版本:跑了 formatter、重生成 codegen、改了 lockfile、一次大范围 find/replace),合并才会用分支的旧内容把目标那次修复悄悄盖掉。squash 最危险不是因为它机制不同,而是它把整个分支压成一个 commit、ancestry 与可审性最弱——这种回退不以独立 commit 出现,事后翻历史几乎看不出来。(别误判成两个极端:既不是"分支没碰过的文件也会被回退",也不是"两侧都改过就一定回退";坑只在同一块内容被分支的旧版本覆盖时。)
合并前(尤其 squash 前)按这个序走:
git fetch origin
TARGET=$(git rev-parse origin/<target>)
BRANCH_OLD=$(git rev-parse <branch>)
OLD_BASE=$(git merge-base "$TARGET" "$BRANCH_OLD")
if git merge-base --is-ancestor "$TARGET" "$BRANCH_OLD"; then echo "分支已含该目标,无需更新"
elif [ $? -eq 1 ]; then echo "分支落后,需先更新到 $TARGET"
else echo "merge-base 出错(非 0/1),先排查别当落后处理"; fi
把落后分支更新到最新目标——先分清分支是否已共享:
- 私有 / 未推送分支:可
git rebase "$TARGET"。
- 已推送 / 挂着 MR / 别人可能在上面工作的分支:默认并入目标/平台 “update branch”,别无脑 rebase;方向/报告见「收尾·合并方向必须可读」
-F。rebase 前必须读 references/shared-branch-rebase.md:git fetch origin 刷新目标;再 fetch 本分支一次(git fetch origin <branch> / git fetch origin <branch>:refs/remotes/origin/<branch>,择一),remote_oid=$(git rev-parse FETCH_HEAD)(禁读 origin/<branch>),推前勿再 fetch 本分支;必须原样 git rev-list --left-right --count <branch>...$remote_oid,右侧(远端独有)非 0 即并入、禁 rebase,git diff 不证拓扑;0 才 rebase;--force-with-lease=<branch>:$remote_oid,禁 --force / 裸 lease;post-push fetch,重审六项 thread / approval / mergeable / CI / commit / 行锚;工具合并重写/发布后逐层重验,绿前禁合并。
冲突解析就是回退的高发点(rebase/merge 只是把碰撞提前暴露,不是修复本身):冲突里别直接取分支那侧的旧快照。对生成物 / lockfile / 格式化产物,从更新后的目标重新生成,不要照搬分支版本,也不要 -X ours/theirs 一把带过——那等于亲手把目标的修复盖掉,而且事后 --stat 看不出来。
合并后必须验证,别假设干净——分两层,--stat 不够:
comm -12 <(git diff --name-only "$OLD_BASE" "$BRANCH_OLD" | sort) <(git diff --name-only "$OLD_BASE" "$TARGET" | sort)
git diff "$TARGET" <AFTER> --stat
git diff "$TARGET" <AFTER> -- <碰撞集文件>
git diff "$OLD_BASE" "$TARGET" -- <碰撞集文件>
--stat 只答“哪些文件、改了多少”,答不了“目标的新 hunk 是否幸存”——文件内的回退 stat 看不出来,必须看碰撞集的全内容 diff。出现意外删除 / 目标修复被改回旧样 = stale 回退红旗,停下排查别推。本会话即按此做:每次 ff-merge 前先 git diff <base>..origin/main -- <我改的文件> 确认无碰撞、再 rebase、落后零碰撞才 ff。
(边界:这条管“合并前把分支更新到最新 + 合并后验证内容”;下面“收尾”管“已集成就清理”,两者是独立的闸——本节验证通过 ≠ 收尾的“已集成”判据成立,清理仍要单独按 ancestor/MR-merged 证明,squash 仍测不到祖先、仍保守保留。)
收尾:worktree 一集成就清理(本地 + 远端,不留垃圾)
worktree 的活一旦集成进目标分支就完了,立刻清理(唯一让位见下方清理序列的前置条件:未完成外部副作用任务等其完成)——别攒,也别为"将来可能还用得上"保留已合并的临时 feature 分支(发版 / 补丁 / 回查都从目标分支另起新分支,不复用已合并分支;"留着备用"是最常见的自我说服,攒着就是一堆 stale worktree/分支,要靠人回头扫)。Claude Code 宿主装有 hooks/remind-post-merge-cleanup.sh(PostToolUse):合并命令跑完自动把本节清理清单注入会话作提醒——非阻断、best-effort,只提升"该清理了"的显著度,不替代本节的已集成判据与安全红线。两条集成路径都要清:
- MR 路径(远端分支合并):合并时顺手删远端分支。
- 本地 merge 路径适用于开发分支之间的同步 / 集成 / 基线更新。
main/默认分支不走本地 merge;agent 不在本地把 feature 分支 merge 进 main/默认分支,也不 push 这种本地 merge 结果。
- 合并方向必须可读(源→目标):agent 执行或报告任何合并,都要让"哪个分支合进哪个分支"一眼可读。本地 merge 一律显式给信息,格式为
Merge branch '<src>' into '<dst>': <一句话目的>。目的句由 agent 自己撰写成一行——不逐字复制仓库/MR/外部文本(commit message 是持久 VCS 元数据,属 product-rd-workflow artifact-egress 门枚举的出口面,机密语义按该门处理;也别把 [skip ci] 之类 CI 指令 token 带进信息)。任何来自仓库/MR/外部文本的内容(分支名、目的句)都不进 shell 插值——git ref 名可以合法包含 `id`/$(...),目的句同理,粘进双引号命令行即命令注入(对抗评审连续多轮各击穿一处插值后,配方收窄为免插值形态):用编辑器/Write 工具把完整信息写进仓外唯一临时文件(mktemp 生成,别用固定 /tmp/xxx 路径——上文共享运行时状态警告同样适用,固定路径会被并行 lane 互相覆盖、合错信息还可能泄漏别条 lane 的目的句;别落在目标检出里被顺手 commit;git 只读不删,merge 后含失败路径都自己清掉),git merge -F <信息文件> -- "$src"(信息内容完全不经 shell;选项在 -- 之前)。$src 同样不手拼:git ref 名可合法包含单引号,粘进任何引号形态的赋值都可能逃逸——从 git 输出赋值(如 src=$(git branch --show-current) 在源 worktree 里取、或 git for-each-ref --format='%(refname:short)' 列表选取;command substitution 的结果只作变量值、不会再被 shell 求值),agent 自建的分支可直接用自己起的安全名——执行前先核对当前分支确实是预期的 <dst>,并用 git -C "<abs-dst-worktree>" merge(别靠 cwd——cwd 会在工具调用间被重置,见核心心法「绝不依赖 ambient cwd」):信息里的方向是标注不是校验,git 不会帮你验,站错分支就会"合进 B、信息却写着 C"(错误合并 + 虚假审计记录);git 只在目标分支非默认分支时才自动补 "into ",且历史信息只有分支名、读不出目的;可 ff 时 -m 会被忽略(不产生 merge commit),按下面 ff 条款走报告;把目标分支合入 feature 分支更新基线的 merge 同样照此注明。ff-merge / rebase / squash 等不产生 merge commit 的集成方式,历史里没有方向记录——在交付报告里补上方向。(信息里的引号定界只是人读标注:ref 名合法含单引号时定界会歧义——机器可读的权威方向记录以交付报告与变量值为准,别拿 commit 信息做解析源。)平台合并(MR/PR)的 merge commit 自带方向,agent 的交付/执行报告仍统一写明「<源分支>(source head SHA=…)→ <目标分支>」,SHA 要点名是源分支 head(被评审的那个对象;已集成后可另附合并后的目标 tip SHA,两者别混写成一个含糊的 "head SHA"),别只说"已合并"。
MR 不是合并授权:agent 可以按任务需要 push 分支、创建/更新 MR、设置 remove-source-branch、查看 CI/MR 状态;这些动作只交付待审入口。创建/更新 MR 时也不得开启 auto-merge / merge-when-pipeline-succeeds / queued merge。未获合并授权,不得执行任何会让 MR/PR 现在或稍后变成 merged、或推进 main/默认分支的动作(例如 glab mr merge、glab mr merge --auto-merge、gh pr merge、gh pr merge --auto、平台 merge API / Web UI、目标是 main/默认分支的 git merge 或 git push;列表非穷尽)。本地开发分支之间的 merge/rebase/push 允许;把目标分支合入/变基到当前 feature worktree 分支用于更新基线也允许,但不得推进 main/默认分支。这些要默认授权 这类查看/推送/建 MR 授权不覆盖合并;过去轮次对别的 MR 的合并授权也不延续到当前 MR。
合并执行协议(canonical——always-on 层「硬纪律 1」指向本节,两面同步修改;执行配方只放这里,不进 always-on 层):
- 前置条件(先于以下所有条款):仅当用户明确下达合并指令后,才进入本节其余条款;未获指令时,本节任何合并命令都不得执行。指令有两种形态:单个合并指令("合并"/"merge"/"land it" 等动词指令,指向当前对话中待合并的那个 MR/PR);批量合并指令("批量合并 N",如"批量合并 30"——对 agent 已展示的发布计划授权至多 N 个平台合并,适用多仓依赖链/批量发布;额度自武装起 4 小时内有效,用户发送任何新消息即清除剩余额度,需 agent 重新请求)。事前一句"做完并合并"不算授权——交付完成后停在待审、等用户明确说合并。展示与确认的分工:agent 交付待审 MR 时照常展示 MR 链接、分支、head SHA、CI/验证状态(展示是 agent 的义务);批量授权前 agent 须已展示发布计划(波次顺序、各仓及其 MR 或将要创建 MR 的方式),批量额度只用于该计划内的合并——计划外新出现的合并对象须重新请求授权;用户回一句合并指令即算授权,无需复述、点名或确认 SHA(点名确认不是用户的义务)。
- 轻量确认:若用户下达合并指令后分支又有新提交、或 CI/mergeable 状态明显变化,先向用户确认一句再合并(批量链式发布中 agent 自己按计划新增的提交/新建的 MR 属于已授权计划内,不触发此条);若当前对话中有多个待合并 MR/PR,一句"合并"指向不明,先问一句是哪一个("批量合并 N"则指向已展示的发布计划,无此歧义);对象唯一且无变化则直接执行。
机械放行阀(Claude Code 宿主):合并授权闸(
hooks/guard-merge-authorization.sh)会机器核验这条用户指令——UserPromptSubmit 哨兵在用户单独回复"合并/merge"(一次性布防)或"批量合并 N"(计数布防,每个平台合并消费 1 个额度,TTL 4 小时锚定武装时刻;见 hooks/merge-authorization-prompt.sh 的锚定匹配)时布防,闸在放行平台合并命令(glab mr merge/gh pr merge/merge API)时消费之;直推/直合 main 的形态与 auto-merge/排队/--admin 形态在任何授权下都永不放行;一条命令内多个合并调用会被拒——拆成逐条执行(批量授权下每条消费 1 个额度)。若合并命令仍被闸拦(授权词嵌在长消息里没被识别),请用户单独回复一句"合并"或"批量合并 N"即可,不要求用户改措辞之外的任何补偿动作;其他宿主(codex 等)无此机械阀,仍按 prose 执行。
- 执行建议(agent 防呆,不增加用户负担):获授权后的执行一次性立即合并、不转 auto-merge/排队;显式点名目标 MR/PR(glab/gh 缺省都解析"当前分支",同分支多 MR/PR 时会合错对象);建议把自己已知的 head SHA 作为守卫传给命令:
glab mr merge <iid> --sha <head SHA> --auto-merge=false --yes / gh pr merge <PR号|URL> --merge --match-head-commit <head SHA>(合并策略显式给 --merge/--squash/--rebase,缺省会进交互)。守卫被平台拒绝通常说明分支已变化——回到第 2 条向用户确认后再执行。一次性合并授权按「命令被放行」消耗,不按「合并成功」消耗:命令因你自己的参数错误而失败(自造不存在的 flag、SHA 用前缀而非平台现读的完整值、点错 MR 号)同样烧掉这次授权,用户得重新放行。所以执行前把 flag 与取值当成不可凭记忆的东西核一遍——flag 拼写以本机该 CLI 的 --help 为准(同名工具跨版本/跨平台差异很大,"我记得有这个 flag" 是最常见的烧授权方式),SHA 一律从平台 API 现读完整值(前缀补全会被守卫拒成 409)。已实测两次:一次前缀补全 409,一次自造 --merge(该版本 glab 无此 flag,合并策略缺省即 merge commit)——守卫两次都按设计挡住了错误合并,代价都是让用户重新授权一次。
- 仓库策略例外:仓库强制 merge queue / auto-merge、或只能直推默认分支时,停下把该仓的合并语义摆给用户裁决,不得套用立即合并流程近似执行。
- 合并后自查:合并后核对实际合入内容与本次交付预期一致,发现超出如实报告用户裁决(回滚/接受),不得静默带过。
"已集成"判据:main/默认分支只认平台 MR/PR 已在当前 head SHA 上完成 merge(或等价的、可追溯到该 head SHA 的平台合并事件);开发分支之间可用 git merge-base --is-ancestor <branch> <target> 判断。squash 合并测不到祖先 → 当作"未确认集成"保守保留,别自动删。
自动清理序列(仅在已集成后,在主检出里跑,不在要删的 worktree 内。动手删之前先确认没有进程仍在使用该 worktree——cwd 在其中,或经其路径持续读写:开发辅助进程——watcher/dev server 之类——正常停掉;承载未完成外部副作用的任务(迁移/部署等)绝不为清理而杀,此时"一集成就清理"让位、等待即是正确的收尾,任务完成后再删。该让位只管本地 worktree/分支的清理时点;远端分支仍按下方「远端分支」条跟随授权合并处理):
删 worktree 前先救 gitignored 产物:git worktree remove(不带 --force)会拒绝脏树/未跟踪文件,但 gitignored 文件不算"脏"——worktree 里生成的 gitignored 内容会随目录一起被删且 git 不会拒绝,删后不可恢复。绝大多数(依赖目录、构建/测试产物、缓存、日志)本就该删;要救的是其中重算代价高的数据产物(data/、output/、feather 等跑很久才拿到的中间数据),所以删前要看一眼而不是一律保留。批量清理开发分支间的已集成积压用 worktree-sweep.sh <integration-ref>(按已安装技能根解析——常见候选 ~/.kimi-code/skills*/、~/.claude/skills*/、本仓检出 skills/——定位后先 test -x 并把探测输出给用户看,缺失/不可执行不得凭记忆声明,给出证据才算降级;dry-run 对任何 ignored/未跟踪/脏文件机械判 KEEP(异常退出 exit 2/非零按没扫处理:停下查因,不得照删),KEEP 清单必须向用户列出并逐条处置,不得改用 --force/rm -rf 绕过、不得先手动删除被拒文件再重跑,--include-ignored 不是"清 KEEP 的开关",但也不必事事请示:dry-run 的 KEEP 行下面会列出该 worktree 里到底是什么(最多 8 条 + 剩余计数),照它按下方判据③判——只剩可重生成产物(.venv、node_modules、构建/测试产物、缓存、日志)就直接用它清掉,遇到重算代价高的数据产物(跑很久的中间数据、采集结果、训练产物)或拿不准才保留。注意该 flag 是整批生效、不是逐个挑选:一批里混了贵产物就别整批加它,先单独处理那一个;--apply 会清掉所有判定可删的,绝不碰远端;默认分支目标它保守 KEEP——默认分支的已集成判据是平台 MR 合并证据,见「批量清积压」条)。任何方式删除单个 worktree 目录之前(remove / --force / rm -rf / IDE / 外部工具,含让位等待结束后的补删),都必须先 git -C <worktree路径> status --ignored -s 扫 gitignored 产物(别省 -C:从主检出对另一 worktree 的路径直接跑 git status 会报 "outside repository")。三条硬判据:① 该命令必须 exit 0——执行失败(报错/非零退出)按没扫处理,停下查原因,不得把失败时的空输出当作"扫出来为空"继续删;② 输出非空即逐条判定保留/丢弃并向用户列出结论;③ 判据看重算代价,不看"是不是 gitignored"——可重生成产物(.venv、node_modules、构建/测试产物、coverage、缓存、日志)直接丢,不必请示;重算代价高的数据产物(跑很久的中间数据、采集结果、训练产物)先 rsync 回主检出(成本低),拿不准按后者处理。worktree 是否已集成同样不自评:默认分支看平台 MR 在当前 head SHA 上的合并证据,开发分支用 git merge-base --is-ancestor <branch> <integration-ref>。(git worktree prune 只清登记不删目录,不在此前置范围;sweep 本身也不适用此前置——它的 has_local_state 是比手工扫更严的内置检查:同样对 git status 非零闭式失败判 KEEP(reason 写 unscannable),不把失败时的空输出当"干净",拒绝即停。)
<skills根>/worktree-isolation/scripts/worktree-sweep.sh <integration-ref>
git -C <path> status --ignored -s
git worktree remove <path>
git branch -d <branch>
git worktree prune
git worktree list && git branch
-
已删 worktree 的路径从此作废:任何还停在该路径上的 shell/会话立即 cd 离开,别让后续命令以它为 cwd 跑(会遇到 "Unable to read current working directory" 一类怪错)。落脚主检出只作停靠点——不在那里开发,要继续干活按 Step 0 重新建 worktree。旧路径不经 worktree add 不得直接复用作 cwd:目录"还存在/又出现"不代表还是原来那个 worktree(可能已被并发任务重建),需要续做就从主检出重新 worktree add(同一路径亦可——add 会重建登记),禁止的是凭记忆直接 cd 进残留或来历不明的目录接着干。(删除导致的路径失效与核心心法「绝不依赖 ambient cwd」的 harness 重置是同一失败类的两个诱因——git 变更一律 git -C "<abs>",不靠 cwd。)
-
远端分支:待审 MR 的远端分支不是 stale;MR 已由用户授权合并后,才用平台的 remove-source-branch 或 git push origin --delete <branch> 清理。不要为了“顺手删远端分支”去触发 MR 合并。本地开发分支 merge 后可按已集成判据清理对应开发分支;main/默认分支没有本地 merge 清理路径。当 MR 的源分支本身是永久/集成分支时(如 dev→main 的 promotion,源是 dev),绝不设 remove-source-branch、也不删除它——该 flag 只用于临时 feature 分支;删掉 dev/集成分支会摧毁团队集成点。 临时 feature 分支合并进任何目标分支(含 dev/集成分支,不止 main/默认分支)后,都随授权合并清理其源分支(本地 + 远端);例外见下两条。
例外一:源分支自身是永久/集成分支时不删(上一句)。
例外二:分支名含 release 的一律不自动删除(release/*、release-1.2、hotfix-release 等,大小写不敏感、匹配分支名任意位置)。判据是名字不是拓扑:发布分支合并后仍要留着打 tag、追溯发版内容、出补丁,而它在 git 拓扑上与临时 feature 分支毫无区别——「已合并」在这里不蕴含「可删」。要删由用户显式指名,agent 不自动清理,也不设 remove-source-branch。同理,remove-source-branch 在建 MR 时就要按这条判断,别等合并后才想起来。发布分支的命名是各仓的约定(rc/1.2、stabilization/v2、hotfix/* 都真实存在),本条只把 release 定为关键字且刻意不做成可配——三轮对抗评审各找出一种「配置传不到下一个克隆」的形状(env 只保护导出它的那一次、.git/config 是单克隆的、新 CI 克隆直接丢),每次都在追脚本自己不拥有的东西;硬编码在共享脚本里反而随技能走到哪都在。别的叫法不受自动保护,这是明写的残留风险:sweep 在 --apply 前会把完整 KEEP/REMOVE 计划打给人看,而删除本来就只由用户指名。要重新加配置源,先解决它怎么到达一个全新克隆。
-
安全红线(承 testing-strategy 的破坏性清理纪律):只在确认已集成后删;用 git worktree remove(不 --force)+ git branch -d(不 -D)——未合并/脏树被拒绝正是防误删未交付工作的网;绝不盲删主检出/默认分支,绝不为图省事 --force/-D。
批量清积压:已攒下的 stale worktree 用 scripts/worktree-sweep.sh——默认 dry-run 只打印,--apply 才动手;它只适合清理开发分支之间可用 ancestor 证明的积压。对 main/默认分支的 MR 分支,不能只靠“tip 已是默认分支祖先”判定可删,必须先有平台 MR/PR 在当前 head SHA 上已合并的证据;拿不到证据就保守保留。脚本跳过主检出/目标分支/detached/脏树(含 gitignored 产物)/git status 扫不动的/未合并,绝不碰远端。它的 --include-ignored 会把"只剩 gitignored 内容"的 worktree 判为可删,且整批生效。按判据③用即可:dry-run 会在 KEEP 行下列出实际内容,只剩可重生成产物(.venv、node_modules、构建/测试产物、缓存、日志)就直接清,遇到重算代价高的数据产物就把那个 worktree 单独拎出来处理、别整批加 flag——本节要救的是后者,不是所有 gitignored 文件。
交互式 merge 选项菜单(PR vs 本地 merge vs 保留分支):若装了 superpowers:finishing-a-development-branch,route 给它出菜单走流程;本技能管的是"已集成就自动两侧清理"这条收尾 gate + 批量 sweep。Step 0 检测与 superpowers:using-git-worktrees 一致,可直接用其原生 worktree 工具;本技能是本仓侧"默认隔离 + 并行迭代 + 收尾清理"策略与硬闸说明。