7af30e97b0
照 `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>
89 lines
5.3 KiB
Bash
Executable File
89 lines
5.3 KiB
Bash
Executable File
#!/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" "根本不是 repo(git 往上找到頂層樹)"
|
||
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 ]
|