修好一次性戳記恆擋:InkStoneCo 專案版 hooks 與 plugin 雙掛載
inkstone/ISEP#122:leo 三次要部署被 prod-write-guard.sh 恆擋,蓋了戳記 也放不了行。根因不在 prod-write-guard.sh 本身,而在 InkStoneCo/.claude/settings.json 還登記著一份 2026-08-20 立 ISEP plugin 之前的舊 hook,跟 plugin 自己的 hooks.json 同時掛在同一個事件上——每個 PreToolUse 都跑了兩次,一次性戳記被第一份消耗掉,第二份永遠看到空戳記。 實查(在這台機器的樹上實數):InkStoneCo 專案版登記著 38 支 hook, 34 支跟 plugin 完全同名同用途(純殘骸)、3 支是已退役機制 (claim-verify-police.sh/subagent-claim-worksheet.sh/sdd-guard.sh, ISEP#60/#91 早已裁定退役但專案版沒跟著退)、1 支(kv-write-guard.sh) 是專案版有、plugin 當時沒有的真閘。 這次: - kv-write-guard.sh 原樣搬進本 repo(B 組,PreToolUse Write|Edit|MultiEdit), 補齊那個真的缺口,附 8 條迴歸測試 - 新增 duplicate-hook-registration-guard.sh(E 組,SessionStart): 往後任何專案的 settings.json 又跟 plugin 長出同名登記,開場就點名, 不必再靠「戳記莫名其妙失效」才發現,附 8 條迴歸測試 - 兩支新閘:61→63 支、84→86 條註冊(在自己的樹上實數,不是加減推) InkStoneCo 那邊的清理(37 支殘骸從 settings.json 與 .claude/hooks/ 移除, kv-write-guard.sh 保留至本次升版並 /plugin update 之後)另外commit, 不在本 repo 範圍內。 驗證:重演過修好前的雙掛載(蓋一次戳記→專案版先吃掉→plugin 版恆擋, EXIT=2),也驗過修好後單次執行放行(EXIT=0)與不蓋戳記仍被擋(EXIT=2)。 待總管定版。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -605,6 +605,55 @@ B 段拿這台機器真正的 `.env` 對帳——**`.env` 不在的機器(雲
|
||||
> ```
|
||||
> ⇒ 第二條正是黑名單會放過去的形狀。
|
||||
|
||||
### A27 — 一次性戳記真的能放行一次:重複掛載清乾淨了,也不會靜靜復發(`inkstone/ISEP#122`)
|
||||
```
|
||||
bash hooks/tests/duplicate-hook-registration-guard.test.sh
|
||||
bash hooks/tests/kv-write-guard.test.sh
|
||||
```
|
||||
**該看到**:兩支各自 `8 通過 / 0 失敗`。全程用假的 plugin root/專案目錄,
|
||||
不碰真正的 `InkStoneCo/.claude/` 或這台機器裝著的 ISEP plugin。
|
||||
|
||||
**它們在守什麼**(2026-09-01 實撞):總管三次要部署 uncle6.me,三次被
|
||||
`prod-write-guard.sh` 恆擋——蓋了戳記、確認戳記還在,執行動作時照樣被擋,
|
||||
再看戳記已經不見。根因:`InkStoneCo/.claude/settings.json` 還登記著一份
|
||||
2026-08-20 立 ISEP plugin 之前的舊 hook(那時 `.claude/hooks/` 是唯一的家),
|
||||
跟 plugin 自己的 `hooks.json` 同時掛在同一個 `PreToolUse` 事件上——每個動作
|
||||
都跑了**兩次**,一次由專案版、一次由 plugin。多數閘只是白跑一次沒感覺,
|
||||
但一次性戳記(蓋了就消耗掉)被第一份吃光,第二份永遠看到空戳記,於是恆擋。
|
||||
|
||||
**實查結果**(在這台機器的樹上實數,不是推算):`InkStoneCo/.claude/settings.json`
|
||||
登記著 38 支專案版 hook——**34 支跟 plugin 完全同名同用途**(純殘骸)、
|
||||
**3 支是已經退役的機制**(`claim-verify-police.sh`/`subagent-claim-worksheet.sh`/
|
||||
`sdd-guard.sh`,`inkstone/ISEP#60`/`#91` 早已裁定退役,但專案版沒有跟著退),
|
||||
**1 支(`kv-write-guard.sh`)是專案版有、plugin 當時沒有的真閘**(2026-08-25
|
||||
立,長效資料不准寫進 KV)。前 37 支已從 `InkStoneCo/.claude/settings.json`
|
||||
與 `.claude/hooks/` 刪除;第 38 支原樣搬進本 repo(同一輪 commit,見 B 組)。
|
||||
|
||||
**失敗**:
|
||||
- `kv-write-guard.test.sh` 任一「該擋」紅 ⇒ 搬過來時邏輯改壞了,
|
||||
KV 寫入的偵測失靈
|
||||
- `kv-write-guard.test.sh` 任一「不該擋」紅 ⇒ 誤攔,這比漏擋嚴重——
|
||||
正常的 `.get`/退休戰的刪除/`kv-ok` 豁免都要放得過
|
||||
- `duplicate-hook-registration-guard.test.sh` 的「該吵」任一紅 ⇒
|
||||
重複掛載又會靜靜發生,**下一次會是另一支帶戳記語意的閘恆擋,
|
||||
而且要等 leo 自己撞到才會被發現**
|
||||
- `duplicate-hook-registration-guard.test.sh` 的「不該吵」任一紅 ⇒
|
||||
誤攔,每個 session 開場都吵一次沒問題的環境,最後被學會忽略
|
||||
|
||||
### A28 — 收工前的實測:蓋一次戳記真的只用得掉一次(手動,這台機器)
|
||||
這一格驗的是**行為**,不是**檔案**——上面兩支測試證明「新機制自己邏輯對」,
|
||||
這格證明「舊的重複掛載真的被拆掉了」。在**新開**的 session 裡跑(PreToolUse hook
|
||||
是 session 啟動時載入的常駐清單,同一支閘改完不會讓已經在跑的 session 重新讀):
|
||||
```
|
||||
bash "$CLAUDE_PLUGIN_ROOT/scripts/gate-ok" prod-write # 蓋一次戳記
|
||||
# 執行一個會被 prod-write-guard.sh 擋的動作(例如 wrangler pages deploy 打非白名單目標)
|
||||
```
|
||||
**該看到**:蓋戳記那個會被擋的動作**這次放行**(① 蓋一次戳記 → ② 動作通過);
|
||||
不蓋戳記重跑同一個動作 → **仍然被擋**(證明沒有把閘弄鬆,只是不再被自己的複本吃掉)。
|
||||
**失敗**:蓋了戳記仍被擋 ⇒ 還有第三份複本沒清乾淨,去跑
|
||||
`bash "$CLAUDE_PLUGIN_ROOT/hooks/duplicate-hook-registration-guard.sh"`
|
||||
(或直接開一個新 session 看它開場有沒有吵)先定位是哪一份。
|
||||
|
||||
### A5 — 開票前的搜尋是跨 repo 的
|
||||
```
|
||||
python3 scripts/ticket where 標籤 模組化
|
||||
|
||||
Reference in New Issue
Block a user