Files
ISEP/scripts/test-guard-cross-project.sh
T
Leo 7af30e97b0 fix: 白名單認的是「哪個 repo」不是路徑前綴(inkstone/ISEP#117)
照 `line-needs-own-worktree.sh` 印的下一步開 worktree(`<repo>-wt-<票號>/`),
接著在裡面寫檔會被 `guard-cross-project.sh` 擋——它的白名單是路徑前綴,
`matrix/arcrun-wt-195/…` 對不上 `matrix/arcrun/*`。
⇒ 照規矩做的人被擋,留在共用目錄裡的人反而過得了(inkstone/Arcrun#195 實撞,
工人只好把 worktree 開到 InkStoneCo 樹之外躲開它)。

判準改成 git 自己算得出來的事實,跟 line-needs-own-worktree 同一個形狀:
`git worktree list --porcelain` 第一筆永遠是主工作目錄 ⇒ 主目錄與它所有的
worktree 回同一個答案。`-wt-` 這個命名慣例一個字都沒有進判準。
名單上沒有的 repo、它的 worktree、根本不是 repo 的目錄,全部照舊擋。

順帶修掉一個獨立的缺陷(實測證據見檔內註解與 wiki):
這支閘在 plugin 裡**從來沒生效過**。它用 `<hook 檔>/../..` 當頂層 repo 根,
搬進 plugin 之後那個路徑指到 plugin cache 的上一層 ⇒ 每一次都靜默放行。
同一份 payload:plugin 那份 exit 0、InkStoneCo 那份 exit 2,而兩個檔案 diff 一字不差。
改用 `$CLAUDE_PROJECT_DIR`(README 路徑規約本來就這樣寫)。
🔴 這也代表:本輪的修法要生效,`InkStoneCo/.claude/hooks/guard-cross-project.sh`
那份現役副本必須退場(刪檔+從 settings.json 取消註冊)——那是頂層的樹,
不在本 PR 裡,交回總管裁。

測試:scripts/test-guard-cross-project.sh(docs/TESTING.md A25)21 條,
真的 git repo + 真的 worktree 當道具,放行 12 條/該擋 9 條兩個方向都驗。
不新增第三支閘。

版本號待總管定版。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 14:46:35 +08:00

89 lines
5.3 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 跨專案越界閘的測試(docs/TESTING.md A25)— inkstone/ISEP#117
#
# 判準:白名單放行的是「**哪個 repo**」,不是「哪個路徑前綴」。
# ⇒ `matrix/arcrun` 的 worktree(照 line-needs-own-worktree.sh 印的那行開出來的
# `matrix/arcrun-wt-<票號>/`)要跟主工作目錄一樣放行;
# ⇒ 名單上沒有的子 repo、它的 worktree、根本不是 repo 的目錄,全部照舊擋。
#
# 這支測試用**真的 git repo 真的 worktree**當道具(不是假路徑字串)——
# 因為被測的判準就是 `git worktree list` 的第一筆,用假路徑等於沒驗到判準本體。
#
# ⚠️ 誠實邊界:本測試用 `CLAUDE_CODE_CHILD_SESSION=0` 代表「總管」。
# 真實環境裡那個變數不可靠(Claude Desktop 的主 session 也是 1
# 見 InkStoneCo/system-dev/wiki/ops-facts.md)。這裡驗的是**閘的分支邏輯**,
# 不是「總管在真機上一定會被擋」——後者是另一件事,本輪沒動。
set -u
cd "$(dirname "$0")/.." || exit 1
H="$PWD/hooks/guard-cross-project.sh"
FIX="$(mktemp -d "${TMPDIR:-/tmp}/isep117.XXXXXX")"
trap 'rm -rf "$FIX"' EXIT
g() { git -C "$1" -c user.email=t@t -c user.name=t "${@:2}"; }
mkrepo() { # mkrepo <路徑>
mkdir -p "$1" && git -C "$1" init -q 2>/dev/null
: > "$1/seed"; g "$1" add -A >/dev/null 2>&1; g "$1" commit -qm seed >/dev/null 2>&1
}
ROOT="$FIX/InkStoneCo"
mkrepo "$ROOT" # 頂層樹本身也是一個 repo
mkrepo "$ROOT/matrix/arcrun" # 白名單上的子 repo
mkrepo "$ROOT/products/arcrun-rag" # 白名單上的子 repo
mkrepo "$ROOT/matrix/other" # **不在**白名單上的子 repo
mkdir -p "$ROOT/matrix/plain-dir" # 根本不是 repo
mkdir -p "$ROOT/_archive/old"
# 照 line-needs-own-worktree.sh 印出來的那一行原樣開(<NAME>-wt-<票號>,兄弟目錄)
g "$ROOT/matrix/arcrun" worktree add -q "$ROOT/matrix/arcrun-wt-117" -b wt117 >/dev/null 2>&1
g "$ROOT/products/arcrun-rag" worktree add -q "$ROOT/products/arcrun-rag-wt-99" -b wt99 >/dev/null 2>&1
g "$ROOT/matrix/other" worktree add -q "$ROOT/matrix/other-wt-1" -b wt1 >/dev/null 2>&1
# 名字長得像 worktree、實際上只是個空目錄
mkdir -p "$ROOT/matrix/arcrun-wt-fake"
# macOS 的 $TMPDIR 是 /var/...symlink),git 回的是 /private/var/... ⇒ 兩種形式都要驗
ROOT_PHYS="$(cd "$ROOT" && pwd -P)"
PASS=0; FAIL=0
run() { # run <want> <CHILD_SESSION> <file_path> <說明>
local want=$1 child=$2 fp=$3 note=$4 got
printf '%s' "{\"tool_input\":{\"file_path\":$(python3 -c 'import json,sys;print(json.dumps(sys.argv[1]))' "$fp")}}" \
| CLAUDE_PROJECT_DIR="$ROOT" CLAUDE_CODE_CHILD_SESSION="$child" bash "$H" >/dev/null 2>&1
got=$?
if [ "$got" = "$want" ]; then PASS=$((PASS+1)); printf ' ✅ '; else FAIL=$((FAIL+1)); printf ' ❌ '; fi
printf 'want=%s got=%s child=%s %s\n' "$want" "$got" "$child" "$note"
}
echo "── 該放行:subagent 在白名單 repo 裡(含照閘 A 開出來的 worktree)──"
run 0 1 "matrix/arcrun/src/foo.ts" "主工作目錄(迴歸:本來就該過)"
run 0 1 "matrix/arcrun-wt-117/src/foo.ts" "★ 本票的病:照閘 A 開的 worktree"
run 0 1 "$ROOT/matrix/arcrun-wt-117/src/foo.ts" "★ 同上,絕對路徑"
run 0 1 "matrix/arcrun-wt-117/a/b/c/還沒建的檔.ts" "worktree 裡的新檔(目錄還不存在)"
run 0 1 "products/arcrun-rag-wt-99/main.go" "另一個白名單 repo 的 worktree"
run 0 1 "products/arcrun-rag/main.go" "主工作目錄(迴歸)"
run 0 1 "$ROOT_PHYS/matrix/arcrun-wt-117/src/foo.ts" "★ 絕對路徑是實體形式(/private/var vs /var"
echo "── 該放行:跟白名單無關的既有規則(迴歸,不能被這次改動弄壞)──"
run 0 0 "matrix/arcrun/docs/HANDOFF-x.md" ".md 一律放行(leo 2026-06-15 拍板)"
run 0 0 "matrix/other/README.md" "非白名單 repo 的 .md 也放行"
run 0 0 "system-dev/wiki/tool.py" "頂層自己的檔"
run 0 0 ".claude/hooks/x.sh" "頂層自己的檔"
run 0 1 "/tmp/somewhere-else/x.ts" "頂層樹之外(本來就不管)"
echo "── 該擋:總管(非 subagent)寫實作進子 repo ──"
run 2 0 "matrix/arcrun/src/foo.ts" "白名單只對 subagent 開,總管照擋"
run 2 0 "matrix/arcrun-wt-117/src/foo.ts" "★ worktree 不是總管的後門"
run 2 0 "products/arcrun-rag/main.go" "同上,另一個 repo"
run 2 0 "_archive/old/x.py" "_archive 也在管轄內"
echo "── 該擋:subagent,但那個 repo 不在白名單上 ──"
run 2 1 "matrix/other/src/foo.ts" "不在名單的子 repo"
run 2 1 "matrix/other-wt-1/src/foo.ts" "★ 不在名單的 repo,它的 worktree 一樣擋"
run 2 1 "matrix/plain-dir/foo.ts" "根本不是 repogit 往上找到頂層樹)"
run 2 1 "matrix/arcrun-wt-fake/foo.ts" "★ 名字像 worktree、其實是空目錄 ⇒ 名字騙不過去"
run 2 1 "_archive/old/x.py" "_archive 不在白名單上"
echo
echo "通過 $PASS 條,失敗 $FAIL 條"
[ "$FAIL" -eq 0 ]