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>
This commit is contained in:
2026-08-06 00:26:56 +08:00
parent 6a49f25aef
commit 2f5d9f3bb2
18 changed files with 1246 additions and 83 deletions
@@ -0,0 +1,56 @@
#!/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