一個 repo 只有一個資料夾:分身住 repo 裡面的 .worktrees/,收工由同一支閘收掉;scripts/worktree sweep 整理現場(inkstone/ISEP#147) #148

Merged
claude-code merged 1 commits from feat/147-one-folder-per-repo into main 2026-09-07 07:19:20 +00:00
Member

【身份】isep-hand/inkstone/ISEP/feat/109-gate-sees-target-repo

票:inkstone/ISEP#109(comment 6629 派工;6587 是第一支的實撞);順手關掉 inkstone/ISEP#125 的缺陷 A 與 D/E(驗收表 A–F 進測試)。

改了什麼(兩支閘同一種病:看的是 hook 自己的環境/cwd,不是指令要動的那個 repo)

09-07 撞到的 改法 判準
line-needs-own-worktree.sh 出路 WORKTREE_OK=1 git -C … checkout … 照貼三次三次被擋 指令字串裡的字面前綴(lib/checkout_target_dir.py --escape WORKTREE_OK=1);環境變數那條路保留 位置不是字:前綴必須掛在會移動 HEAD 的那條 git 指令上——echo WORKTREE_OK=1; git checkout、掛在別的指令上、值不是 1、寫在 commit 訊息裡,四種照擋
同上(#125 D/E) heredoc 寫文件、內含 cd <目錄> && git checkout X 示範被擋 lib/strip_heredoc.py 剝 body 再解析(D20 閘同一支 helper) 真的切(#125-F)仍 2
github-contact-guard.sh 薄殼 cwd git -C <InkStoneCo> push origin x 被判「origin 指向 GitHub」 remote 名在這條指令實際會推的那個 repo 裡解(沿用 lib/push_target_dir.py:cd 鏈/-C/子殼不外洩),解不出來才退回 cwd 仍是 remote URL 主機這個唯一識別碼,不是「有 -C 就放行」——站在 Gitea 那份 -C 到薄殼推 GitHub 照擋(舊版這個方向其實漏擋

push_target_dir._classifystrip_env 時把前綴留在 verb 事件裡;find_push_target 行為不變(推 main 那道閘 10+19 條照綠)。
沒加閘:ls hooks/*.sh | wc -l=61、grep -c '"command":' hooks/hooks.json=85,改完當場數的;盤點表三格集合對帳(A1/A2)皆空。

測過的(全部貼原始輸出在票上 comment)

  • bash hooks/tests/line-needs-own-worktree.test.sh77/77(59→77:E 群把閘印的那一行原樣餵回去;F 群=#125 A–F)。修之前拿新測試跑舊閘:(60)(63)(68) 三條紅
  • bash scripts/test-github-contact-guard.sh26/26(14→26:payload 帶 cwd、cwd≠目標 repo)。舊閘 7 條紅=4 誤攔+3 漏擋
  • main-and-prod-push-guard 10/10、cross-repo 19/19;check-version-consistency.sh 0.26.0;claude plugin validate .
  • 真路徑實跑(cwd=/home/user/inkstoneco 薄殼,origin=github.com):git -C …/InkStoneCo push origin x → exit 0;git push origin x → exit 2 並印出「/home/user/inkstoneco 的 remote「origin」指向 GitHub」;git -C …/ISEP checkout main(CHILD=1)→ 2,把它印的那行填上分支原樣餵回 → 0(舊版同一行 → 2)

沒做/另報

  • tokenize 層把換行當空白吞:…EOF⏎git checkout main 從第一版起就看不到(舊閘 exit=0,漏擋不是誤攔),寫成 (76) 已知邊界測試;修它會連帶改推 main 那道閘的偵測範圍,與 (53) 同層,另報
  • not-my-branch-guard.shNOT_MY_BRANCH_OK=1 同樣只讀環境變數(第 28 行),沒動、沒實測

版本:待總管定版(改了會被載入的 hook 與 lib)。

🤖 Generated with Claude Code

https://claude.ai/code/session_0178ef1fGw3XeZtpN7LaZrm4

【身份】isep-hand/inkstone/ISEP/feat/109-gate-sees-target-repo 票:inkstone/ISEP#109(comment 6629 派工;6587 是第一支的實撞);順手關掉 inkstone/ISEP#125 的缺陷 A 與 D/E(驗收表 A–F 進測試)。 ## 改了什麼(兩支閘同一種病:看的是 hook 自己的環境/cwd,不是指令要動的那個 repo) | 閘 | 09-07 撞到的 | 改法 | 判準 | |---|---|---|---| | `line-needs-own-worktree.sh` | 出路 `WORKTREE_OK=1 git -C … checkout …` 照貼三次三次被擋 | 認**指令字串裡**的字面前綴(`lib/checkout_target_dir.py --escape WORKTREE_OK=1`);環境變數那條路保留 | 位置不是字:前綴必須掛在會移動 HEAD 的那條 git 指令上——`echo WORKTREE_OK=1; git checkout`、掛在別的指令上、值不是 1、寫在 commit 訊息裡,四種照擋 | | 同上(#125 D/E) | heredoc 寫文件、內含 `cd <目錄> && git checkout X` 示範被擋 | 先 `lib/strip_heredoc.py` 剝 body 再解析(D20 閘同一支 helper) | 真的切(#125-F)仍 2 | | `github-contact-guard.sh` | 薄殼 cwd `git -C <InkStoneCo> push origin x` 被判「origin 指向 GitHub」 | remote 名在**這條指令實際會推的那個 repo** 裡解(沿用 `lib/push_target_dir.py`:cd 鏈/`-C`/子殼不外洩),解不出來才退回 cwd | 仍是 remote URL 主機這個唯一識別碼,**不是「有 -C 就放行」**——站在 Gitea 那份 `-C` 到薄殼推 GitHub 照擋(舊版這個方向其實**漏擋**) | `push_target_dir._classify` 在 `strip_env` 時把前綴留在 verb 事件裡;`find_push_target` 行為不變(推 main 那道閘 10+19 條照綠)。 沒加閘:`ls hooks/*.sh | wc -l`=61、`grep -c '"command":' hooks/hooks.json`=85,改完當場數的;盤點表三格集合對帳(A1/A2)皆空。 ## 測過的(全部貼原始輸出在票上 comment) - `bash hooks/tests/line-needs-own-worktree.test.sh` → **77/77**(59→77:E 群把閘印的那一行原樣餵回去;F 群=#125 A–F)。修之前拿新測試跑舊閘:(60)(63)(68) 三條紅 - `bash scripts/test-github-contact-guard.sh` → **26/26**(14→26:payload 帶 cwd、cwd≠目標 repo)。舊閘 **7 條紅=4 誤攔+3 漏擋** - `main-and-prod-push-guard` 10/10、cross-repo 19/19;`check-version-consistency.sh` ✅ 0.26.0;`claude plugin validate .` ✅ - 真路徑實跑(cwd=`/home/user/inkstoneco` 薄殼,origin=github.com):`git -C …/InkStoneCo push origin x` → exit 0;`git push origin x` → exit 2 並印出「`/home/user/inkstoneco` 的 remote「origin」指向 GitHub」;`git -C …/ISEP checkout main`(CHILD=1)→ 2,把它印的那行填上分支原樣餵回 → 0(舊版同一行 → 2) ## 沒做/另報 - tokenize 層把**換行**當空白吞:`…EOF⏎git checkout main` 從第一版起就看不到(舊閘 exit=0,漏擋不是誤攔),寫成 (76) 已知邊界測試;修它會連帶改推 main 那道閘的偵測範圍,與 (53) 同層,另報 - `not-my-branch-guard.sh` 的 `NOT_MY_BRANCH_OK=1` 同樣只讀環境變數(第 28 行),沒動、沒實測 **版本:待總管定版**(改了會被載入的 hook 與 lib)。 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0178ef1fGw3XeZtpN7LaZrm4
claude-code added 1 commit 2026-09-07 07:14:39 +00:00
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
claude-code merged commit 7750299b18 into main 2026-09-07 07:19:20 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: inkstone/ISEP#148