Files
ISEP/system-dev/wiki/mistakes.md
T
claude-code e0557334bc 閘掛在「收工後」,而 leo 是在「被問到」的當下被打擾——補上那一刻的攔截
leo 2026-08-26:「今天已經好幾次問我,為什麼 hooks 沒有攔下來?」

實查:總管問 leo 走的動作是 AskUserQuestion,而 hooks.json 裡它出現 0 次,
沒有任何 matcher。判準其實早就寫好了——self-drive-police / self-drive-judge
用的就是同一套四題公式——但那兩支只掛在 Stop 與 SubagentStop,
是回合結束後才跑的。問題早就送到他眼前了,事後再反問「你查過了嗎」,
成本已經轉嫁出去。判準對了,時機錯了。

新增 hooks/ask-user-question-guard.sh(PreToolUse / AskUserQuestion):
- 觸發條件是那個動作本身,不是任何句型或關鍵字——全檔零個判擋用的正則,
  換句話說閃不過去,講得謹慎也不會被多罰(leo 2026-08-17 文字層封路的檢驗)
- 進來之後用四題公式的 haiku 判官分「該問 / 不該問」,命中任一題一律放行
- 同一個問題只擋一次(雜湊戳記):判官誤判時重送即過,
  leo 該收到的問題不會因為一支閘而永遠送不到
- 判官掛掉/沒網路/claude 不在 PATH 一律 fail-open,壞掉等於它不存在

版本 0.3.9 → 0.4.0。這不是儀式,是傳輸機制本身:產物按版本號分資料夾
(~/.claude/plugins/cache/inkstone/isep/<版本>/),版本沒動就不會長出新資料夾,
這支閘一個 session 都載入不到。docs/TESTING.md 開頭那段講的就是這件事,
而第一版我漏了——總管複驗時量出來的。

假設(沒有前例可循,先裁再記):跳 0.4.0 而不是 0.3.10。理由是這一版第一次
掛上 AskUserQuestion 這個事件面,是新能力不是修補;而且 0.3.10 在
plugin 快取目錄的 ls 裡會排到 0.3.1 旁邊,肉眼不好認。錯了打回,改號很便宜。

順手修掉自己寫出來的一個坑:訊息原本用沒加引號的 heredoc,
反引號被當命令執行,wiki 路徑與豁免指令兩行變成空白(閘照擋,只看離開碼看不出來)。
已收成 mistakes.md 一條,並由 ⑩b 這條測試守著。

實測:
- hooks/tests/ask-user-question-guard.test.sh      14/14(離線,不花錢)
- hooks/tests/ask-user-question-guard.live.test.sh 9/9 連跑三次(真的叫 haiku)
  A 群 5 條真人閘(花錢/不可逆/跨專案結構/品味方向/物理人閘)全部放行,誤攔 0
  B 群 4 條純技術路徑選擇全部擋下
- 既有 7 支測試與改動前逐條對照,結果完全相同(沒有被我弄壞)

順手對帳:plugin.json 與 marketplace.json 的描述寫「43 支機械閘、53 條註冊」,
實際數過是 46/56(含本次新增這支)。docs/hooks-inventory.md 一併更正。

【工單】inkstone/InkStoneCo#55
2026-08-26 22:36:08 +08:00

5.2 KiB
Raw Blame History

已知誤解 / 踩過的坑

這是 ISEP 自己的坑,不是 InkStoneCo 的(不轉抄,複製即 fork,fork 即漂移)。 撞到新坑就 append 一條;session 開場只推最近幾條標題(見 hooks/session-start-recall.sh push 5/5),全文在這裡。

⚠️ MISTAKE: 「環境設定」曾經拆成兩份,各自會漂 症狀: 本機在跑 InkStoneCo/.claude/(真身),雲端跑的是 generate-shell-payload.py 另外產生塞進 GitHub 私 repo 的一份(薄殼)。inkstone/InkStoneCo#57 實測:薄殼比真身 少 7 支閘,其中兩支是前一天才立的;#14 更早查到雲端 33 支 guard 一支都沒生效。 正確做法: 只留一份——ISEP 這個 repo 本身就是唯一真相源,本機與雲端裝同一個 plugin。 改動一律只改這裡,然後兩邊各自 /plugin update。不要再改 InkStoneCo/.claude/hooks/(退場中,早晚會刪)。 原因: 「一份東西兩個副本」在沒有機制強制同步的情況下必然漂移——差異不是誰疏忽, 是結構本身允許漂移發生。 日期: 2026-08-20ISEP c263866 建立時就是為了解這個病)

⚠️ MISTAKE: hook 路徑寫死會在雲端斷 症狀: 舊版 hook 若用 $CLAUDE_PROJECT_DIR/.claude/hooks/... 或寫死的絕對路徑指向 hook 腳本自己,雲端執行時的 cwd 不是本機那個真身目錄,路徑就對不到、hook 直接失效 (正是上一條「雲端 33 支 guard 一支都沒生效」的根因)。 正確做法: hook 指自己(找到自己在哪、要 source 的其他 hook 檔)一律用官方 ${CLAUDE_PLUGIN_ROOT}。腳本內部要指專案裡的檔案(如 system-dev/wiki/system-dev/docs/)才用 $CLAUDE_PROJECT_DIR——那些檔案本來就该住在被操作的 那個 repo 裡,跟 hook 自己的路徑是两回事,别混。 原因: 兩種路徑指的是完全不同的東西(「plugin 安裝到哪」vs「正在操作哪個專案」), 混用就是這條坑的直接原因。 日期: 2026-08-20README「路徑規約」段記錄,ISEP 0.1.0 把 51 條 hook 路徑全部改過一輪)

⚠️ MISTAKE: 「裝好了」不等於「它在跑」

2026-08-20 實查:ISEP repo 建好、README 寫著 0.1.0、41 支閘都在裡面—— 但 claude plugin list根本沒有 ISEP。本機仍然在跑 InkStoneCo/.claude/ Gitea 上一個 release tag 都沒有

⇒ 「東西做出來了」與「有人在用它」是兩件事,而只有後者算交付。 ⇒ 判準:去執行環境查它有沒有被載入claude plugin list / claude plugin details), 不要從 repo 裡有什麼檔案去推論。

⚠️ MISTAKE: 判準寫在閘裡了,但那個閘掛在做完之後才跑的時機上

票: inkstone/InkStoneCo#55 日期: 2026-08-26

症狀: leo 一天內好幾次被丟純技術路徑選擇,當場問「今天已經好幾次問我, 為什麼 hooks 沒有攔下來?」,其中一次他直接說「這種問題不要問我, 我要的是你解決了以後給我 prod」。

實查: 總管問 leo 走的動作是 AskUserQuestion 這個工具,而 hooks.jsonAskUserQuestion 出現 0 次——沒有任何 matcher,它是裸的。 判準其實早就寫好了(self-drive-police.sh / self-drive-judge.sh 用的就是四題公式), 但那兩支只掛在 StopSubagentStop

原因: 判準對了,時機錯了。 Stop 是回合結束後才跑——問題早就送到 leo 眼前、 他早就被打斷了,這時再反問 AI「你查過了嗎」,成本已經轉嫁出去了。

正確做法: 攔截點要長在那個動作發生的那一刻PreToolUse / AskUserQuestion)。 新增 hooks/ask-user-question-guard.sh🔴 推廣:以後看到「規則寫了卻沒被攔下來」,先問的不是「判準對不對」, 而是「這支閘掛在哪個事件上、那個事件發生時傷害造成了沒有」。

⚠️ MISTAKE: hook 訊息用沒加引號的 heredoc,反引號會被當成命令執行

票: inkstone/InkStoneCo#55 日期: 2026-08-26

症狀: ask-user-question-guard.sh 擋下之後,stderr 冒出 line 218: system-dev/wiki/: is a directory,而訊息裡 「去查 system-dev/wiki/」和「touch /tmp/.ask-ok-<session_id>」兩行 變成空白。閘照擋 exit 2,所以測試若只看離開碼完全看不出來

原因: 寫成 cat >&2 <<EOF(heredoc 標記沒加引號)⇒ shell 會對內容做展開, 而本 repo 的 hook 訊息慣例上大量使用反引號標路徑與指令 ⇒ 每一組反引號都被當成命令替換真的去執行。

正確做法: hook 的訊息一律用 cat <<'EOF'(標記加單引號)。 需要塞變數就留 __PLACEHOLDER__,事後用 python 換掉—— 不要用 sed,正體中文加上訊息裡的 /&\ 讓跳脫非常脆。 迴歸測試要檢查訊息內容,不能只檢查離開碼 hooks/tests/ask-user-question-guard.test.sh 的 ⑩b 就是這一條)。