Files
system-dev-template/template/.claude/hooks/install-artifact-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

93 lines
4.4 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
# install-artifact-guard.sh — 實例不准改機制(分離規格 防糾纏閘 S1)
#
# 掛 PreToolUsematcher: Write|Edit|MultiEdit)。**排在整條鏈的最前面**——
# 範圍最大的規則先擋,錯誤訊息才會指向最根本的那條。
#
# ── 擋什麼 ──────────────────────────────────────
# 寫入「安裝產物區」= 框架發下來的機制檔(hook、範本、安裝腳本、plugin 目錄)。
# 判準來自 system-dev/.template-manifest**凡是框架管的檔(class=overwrite)都算**。
# 不另寫一份路徑表——兩處維護同一條規則,遲早不同步。
#
# ── 為什麼要擋(實測數據,不是潔癖)─────────────────
# 拿一個真實使用中的實例對照框架:6 支框架 hook 裡 **4 支已被手改**
# 另有 11 支是實例自己發明的,而**沒有任何機制知道這件事**。
# 兩層後果:
# ① 框架之後修好的 bug,永遠送不到那些被手改的檔
# ② 手改的內容沒人審、也不會回饋給框架 ⇒ 同一個坑每個實例各踩一次
# 機制要改就回上游改,一次修全家;在自己家裡改,只有自己受惠、且下次更新就孤立。
#
# ── 怎麼放行 ────────────────────────────────────
# 框架 repo 自己開發時本來就要改這些檔 ⇒ 兩種放行方式:
# · repo 根目錄有 .sdt-framework-dev 檔(框架 repo 自帶並 commit,零記憶負擔)
# · 環境變數 SDT_FRAMEWORK_DEV=1(臨時情境)
# 注意:官方**沒有** --framework-dev 這個 CLI 參數(實查,非記憶),別去找。
#
# 誠實限制:擋直接寫檔。bash 繞道改檔擋不到。留痕可審,不宣稱防偽。
set -uo pipefail
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$HOOK_DIR/lib/role-lib.sh" 2>/dev/null || exit 0
# ── 框架開發模式 → 放行 ────────────────────────────
ROOT="$(sdt_repo_root)"
[ -f "$ROOT/.sdt-framework-dev" ] && exit 0
[ "${SDT_FRAMEWORK_DEV:-}" = "1" ] && exit 0
INPUT="$(cat)"
FILE_PATH="$(sdt_file_path "$INPUT")"
[ -z "$FILE_PATH" ] && exit 0
REL="$(sdt_rel_path "$FILE_PATH")"
MANIFEST="$ROOT/system-dev/.template-manifest"
is_managed_artifact() {
# ⓪ 使用者的自訂插槽 → 一律放行,**這一條要排在最前面**
# 它們存在的目的就是給實例填自己的規則,而且 update 不會覆蓋它們。
# (踩過:本 hook 的錯誤訊息叫人「去改 pre-write-guard.sh」,
# 結果自己把那支也擋了——叫人走的路自己堵住,是最糟的一種閘。)
case "$1" in
.claude/hooks/pre-write-guard.sh) return 1 ;;
*.template.sh|*.template.md) return 1 ;;
.claude/settings.json) return 1 ;;
esac
# ① manifest 是權威:它說這是框架管的邏輯檔(class=overwrite)才擋
if [ -f "$MANIFEST" ]; then
if awk -F'\t' -v d="$1" '!/^#/ && $1==d && $2=="overwrite" {found=1} END{exit !found}' "$MANIFEST"; then
return 0
fi
fi
# ② manifest 還沒有/這個檔還沒登記(舊實例、或框架剛加的新檔)
# → 退回路徑慣例,別因為沒表就整條規則失效
case "$1" in
.claude/hooks/*|.claude/plugins/*) return 0 ;;
system-dev/scripts/*) return 0 ;;
esac
return 1
}
if is_managed_artifact "$REL"; then
cat >&2 <<EOF
❌ BLOCKED by install-artifact-guard(防糾纏閘 S1
這個檔是**框架發下來的機制**,實例不改機制。
路徑:$REL
正確做法:
· 機制要改 → 回框架/政策包 repo 提案,bump 版本,實例再 update
(一次修全家,別人也拿得到;在這裡改只有你受惠,而且下次更新就孤立)
· 只是這個專案的特殊禁令 → 寫進 .claude/hooks/pre-write-guard.sh 的自訂區
(那支就是留給你填的插槽,不會被 update 覆蓋)
· 你就是在開發框架本身 → 這個 repo 根目錄該有 .sdt-framework-dev
為什麼擋(實測,不是原則潔癖):
對照過一個真實使用中的實例——6 支框架 hook 裡 4 支已被手改,
而沒有任何人知道。那 4 支從此收不到框架的任何修正。
EOF
exit 2
fi
exit 0