From a671948fa56567906c12d8d521f05d3e834da193 Mon Sep 17 00:00:00 2001 From: richblack Date: Thu, 27 Aug 2026 18:24:20 +0800 Subject: [PATCH 1/2] =?UTF-8?q?docs(inventory):=20=E8=A3=9C=E5=9B=9E?= =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E6=BC=8F=E6=8E=89=E7=9A=84=E5=85=A9=E5=88=97?= =?UTF-8?q?=E4=BA=BA=E8=A9=B1=EF=BC=8C=E4=B8=A6=E8=A3=9C=E4=B8=8A=E9=A9=97?= =?UTF-8?q?=E5=BE=97=E5=87=BA=E9=80=99=E7=A8=AE=E6=BC=8F=E7=9A=84=E9=82=A3?= =?UTF-8?q?=E7=B5=84=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 標頭寫 48 支,下面的表格只列得出 46 支——缺的是 `isep-presence-beacon.sh`(E 組 SessionStart)與 `milestone-due-guard.sh`(A 組 PreToolUse/Bash)這兩列。 不是本次合併造成的,`origin/main` 上數同一個指令也是這兩支。 為什麼這個漏會活這麼久:本頁「落差偵測」只有一組 comm,比的是 `hooks.json`(驗有沒有註冊),驗不了「有沒有寫進這張人話表」。 所以本次同時補上第二組 comm(fs 檔名 vs 表格列出的閘名), 並把「怎麼跟實況對帳」第 1 條指到那一組。 順手修掉同一頁另外兩處自己會騙人的地方: - 標頭的複驗指令 `grep -c '"command"'` 會連 `"type": "command"` 一起數, 實跑回 118 不是 59;正確寫法要帶冒號。 - 「其餘 41 支」是 43 支閘那一版數的。改成拿掉數字而不是改成 46—— 改成 46 等於宣稱逐支重讀過源碼,而這次沒有。 版本 0.6.0 → 0.6.1(v0.6.0 已出 release,不升版沒有人吃得到這頁)。 實測:48 檔 vs 48 列,兩個方向的 comm 都空; 18 個測試檔全綠(0 失敗);claude plugin validate 通過。 inkstone/ISEP#59(comment 4779 第二件) --- .claude-plugin/plugin.json | 2 +- docs/hooks-inventory.md | 39 +++++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 36502c4..05acc48 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "isep", "description": "InkStone Environment Plugin —— leo 的 Claude Code 環境唯一真相源:48 支機械閘(59 條註冊,白話盤點見 docs/hooks-inventory.md)、7 支 slash command、2 支 skill、27 支腳本,外加治理規範與標籤真相源。本機與雲端裝同一份,沒有子集。", - "version": "0.6.0", + "version": "0.6.1", "keywords": [ "inkstone", "guardrails", diff --git a/docs/hooks-inventory.md b/docs/hooks-inventory.md index bb79d79..87cb217 100644 --- a/docs/hooks-inventory.md +++ b/docs/hooks-inventory.md @@ -14,7 +14,9 @@ > 🔴 **這兩個數字上一版是錯的(2026-08-26 實際數過才發現)**:本頁原本寫「43 個檔、53 條註冊」, > 而當時真實是 **45 個檔、55 條註冊**——中間有兩支閘進來時沒有回頭改這裡。 > 現在的寫法是實際數出來的: -> `ls hooks/*.sh | wc -l` = 48;`grep -c '"command"' hooks/hooks.json` = 59。 +> `ls hooks/*.sh | wc -l` = 48;`grep -c '"command":' hooks/hooks.json` = 59。 +> ⚠️ **冒號不能省**:`grep -c '"command"'`(沒冒號)會連 `"type": "command"` 一起數到,回 **118**。 +> 本頁 2026-08-27 之前寫的是沒冒號那版——**照著它跑會拿到一個跟本頁不符的數字**。 > **一份會偷偷過期的盤點表,跟沒有盤點表差不多**——見本頁最後「怎麼跟實況對帳」那段。 > > 📌 **`0.6.0`(`inkstone/ISEP#59`,2026-08-27)這兩個數字是併完之後當場數出來的**: @@ -23,6 +25,10 @@ > 加上 `ISEP#72` 的 `reply-identity-guard.sh` 已在 main 上。 > 一加一減之後檔數不變、註冊條數落在 59——**這正是「不能靠加減推算、只能實數」的例子**: > 上一版寫 58,就是把兩批改動的加減兜出來、沒有真的數過。 +> +> 🔴 **`0.6.1`(2026-08-27)補回表格漏掉的兩列**:`isep-presence-beacon.sh`(E 組)、 +> `milestone-due-guard.sh`(A 組)。**標頭寫 48 支的同時,下面的表格只列得出 46 支** +> ——數字是對的,人話少了兩行。發現的方法與防它再犯的指令,見下面「落差偵測」第二組 `comm`。 --- @@ -51,6 +57,7 @@ | `leo21c-write-guard.sh` | AI 想寫東西到你私人帳號 `leo21c` 就擋下——這個帳號只准你自己用,AI 的實驗一律走別的測試帳號。 | 🛑 擋 | | `release-tag-guard.sh` | AI 想打版本 tag,但 `plugin.json` 裡寫的版本號跟 tag 對不上就擋下——避免「這裡說 0.2.0、那裡說 0.1.0」各說各話。 | 🛑 擋 | | `ticket-api-bypass-guard.sh` | AI 想繞過 `scripts/ticket` 工具、直接打 API 新增 Gitea 東西(開新票/milestone/label/PR,會漏掉「新增前先搜過」這道檢查)就擋下——不管是隱式 POST(urllib 傳 data= 沒寫 method)還是小寫 `requests.post(...)`。 | 🛑 擋 | +| `milestone-due-guard.sh` | AI 想開一個新的里程碑(milestone)卻沒填期限就擋下,**填 `9999-01-01` 一樣擋**——那是「沒有期限」穿了一件期限的衣服,盤點時每一格看起來都有值,其實一格都沒有。只管「建里程碑」這個動作,查詢/改別的欄位都放行。 | 🛑 擋 | | `reply-identity-guard.sh` | 直接打 Gitea API 貼留言到票上時,內文第一行沒有 `【身份】`(`總管`/`subagent`/`leo`)就擋下——票上多條線並行時,看不出哪一則是誰寫的。純讀取、走 `scripts/ticket` 正門、指令裡加 `reply-identity-ok` 都放行。 | 🛑 擋 | | `comment-carries-task-guard.sh` | AI 要在票裡留一則「等某某上線才驗得了」這種**還沒做完的事**,卻沒有把它開成一張子票時,擋一次。留言沒人會回頭讀,票的 open/closed 撈一次就看得到。 | 🛑 擋(同一輪只擋一次) | @@ -103,6 +110,7 @@ | 閘名 | 對你意味著什麼 | 動作 | |---|---|---| +| `isep-presence-beacon.sh` | 對話一開始印一行 `🟢 ISEP vX.Y.Z 已載入(N 支閘|來源:…)`。**這行不是裝飾,是唯一能證明「這個 session 真的有閘」的東西**——它自己就住在 plugin 裡,看得到它就表示 plugin 載入了;某個 session 從頭到尾沒有這行,那個 session 是零閘狀態,先修 plugin 再做事。同一台機器可能同時有兩份 ISEP(marketplace 裝的、repo 裡 vendor 的),所以那行會講出這次是哪一份在說話。 | 📝 記錄(context 注入) | | `session-start-recall.sh` | 對話一開始就自動把「全局現況」(Gitea 各 repo 的票、KBDB 的藏書地圖)推到 AI 眼前,不必等它自己想到要查。 | 📝 記錄(context 注入) | | `skill-deploy-drift-guard.sh` | 如果「全機真正在用的 skill」跟「repo 裡版控的正本」內容對不上,就在開場講出來——避免用著一份沒人知道已經跟正本分家的舊拷貝。 | 📝 記錄 | @@ -177,6 +185,27 @@ $ comm -13 /tmp/fs_hooks.txt /tmp/registered_hooks.txt (無輸出) ``` +🔴 **上面那兩行驗不出「表格漏列」**——它們比的是 `hooks.json`,不是這張表。 +「有閘、但這頁沒有它那一行人話」要另外一組指令才看得見,就是下面這組 +(2026-08-27 就是靠它抓到本頁漏了 `isep-presence-beacon.sh`/`milestone-due-guard.sh` 兩列): + +``` +$ ls hooks/*.sh | xargs -n1 basename | sort > /tmp/inv_fs.txt +$ grep -oE '^\| `[a-zA-Z0-9_.-]+\.sh`' docs/hooks-inventory.md \ + | grep -oE '[a-zA-Z0-9_.-]+\.sh' | sort -u > /tmp/inv_table.txt + +# 有這支閘、但這頁沒有它那一列 → 要補人話 +$ comm -23 /tmp/inv_fs.txt /tmp/inv_table.txt +(無輸出) + +# 這頁列了、但檔案已經不在了 → 要刪那一列 +$ comm -13 /tmp/inv_fs.txt /tmp/inv_table.txt +(無輸出) +``` + +📌 **標頭的 48/59 是對的,錯的是表格**——這正是這頁最容易壞的方式: +數字改了,人話沒補。**數字對不代表這頁沒漏**,兩組指令都要跑。 + **這次順便抓到的另一個落差**:`.claude-plugin/plugin.json` 的說明文字寫「42 支機械閘(52 條註冊)」, `README.md` 也寫「42 支 + `hooks.json`」「共 52 條註冊」——但實測是 **43 支檔案、53 條註冊**。 差 1 支、差 1 條,猜測是今天(2026-08-20)新增的 `leo21c-write-guard.sh`/`release-tag-guard.sh`/ @@ -232,12 +261,16 @@ $ comm -13 /tmp/fs_hooks.txt /tmp/registered_hooks.txt - **`micromanage-guard.sh`/`irreversible-dispatch-guard.sh`/`no-ticket-no-dispatch.sh` 的雙重矩比對(`Task` + `Agent`)是刻意保險還是舊名稱沒清掉**——見上面「重複掛載」段落,我沒有把握單靠讀檔案判斷,需要看實際觸發紀錄或問總管。 - **`stage-before-prod-guard.sh` 跟 `main-and-prod-push-guard.sh` 的分工邊界**:`main-and-prod-push-guard.sh` 的檔頭明講自己是在「補 `stage-before-prod-guard.sh` 的破口」(那支只認 3 個關鍵字,抓不到 `wrangler deploy`),但兩支都還掛著、都還在管「prod 出貨」這件事。這是「新的補洞、舊的continua」還是「舊的該退休了」,這份盤點表不下判斷,留給你在下一步的分類會議裡定奪。 -除了以上兩點,其餘 41 支的行為都能從檔頭與源碼直接讀出,沒有「猜」的部分。 +除了以上兩點,其餘各支的行為都能從檔頭與源碼直接讀出,沒有「猜」的部分。 + +> 📌 這句話原本寫「其餘 **41** 支」——那是 43 支閘那一版(`v0.2.1`)數的,之後沒有人重跑過。 +> 2026-08-27 把數字拿掉而不是改成 46:**改成 46 等於宣稱我逐支重讀過源碼,而我沒有。** +> 要恢復一個數字,就重跑一次上面「抽驗」那件事,然後把當天的日期寫上去。 --- ## 這份表怎麼跟實況對帳(半年後怎麼發現漂移) -1. **有沒有新閘沒被收進這張表**:跑本文「落差偵測」段落的兩行 `comm` 指令,比對 `hooks/*.sh` 的檔名清單跟這張表列出的閘名清單(不是跟 `hooks.json`,那個只驗證有沒有註冊,驗不了有沒有寫進這張人話表)。 +1. **有沒有新閘沒被收進這張表**:跑本文「落差偵測」段落**第二組** `comm` 指令(`inv_fs` vs `inv_table`),比對 `hooks/*.sh` 的檔名清單跟這張表列出的閘名清單。**不是第一組**——第一組比的是 `hooks.json`,只驗證有沒有註冊,驗不了有沒有寫進這張人話表。2026-08-27 漏掉的那兩列,第一組是綠的、第二組才紅。 2. **有沒有閘的行為跟這裡寫的不一樣了**:抽幾支重新讀一次源碼,跟這裡的「一句話」對一遍——就是本文「抽驗 5 支」做的事,可以照同樣方法定期重做。 3. **`.claude-plugin/plugin.json` 與 `README.md` 的數字**:這兩處各自寫了一次「幾支、幾條註冊」,前面已經抓到一次對不上(42/52 vs 實際 43/53)。這兩個數字沒有機制保證跟著 `hooks/` 目錄自動更新,是本表發現的第一個具體漂移案例。 -- 2.52.0 From fa2f76177e69c19d42eb7b972f0556d9728af983 Mon Sep 17 00:00:00 2001 From: richblack Date: Thu, 27 Aug 2026 18:35:49 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs(inventory):=20=E5=8F=8D=E5=90=91?= =?UTF-8?q?=E9=82=A3=E5=8D=8A=E8=A3=9C=E4=B8=8A=E7=9C=9F=E6=AD=A3=E9=A9=97?= =?UTF-8?q?=E5=BE=97=E5=88=B0=E5=B9=BD=E9=9D=88=E7=9A=84=E9=82=A3=E4=B8=80?= =?UTF-8?q?=E6=A0=BC=EF=BC=88=E7=B8=BD=E7=AE=A1=E8=A4=87=E9=A9=97=E9=80=80?= =?UTF-8?q?=E5=9B=9E=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 總管複驗指出反向 comm 有 3 個輸出。實跑重現了,但逐個查過之後: **三個都不是幽靈,三個都不該刪。** claim-verify-police.sh / subagent-claim-worksheet.sh → 全部 5 處都在內文,講的是「已於 ISEP#60 移除」。表格列(第一欄) 命中數 = 0。刪掉它們等於刪掉「G 組為什麼是空的」這段歷史。 github-arm.sh → 它存在,在 scripts/ 不在 hooks/;出現在 github-contact-guard 那一列的 描述裡,是告訴人「怎麼解鎖」的逃生口。刪掉那一列就不再說得出怎麼過閘。 成因是那組 grep 拿掉了 `^\| \`` 錨點。有錨點=「這一行的第一欄就是這支閘」= 表格真的列了它;沒錨點=全頁所有反引號。**0 次正確攔截、3 次誤攔。** 但「頁面上出現根本不存在的檔名」確實該被抓到,只是不能靠文字判斷。 本次補上第三格 C:頁面提到的每個 *.sh,要嘛現在存在,要嘛 git 歷史裡存在過。 **「現在沒有但曾經有」=歷史;「從來沒有過」=幽靈。** 它問的是 git 不是用字。 順帶:標頭區那個 43 是 2026-08-20 快照段落裡的歷史數字,不是現況宣稱, 但原文用現在式讀起來像現況 ⇒ 加上「這一段每個數字都是那天的」標記。 實測(含兩個反向對照,證明新那格不是橡皮圖章): 現況 A1[] A2[] C[] 48 檔 / 48 列 表格種一支假閘 A2 與 C 都叫 散文種一支假閘 A1/A2 抓不到、C 抓到 ← 只有 C 守得住這種 18 個測試檔 0 失敗;claude plugin validate 通過。 inkstone/ISEP#75(總管複驗 comment 4822) --- docs/hooks-inventory.md | 42 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/hooks-inventory.md b/docs/hooks-inventory.md index 87cb217..991e3b0 100644 --- a/docs/hooks-inventory.md +++ b/docs/hooks-inventory.md @@ -203,10 +203,44 @@ $ comm -13 /tmp/inv_fs.txt /tmp/inv_table.txt (無輸出) ``` -📌 **標頭的 48/59 是對的,錯的是表格**——這正是這頁最容易壞的方式: -數字改了,人話沒補。**數字對不代表這頁沒漏**,兩組指令都要跑。 +🔴 **`^\| \`` 這個錨點不能拿掉**——它的意思是「**這一行的第一欄就是這支閘**」, +也就是「表格真的列了它」。把錨點拿掉、改成抓全頁所有反引號裡的 `*.sh`, +反向那半會吐出 **3 個假警報**(2026-08-27 實跑): -**這次順便抓到的另一個落差**:`.claude-plugin/plugin.json` 的說明文字寫「42 支機械閘(52 條註冊)」, +``` +claim-verify-police.sh ← 出現在「已於 ISEP#60 移除」那段**移除紀錄**裡 +subagent-claim-worksheet.sh ← 同上 +github-arm.sh ← 它在 `scripts/`,不在 `hooks/`;是 github-contact-guard + 那一列裡告訴你「怎麼解鎖」的逃生口 +``` + +**三個都不是幽靈,三個都不該刪**:刪掉前兩個等於刪掉「G 組為什麼是空的」這段歷史, +刪掉第三個等於讓那一列不再告訴你怎麼過閘。 +⇒ **0 次正確攔截、3 次誤攔**——照這個訊號去改,會把好東西改壞。 + +那「頁面上出現了一個根本不存在的檔名」怎麼辦?**那要另外一格量,而且判準不是文字是事實**: + +``` +# C. 頁面提到的每個 *.sh,現在存在,或者 git 歷史裡存在過 +$ for n in $(grep -oE '`[a-zA-Z0-9._-]+\.sh`' docs/hooks-inventory.md | tr -d '`' | sort -u); do + [ -f "hooks/$n" ] || [ -f "scripts/$n" ] && continue + [ -n "$(git log --all --oneline --diff-filter=AD -- "hooks/$n" "scripts/$n")" ] && continue + echo "$n" + done +(無輸出) +``` + +**「現在沒有、但曾經有」=歷史,合法;「從來沒有過」=打錯字或憑空捏造,才是幽靈。** +這一格問的是 git,不是問這頁的用字,所以它不會因為誰換個寫法就失效。 + +📌 **三格都要跑,而且它們量的是不同東西**: +A1 缺人話、A2 表格有幽靈、C 全頁有幽靈。 +**標頭的 48/59 對不代表這頁沒漏**——補進 2 支真的、同時留著 2 支假的,數字照樣湊得到 48。 +**數量相等不等於集合相等**,所以驗的一律是集合(`comm`),不是 `wc -l`。 + +**這次順便抓到的另一個落差**(🕐 **這一段是 2026-08-20 的快照,裡面每個數字都是那天的,不是現況**—— +留著是因為它是「兩份文件的數字不會自己跟上」這個病的第一個案例;現況數字看本頁標頭): +`.claude-plugin/plugin.json` 的說明文字寫「42 支機械閘(52 條註冊)」, `README.md` 也寫「42 支 + `hooks.json`」「共 52 條註冊」——但實測是 **43 支檔案、53 條註冊**。 差 1 支、差 1 條,猜測是今天(2026-08-20)新增的 `leo21c-write-guard.sh`/`release-tag-guard.sh`/ `ticket-api-bypass-guard.sh` 這批(檔頭日期都是今天)加了之後,兩份文件的數字沒有跟著更新。 @@ -271,6 +305,6 @@ $ comm -13 /tmp/inv_fs.txt /tmp/inv_table.txt ## 這份表怎麼跟實況對帳(半年後怎麼發現漂移) -1. **有沒有新閘沒被收進這張表**:跑本文「落差偵測」段落**第二組** `comm` 指令(`inv_fs` vs `inv_table`),比對 `hooks/*.sh` 的檔名清單跟這張表列出的閘名清單。**不是第一組**——第一組比的是 `hooks.json`,只驗證有沒有註冊,驗不了有沒有寫進這張人話表。2026-08-27 漏掉的那兩列,第一組是綠的、第二組才紅。 +1. **有沒有新閘沒被收進這張表**:跑本文「落差偵測」段落**第二組**(`inv_fs` vs `inv_table` 的兩行 `comm`)**加第三格 C**,比對 `hooks/*.sh` 的檔名清單跟這張表列出的閘名清單。**不是第一組**——第一組比的是 `hooks.json`,只驗證有沒有註冊,驗不了有沒有寫進這張人話表。2026-08-27 漏掉的那兩列,第一組是綠的、第二組才紅。**抓表格列的那組 `grep` 一定要帶 `^\| \`` 錨點**,理由與拿掉之後會冒出的 3 個假警報,就寫在那一段裡。 2. **有沒有閘的行為跟這裡寫的不一樣了**:抽幾支重新讀一次源碼,跟這裡的「一句話」對一遍——就是本文「抽驗 5 支」做的事,可以照同樣方法定期重做。 3. **`.claude-plugin/plugin.json` 與 `README.md` 的數字**:這兩處各自寫了一次「幾支、幾條註冊」,前面已經抓到一次對不上(42/52 vs 實際 43/53)。這兩個數字沒有機制保證跟著 `hooks/` 目錄自動更新,是本表發現的第一個具體漂移案例。 -- 2.52.0