ship-check 送到得了任何 session:內容原樣搬過來,歸屬寫下來讓機器去比 #127
@@ -56,15 +56,32 @@ STAMP="/tmp/.history-guard-$(printf '%s' "$FILE_PATH" | shasum | cut -c1-12)"
|
||||
#
|
||||
# 時戳由 kbdb-asked-stamp.sh(PostToolUse,matcher 對 kbdb_* 工具)寫下。
|
||||
# 逃生口:KBDB 真的連不上時 `touch /tmp/.kbdb-down`——放行但留痕,且回覆裡要說明。
|
||||
KBDB_STAMP=/tmp/.kbdb-asked
|
||||
KBDB_DOWN=/tmp/.kbdb-down
|
||||
#
|
||||
# 🔴 **逃生門要真的走得通**(inkstone/ISEP#122,2026-09-02 實撞,本檔第二次修):
|
||||
# 上面那句訊息叫人 `touch /tmp/.kbdb-down`,而 `touch` 造出來的是**空檔**
|
||||
# ⇒ 舊版 `t=$(cat "$f")` 讀到空字串 ⇒ 算式當成 0 ⇒ 「距今 17 億秒」
|
||||
# ⇒ 永遠不新鮮 ⇒ **照著訊息打完,還是被擋**。
|
||||
# 實測(2026-09-02,KBDB 兩支工具都回 Connection closed 時):
|
||||
# $ touch /tmp/.kbdb-down → 再送同一個編輯 → 一模一樣的擋,訊息一字不差
|
||||
# ⇒ 那行逃生門是印出來好看的,**沒有人照著打過一次**
|
||||
# (同族:ISEP#125 worktree 閘印的逃生門原文照打 rc=2;本票 comment 6071)。
|
||||
#
|
||||
# 改法:**內容不是數字就用檔案的 mtime**——`touch` 做的正是更新 mtime,
|
||||
# 所以訊息裡那一行從此真的走得通,而 `kbdb-asked-stamp.sh` 寫數字的舊格式照樣相容。
|
||||
# 路徑用 `KBDB_STAMP_DIR` 可覆寫,**只為了讓迴歸測試不去動這台機器真正的戳記**
|
||||
# (測試把別的 session 的戳記清掉=把閘弄成隨機的)。
|
||||
KBDB_STAMP="${KBDB_STAMP_DIR:-/tmp}/.kbdb-asked"
|
||||
KBDB_DOWN="${KBDB_STAMP_DIR:-/tmp}/.kbdb-down"
|
||||
kbdb_fresh=0
|
||||
now=$(date +%s)
|
||||
for f in "$KBDB_STAMP" "$KBDB_DOWN"; do
|
||||
if [ -f "$f" ]; then
|
||||
t=$(cat "$f" 2>/dev/null || echo 0)
|
||||
[ $((now - t)) -lt 3600 ] && kbdb_fresh=1
|
||||
fi
|
||||
[ -f "$f" ] || continue
|
||||
t=$(cat "$f" 2>/dev/null || true)
|
||||
case "$t" in
|
||||
''|*[!0-9]*) t=$(date -r "$f" +%s 2>/dev/null || stat -c %Y "$f" 2>/dev/null || echo 0) ;;
|
||||
esac
|
||||
[ "${t:-0}" -gt 0 ] || continue
|
||||
[ $((now - t)) -lt 3600 ] && kbdb_fresh=1
|
||||
done
|
||||
if [ "$kbdb_fresh" -eq 0 ]; then
|
||||
cat >&2 <<'KEOF'
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
# KBDB(一次看所有庫,最便宜)→ 該 repo 的 wiki/tasks → git log -S → 讀源碼(最貴,
|
||||
# 只看得到「現在長怎樣」,看不到「為什麼變成這樣」)
|
||||
set -eu
|
||||
date +%s > /tmp/.kbdb-asked
|
||||
# 路徑跟 history-first-guard.sh 用同一個覆寫變數,否則測試會兩邊指到不同地方
|
||||
date +%s > "${KBDB_STAMP_DIR:-/tmp}/.kbdb-asked"
|
||||
exit 0
|
||||
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
# history-first-guard.sh 的迴歸測試 —— 重點在**逃生門真的走得通**(inkstone/ISEP#122)
|
||||
#
|
||||
# ── 這支在守什麼 ────────────────────────────────────────────────────
|
||||
# 2026-09-02 實撞:KBDB 兩支工具都回 `Connection closed`,照這支閘印出來的那行
|
||||
# `touch /tmp/.kbdb-down`
|
||||
# 打完再送同一個編輯,**被一模一樣地擋第二次**——因為 `touch` 造的是空檔,
|
||||
# 而舊版是 `cat` 檔案內容當時戳,空字串被當成 0 ⇒ 「距今 17 億秒」⇒ 永遠不新鮮。
|
||||
#
|
||||
# 🔴 一道閘印出來的出路走不通,比沒有出路更糟:人照做了、還是被擋,
|
||||
# 下一步就是去繞過這道閘(本 repo 的 ticket-api-bypass-guard.sh 檔頭記過同一課)。
|
||||
# 所以這支測試的第一等公民是**逃生門那一條**,不是「擋不擋得住」。
|
||||
#
|
||||
# 🔴 **這支測試自己怎麼被證明有鑑別力**(不要只看它全綠):
|
||||
# 把修好之前那一版拿來跑,第 ④ 條要紅。因為舊版沒有 KBDB_STAMP_DIR,
|
||||
# 直接餵它會去讀這台機器真正的 /tmp 戳記而「剛好過關」,所以要先把路徑隔離:
|
||||
# git show <修好前的 commit>:hooks/history-first-guard.sh > /tmp/old.sh
|
||||
# sed 's#/tmp/\.kbdb-asked#${KBDB_STAMP_DIR:-/tmp}/.kbdb-asked#;
|
||||
# s#/tmp/\.kbdb-down#${KBDB_STAMP_DIR:-/tmp}/.kbdb-down#' /tmp/old.sh > /tmp/old-iso.sh
|
||||
# bash hooks/tests/history-first-guard.test.sh /tmp/old-iso.sh
|
||||
# 2026-09-02 實跑:舊版 9/10(④ 紅),修好後 10/10。
|
||||
# ⚠️ 第 ③ 條**兩版都是綠的**——它只驗離開碼 2,而過了第 0 道之後歷史警察照樣會擋。
|
||||
# 真正分辨得出來的是 ④「擋人的是誰」。這一格是本支最容易寫成假綠的地方。
|
||||
#
|
||||
# 🔴 全程用 KBDB_STAMP_DIR 指到 TMP,**不碰這台機器真正的 /tmp/.kbdb-* 戳記**
|
||||
# (把別的 session 的戳記清掉=把閘弄成隨機的)。不打網路。
|
||||
set -u
|
||||
HOOK="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/history-first-guard.sh}"
|
||||
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
|
||||
export KBDB_STAMP_DIR="$TMP/stamps"; mkdir -p "$KBDB_STAMP_DIR"
|
||||
|
||||
PASS=0; FAIL=0; N=0
|
||||
|
||||
# 造一個「有前科」的 code 檔:放在一個有 git 歷史的假 repo 裡
|
||||
REPO="$TMP/repo"; mkdir -p "$REPO"
|
||||
( cd "$REPO" && git init -q && git config user.email t@t && git config user.name t \
|
||||
&& printf 'echo old\n' > victim.sh && git add . && git commit -qm "第一版" ) >/dev/null 2>&1
|
||||
VICTIM="$REPO/victim.sh"
|
||||
|
||||
# run <檔案> → 印出離開碼(送一份 Edit 的 payload 進去)
|
||||
run() {
|
||||
printf '{"tool_input":{"file_path":"%s"}}' "$1" \
|
||||
| CLAUDE_PROJECT_DIR="$REPO" bash "$HOOK" >/dev/null 2>"$TMP/err"
|
||||
printf '%s' "$?"
|
||||
}
|
||||
|
||||
check() { # check <說明> <實際> <期望>
|
||||
N=$((N+1))
|
||||
if [ "$2" = "$3" ]; then printf ' ✅ %s\n' "$1"; PASS=$((PASS+1))
|
||||
else printf ' ❌ %s —— 期望離開碼 %s,實際 %s\n' "$1" "$3" "$2"; sed 's/^/ /' "$TMP/err" | head -6; FAIL=$((FAIL+1)); fi
|
||||
}
|
||||
|
||||
clean_stamps() { rm -f "$KBDB_STAMP_DIR/.kbdb-asked" "$KBDB_STAMP_DIR/.kbdb-down"; rm -f /tmp/.history-guard-*; }
|
||||
|
||||
echo "── 第 0 道:問過 KBDB 了沒 ──────────────────────────────────"
|
||||
clean_stamps
|
||||
check "① 沒有任何戳記 → 擋" "$(run "$VICTIM")" 2
|
||||
grep -q "KBDB" "$TMP/err" && printf ' ✅ ② 擋下時訊息講得出是哪一道\n' && PASS=$((PASS+1)) || { printf ' ❌ ② 訊息沒提到 KBDB\n'; FAIL=$((FAIL+1)); }; N=$((N+1))
|
||||
|
||||
echo "── 🔴 逃生門:照訊息打那一行,就要真的過得去 ──────────────────"
|
||||
clean_stamps
|
||||
touch "$KBDB_STAMP_DIR/.kbdb-down" # ← 訊息裡原文那一行做的事
|
||||
check "③ touch 出來的空檔(本票的實撞)→ 要放行過第 0 道" "$(run "$VICTIM")" 2
|
||||
# ↑ 期望仍是 2:過了第 0 道之後會被「歷史警察」擋(這個檔真的有前科),
|
||||
# 所以要看訊息換人了沒——這是這支測試最容易寫錯的一格。
|
||||
grep -q "歷史警察" "$TMP/err" && printf ' ✅ ④ 擋人的換成歷史警察 ⇒ 第 0 道確實被逃生門放過了\n' && PASS=$((PASS+1)) || { printf ' ❌ ④ 還是卡在 KBDB 那一道 ⇒ 逃生門是假的\n'; sed 's/^/ /' "$TMP/err" | head -4; FAIL=$((FAIL+1)); }; N=$((N+1))
|
||||
|
||||
clean_stamps
|
||||
date +%s > "$KBDB_STAMP_DIR/.kbdb-down" # 舊格式(檔案內容是時戳)
|
||||
run "$VICTIM" >/dev/null
|
||||
grep -q "歷史警察" "$TMP/err" && printf ' ✅ ⑤ 舊格式(內容寫時戳)照樣認得 ⇒ 沒有把既有的弄壞\n' && PASS=$((PASS+1)) || { printf ' ❌ ⑤ 舊格式壞了\n'; FAIL=$((FAIL+1)); }; N=$((N+1))
|
||||
|
||||
clean_stamps
|
||||
date +%s > "$KBDB_STAMP_DIR/.kbdb-asked"
|
||||
run "$VICTIM" >/dev/null
|
||||
grep -q "歷史警察" "$TMP/err" && printf ' ✅ ⑥ 真的問過 KBDB(kbdb-asked-stamp.sh 寫的戳記)→ 過第 0 道\n' && PASS=$((PASS+1)) || { printf ' ❌ ⑥ 問過了還被擋\n'; FAIL=$((FAIL+1)); }; N=$((N+1))
|
||||
|
||||
echo "── 過期的戳記不算數(逃生門不能變成永久後門)──────────────────"
|
||||
clean_stamps
|
||||
touch -t "$(date -v-2H '+%Y%m%d%H%M' 2>/dev/null || date -d '2 hours ago' '+%Y%m%d%H%M')" "$KBDB_STAMP_DIR/.kbdb-down"
|
||||
run "$VICTIM" >/dev/null
|
||||
grep -q "KBDB" "$TMP/err" && printf ' ✅ ⑦ 兩小時前的空戳記 → 過期,回到擋\n' && PASS=$((PASS+1)) || { printf ' ❌ ⑦ 過期的戳記還在放行 ⇒ 一次 touch 就永久免疫\n'; FAIL=$((FAIL+1)); }; N=$((N+1))
|
||||
|
||||
echo "── 不該擋的(誤攔比漏擋嚴重)────────────────────────────────"
|
||||
clean_stamps
|
||||
printf '# doc\n' > "$REPO/readme.md"
|
||||
check "⑧ 文件檔(.md)→ 一路放行,連第 0 道都不該碰" "$(run "$REPO/readme.md")" 0
|
||||
clean_stamps
|
||||
check "⑨ 不存在的新檔 → 放行" "$(run "$REPO/brand-new.sh")" 0
|
||||
clean_stamps
|
||||
mkdir -p "$REPO/tests"; printf 'echo t\n' > "$REPO/tests/a.sh"
|
||||
check "⑩ 測試檔 → 放行" "$(run "$REPO/tests/a.sh")" 0
|
||||
|
||||
echo
|
||||
printf '通過 %s 條,失敗 %s 條(共 %s 條)\n' "$PASS" "$FAIL" "$N"
|
||||
[ "$FAIL" = 0 ] || exit 1
|
||||
Reference in New Issue
Block a user