#!/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 印出來的那一行原樣開(-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 <說明> 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 ]