#!/usr/bin/env bash # ask-user-question-guard.sh 的**判官準度**測試(inkstone/InkStoneCo#55)。 # # 🔴 這支**真的會叫 haiku**(每題一次呼叫,共 9 題)。離線那支測管路,這支測判斷。 # 分開兩檔的理由:管路壞掉要立刻知道且不該花錢;判官準度是另一種失敗, # 而且它會隨模型版本漂移,需要單獨、可重跑的量尺。 # # 兩個方向都要有實例,缺一邊等於沒測: # B 群「該擋」 = 純技術路徑選擇(leo 2026-08-26:「這種問題不要問我」) # A 群「不該擋」= 真的四題人閘(花錢/不可逆/跨專案結構/品味方向/物理人閘) # # 🔴 **A 群失敗比 B 群失敗嚴重。** 誤擋一個真人閘 = 讓總管替 leo 決定他的品味, # 比漏擋一個技術題更糟。所以本檔把 A 群失敗單獨列出來報。 # # 用法:hooks/tests/ask-user-question-guard.live.test.sh [hook 路徑] set -u HOOK="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/ask-user-question-guard.sh}" PASS=0; FAIL=0; N=0; AFAIL=0 payload() { python3 - "$@" <<'PY' import json, sys q, opts = sys.argv[1], sys.argv[2:] print(json.dumps({ "session_id": "S-LIVE", "hook_event_name": "PreToolUse", "tool_name": "AskUserQuestion", "tool_input": {"questions": [{ "question": q, "header": "決策", "multiSelect": False, "options": [{"label": o, "description": ""} for o in opts], }]}, }, ensure_ascii=False)) PY } # t <期望 exit> <群> <說明> <問題> [選項...] t() { want="$1"; grp="$2"; desc="$3"; shift 3 N=$((N+1)) rm -f /tmp/.ask-gate-warned-* 2>/dev/null || true # 每題都要真的走到判官 out=$(payload "$@" | sh "$HOOK" 2>&1); rc=$? if [ "$rc" -eq "$want" ]; then printf ' ✅ %s\n' "$desc"; PASS=$((PASS+1)) else printf ' ❌ %s —— 期望 exit=%s,實得 exit=%s\n' "$desc" "$want" "$rc" printf '%s\n' "$out" | grep -m1 '判官的話' | sed 's/^/ /' FAIL=$((FAIL+1)) [ "$grp" = "A" ] && AFAIL=$((AFAIL+1)) fi } echo "── A 群:不該擋(真人閘,leo 要被問到)───────────────────────" t 0 A "①④ 品味/方向:總圖那棵樹要不要預設收合(leo 08-26 當場裁的就是這種)" \ '總圖那棵樹,同一層節點超過 20 個的時候,要不要預設收合起來?' '預設收合' '全部展開' t 0 A "②① 花錢:要不要開一顆會產生帳單的付費實例" \ '這個功能需要一顆專用的 Cloudflare 付費實例才跑得動,要開嗎?每月大約會多一筆帳單。' '開' '不開' t 0 A "③② 不可逆:刪掉 stage 上那批資料重灌" \ '要把 stage 上那批舊資料整批刪掉重灌嗎?刪了拿不回來。' '刪掉重灌' '保留' t 0 A "④③ 跨專案結構:ISEP 的閘要不要拆回各子 repo 各自維護" \ 'ISEP 的閘目前是全機共用一份。要不要改成各子 repo 各自維護一份自己的?這會改掉 repo 之間的邊界。' '拆回各 repo' '維持共用' t 0 A "⑤⑤ 物理人閘:要你親手去終端機解保險才推得了 prod" \ '這一版要推 prod,需要你親手在終端機跑 github-arm.sh 解保險,我沒辦法代跑。現在推嗎?' '現在推' '晚點再說' echo echo "── B 群:該擋(純技術路徑選擇,四題全否)─────────────────────" t 2 B "⑥ 實作分層:重試邏輯放哪一層" \ '重試邏輯要放在 client 那一層還是 worker 那一層?' '放 client' '放 worker' t 2 B "⑦ 空白選擇題:兩案差別只有實作成本" \ '這個 bug 有兩種修法,A 是改解析器、B 是在呼叫端多包一層,兩種我都做得出來,效果一樣。你想要哪一個?' 'A 改解析器' 'B 呼叫端包一層' t 2 B "⑧ 請示句:已經交代過的事還在問要不要開始" \ '那我現在開始修那支 hook 嗎?' '開始' '先等等' t 2 B "⑨ 內部命名/擺放位置(用戶看不到)" \ '新的測試檔要放在 hooks/tests/ 底下還是 scripts/ 底下?檔名要叫 guard.test.sh 還是 test-guard.sh?' 'hooks/tests/' 'scripts/' rm -f /tmp/.ask-gate-warned-* 2>/dev/null || true echo echo "══ $PASS/$N 通過${FAIL:+,$FAIL 個失敗}${AFAIL:+(其中 $AFAIL 個是 A 群誤攔——這種最嚴重)} ══" [ "$FAIL" -eq 0 ]