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

57 lines
3.1 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
# station-done-guard.sh — 收工判準是「站的考題全綠」,不是「任務全關」(JDD 規則 J6)
#
# 掛 Stop / SubagentStop(收工那一刻),只在 orchestrator 實例生效。
#
# ── 它在攔什麼 ──────────────────────────────────
# 「任務都關了 ⇒ 這期做完了」——這句話是整套假綠的源頭。
# 任務是**技術軌**的單位(沿系統結構切),站是**PM 軌**的單位(沿人的經歷切)。
# 零件全部做完、每個都對,人還是可能掉進零件之間的縫裡。
# 所以收工只認一件事:**指定站的考題有沒有實測通過**。
#
# ── 它不做什麼(重要)────────────────────────────
# 它**不會**自己去跑考題判斷過沒過——Gherkin 的 Then 寫的是「使用者看到什麼」,
# 那本來就不是 shell 判得出來的。它做的是:在你要收工的那一刻,
# 把「指定站」和「它們現在的狀態」攤在你眼前,逼你面對還沒填的那幾格。
# 自動判綠反而危險:那會製造一個「機器說過了」的假權威。
#
# 誠實限制:讀的是 sprint.mdjourneys.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
ROOT="$(sdt_repo_root)"
SPRINT="$ROOT/system-dev/docs/sprint.md"
[ -f "$SPRINT" ] || exit 0 # 還沒開始用站號 sprint → 不干擾
# 本期指定的站
STATIONS="$(grep -oE 'S-?[0-9]+' "$SPRINT" 2>/dev/null | sort -u | tr '\n' ' ')"
[ -z "$STATIONS" ] && exit 0
# 還沒亮的站(sprint.md 的「❌ 未亮」「◐ 半通」段)
UNLIT="$(awk '/未亮|◐|半通/ {print}' "$SPRINT" 2>/dev/null | grep -oE 'S-?[0-9]+' | sort -u | tr '\n' ' ')"
echo "════════════════════════════════════════════════"
echo "🚦 收工判準:站的考題全綠,不是任務全關(J6)"
echo "════════════════════════════════════════════════"
echo " 本期指定站:${STATIONS}"
if [ -n "$UNLIT" ]; then
echo " ⚠️ 還沒亮:${UNLIT}"
echo ""
echo " 這幾站沒亮,這期就還沒收。收工前擇一:"
echo " · 真的通了 → 在 journeys.md 標記點亮,**附實測輸出**(不是「應該會過」)"
echo " · 半通 → 標 ◐ 並寫明缺什麼"
echo " · 沒通 → 標 ❌ 誠實留著,搬到下一期並寫一行為什麼"
echo ""
echo " ⛔ 不准用「任務都關了」當收工理由——那是技術軌的單位,不是驗收線。"
else
echo " ✅ 本期指定站都已標記點亮。記得每一站都要附得出實測證據。"
fi
echo ""
exit 0