#!/bin/bash # orchestrator-scope-guard.sh — 總管不進成員 repo 動實作(分離規格 防糾纏閘 S4) # # 只裝在 orchestrator profile。掛 PreToolUse(Write|Edit|MultiEdit)。 # # ── 與 role-guard 的分工(刻意分兩支,不重疊)──────────── # role-guard 管「這個**角色**能寫什麼**類型**的檔」(code?考卷?任務池?) # orchestrator-scope-guard 管「總管能不能進這個**位置**」(成員 repo 的地盤) # 兩件事正交:總管在自己家寫 md 沒問題,進成員 repo 寫 md 也沒問題(那是交辦), # 但進成員 repo 寫實作檔就是越界——那是該 repo 的 engineer 的事。 # # ── 為什麼「只擋非 .md」────────────────────────── # .md 放行是刻意的:總管要能在成員 repo 裡留交辦文件、筆記、規格討論。 # 擋的是「總管自己下去改人家的實作」——那會讓該 repo 的 CC 完全不知道發生什麼事, # 而且繞過了該 repo 自己的規格與 wiki 紀律。 # # ── 自動派工放行 ──────────────────────────────── # 當「總管派出去的 subagent 已經戴著該 repo 的人格」時,它寫該 repo 的 code 是合理的 # (心智已經在那個 repo 裡,context 是隔離的)。雙重夾: # ① 環境變數 CLAUDE_CODE_CHILD_SESSION=1(subagent 標籤;總管主 session 沒有) # ② 路徑在下方白名單內 # ⚠️ 隔離保證是 prompt reset(軟的,靠監測),不是結構級。要絕對純淨走獨立 session。 # # 誠實限制:擋的是路徑語法層。「把實作偽裝成 .md」或「該交辦卻判斷成可直改」 # 這種語意層越界擋不到。它是底線,不是萬能。 set -uo pipefail HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" . "$HOOK_DIR/lib/role-lib.sh" 2>/dev/null || exit 0 # 只在總管實例生效(裝錯地方就安靜退出) [ "$(sdt_scope)" = "orchestrator" ] || exit 0 INPUT="$(cat)" FILE_PATH="$(sdt_file_path "$INPUT")" [ -z "$FILE_PATH" ] && exit 0 ROOT="$(sdt_repo_root)" REL="$(sdt_rel_path "$FILE_PATH")" # ── 成員 repo 目錄怎麼認 ───────────────────────── # 不寫死任何專案名(框架不含實例資料)。兩種來源: # ① 實例自填的清單:system-dev/.member-dirs(一行一個目錄前綴) # ② 沒有該檔 → 動態偵測:頂層下「自己帶 .git 的子目錄」就是成員 repo MEMBER_DIRS_FILE="$ROOT/system-dev/.member-dirs" top="${REL%%/*}" [ "$top" = "$REL" ] && exit 0 # 不在任何子目錄裡=總管自己的檔,放行 in_member_repo=1 if [ -f "$MEMBER_DIRS_FILE" ]; then while IFS= read -r d; do case "$d" in ''|'#'*) continue ;; esac d="${d%/}" case "$REL" in "$d"/*) in_member_repo=0; break ;; esac done < "$MEMBER_DIRS_FILE" else [ -d "$ROOT/$top/.git" ] && in_member_repo=0 fi [ "$in_member_repo" -eq 0 ] || exit 0 # ── 自動派工白名單(實例自填,框架不預設任何路徑)──── ALLOW_FILE="$ROOT/system-dev/.autodispatch-allow" if [ "${CLAUDE_CODE_CHILD_SESSION:-}" = "1" ] && [ -f "$ALLOW_FILE" ]; then while IFS= read -r a; do case "$a" in ''|'#'*) continue ;; esac a="${a%/}" case "$REL" in "$a"/*) echo "🤝 [orchestrator-scope-guard] subagent 放行:$a(自動派工)" >&2; exit 0 ;; esac done < "$ALLOW_FILE" fi # ── 成員 repo 內:.md 放行(交辦/文件),其餘擋 ──── case "$REL" in *.md|*.MD|*.markdown) exit 0 ;; esac cat >&2 <