leo 2026-09-07:「一個 repo 只看得到一個資料夾」「禁止這樣產出一大堆資料夾」。 根因:line-needs-own-worktree.sh 教工人開在 repo 旁邊,「收工 remove」只寫在閘訊息裡、沒機制驗, 每條線留一份 ⇒ tech_projects/ 長出 ISEP-wt-117/InkStoneCo-wt-112…一排。規則在文字裡不在機器上。 - scripts/worktree(新):open/close/list/sweep 四個動詞。分身住 <repo>/.worktrees/<票 repo>-<N> (走 .git/info/exclude,不動被追蹤的檔);「推了沒」只有這一份判準(三態:有/沒有/問不到, 問不到不當成沒推也不收);sweep 預設只列不動,--apply 只收「推了且乾淨」,--skip 是操作者點名。 - hooks/line-needs-own-worktree.sh:擋下時印 `scripts/worktree open`(絕對路徑,貼了就跑); 同一支多掛 PostToolUse Agent|Task——從【工單】認出分身,推了收、沒推點名分支+路徑(exit 2), 不 force、不刪。+0 支、+1 條(61 支/86 條,當場數的)。 - docs/governance/worktree-placement.md(新)+ principles 一行:禁令與閘互指。 - 測試:line-needs-own-worktree.test.sh 77→110(G 群:閘印的 open 那行原樣跑、旁邊不多任何東西; H 群:PostToolUse 推了收/沒推點名/安靜的方向);scripts/test-worktree.sh 42 條(七個分身的 fixture, 名字照 leo 看到的那排)。舊閘跑新測試 90/110,紅的 20 條正是這次改到的行為。 - permissions-allow.json 加 worktree 四種形狀(出路在雲端被分類器擋就走不通)。 - README/plugin.json/hooks-inventory 的數字在這棵樹上實數:61 支、86 條、62 支腳本。 待總管定版(改了會被載入的東西)。沒碰 hooks/lib/mainline.py。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0178ef1fGw3XeZtpN7LaZrm4
4.7 KiB
一個 repo 只有一個資料夾:分身住 .worktrees/,收工就被收(inkstone/ISEP#147)
leo 2026-09-07(兩則,一字不改): 「我看到的是一個 repo,你會產生大量的同一個頭不同尾巴的 repo,全部拆開就更亂了, 為什麼要產出新的 repo?這個 repo 的目的就是收納所有相關的內容,這樣散開造成凌亂, ISEP, Inkstoneco, Course Gen 這些都產出好多個資料夾,這要整理回去」 「另外要在 ISEP 裡寫明,禁止這樣產出一大堆資料夾」
規約(三句)
- 🔴 禁止在專案目錄旁邊產出分身資料夾。
tech_projects/底下一個 repo 只能有一個資料夾;ISEP-wt-117、InkStoneCo-wt-112、course_gen-ticket-3-notes這種同頭不同尾的東西不准再長出來。 - 一條線要隔離工作目錄,分身住 repo 裡面:
<repo>/.worktrees/<票的 repo 名>-<票號>。 Finder 看不到(點開頭)、git status看不到(寫進.git/info/exclude)、git worktree list找得到。 - 分身是借的,收工要還:分支推上遠端且工作樹乾淨 ⇒ 收掉;沒推 ⇒ 點名分支與路徑,等人決定。 絕不 force、絕不刪沒推的 commit。
為什麼會長成那樣(根因,不是誰疏忽)
hooks/line-needs-own-worktree.sh(inkstone/ISEP#109,08-29 立)擋下線在共用目錄切分支,教它
「開一份在 repo 旁邊、不要在 /tmp、收工時 worktree remove」。
那時比較的只有「旁邊 vs /tmp」——/tmp 會被系統清掉、登記簿說謊;旁邊的一份都沒掉。
一份都沒掉,也一份都沒收:remove 那一步只寫在閘訊息裡,沒有任何機制驗。
每個工人留一份,tech_projects/ 就長成那排。規則在文字裡,不在機器上
(同日 ISEP#109 comment 6629 是同一種病:出路寫在訊息裡,機器讀不到)。
機器上的兩端
| 端 | 在哪 | 做什麼 |
|---|---|---|
| 開 | hooks/line-needs-own-worktree.sh(PreToolUse Bash) |
擋下共用目錄裡的 checkout,印出 scripts/worktree open …(絕對路徑,貼了就能跑) |
| 收 | 同一支閘多掛 PostToolUse Agent|Task |
從派工單 【工單】owner/repo#N 認出分身名字,跑 scripts/worktree close:推了收、沒推點名(exit 2) |
| 工具 | scripts/worktree |
open/close/list/sweep 四個動詞;「推了沒」只有這一份判準(三態:有/沒有/問不到,問不到不當成沒推) |
為什麼收的那一半不另開一支閘:教你開的和替你收的是同一件事的兩半,拆成兩支就又是「規則在兩個地方」
(countdown-guard.sh 同一種形狀:注入與查核是一支閘掛兩個事件)。
為什麼掛 PostToolUse 不掛 SubagentStop:票號在派工單(Agent 的 tool_input.prompt)裡,SubagentStop 拿不到
(baton-handback-guard.sh 檔頭那段實撞)。
工人怎麼用
python3 <plugin>/scripts/worktree open inkstone/ISEP#147 --repo <repo> -b feat/147-xxx # 開
cd <repo>/.worktrees/ISEP-147 # 在裡面做事
python3 <plugin>/scripts/worktree close inkstone/ISEP#147 --repo <repo> # 收(推了才收得掉)
忘了 close 也沒關係——線收工那一刻閘會替你跑;但沒推的它不會替你收,只會點名。
不要用 git worktree add <repo>/../<repo>-wt-… 這條老路;閘訊息已經不印它了。
leo 在 Mac 上整理現場(一次)
python3 ~/.claude/plugins/cache/inkstone/isep/<版本>/scripts/worktree sweep ~/tech_projects # 只列,不動
python3 ~/.claude/plugins/cache/inkstone/isep/<版本>/scripts/worktree sweep ~/tech_projects --apply --skip course_gen
- 預設只列不動;
--apply才收,而且只收「推了且乾淨」的。 - 沒推的、有未 commit 的、問不到遠端的、沒有任何 repo 登記它的:全部列出來、一個都不動,等他決定。
--skip <repo 名>是操作者點名不碰的 repo(09-07:course_gen地端正在處理,列可以、不收不動)。 它是操作者的明示指令,不是判準——判準永遠是「這個目錄是不是某個 repo 的 worktree、分支推了沒、乾不乾淨」。
測試
hooks/tests/line-needs-own-worktree.test.shG 群(開在裡面、旁邊不多任何東西、閘印的那行原樣跑得動) 與 H 群(PostToolUse:推了收掉、沒推點名、沒工單/沒分身安靜)。scripts/test-worktree.sh:七個分身的 fixture(照 leo 09-07 看到的那排)跑sweep—— dry-run 不動任何東西;--apply只收已推的、--skip的一根寒毛不動、沒推的印分支與路徑。