Files
ISEP/scripts/test-gate-report.sh
T
Claude dda4852938 gate-log: 透明記錄層——每一支閘的擋/放行/繞過都留得下(InkStoneCo#48)
在此之前 36 支閘裡只有 2 支會記錄自己擋了什麼,六種逃生口
(NOT_MY_BRANCH_OK=1/no-ticket-needed/wiki-secret-ok/credential-ok/
solo-ok/kbdb-down)用了完全不留痕 ⇒ 擋對 100 次跟擋錯 100 次在資料上
一模一樣 ⇒ 規則只會累積不會被淘汰。

機制:hooks/lib/gate-log-wrap.sh 包在每一支閘外面(hooks.json 的 82 條
gate command 全部前綴這支 wrapper;2 條 scripts/ refresher 不是閘、不包)。
它從不 exit 2,只把真正那支閘的離開碼原樣傳出去 ⇒ 記錄壞掉最多漏記一筆,
不可能誤攔(票紅線:寧可漏記,不可擋錯)。每次留一筆中繼資料:
哪支閘/何時/對什麼工具/判成什麼/(若繞過)用了哪個逃生口。

- 只記中繼資料,完全不寫入指令原文或 payload ⇒ 金鑰在結構上進不了帳本(D36)
- 純 POSIX shell 記錄,不另起 python ⇒ 不讓閘變慢變脆
- 紀錄長在既有閘動作上,零輪詢/零 cron(D20)
- 帳本沿用 milestone-account 慣例(不寫進會被 plugin update 換掉的目錄)

報告:scripts/gate-report all 回答票上三題(誤擋嫌疑排序/最近沒擋過的規則/
同一 session 反覆撞同一支閘)。基線與反面驗證見票留言。

測試:hooks/tests/gate-log.test.sh 22 條(透明/留痕/反面驗證/不變脆/不外洩)
+ scripts/test-gate-report.sh 8 條(三題數字對不對)。
hooks 檔數 61、hooks.json 註冊 84 均不變(wrapper 住 hooks/lib,非 gate .sh)。

版本:待總管定版(改了會被載入的東西需升版才傳得到,但 tag 由總管打)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TNfNypeDaCVEZQW1Wqwfxq
2026-09-06 21:01:42 +00:00

67 lines
4.0 KiB
Bash
Executable File
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.
#!/usr/bin/env bash
# scripts/gate-report 的迴歸測試(inkstone/InkStoneCo#48 驗收 #2
#
# 用一份寫死的 fixture 帳本,驗這三題「查得出來、答得對」,不是憑印象:
# Q1 哪支閘擋最多?其中多少被繞過?
# Q2 哪條規則最近 N 天一次都沒擋過?
# Q3 同一個 session 反覆撞同一支閘幾次?
# 🔴 出路要真的走得通(mistakes.md #158):這裡不驗「有沒有印字」,
# 驗「印出來的數字對不對」。
set -u
REPORT="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/gate-report}"
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
LED="$TMP/gate-log.jsonl"
PASS=0; FAIL=0; N=0
ok() { N=$((N+1)); printf ' ✅ %s\n' "$1"; PASS=$((PASS+1)); }
bad() { N=$((N+1)); printf ' ❌ %s\n' "$1"; FAIL=$((FAIL+1)); shift; for l in "$@"; do printf ' %s\n' "$l"; done; }
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
OLD=$(python3 -c "import datetime as d;print((d.datetime.now(d.timezone.utc)-d.timedelta(days=30)).strftime('%Y-%m-%dT%H:%M:%SZ'))")
row() { printf '{"ts":"%s","gate":"%s","event":"PreToolUse","tool":"Bash","verdict":"%s","bypass":"%s","sid":"%s","child":%s}\n' \
"$1" "$2" "$3" "$4" "$5" "${6:-0}" >> "$LED"; }
# gateA:擋 3 次、繞過 2 次(誤擋嫌疑最高);session sA 撞它 3 次
row "$NOW" gateA block "" sA 0
row "$NOW" gateA block "" sA 0
row "$NOW" gateA block "" sA 0 # sA 撞 gateA 共 3 次(Q3
row "$NOW" gateA bypass NOT_MY_BRANCH_OK sB 1
row "$NOW" gateA bypass NOT_MY_BRANCH_OK sB 1
# gateB:擋 1 次
row "$NOW" gateB block "" sC 0
# gateC:最近只有放行,從沒擋過(Q2 該點名)
row "$NOW" gateC pass "" sC 0
row "$NOW" gateC pass "" sD 0
# gateD:只有 30 天前擋過,最近 7 天沒擋(Q2 該點名)
row "$OLD" gateD block "" sE 0
echo "── Q1:擋最多的是 gateA,且看得出被繞過 2 次 ────────────────"
Q1=$(ISEP_GATE_LOG="$LED" "$REPORT" q1 2>&1)
FIRST=$(printf '%s' "$Q1" | grep -E '^\s+gate[A-Z]' | head -n1)
case "$FIRST" in *gateA*) ok "① 誤擋嫌疑排序第一名是 gateA" ;; *) bad "① 排序第一名不是 gateA" "$Q1" ;; esac
# gateA 那行:擋下 3、被繞過 2、本要擋 5
if printf '%s' "$Q1" | grep -E 'gateA' | grep -qE '\b3\b.*\b2\b.*\b5\b'; then
ok "② gateA 的數字對:擋下3 / 被繞過2 / 本要擋5"
else bad "② gateA 數字不對" "$(printf '%s' "$Q1" | grep gateA)"; fi
echo "── Q2:最近 7 天沒擋過的是 gateC(只放行)與 gateD30天前才擋)─"
Q2=$(ISEP_GATE_LOG="$LED" "$REPORT" q2 --days 7 2>&1)
printf '%s' "$Q2" | grep -q 'gateC' && ok "③ gateC(活著但只放行)被點名" || bad "③ 沒點名 gateC" "$Q2"
printf '%s' "$Q2" | grep -q 'gateD' && ok "④ gateD(最近7天沒擋)被點名" || bad "④ 沒點名 gateD" "$Q2"
printf '%s' "$Q2" | grep -qE '^\s+·\s+gateA' && bad "⑤ 誤點名 gateA(它最近有擋)" "$Q2" || ok "⑤ 有擋過的 gateA 不被點名"
echo "── Q3sA 反覆撞 gateA 3 次 ─────────────────────────────────"
Q3=$(ISEP_GATE_LOG="$LED" "$REPORT" q3 2>&1)
if printf '%s' "$Q3" | grep -E 'gateA' | grep -q 'sA'; then
if printf '%s' "$Q3" | grep -E 'sA' | grep -qE '\b3\b'; then ok "⑥ sA 撞 gateA 記成 3 次"; else bad "⑥ 次數不是 3" "$Q3"; fi
else bad "⑥ 沒抓到 sA×gateA" "$Q3"; fi
# sC 撞 gateB 只 1 次,不該出現在 Q3
printf '%s' "$Q3" | grep -E 'gateB' | grep -q 'sC' && bad "⑦ 撞1次的 sC×gateB 不該出現" "$Q3" || ok "⑦ 只撞1次的不入 Q3"
echo "── 基線:空帳本也要能跑(不爆炸)───────────────────────────"
EMPTY="$TMP/empty.jsonl"; : > "$EMPTY"
if ISEP_GATE_LOG="$EMPTY" "$REPORT" all >/dev/null 2>&1; then ok "⑧ 空帳本跑 all 不報錯"; else bad "⑧ 空帳本跑 all 爆了"; fi
echo ""
echo "通過 $PASS 條,失敗 $FAIL 條(共 $N"
[ "$FAIL" = 0 ]