Compare commits

..

2 Commits

Author SHA1 Message Date
Leo e08c35a109 release: v0.31.1——三支壞測試修好,全套在 macOS 上全綠(inkstone/ISEP#161)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 13:37:03 +08:00
Leo 5173c7f3e5 fix(tests):修好三支跟工具無關的壞測試(inkstone/ISEP#161)
三支測試在乾淨 main 上一直紅,全部是測試自己的 bug,不是工具的離開碼漂了:

1. hooks/tests/wiki-compress.test.sh(⑯⑰㉒):E 群拼 payload 時把
   {'file_path':...} 直接嵌進三層巢狀 "$(...)" 裡;其中一層雙引號提早收口,
   讓大括號在 bash 眼裡變成「未加引號」,觸發大括號展開——逗號分隔的
   內容被拆成三條各自獨立的 python3 -c 呼叫,`{`/`}` 本身被吃掉,
   python 因此噴三次 SyntaxError。改用兩支小函式(json_edit/json_write)
   把內容經環境變數傳給 python,內容永遠不會被當成 bash 原始碼解析。

2. scripts/test-stage.sh(A1–A4、D1–D4、D14):全部卡在
   `[ "$(wc -l < "$LOG")" = 0 ]`——macOS/BSD 的 `wc -l` 會在數字前墊空白
   (GNU coreutils 不會),比對永遠是 "       0" != "0"。工具本身的離開碼
   是對的(rc=2,正確拒絕),是測試的比對方式在這台機器上有 bug。
   一律加 `| tr -d " "` 去掉墊白。

3. scripts/test-milestone-account.sh(⑰):同一款 macOS `wc -l` 墊白 bug,
   帳本筆數比對 `[ "$(wc -l < ...)" = "4" ]` 永遠比不過。同樣修法。

三支都在 `env -u CLAUDE_CODE_CHILD_SESSION` 下重跑到 exit 0:
  wiki-compress.test.sh        25/25 通過
  test-stage.sh                 45/45 通過(連跑 3 次穩定)
  test-milestone-account.sh     23/23 通過

跑過 docs/TESTING.md 記載的其餘 52 支測試(不含兩支需要真 Claude CLI 的
live 測試),全數 0 失敗,唯一例外是 hooks/tests/mainline-members-by-dependency.test.sh
的 ㊽(帳本 cwd 形狀)——在乾淨的 main(未套用本次修改)上同樣紅,
與本次改動的三個檔案無關,非本票範圍,另行處理。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 13:32:59 +08:00
4 changed files with 49 additions and 31 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "isep",
"description": "InkStone Environment Plugin —— leo 的 Claude Code 環境唯一真相源:65 支機械閘(91 條註冊,白話盤點見 docs/hooks-inventory.md)、7 支 slash command、2 支 skill、8 位有名字的工人(agents/,見 docs/governance/worker-roster.md)、69 支腳本,外加治理規範與標籤真相源。本機與雲端裝同一份,沒有子集。",
"version": "0.31.0",
"version": "0.31.1",
"keywords": [
"inkstone",
"guardrails",
+31 -18
View File
@@ -132,38 +132,51 @@ echo "── E 群:閘——大幅刪減要擋,正常編輯不准擋 ──
mk(){ printf '{"hook_event_name":"PreToolUse","session_id":"wctest%s","tool_name":"%s","tool_input":%s}' "$1" "$2" "$3"; }
run(){ rm -f /tmp/.wiki-size-guard-wctest*; out=$(printf '%s' "$1" | bash "$HOOK" 2>&1); printf '%s|%s' "$?" "$(printf '%s' "$out" | tr '\n' ' ')"; }
# 🔴 這兩支輔助函式的存在理由(inkstone/ISEP#161):舊版把 $BIG$SMALL 直接嵌進
# `python3 -c "..."` 的原始碼字串裡,而外層又套了三層 "$(...)"。當中一層雙引號
# 提早收口,讓 `{'file_path':...}` 這段在 bash 眼裡變成「未加引號」,觸發**大括號展開**
# (逗號分隔 → 拆成三條各自獨立的 python3 -c 呼叫,`{``}` 本身被吃掉)——
# 症狀就是 payload 少了 dict 的大括號、且 python 噴三次 SyntaxError。
# 改用環境變數把內容傳給 python:內容永遠不會被當成 bash 原始碼解析,
# 也不必再猜三層引號怎麼配對。
json_edit(){ # file_path old_string new_string
PL_FILE="$1" PL_OLD="$2" PL_NEW="$3" python3 -c '
import json, os
print(json.dumps({
"file_path": os.environ["PL_FILE"],
"old_string": os.environ["PL_OLD"],
"new_string": os.environ["PL_NEW"],
}, ensure_ascii=False))'
}
json_write(){ # file_path content
PL_FILE="$1" PL_CONTENT="$2" python3 -c '
import json, os
print(json.dumps({
"file_path": os.environ["PL_FILE"],
"content": os.environ["PL_CONTENT"],
}, ensure_ascii=False))'
}
BIG=$(python3 -c 'print("這是一段很長的舊內容。" * 200)')
SMALL="剩這一句。"
R=$(run "$(mk 1 Edit "$(python3 -c "
import json,sys
print(json.dumps({'file_path':'$W/mistakes.md','old_string':'''$BIG''','new_string':'$SMALL'}, ensure_ascii=False))")")")
R=$(run "$(mk 1 Edit "$(json_edit "$W/mistakes.md" "$BIG" "$SMALL")")")
case "$R" in 2\|*) ok "⑯ 砍掉一大段 wiki ⇒ 擋一次" ;; *) no "⑯ 該擋" "實得 $R" ;; esac
has "$R" "只搬不改" && ok "⑰ 擋的訊息說得出出路(三步指令)" || no "⑰ 出路" "$R"
R=$(run "$(mk 2 Edit "$(python3 -c "
import json
print(json.dumps({'file_path':'$W/mistakes.md','old_string':'這是一句舊的話,長度普通。','new_string':'這是一句新的話,長度也普通,改了幾個字。'}, ensure_ascii=False))")")")
R=$(run "$(mk 2 Edit "$(json_edit "$W/mistakes.md" "這是一句舊的話,長度普通。" "這是一句新的話,長度也普通,改了幾個字。")")")
case "$R" in 0\|*) ok "⑱ 一般編輯不擋(誤攔比漏擋更該修)" ;; *) no "⑱ 不該擋" "實得 $R" ;; esac
R=$(run "$(mk 3 Edit "$(python3 -c "
import json
print(json.dumps({'file_path':'/somewhere/src/app.ts','old_string':'x'*3000,'new_string':'y'}, ensure_ascii=False))")")")
R=$(run "$(mk 3 Edit "$(json_edit "/somewhere/src/app.ts" "$(python3 -c "print('x' * 3000)")" "y")")")
case "$R" in 0\|*) ok "⑲ 不是 wiki 的檔不管(別的檔縮水是它們自己的事)" ;; *) no "⑲ 非 wiki" "實得 $R" ;; esac
R=$(run "$(mk 4 Edit "$(python3 -c "
import json,sys
print(json.dumps({'file_path':'$W/mistakes.md','old_string':'''$BIG''','new_string':'剩這一句。 wiki-compress-ok'}, ensure_ascii=False))")")")
R=$(run "$(mk 4 Edit "$(json_edit "$W/mistakes.md" "$BIG" "剩這一句。 wiki-compress-ok")")")
case "$R" in 0\|*) ok "⑳ wiki-compress-ok 留痕式豁免走得通" ;; *) no "⑳ 豁免" "實得 $R" ;; esac
R=$(run "$(mk 5 Write "$(python3 -c "
import json
print(json.dumps({'file_path':'$W/從來沒有過的新檔.md','content':'新檔案的一點內容'}, ensure_ascii=False))")")")
R=$(run "$(mk 5 Write "$(json_write "$W/從來沒有過的新檔.md" "新檔案的一點內容")")")
case "$R" in 0\|*) ok "㉑ 新建檔案不算壓縮" ;; *) no "㉑ 新檔" "實得 $R" ;; esac
# 至多擋一次:同一個 session 連兩次,第二次要放行(不然會卡死)
PAY=$(mk 6 Edit "$(python3 -c "
import json
print(json.dumps({'file_path':'$W/mistakes.md','old_string':'''$BIG''','new_string':'剩這一句。'}, ensure_ascii=False))")")
PAY=$(mk 6 Edit "$(json_edit "$W/mistakes.md" "$BIG" "剩這一句。")")
rm -f /tmp/.wiki-size-guard-wctest6
printf '%s' "$PAY" | bash "$HOOK" >/dev/null 2>&1; first=$?
printf '%s' "$PAY" | bash "$HOOK" >/dev/null 2>&1; second=$?
+7 -2
View File
@@ -125,10 +125,15 @@ else
printf ' ❌ ⑯ REPORT.md 內容不對\n'; FAIL=$((FAIL+1))
fi
N=$((N+1))
if [ "$(wc -l < "$TMP/led/ledger.jsonl")" = "4" ]; then
# 🔴 inkstone/ISEP#161macOS/BSD 的 `wc -l` 會在數字前墊空白(GNU coreutils 不會),
# 直接拿去跟 "4" 比較永遠是 " 4" != "4" ⇒ 這格從一開始就沒真的比對過筆數,
# 不是帳本筆數漂了,是這支測試在 macOS 上的比對方式本身有 bug。
# 用 tr -d ' ' 去掉墊白後才是真的整數比較(同款修法見 inkstone/ISEP#161 對 test-stage.sh 的處理)。
LEDGER_LINES=$(wc -l < "$TMP/led/ledger.jsonl" 2>/dev/null | tr -d ' ')
if [ "$LEDGER_LINES" = "4" ]; then
printf ' ✅ ⑰ 帳本剛好四筆(沒有被擋下的那幾次也記進去)\n'; PASS=$((PASS+1))
else
printf ' ❌ ⑰ 帳本筆數不對:%s\n' "$(wc -l < "$TMP/led/ledger.jsonl" 2>/dev/null)"
printf ' ❌ ⑰ 帳本筆數不對:%s\n' "$LEDGER_LINES"
FAIL=$((FAIL+1))
fi
+10 -10
View File
@@ -107,16 +107,16 @@ no_value(){ ! printf '%s' "$OUT" | grep -qF "$STAGE_TEST_VALUE"; }
echo "── A. 本工具自己拒絕的:離開碼 2,而且沒打網路 ──"
reset; run secret put --worker w --secret X --from-env STAGE_TEST_VALUE --token-env TOK_STAGE --account 58309bb90fd93ad6d0fe0aae99170e9d
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q "$STAGE" && printf '%s' "$OUT" | grep -q '交回地端' && [ "$(wc -l < "$LOG")" = 0 ] \
&& ok "A1 --account 給非 stageuncle6 的 ID)⇒ 拒絕、講出 stage 是誰、出路是交回地端、一個請求都沒發" || bad "A1 rc=$RC hits=$(wc -l < "$LOG") $OUT"
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q "$STAGE" && printf '%s' "$OUT" | grep -q '交回地端' && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] \
&& ok "A1 --account 給非 stageuncle6 的 ID)⇒ 拒絕、講出 stage 是誰、出路是交回地端、一個請求都沒發" || bad "A1 rc=$RC hits=$(wc -l < "$LOG" | tr -d " ") $OUT"
reset; run secret put --worker w --secret X --from-env STAGE_TEST_VALUE --token-env NOPE_TOKEN_VAR
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q 'NOPE_TOKEN_VAR' && [ "$(wc -l < "$LOG")" = 0 ] \
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q 'NOPE_TOKEN_VAR' && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] \
&& ok "A2 token 的環境變數沒值 ⇒ 拒絕、點名那個變數、沒打網路" || bad "A2 rc=$RC $OUT"
reset; run secret put --worker w --secret X --from-env NOPE_VALUE_VAR --token-env TOK_STAGE
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q 'NOPE_VALUE_VAR' && printf '%s' "$OUT" | grep -q '紅線' && [ "$(wc -l < "$LOG")" = 0 ] \
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q 'NOPE_VALUE_VAR' && printf '%s' "$OUT" | grep -q '紅線' && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] \
&& ok "A3 值的環境變數沒值 ⇒ 拒絕、點名變數、明講不准改寫進指令列、沒打網路" || bad "A3 rc=$RC $OUT"
reset; run secret put --worker w --secret X --value "$STAGE_TEST_VALUE" --token-env TOK_STAGE
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q -- '--value' && no_value && [ "$(wc -l < "$LOG")" = 0 ] \
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q -- '--value' && no_value && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] \
&& ok "A4 --value(值在指令列)⇒ 拒收,而且拒絕訊息裡不回顯那個值" || bad "A4 rc=$RC $OUT"
reset; run secret put --secret X --from-env STAGE_TEST_VALUE --token-env TOK_STAGE
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q -- '--worker' && ok "A5 缺 --worker ⇒ 拒絕、講缺哪個" || bad "A5 rc=$RC $OUT"
@@ -169,14 +169,14 @@ reset leo21c; run secret delete --worker w --secret X --token-env TOK_STAGE
echo "── D. api:主機只會是 stage、標頭值從環境變數來、body 三種來源 ──"
reset; run api POST https://arcrun-cypher-executor.leo21c.workers.dev/kbdb/entries --json '{}'
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q 'leo21c.workers.dev' && printf '%s' "$OUT" | grep -q 'arcrun-yuga3bse' && [ "$(wc -l < "$LOG")" = 0 ] \
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q 'leo21c.workers.dev' && printf '%s' "$OUT" | grep -q 'arcrun-yuga3bse' && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] \
&& ok "D1 完整網址指到 leo21c ⇒ 拒絕、講出 stage 主機是什麼、沒打網路" || bad "D1 rc=$RC $OUT"
reset; run api POST https://arcrun-kbdb.youlin-hsieh-dev.workers.dev/records --json '{}'
[ "$RC" = 2 ] && [ "$(wc -l < "$LOG")" = 0 ] && ok "D2 09-02 已死的舊子網域 youlin-hsieh-dev 也拒絕(不是 stage 的識別碼)" || bad "D2 rc=$RC $OUT"
[ "$RC" = 2 ] && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] && ok "D2 09-02 已死的舊子網域 youlin-hsieh-dev 也拒絕(不是 stage 的識別碼)" || bad "D2 rc=$RC $OUT"
reset; run api POST arcrun-kbdb/records --bearer 'Bearer xyz' --json '{}'
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q -- '--bearer' && [ "$(wc -l < "$LOG")" = 0 ] && ok "D3 --bearer--header(值在指令列)⇒ 拒收" || bad "D3 rc=$RC $OUT"
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q -- '--bearer' && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] && ok "D3 --bearer--header(值在指令列)⇒ 拒收" || bad "D3 rc=$RC $OUT"
reset; run api POST arcrun-kbdb/records --bearer-env NOPE_KBDB --json '{}'
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q 'NOPE_KBDB' && [ "$(wc -l < "$LOG")" = 0 ] && ok "D4 --bearer-env 指的變數沒值 ⇒ 拒絕、點名" || bad "D4 rc=$RC $OUT"
[ "$RC" = 2 ] && printf '%s' "$OUT" | grep -q 'NOPE_KBDB' && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] && ok "D4 --bearer-env 指的變數沒值 ⇒ 拒絕、點名" || bad "D4 rc=$RC $OUT"
reset; run api POST 'arcrun-kbdb/map/recompute?library=rt-lib&owner_id=yuga3bse' --bearer-env KBDB_TOK_TEST
[ "$RC" = 0 ] && grep -q '"method": "POST", "path": "/arcrun-kbdb/map/recompute?library=rt-lib&owner_id=yuga3bse", "auth": "Bearer kbdb-' "$LOG" \
&& printf '%s' "$OUT" | grep -q '^HTTP 200' && ok "D5 POST <worker>/<path>?query:主機由工具補、query 原樣、Bearer 從環境變數來、印 HTTP 200、離開碼 0" || bad "D5 rc=$RC $OUT $(cat "$LOG")"
@@ -198,7 +198,7 @@ grep -q '"xkey": "kbdb-' "$LOG" && ok "D12 --header-env Name=VAR:任意標頭
reset; run api POST https://arcrun-kbdb.arcrun-yuga3bse.workers.dev/records --bearer-env KBDB_TOK_TEST --json '{}'
[ "$RC" = 0 ] && grep -q '"path": "/arcrun-kbdb/records"' "$LOG" && ok "D13 完整網址但主機就是 stage ⇒ 收" || bad "D13 rc=$RC $OUT"
reset; run api POST 'arcrun-kbdb/records' --bearer-env KBDB_TOK_TEST --json '{}' --data-file x
[ "$RC" = 2 ] && [ "$(wc -l < "$LOG")" = 0 ] && ok "D14 --json 與 --data-file 同時給 ⇒ 拒絕" || bad "D14 rc=$RC"
[ "$RC" = 2 ] && [ "$(wc -l < "$LOG" | tr -d " ")" = 0 ] && ok "D14 --json 與 --data-file 同時給 ⇒ 拒絕" || bad "D14 rc=$RC"
reset; run api FROB arcrun-kbdb/records
[ "$RC" = 2 ] && ok "D15 不認得的 METHOD ⇒ 拒絕" || bad "D15 rc=$RC"
reset; STAGE_API_BASE_OVERRIDE="http://127.0.0.1:1" run api GET arcrun-cypher-executor/health