Claude Code 的 worktree 守門員:從會被繞過、到連 heredoc 都擋,再到 2.1.299 還在補 awk
官方文件講 worktree 隔離怎麼執行的那一段,最後一道「指令形狀」檢查的說明,用一句很硬的話收尾:「You can’t turn this check off.」這一道關不掉,沒有設定可以調。可是緊接著的下一段,文件又很平靜地說,這幾道檢查沒有一道會追蹤 shell 指令寫了哪些檔案,所以一個不在主 checkout 跑 git、只是用 cp 或 shell 重導向把東西寫進主 checkout 的指令,它們不會擋。
關不掉的那一道,放 cp 過去。
乍看像矛盾。把 changelog 裡跟它有關的十幾條從頭排一次,這兩句話其實對得上:這些檢查從頭到尾只讀指令文字裡寫得出來的目的地,不追蹤指令實際寫了什麼。5 月到 10 月的版本紀錄,大半都在處理「讀不懂的時候要多嚴」:先是管太鬆被繞過,接著管太緊連不碰 git 的迴圈都擋,鬆開之後又回頭補洞。
這篇的事實全部來自 Claude Code 的 CHANGELOG 和三頁官方文件(worktrees、sub-agents、errors),版本上架時間取自 npm view @anthropic-ai/claude-code time,以 2026 年 10 月 15 日查詢為準。我沒有安裝其中任何一版,也沒有在 worktree session 裡實際觸發過一次拒絕,下面所有「會被擋」「不會被擋」都是文件與 changelog 的說法。
先講物流站的分揀員
把守門員想成物流站的分揀員會比較好懂。
每個箱子上貼著一張託運單,分揀員看的是收件地址。寄到自己這一區,放上輸送帶;寫的是別區,攔下來。他不拆箱,箱子裡裝了什麼、收件人拿到之後又轉手送去哪裡,他不管。
還有一條規矩讓他很難當:託運單上的字如果他看不懂,他就不放行。他沒辦法確定這箱要去哪,只好當成要去不該去的地方。
worktree 守門員就是這個分揀員。「自己這一區」是 session 所在的 worktree,「別區」是你的主 checkout,託運單是 Edit 的目標路徑、指令跑的目錄和指令文字本身。他讀得最細的一欄,是 git 會被帶去哪裡。
以前:換個目錄就算隔離了
--worktree(-w)這個旗標是 2.1.49 加的,2.1.49 上 npm 是 2026 年 2 月 19 日;同一版 subagent 也可以用 isolation: "worktree" 跑在臨時的 git worktree 裡,隔天的 2.1.50 再讓 agent 定義檔可以直接宣告 isolation: worktree。站上 4 月那篇〈Claude Code Git Worktrees 完整教學〉講的就是這個階段的用法。
那時候的隔離,概念上就是「給你另一個目錄」。目錄分開了,大家就不會互踩。
問題在於目錄會漏。2.1.97 修了一條:設了 worktree 隔離或 cwd: 覆寫的 subagent,會把自己的工作目錄漏回父 session 的 Bash 工具。兩天後的 2.1.101 又修了反方向的問題:跑在隔離 worktree 裡的 sub-agent,讀寫自己 worktree 裡的檔案反而被拒絕。一邊漏、一邊把自己鎖在門外,這個「鬆緊兩頭都會出錯」的形狀,後面會一直重複出現。
「isolation guard」這個詞在 changelog 裡第一次出現是 2.1.144(2026 年 5 月 18 日上 npm),講的是背景 session 的守門擴大到設了 WorktreeCreate hook 的非 git VCS 使用者。這只是 changelog 文字上的首見,changelog 不一定記下所有內部變更,內部是不是更早就有,我不知道。
同一個動作,前後兩種結局
拿一個具體動作來對照:一個跑在背景 session 裡的 subagent,想把檔案寫進共用的主 checkout。
2.1.154 以前,這件事做得到。changelog 原句是「Fixed subagents in background sessions bypassing the worktree-isolation guard and writing to the shared checkout」,2.1.154 上 npm 是 5 月 28 日。也就是說分揀員已經上工了,但有一條側線繞過了他。
側線補起來之後,接下來兩個月補的是另一類漏洞:指令本身在 worktree 裡跑,git 卻被帶去主 checkout 辦事。從 2.1.203(7 月 7 日)修 subagent 有時在父 checkout 跑 shell 指令、2.1.210(7 月 14 日)修 isolation: 'worktree' 的 subagent 能對主 repo checkout 跑改動 git 狀態的指令,到 2.1.222(8 月 4 日)讓隔離套用到所有 session 類型的檔案編輯與 Bash,中間 2.1.216(7 月 20 日)堵掉的是最典型的一條:用 git -C、--git-dir 或 GIT_DIR/GIT_WORK_TREE 環境變數,把 git 導回共用 checkout。
sub-agents 文件對 2.1.210 那條有更白話的補充:在 v2.1.210 以前,檢查只涵蓋啟動目錄本身。如果你是從 monorepo 的子目錄啟動 Claude Code,一個工作目錄解析到 repo 根目錄的指令,會直接在那裡執行,而不是失敗。
換回分揀員的比喻,這段時間的修補都在做同一件事:託運單上不只要看收件地址,還要看備註欄有沒有寫「到了請改送別區」。git -C 就是那個備註欄。
然後換成擋太多
到這裡,分揀員學會了看備註欄,也學會了「看不懂就不放」。接下來的麻煩是他看不懂的東西太多了。
2.1.238(8 月 20 日)先修了一個很尷尬的問題:Bash 被守門擋下來時,拒絕訊息叫你拿掉一個重導向,可是那條指令根本沒有重導向。
真正的重頭戲是 2.1.257,9 月 1 日上 npm。changelog 原句:
「Fixed worktree-isolated sessions refusing Bash loops, $VAR reads, "$(…)" and heredocs that never touch git as “too complex to verify that it stays inside the worktree”」
迴圈、讀變數、命令替換、heredoc,全都是寫 shell 最日常的東西,而且這些指令根本不碰 git。在 2.1.257 以前,它們會被當成「太複雜、無法確認有沒有留在 worktree 裡」而拒絕。依 changelog 列的四類,大概是這種長相的指令(示意寫法,不是我實跑的紀錄):
1 | # 2.1.257 修掉的誤擋,changelog 列的四類(示意) |
隔天,2.1.259(9 月 2 日)又來一條:修掉 worktree 隔離 session 拒絕常見 Bash 迴圈、xargs 管線,以及被 launcher 包起來、根本碰不到主 checkout 的指令。同一版還修了另一個誤擋:在 git rev-parse 失敗訊息不是「not a git repository」的機器上,hook 建立的 worktree 會被拒絕。
兩天連修兩版,修的都是「擋錯人」。changelog 沒寫為什麼會這樣,我也不打算替它猜原因。可以確定的只有結果:一個「看不懂就不放」的分揀員,最直接的副作用,就是連本來就該留在自己這區的箱子也攔下來。
鬆開之後,回頭補洞
鬆開沒多久,方向又轉回來了。
2.1.274(9 月 16 日):某些巢狀的 shell 展開原本會被放行,現在改成拒絕。2.1.299(2026 年 10 月 14 日 UTC 上 npm):「Fixed worktree-isolated sessions accepting some awk programs the isolation guard should refuse」,有些 awk 程式守門員本來該擋卻放過了。
changelog 沒說是哪些 awk 程式,也沒說守門員內部怎麼判斷。我能補的只有一般常識:awk 本身是個小語言,可以用 system() 在程式裡呼叫外部指令,所以光看 shell 那一層的文字,未必看得出一段 awk 最後會跑什麼。這是我的理解,不是 changelog 寫的原因;換成分揀員的比喻,大概就是備註欄改用另一種文字寫。
把這串修補排在一起看,比較清楚:
worktree 守門員的鬆與緊
2.1.154(2026-05-28)
修:背景 session 的 subagent 繞過守門,寫進共用 checkout。
2.1.161(2026-06-02)
修:背景 session 裡用 isolation: “worktree” 派出的 Workflow agent,被擋下不准改自己 worktree 裡的檔案。
2.1.216(2026-07-20)
修:subagent 用 git -C、–git-dir、GIT_DIR/GIT_WORK_TREE 把 git 導回共用 checkout。
2.1.222(2026-08-04)
修:可以對主 checkout 跑破壞性 git 指令;隔離擴及所有 session 類型。
2.1.257(2026-09-01)
修:不碰 git 的迴圈、$VAR、”$(…)”、heredoc 被當成 too complex 擋掉。
2.1.259(2026-09-02)
修:常見迴圈、xargs 管線、launcher 包裝的指令被誤擋。
2.1.274(2026-09-16)
修:某些巢狀 shell 展開被放行,改為拒絕。
2.1.299(2026-10-14)
修:某些該擋的 awk 程式被放行。
日期是各版上 npm 的 UTC 日期。漏、緊、鬆、再補,幾乎是交替出現。
現在的分揀員看哪幾樣
文件現在把規則寫成四道檢查,不管你是用 --worktree 開 session、Claude 用 EnterWorktree 進入 worktree,還是 resume 一個 worktree session,規則都一樣,而且從這個 session 派出去的每個 subagent 都適用。
第一道看檔案編輯:Edit、Write、NotebookEdit 的目標路徑落在主 checkout,就擋。第二道看工作目錄:Bash、PowerShell、Monitor 指令的工作目錄解析到主 checkout,或者無法確認它在外面,就擋。第三道看 git 有沒有被導走。第四道看指令形狀:從文字上確認不了它跑的 git 會留在 worktree 裡,就擋,例如命令名稱是執行期才算出來的、語法解析不了,或是 ${!name}、${ command; } 這種展開。PowerShell 只做工作目錄那一道。
照文件的分類排一下,大概是這樣(同樣是示意,沒有實跑):
1 | # 依官方文件「How Claude Code enforces isolation」的四道檢查整理(示意) |
被擋的時候,訊息長這樣(errors 文件裡的範例原文):
1 | This session is isolated in the worktree /path/to/worktree, but this command evaluates ${!x@P} arithmetically inside a construct too complex to verify, which can run a command hidden in a variable's value. Refusing to run it — a worktree-isolated session's git operations must target its own worktree. Split it into plain, separate commands and run them from /path/to/worktree. |
最後一句就是解法:拆成普通、分開的指令,從 worktree 的路徑執行。文件也特別提到,一條從頭到尾沒寫 git 的指令,還是可能因為這個理由被擋,因為 ${!name} 這種間接展開,或 ${ command; } 這種函式替換,在執行期產生的值本身就可能是一條指令。所以看到「too complex to verify」不用去檢查自己是不是偷跑了 git,把指令攤平就好。
我的立場:它讀託運單,不是沙箱
「關不掉」跟「不管 cp」放在一起,意思其實很清楚:這個守門員管的是從文字讀得出來的目的地,Edit 要改的路徑、指令在哪個目錄跑、git 會被導去哪,而在讀不懂的時候它寧可錯殺。shell 指令實際寫了哪些檔案,它不追蹤;文件把 cp 和重導向交給 permissions 和 sandbox,它們就跟其他 shell 指令一樣,照你的權限與沙箱設定處理。
所以我會這樣用它:worktree 隔離負責讓 subagent 的 Edit 和 git 操作不會落到主 checkout,主 checkout 的檔案會不會被寫,另外靠 sandbox 和 permissions 去顧。如果你的 subagent 只用 Edit 改檔、只跑 git、從來不用 cp 或重導向往外寫,那守門員已經夠你用;只要有任何一條流程會把產物複製到 worktree 以外,就別把隔離當成牆。
這也跟站上 10 月 9 日那篇〈hook 打錯路徑,閘門就默默打開〉剛好是兩個方向:那篇是閘門出事時預設打開,這篇的守門員是看不懂就預設關上。關上的代價,就是 2.1.257 和 2.1.259 那兩天修掉的誤擋。
還有一個版本上的現實要留意。2026 年 10 月 15 日查 npm 的 dist-tags,stable 標籤指向 2.1.292,latest 是 2.1.300,next 是 2.1.301。2.1.292 比 2.1.299 舊,照 stable 標籤裝到的版本不含那條 awk 修正。2.1.301 是 15 日(UTC)上 npm 的,我抓 GitHub 主幹的 CHANGELOG 時還沒有它的區塊,裡面有沒有跟 worktree 相關的變更,我沒查到。
管「檔案寫到哪」的是官方的 sandboxing 與 permissions 文件;如果你在 auto mode 裡跑 worktree,〈auto mode 想加一條邊界,要寫在 CLAUDE.md、ask、deny 還是 autoMode?〉整理了這幾層規則誰蓋得掉誰。
原文來源:Claude Code Docs:Worktrees(How Claude Code enforces isolation)、Claude Code Docs:Subagents(Write subagent files)、Claude Code Docs:Errors(Command blocked by the worktree isolation checks),三頁皆以 2026 年 10 月 15 日讀取的版本為準;Claude Code CHANGELOG(2.1.49 至 2.1.300 的 worktree 相關條目);版本上架時間與 dist-tags 取自 npm view @anthropic-ai/claude-code(2026 年 10 月 15 日)。
















