Files
system-dev-template/template/profiles/orchestrator/hooks/orchestrator-scope-guard.sh
T
Leo 2f5d9f3bb2 feat(W2 Phase 2-3): JDD 文件範本+八條封路 hook+還清兩件舊債
SDD: docs/3-specs/jdd-dual-profile(active)。編號 task 26/33 完成,Phase 4-5 未開工。

■ Phase 2 JDD 文件範本(orchestrator profile)
範本形狀對齊「實際跑出來的那兩份」(總管已寫的 root.md 15 卡、journeys.md J-1 九站),
不是照規格憑空造:
- 卡片是巢狀 bullet(`- **P1** 🟢 …` + 子項放來源/對帳),非規格畫的平行文字行
- 站點索引**巢狀 bullet 不用表格**(表格會把層級壓平,看不出從屬)
- 兩份都保留「這卷還缺什麼(誠實記)」收尾段——規格沒有,但那是防假綠的地方
新增:root.md / journeys.md / sprint.md / triage-map.md 四範本(add-if-missing,
填了就永不覆蓋)+ plugin-load-order.md(W3 插槽,框架不發明平行外掛格式)

■ Phase 3 封路 hook(八條規則落六支檔)
- role-guard(J1+J2+J3)★命門:考生不能改考卷。六組實測含「考題藏在別的 md 裡」也擋
- jdd-format-guard(J4+J5+J8):紅卡缺對帳日/任務缺站號/PM 文件混技術名詞
- station-done-guard(J6):收工判準是站的考題全綠,不是任務全關
- regression-scope(J7):動實作 → 列出要重考哪幾題(只提醒不擋)
- install-artifact-guard(S1):實例不改機制
- orchestrator-scope-guard(S4):總管不進成員 repo 動實作(從實例上收進框架,
  路徑清單改由實例自填,範本零專名)
掛載鏈依「範圍大的擋在前」:改機制 → 角色 → 位置 → 格式 → 既有三支

■ 還清兩件舊債
- update.sh 檔案清單改讀 manifest(舊硬編降為抓不到來源時的 fallback)
  ——install/update 兩份手抄清單漂移的根因全修
- CLAUDE.md 界標補植:舊實例全文原封包進本地區、框架區重鋪、原檔備份、冪等
  ——解開「沒界標⇒不敢覆蓋⇒框架改的憲法永遠送不到既有實例」這個死結

■ 修掉三個自己造的問題(實測抓出來的,不是想出來的)
- jdd-format-guard 誤擋真實 journeys.md 的「這卷還缺什麼」自述段
  → 排除法改**正面圈定**(只掃卡片本體與站內文),說明區/自述段/索引自然不在範圍
- install-artifact-guard 把 pre-write-guard.sh 也擋了——而它的錯誤訊息正叫人去改那支
  → 使用者自訂插槽列為最優先放行
- check-legacy-paths 用 HEAD 當基準會**自我弱化**:改成清單驅動後保護範圍 35→29 條
  → 基準改指最後一次真正發佈的版本

■ 實測(全部貼過輸出)
- G2 考生改考卷:6/6,含 orchestrator 寫 code/engineer 改考題/考題藏別處
- G4 憲法分流:兩環境重裝,總管版技術軌關鍵字 0、成員版上游指針 8,界標 4/4
- G6 實例改機制:4/4,含框架開發標記放行與自訂插槽放行
- G7 CI 擋實例名:注入違規 → 指出檔案行號 exit 1
- G3 進度以站計量:起牀推「J-1 已點亮 2/9 站」、收工列未亮站並禁用任務數當理由
- 回歸考、界標補植冪等、orchestrator-scope-guard 四組:全通

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 00:26:56 +08:00

98 lines
4.5 KiB
Bash
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
# orchestrator-scope-guard.sh — 總管不進成員 repo 動實作(分離規格 防糾纏閘 S4)
#
# 只裝在 orchestrator profile。掛 PreToolUseWrite|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=1subagent 標籤;總管主 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 <<EOF
❌ BLOCKED by orchestrator-scope-guard(防糾纏閘 S4
總管不進成員 repo 動實作。
成員 repo$top
路徑:$REL
正確做法:
· 要那個 repo 改什麼 → 在**那個 repo** 開一張交辦(.md 放行),
給判準與考題,成品由該 repo 的 CC 按它自己的規格產出
· 你能派 subagent 去做 → 那才是正解,不是自己下去改,也不是叫人去開另一個 session
· 真要放行 subagent 自動寫某個 repo → 把路徑加進 system-dev/.autodispatch-allow
(⚠️ 加=放權,想清楚它影響誰)
為什麼擋:你直接改人家的實作,該 repo 的 CC 完全不知道發生過什麼事——
它的規格、wiki、驗收全部被繞過,下次它照自己的紀律工作時就會把你的改動蓋掉。
EOF
exit 2