三張票同一族(誰在派、派給誰、派的內容住哪裡),做在同一條分支: #86 工人名單:agents/ 七位有名字的工人+scripts/roster+hooks/roster-guard.sh 派工用 Task 的 subagent_type 指名,派工單格式一個字都沒改; 指對名字就把那位的檔案原文注入(你是誰/先讀什麼/你的紅線)。 【身份】欄同時吃得下工人名字(原本三個角色照舊)。 #87 未經調查不寫診斷:hooks/diagnosis-evidence-guard.sh + investigate-first-stamp.sh 三個結構訊號(派過人查沒/有沒有走得過去的出處/有沒有份量), 一個關鍵字比對都沒有;轉述有出處不會被誤擋。 #88 回覆也是派工:不另造閘,把攔截點加掛上去。 hooks/lib/dispatch_parse.py 的 tool_channel() 一次列全所有通往 subagent 的路 (SendMessage/雲端 session・trigger/claude -p);擋下來時把那段內容原文印出來。 subagent 往上回報(to: "main")=交件不是派工,刻意不管。 順手修掉一個真的會咬人的 flake:dispatch-format-guard 原本開四支 python 各讀一個欄位, 機器忙的時候某個欄位會靜靜變空字串(實測連跑 10 次有 1 次「豁免了卻還是被擋」)。 四個欄位改成一次讀完。 版本號待總管定(plugin.json 只更新了描述裡的數字,版本沒動)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZBu4Sa1cGntKFRBYNZ6xs
9.6 KiB
派工單與交件回覆的格式(共通規定)
leo 2026-08-27(
inkstone/ISEP#30comment 4322 / 4325 / 4327): 「你用一個 output parser 把你給 subagent 的指令規範,分作幾點,每一點規定格式, 照這種散文寫法根本無法迭代」/「警察也不能抓」/ 「交件方式不需要寫,定義在原則裡,每張票都要做這件事⋯⋯每次都一樣提取出來變成共通規定」/ 「(那些 session 事實)這些為什麼不寫到票裡?」/「subagent 回覆時要表明身份」
本檔就是那份「共通規定」。 它不是給人讀完記住的——
hooks/dispatch-format-guard.sh 會把 §2 自動注入每一次派工,
所以收工方沒讀派工單也會拿到。
1. 派工單 = 票號。就這樣
【工單】inkstone/ISEP#30 → comment 4322
要帶兩張票就兩行 【工單】。沒有第二個欄位。
為什麼
派工單裡想寫的東西只有兩種,兩種都不該留在派工單:
| 種類 | 舉例 | 該住哪 |
|---|---|---|
| 每次都一樣 | 交件方式、不要 push main、org 是 inkstone、先讀該 repo 的 CLAUDE.md |
本檔 §2(機器自動注入) |
| 這次才知道 | main 現在是哪顆、今天撞過什麼、另一條線正在動同一個 repo |
寫進那張票 |
判準一句話:「這句話換一張票還成立嗎?」 還成立 ⇒ 共通規定。只有這次成立 ⇒ 寫進這張票。兩種都不進派工單。
🔴 「票上還沒有」不是把它寫進 prompt 的理由——它是「去把它寫上票」的指令。 寫進 prompt 的後果:那個 agent 被停掉或換人接手,那段事實就隨 prompt 消失。 2026-08-27 實害:總管停掉重派 3 次,前兩次的任務與 session 事實全部蒸發。
機械閘
hooks/dispatch-format-guard.sh(PreToolUse Task/Agent)——
【工單】 以外還有實質內容就擋,並指出那些內容該搬去哪一格。
判準是**「這一行是不是【工單】欄位」**(在不在),不是「它寫了什麼」。
1.5 派給誰:從名單裡挑一個有名字的工人(inkstone/ISEP#86)
派工單只有票號,那「這是誰的活」寫在哪?——不寫在派工單裡,寫在 subagent_type。
Task(subagent_type="isep-hand", prompt="【工單】inkstone/ISEP#86")
▲ 名單上的名字 ▲ 派工單仍然只有這一行
| 名單在哪 | agents/,一個檔案一位工人(規約:docs/governance/worker-roster.md) |
| 怎麼看 | python3 scripts/roster list/show <名字>/which <owner/repo> |
| 機械閘 | hooks/roster-guard.sh——沒指名或名單上沒有這個名字就擋,並印出整份名單 |
為什麼「你是誰」不能收進 §2 的共通規定:共通規定是「每次都一樣」的東西,
而「你是誰、你該讀哪些、你的紅線」每個工人都不一樣。所以它有自己的家(名單),
由 roster-guard.sh 在派工當下注入——同樣不必有人記得寫。
1.6 回覆也是派工:同一套規矩,同一支閘(inkstone/ISEP#88)
派工單被壓成只剩票號之後,回覆一個正在跑的 subagent 這條路沒有被管到——
那支閘掛在 PreToolUse(Task|Agent),而回覆走的是別的工具,根本不經過那個攔截點。
實況(2026-08-27):第一次派工乾乾淨淨只有票號,中途回覆時又把一長串修改要求丟過去。 那些話票上一個字都沒有,那條線被停掉或換人接手就消失。
⇒ 不另造一支平行的閘:同一支 dispatch-format-guard.sh、同一個判斷函式,
只是把所有通往 subagent 的路一次列全(表在 hooks/lib/dispatch_parse.py 的 tool_channel()):
| 路 | 裝話的欄位 |
|---|---|
Task/Agent |
prompt |
SendMessage |
message |
雲端 create_session |
prompt |
雲端 send_message |
text/message |
雲端 create_trigger/update_trigger |
prompt |
雲端 fire_trigger |
text |
雲端 send_later |
message |
Bash claude -p <prompt> |
指令裡那段 prompt |
🔴 新增一條路要加在那張表裡,不要再開一支閘。
「認動作的方式漏了一條路」是這一族的通病——ticket-api-bypass-guard.sh 的檔頭
記過同一課(第一版只認大寫裸字 POST,requests.post() 與隱式 POST 全部漏掉)。
刻意不管的方向:subagent 往上回報(SendMessage to: "main")=交件不是派工。
擋它等於擋掉交件本身。
2. 共通規定(每一次派工由機器自動注入給收工方)
你收到的派工單只有一個票號。任務全文在票上。
- 第一個動作是去讀那張票(含每一則 comment)。派工單不會再給你別的東西—— 這是刻意的:票活得比任何一個 agent 久。
- 票上的脈絡不夠 ⇒ 回票上問,不要憑猜測動手,也不要回頭問派工的人要細節。
- 交件=貼回那張票(
scripts/ticket say <owner/repo#N> -F <檔>), 不是只在對話裡回報。回覆第一行必須是身份欄,見下。 - 回覆第一行一律是:
【身份】<你的名字>/<owner/repo>/<你的分支>名字用機器注入給你的那個(isep-hand、scout…);沒有名字時才退回總管/subagent/leo三選一。票上要看得出是哪個工人做的, 而三條線並行時「subagent」這個字回答不了「是誰」。 - 不准 push 到
main/master,也不准部署 prod。做在自己的分支上,交回分支名。 - Gitea 的 org 是
inkstone(不是Leo);gh打不到 Gitea; 標籤是s/*不是status/*。 - 先讀你要動的那個 repo 的
CLAUDE.md與system-dev/wiki/,照它的慣例走, 不要照你自己習慣的做法。 - 交出去之前,你要知道它能不能用——貼實測輸出,不是「我測過了」。 有一格沒驗 ⇒ 那是 report 不是 deliver,講清楚哪一格。
- 改了會被載入的東西(plugin/worker/bundle)就要升版, 否則產物按版本號分資料夾,你的改動到不了任何人手上。
🔴 這一段是「每次都一樣」的唯一真相源。 想在派工單裡加一句叮嚀之前,先問:它換一張票還成立嗎? 成立就加在這裡(改一次,全機生效),不要加在那一次的 prompt 裡。
3. 交件回覆 = 第一行表明身份
【身份】subagent/inkstone/ISEP/feat/ticket-carries-the-task
- 角色是允許清單:
總管/subagent/leo,再加上agents/名單上的每一個工人名字 (isep-hand/arcrun-hand/scout…,inkstone/ISEP#86)。清單以外的名字一律擋。 - 第二格是你動的 repo,第三格是分支(沒有就寫
-)
🔴 有名字就用名字。 三條線並行時三則「【身份】subagent/…」長得一模一樣, 等於沒有身份——這正是 ISEP#86 要解的那件事。
為什麼
2026-08-27 實害:多條線並行時票上的留言看不出身份, 總管寫的診斷被當成 subagent 的結論,而其中一則是錯的。
🔴 這條管所有人,不是只管 subagent。 總管寫在票上的東西同樣要標
【身份】總管/…——leo 要分得出哪一則是誰寫的。
機械閘(兩道,因為這個動作有兩條路)
| 路 | 閘 |
|---|---|
正門 scripts/ticket say / decide |
腳本內建檢查,貼上去之前就擋 |
| 側門 直接打 Gitea API 貼 comment | hooks/reply-identity-guard.sh(PreToolUse Bash) |
只封正門的閘等於沒封——
ticket-api-bypass-guard.sh的檔頭已經記過這一課: 「規範有、閘也有,但閘長在『工具』上,而那個動作有兩條路,只封了一條。」
4. 這份規範自己怎麼被驗
bash hooks/tests/dispatch-format-guard.test.sh 派工單閘+**回覆那條路**:該擋的與不該擋的
bash hooks/tests/reply-identity.test.sh 身份欄:正門與側門兩道
bash hooks/tests/roster-guard.test.sh 工人名單:指名、沒有這個人、注入
bash hooks/tests/diagnosis-evidence-guard.test.sh 未經調查不寫診斷
測資裡放的是真的發生過的那幾份違規派工單,不是想像出來的例子。
5. 寫上票之前:未經調查不寫診斷(inkstone/ISEP#87)
順序只有一個方向:先派人查 → 拿到查的結果 → 才可以寫診斷。
「診斷寫完了,工人就不知道自己要做什麼——它會照著我的結論去驗證,而不是去查。 而我不是那個 repo 的專家,它才是。」
機械閘 hooks/diagnosis-evidence-guard.sh 看三個結構訊號(一個關鍵字都沒有):
- 這個 session 有沒有派人查過那張票(
investigate-first-stamp.sh的戳記) - 這段話裡有沒有走得過去的出處(檔案:行號/commit/comment 號/票號/網址/實測輸出)
- 這段話有沒有份量(一句「收到」不可能是診斷)
①有 ⇒ 放行。①無+②有 ⇒ 放行(轉述有出處不能被誤擋)。三者皆缺 ⇒ 擋一次。
🔴 「根因」「因為」「應該是」這類詞一律不准當判準——leo 2026-08-17 已證偽文字層封路: 當日 8 次誤攔、0 次正確攔截,而且紅線寫得越細,命中關鍵字的機率越高 ⇒ 那些閘在懲罰謹慎。