把 main(v0.15.0,派工紀律三件 #105)併進來,逐支點名複驗過 hooks.json

## hooks.json:這次 git 自動合成功了,而那正是最危險的情況

`hooks.json` 是 JSON——**合錯了語法照樣合法、閘卻不見了,不會有任何東西喊一聲。**
所以「沒有衝突」不等於「沒掉東西」,一律再做兩件事:

① 逐支點名(指令已寫進 README「裝什麼」那段)→ 82 條,60 支各自幾條全部列出
② 跟**兩個父節點**做集合對照:

    merged 82 = main 79 + 我的 3
    從 main 掉了:0 條
    從我這邊掉了:0 條
    兩邊都沒有卻冒出來的:0 條

`#105` 三支與我的兩支同時在,位置也對:

    roster-guard.sh              PreToolUse  Agent / Task
    diagnosis-evidence-guard.sh  PreToolUse  Bash
    investigate-first-stamp.sh   PostToolUse Agent|Task / SendMessage
    overdue-nag-guard.sh         SessionStart
    wiki-size-guard.sh           SessionStart / PreToolUse Write|Edit|MultiEdit

(總管提醒的 `roster-guard` vs `mainline-focus-guard` 那種「同一陣列位置兩邊各一支」
 這次沒有再發生衝突,但兩支都在,各 2 條。)

## 三處文字衝突:兩邊都留,數字重數

- plugin.json     版本吃 main 的 **0.15.0**(總管已定版,**我沒有動它**);
                  描述欄補上 main 新增的「7 位有名字的工人」,三個數字重數
- README.md       吃 main 的新表格結構(多了 agents/commands/skills/scripts 四列),
                  數字重數;並把累積成**四列**的 `hooks/` 收成一列
- hooks-inventory 標頭被貼成兩份、「一句話結論」被貼成**三份**(數字還互相打架),
                  各收成一份;版本註記兩邊都留

## 盤點(全部在合併後的樹上實數,不是加減推的)

    ls hooks/*.sh | wc -l                    58 → 60
    grep -c '"command":' hooks/hooks.json    79 → 82
    ls agents/*.md | wc -l                        7
    ls -p scripts | grep -v / | wc -l            48
    未掛進 hooks.json 的 .sh                       3(與本頁原本寫的一致,重數過)

📌 抓到的漂移(每次併都有一個,這次三個):
   ① hooks-inventory 的標頭 ×2、一句話結論 ×3
   ② README 的 `hooks/` 列 ×4,其中兩列是舊數字
   ③ plugin.json 描述欄寫「49 支腳本」,而用本頁寫死的那道指令實數是 48

## 測試(合併後全部重跑,帶對參數)

hooks/tests 23 支全綠:ask-user-question 14、countdown 20、diagnosis-evidence 26、
dispatch-format 52、factory-idle 33、gate-ok 17、gitea-arm-check 16、
presence-beacon 14、cross-repo 19、main-and-prod-push 10、mainline-focus 24、
mainline-idle 61、milestone-account 22、**overdue-nag 36**、pr-verdict 52、
prod-write 37、reply-identity 11、roster 24、sdd 8、search-is-not-proof 31、
stage-before-prod 16、unpushed-police 10、**wiki-compress 25**

scripts/test-* 13 支全綠。
claude plugin validate . ✔ passed;check-version-consistency  0.15.0 = v0.15.0。
兩支新 SessionStart hook 併完實跑過,閘判定探針對 v0.15.0 的 prod-write-guard 回 pass。

🔴 跑之前清 `CLAUDE_CODE_CHILD_SESSION`;四支要帶參數
(三支帶 hook 絕對路徑、cross-repo 帶 hook 絕對路徑、gitea-arm-check 帶 repo 根目錄)
——不帶會紅,而那是假紅。
This commit is contained in:
Claude Code
2026-08-28 01:25:49 +00:00
25 changed files with 1884 additions and 54 deletions
+37 -12
View File
@@ -1,4 +1,4 @@
# 57 支閘,白話盤點表
# 60 支閘,白話盤點表
> 回應 `inkstone/InkStoneCo#40`:「如果加入了,我應該可以白話文看到 hooks 的內容?」
> 這份表就是那個「白話文」——不用點開任何 `.sh` 檔,一行看懂一支閘在管什麼。
@@ -7,14 +7,14 @@
## 一句話結論
`hooks/` 底下有 **57 個 `.sh` 檔**`hooks.json` 實際掛上 **75 條註冊**(同一支閘常被多種情境同時掛上);
`hooks/` 底下有 **60 個 `.sh` 檔**`hooks.json` 實際掛上 **82 條註冊**(同一支閘常被多種情境同時掛上);
其中 **3 支檔案存在但沒被掛上**(2 支是待人填的空範本、1 支是刻意留著沒開的止血帶,見下面「未生效」表)。
下面按「你會在什麼時候撞到它」分組,一支一行。
> 🔴 **這兩個數字上一版是錯的(2026-08-26 實際數過才發現)**:本頁原本寫「43 個檔、53 條註冊」,
> 而當時真實是 **45 個檔、55 條註冊**——中間有兩支閘進來時沒有回頭改這裡。
> 現在的寫法是實際數出來的:
> `ls hooks/*.sh | wc -l` 57`grep -c '"command":' hooks/hooks.json` 75
> `ls hooks/*.sh | wc -l` 60`grep -c '"command":' hooks/hooks.json` 82
> ⚠️ **冒號不能省**`grep -c '"command"'`(沒冒號)會連 `"type": "command"` 一起數到,回 **120**。
> 本頁 2026-08-27 之前寫的是沒冒號那版——**照著它跑會拿到一個跟本頁不符的數字**。
> **一份會偷偷過期的盤點表,跟沒有盤點表差不多**——見本頁最後「怎麼跟實況對帳」那段。
@@ -46,14 +46,37 @@
> 📌 **`0.7.0``inkstone/ISEP#30`2026-08-27)進來一支**`mainline-idle-guard.sh`F 組,Stop)。
> 48→**49** 支、59→**60** 條,兩個數字都是加完之後當場數出來的(指令同上)。
>
> 📌 **`inkstone/ISEP#93` `#89`2026-08-28)進來兩支**
> 📌 **`inkstone/ISEP#86``#87``#88`2026-08-28)進來三支、+7 條**
> `roster-guard.sh`D 組,`Task``Agent` 各一條)、
> `diagnosis-evidence-guard.sh`A 組,Bash)、
> `investigate-first-stamp.sh`H 組,`Agent|Task` 與 `SendMessage` 各一條),
> 外加 `dispatch-format-guard.sh` 多掛兩處——Bash(接 `claude -p` 這條路)與
> **新的「回覆 subagent」matcher**`SendMessage` 與雲端 sessiontrigger 那幾支)。
> 53→**56** 支、68→**75** 條,兩個數字都是**在這棵樹上當場數出來的**
> `ls hooks/*.sh | wc -l``grep -c '"command":' hooks/hooks.json`),
> 不是拿上一版加減推的。順手改掉兩個過期的數字:`README.md` 的「48 支/59 條註冊/23 支腳本」
> ——實數是 53/68/35(那是**併之前** main 上的實況),本版之後是 56/75/36。
> **同一個病,只是換一個檔案。**
>
> 📌 **`inkstone/ISEP#93` `#89`2026-08-28)進來兩支、+3 條**
> `overdue-nag-guard.sh`E 組,SessionStart)與 `wiki-size-guard.sh`E 組 B 組,兩個事件)。
> 55→**57** 支、72→**75** 條——**兩個數字都是在「把 main 併進來之後」的樹上當場數出來的**,
> 58→**60** 支、79→**82** 條——**兩個數字都是在「把 main 併進來之後」的樹上當場數出來的**,
> 不是拿上一版加減推的(本頁自己上面就記著「不能靠加減推算、只能實數」)。
> 順手拿掉一組重複貼上的行(那條 `grep` 指令被貼了兩次)。
> 📌 **併回來時抓到的一個漂移**:`plugin.json` 描述欄寫「48 支腳本」
> 而用本頁寫死的那道指令(`ls -p scripts | grep -v / | wc -l`,只數檔案)
> 在合併後的樹上實數是 **47**——**同一個病,第 N 次,只是換一欄。**
>
> 🔴 **這次併 `hooks.json` 學到一件比數字更重要的事**:它是 JSON
> **合錯了語法照樣合法、閘卻不見了,不會有任何東西喊一聲。**
> 「同一個陣列位置兩邊各有一支」時(本次是 `roster-guard` vs `mainline-focus-guard`),
> 正解是**展開成兩個項目**,不是二選一——選一邊就會讓一支真的閘靜靜消失。
> ⇒ 合完一律再跑一次**逐支點名**(指令寫在 `README.md`「裝什麼」那段),
> 並跟兩個父節點做集合對照:**從 main 掉了幾條、從自己這邊掉了幾條、冒出幾條**。
> 本次三個答案都是 0。
>
> 📌 **順手抓到的漂移(每次併都抓到一個,這次是三個)**:
> ① 本頁的「一句話結論」被貼成**三份**、標頭被貼成**兩份**(數字還互相打架);
> ② `README.md` 的 `hooks/` 那列累積成**四列**,其中兩列是舊數字;
> ③ `plugin.json` 描述欄寫「49 支腳本」,而用本頁寫死的那道指令
> `ls -p scripts | grep -v / | wc -l`,只數檔案)在合併後的樹上實數是 **48**。
> **同一個病,第 N 次,只是換一欄。**
> 📌 **`0.10.0``inkstone/ISEP#81`2026-08-28)進來一支**`pr-verdict-guard.sh`F 組,Stop)。
> 51→**52** 支、64→**65** 條,兩個數字都是加完之後當場數出來的(指令同上)。
@@ -101,7 +124,7 @@
| `reply-identity-guard.sh` | 直接打 Gitea API 貼留言到票上時,內文第一行沒有 `【身份】``總管``subagent``leo`)就擋下——票上多條線並行時,看不出哪一則是誰寫的。純讀取、走 `scripts/ticket` 正門、指令裡加 `reply-identity-ok` 都放行。 | 🛑 擋 |
| `comment-carries-task-guard.sh` | AI 要在票裡留一則「等某某上線才驗得了」這種**還沒做完的事**,卻沒有把它開成一張子票時,擋一次。留言沒人會回頭讀,票的 open/closed 撈一次就看得到。 | 🛑 擋(同一輪只擋一次) |
| `search-is-not-proof-guard.sh` | AI 用知識庫的**搜尋**找到一筆東西,然後把那筆東西的原始編號直接貼進票裡或派工單裡,當成「某個功能壞了」的證據——而它從沒用正規的檢索路徑去查過那筆——擋一次。你關心的是:不會再有人拿錯的方法量出一個錯的結論,然後退回一張其實已經做完的票。 | 🛑 擋(同一輪只擋一次) |
| `diagnosis-evidence-guard.sh` | 你想把一段有份量的東西寫上票,而這個 session **沒派人去查過那張票**、那段話裡也**沒有任何走得過去的出處**(檔案:行號/commit/comment 號/票號/網址/貼出來的實測輸出)就擋一次——未經調查的診斷寫在票上會長得像事實,工人會照著它去驗證而不是去查。派過人查、或把出處帶上,都放行;一句短回覆不管。 | 🛑 擋 |
## B. AI 想寫檔案/改程式碼的當下(PreToolUse / Write·Edit·MultiEdit
| 閘名 | 對你意味著什麼 | 動作 |
@@ -134,6 +157,8 @@
| `micromanage-guard.sh` | 派工單寫得太細(指名檔案函式、編號步驟、要求每做一項回報一次…)就擋下——subagent 該被當成有能力的同事,不是照抄劇本的工具。 | 🛑 擋 |
| `irreversible-dispatch-guard.sh` | 派工單裡出現「刪分支」「drop table」「rm -rf」這類不可逆動作,卻沒寫「先停下來等回覆才執行」就擋下。 | 🛑 擋 |
| `no-ticket-no-dispatch.sh` | 派工單裡沒有寫工單號(`【工單】owner/repo#N`),或那張票已經關閉/根本不存在,就擋下——沒有票號的工作沒有人追得到進度。 | 🛑 擋 |
| `dispatch-format-guard.sh` | **派工單 票號,就這樣。** 票號以外還寫了別的東西(背景、驗收條件、紅線、交件方式、「這個 session 才知道的事」)就擋下,並告訴你那些內容該搬去哪:每次都一樣的 → 共通規定;這次才知道的 → **寫進那張票**。反過來,合規的派工它會**自動把共通規定注入**給收工方(交件方式、不准 push main、org 是 `inkstone`…),所以你不寫也不會漏。判準是「這一行是不是【工單】欄位」——**在不在**,不是寫了什麼,所以講得再謹慎也不會被多罰。純禮貌收尾(「謝謝」)不算違規;`(→ comment M` 不論全形或半形括號都認得(ISEP#65 修過一次:CLAUDE.md 自己規定的合格寫法曾經被這支閘自己擋下)。**`ISEP#88` 起同一支閘也掛在「回覆 subagent」那條路上**(`SendMessage`、雲端 sessiontrigger、以及 Bash 的 `claude -p`)——回覆也是派工,同一套規矩;擋下來時會把那段內容**原文印出來**方便直接貼上票。subagent 往上回報(`to: "main"`)不管,那是交件不是派工。 | 🛑 擋(+注入) |
| `roster-guard.sh` | 派工要**指名派給誰**`Task``subagent_type` 得是 `agents/` 名單上的名字(`isep-hand``arcrun-hand``scout`…),沒指名或指了不存在的名字就擋下,並把整份名單印出來。指對了它會**自動把那位工人的檔案原文注入**給它——你是誰、負責哪個 repo、開工前先讀什麼、你的紅線是什麼(這些每個工人都不一樣,共通規定補不了)。名單是資料:加一個工人=在 `agents/` 加一個 `.md`,不必改任何 hook。 | 🛑 擋(+注入) |
| `mainline-focus-guard.sh` | 派出去的票**不在「現在的主線」上**,就擋一次,並問你是「補收」(它本來就該掛在主線上)還是「跳線」(真的插件事)。背景:2026-08-27 實查有 **14 個 milestone 同時開著**,其中「Mira 現代化」同名活在 5 個 repo ⇒ SOP 說的「只做該 milestone 的事」在現場**沒有指涉對象**,等於不存在。現在主線是一個查得到的事實(`scripts/mainline`,答案唯一),這支就是那個事實的用處。🔴 **沒有標主線 ⇒ 一律放行**;多張票只要有一張在主線上就放行;查不到那張票掛在哪也放行(**讀不到 ≠ 不屬於**);**同一張票只擋一次**,重送就過。跳線會留一筆紀錄——不留痕的話,跳線與專注在機器上長得一模一樣。 | 🛑 擋(同一張票至多一次) |
| `dispatch-format-guard.sh` | **派工單 票號,就這樣。** 票號以外還寫了別的東西(背景、驗收條件、紅線、交件方式、「這個 session 才知道的事」)就擋下,並告訴你那些內容該搬去哪:每次都一樣的 → 共通規定;這次才知道的 → **寫進那張票**。反過來,合規的派工它會**自動把共通規定注入**給收工方(交件方式、不准 push main、org 是 `inkstone`…),所以你不寫也不會漏。判準是「這一行是不是【工單】欄位」——**在不在**,不是寫了什麼,所以講得再謹慎也不會被多罰。純禮貌收尾(「謝謝」)不算違規;`(→ comment M` 不論全形或半形括號都認得(ISEP#65 修過一次:CLAUDE.md 自己規定的合格寫法曾經被這支閘自己擋下)。 | 🛑 擋(+注入) |
@@ -197,7 +222,7 @@
| `issue-status-autoflip.sh` | AI 一派工出去,就自動把對應的 Gitea 票改成「進行中(s/doing)」,不必等人手動改標籤。 | 📝 記錄(自動改票) |
| `baton-handback-guard.sh` | 一條派工線做完了,就去看它那張票有沒有**指派給人/標籤有沒有說它卡在哪/有沒有寫下一步**,缺哪一格就當場說出來。你關心的是:不會再有票做完了卻沒人接手,躺在那裡沒人發現。 | 📝 記錄(提醒,不擋) |
| `kbdb-evidence-stamp.sh` | AI 每次去知識庫拿資料,就記一筆「這是**搜尋**搜到的,還是**走正規檢索路徑**拿到的」。你關心的是:它等一下要拿這筆資料下結論時,有東西可以對照它走的是哪條路。 | 📝 記錄 |
| `investigate-first-stamp.sh` | 你真的**派人去查某張票**之後,按「session + 票號」留一個時間戳——給 `diagnosis-evidence-guard.sh` 判斷「這件事有沒有人查過」用。按票分而不是按 session 分:早上派人查了 A 票,不該等於解鎖了 B 票的診斷。 | 📝 記錄 |
## I. 你會撞到但跟「派工/收工」無關的一支(Edit·MultiEdit
| 閘名 | 對你意味著什麼 | 動作 |
@@ -297,7 +322,7 @@ A1 缺人話、A2 表格有幽靈、C 全頁有幽靈。
**這次順便抓到的另一個落差**(🕐 **這一段是 2026-08-20 的快照,裡面每個數字都是那天的,不是現況**——
留著是因為它是「兩份文件的數字不會自己跟上」這個病的第一個案例;現況數字看本頁標頭):
`.claude-plugin/plugin.json` 的說明文字寫「55 支機械閘(52 條註冊)」,
`.claude-plugin/plugin.json` 的說明文字寫「58 支機械閘(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` 這批(檔頭日期都是今天)加了之後,兩份文件的數字沒有跟著更新。