#!/usr/bin/env bash # guard-cross-project.sh — 頂層總管 guardrail # # 職責邊界(InkStoneCo CLAUDE.md D9):頂層只做「跨專案的整理與安排」(總管), # 不進單一子 repo 做實作。子 repo 的程式碼/設定實作 = 在那個子 repo 的專案裡執行, # 頂層只負責「交棒」(寫 docs/HANDOFF-*.md)並記錄安排。 # # 本 hook 攔 PreToolUse 的 Write/Edit/MultiEdit: # - 目標路徑落在子 repo 目錄(matrix/ polaris/ products/ _archive/)內 # - 且不是 .md 文件 # → 擋下(exit 2),提示改用交棒(寫 HANDOFF)。 # 放行:頂層自己的檔(CLAUDE.md / .claude/ / docs/ / MEMORY.md…)、 # 以及子 repo 內的 .md(交棒 HANDOFF / 筆記 / 文件——leo 2026-06-15 拍板放行所有 .md)。 # # 誠實限制(arcrun mindset §7):本 hook 擋的是「路徑語法層」——能擋「寫程式碼進子 repo」, # 擋不了「把實作偽裝成 .md」或「該交棒卻判斷成可直改」這種語意層越界。它是底線,不是萬能。 input="$(cat)" # 取 file_path(Write/Edit/MultiEdit 都用 tool_input.file_path) file_path="$(printf '%s' "$input" | python3 -c ' import sys, json try: d = json.load(sys.stdin) print(d.get("tool_input", {}).get("file_path", "")) except Exception: print("") ' 2>/dev/null || true)" # 拿不到路徑就放行(不誤擋;hook 不該因解析失敗卡死正常操作) [ -z "$file_path" ] && exit 0 # 頂層 repo 根(hook 檔位於 /.claude/hooks/) repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" # 轉成相對 repo_root 的路徑(file_path 可能是絕對或相對) case "$file_path" in /*) rel="${file_path#"$repo_root"/}" ;; # 絕對路徑 → 去掉 repo_root 前綴 *) rel="$file_path" ;; # 相對路徑 → 假定相對 repo_root esac # 不在子 repo 目錄內 → 放行(頂層自己的檔) case "$rel" in matrix/*|polaris/*|products/*|_archive/*) : ;; # 落在子 repo,繼續判斷 *) exit 0 ;; esac # 自動派工放行(2026-06-28 leo 拍板上線,實測通過): # 子 repo 的 subagent 可直接寫該 repo code(繞 D9)——因 subagent 心智已是該 repo # (A/B 實驗證 ≈ claude -p),讓「心智是該 repo 的小弟寫該 repo」符合 context 隔離。 # 雙重夾:① CHILD_SESSION=1(harness 內建 subagent 標籤,總管主 session 無此標籤仍被擋) # ② 路徑在下面白名單 array 內。 # 隔離保證=prompt reset(軟,靠監測)非結構級;要絕對純走 claude -p。心法見 wiki。 # # 🔧 leo 維護:要放開一個 repo 給 subagent 自動寫 code,就在這 array 加一行路徑前綴。 # ⚠️ 加 = 放權,想清楚私人/共用(改它影響誰)再加。array 空 = 誰都不放(安全預設)。 AUTODISPATCH_ALLOW=( "polaris/mira" # 私人環境(只影響 leo) "matrix/arcrun" # 共用框架(影響全部人,走 PR 把關) "products/arcrun-rag" # 產品組裝 repo(2026-07-13 leo 放行 G8:回覆「放行」) ) if [ "$CLAUDE_CODE_CHILD_SESSION" = "1" ]; then for allowed in "${AUTODISPATCH_ALLOW[@]}"; do case "$rel" in "$allowed"/*) echo "🤝 [guard] subagent 放行:$allowed(自動派工)" >&2; exit 0 ;; esac done fi # 在子 repo 內:.md 放行(交棒 / 文件),其餘擋 case "$rel" in *.md|*.MD|*.markdown) exit 0 ;; esac # 走到這 = 寫非 .md 檔進子 repo 目錄 = 越界實作 → 擋 sub="${rel%%/*}" cat >&2 <