Files
ISEP/docs/governance/worktree-placement.md
T
isep-hand 7cacfdcf11 一個 repo 只有一個資料夾:分身住 repo 裡面的 .worktrees/,收工由同一支閘收掉(inkstone/ISEP#147)
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
2026-09-07 07:13:21 +00:00

4.7 KiB
Raw Blame History

一個 repo 只有一個資料夾:分身住 .worktrees/,收工就被收(inkstone/ISEP#147

leo 2026-09-07(兩則,一字不改): 「我看到的是一個 repo,你會產生大量的同一個頭不同尾巴的 repo,全部拆開就更亂了, 為什麼要產出新的 repo?這個 repo 的目的就是收納所有相關的內容,這樣散開造成凌亂, ISEP, Inkstoneco, Course Gen 這些都產出好多個資料夾,這要整理回去」 「另外要在 ISEP 裡寫明,禁止這樣產出一大堆資料夾」

規約(三句)

  1. 🔴 禁止在專案目錄旁邊產出分身資料夾。 tech_projects/ 底下一個 repo 只能有一個資料夾; ISEP-wt-117InkStoneCo-wt-112course_gen-ticket-3-notes 這種同頭不同尾的東西不准再長出來。
  2. 一條線要隔離工作目錄,分身住 repo 裡面<repo>/.worktrees/<票的 repo 名>-<票號>。 Finder 看不到(點開頭)、git status 看不到(寫進 .git/info/exclude)、git worktree list 找得到。
  3. 分身是借的,收工要還:分支推上遠端且工作樹乾淨 ⇒ 收掉;沒推 ⇒ 點名分支與路徑,等人決定。 絕不 force、絕不刪沒推的 commit。

為什麼會長成那樣(根因,不是誰疏忽)

hooks/line-needs-own-worktree.shinkstone/ISEP#10908-29 立)擋下線在共用目錄切分支,教它 「開一份在 repo 旁邊、不要在 /tmp、收工時 worktree remove」。

那時比較的只有「旁邊 vs /tmp」——/tmp 會被系統清掉、登記簿說謊;旁邊的一份都沒掉。 一份都沒掉,也一份都沒收remove 那一步只寫在閘訊息裡,沒有任何機制驗。 每個工人留一份,tech_projects/ 就長成那排。規則在文字裡,不在機器上 (同日 ISEP#109 comment 6629 是同一種病:出路寫在訊息裡,機器讀不到)。

機器上的兩端

在哪 做什麼
hooks/line-needs-own-worktree.shPreToolUse Bash 擋下共用目錄裡的 checkout,印出 scripts/worktree open …(絕對路徑,貼了就能跑)
同一支閘多掛 PostToolUse Agent|Task 從派工單 【工單】owner/repo#N 認出分身名字,跑 scripts/worktree close:推了收、沒推點名(exit 2
工具 scripts/worktree opencloselistsweep 四個動詞;「推了沒」只有這一份判準(三態:有/沒有/問不到,問不到不當成沒推)

為什麼收的那一半不另開一支閘:教你開的和替你收的是同一件事的兩半,拆成兩支就又是「規則在兩個地方」 (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.sh G 群(開在裡面、旁邊不多任何東西、閘印的那行原樣跑得動) 與 H 群(PostToolUse:推了收掉、沒推點名、沒工單/沒分身安靜)。
  • scripts/test-worktree.sh:七個分身的 fixture(照 leo 09-07 看到的那排)跑 sweep—— dry-run 不動任何東西;--apply 只收已推的、--skip 的一根寒毛不動、沒推的印分支與路徑。