Files
ISEP/docs/governance/dispatch-and-reply-format.md
T
Leo 3bc7f3c5f5 派工單只剩票號——閘從驗「有沒有票號」改成驗「是不是只有票號」
leo 2026-08-27(inkstone/ISEP#30 comment 4322/4325/4327):
「這些話票上都沒有,你根本沒照規則做事,你的 hook 讓你這樣搞?」
「你用一個 output parser 把你給 subagent 的指令規範,分作幾點,每一點規定格式,
  照這種散文寫法根本無法迭代」「警察也不能抓」
「交件方式不需要寫,定義在原則裡⋯⋯每次都一樣提取出來變成共通規定」
「(那些 session 事實)這些為什麼不寫到票裡?」「subagent 回覆時要表明身份」

病根:no-ticket-no-dispatch.sh 驗的是「有沒有一行【工單】owner/repo#N」,
而規則的原文是「派工單只寫票號」。⇒ 把 40 行任務全寫在 prompt 裡、票號補一行,
閘照樣放行。2026-08-27 一天內這樣做了 5 次,每次票上都沒有那份任務。
規則存在,閘只驗了它的殼——同款第 N 次(history-first/KBDB-first/stage-first)。

新增 hooks/dispatch-format-guard.sh(PreToolUse Task|Agent),兩件事:
- 擋:【工單】以外還有實質內容就 exit 2,並指出那些內容該搬去哪
  (每次都一樣 → 共通規定;這次才知道 → 寫進那張票。
   判準「這句話換一張票還成立嗎?」)
- 注入:合規的派工自動把共通規定送給收工方(交件方式、不准 push main、
  org 是 inkstone…)——這是「派工單只剩票號」能成立的前提,
  leo 的驗收條件之一就是「收工方沒讀派工單也知道要貼回原票」

判準是結構不是文字(leo 2026-08-17 那條檢驗):問的是「這一行是不是【工單】欄位」
——在不在,不是寫什麼。hooks/lib/dispatch_parse.py 全檔零個「命中某個詞就違規」的比對。
⇒ 也因此不需要語意判官:免費、瞬間、每次結果一樣。

新增 hooks/reply-identity-guard.sh(PreToolUse Bash)+ scripts/ticket 內建檢查:
票上每一則留言第一行要有【身份】(總管/subagent/leo)。貼留言有兩條路,兩條都封
——ticket-api-bypass-guard 是刻意放行「對既有票留言」的,只封正門等於沒封。
實害:多條線並行時總管寫的診斷被當成 subagent 的結論,而其中一則是錯的。

規約寫成文件:docs/governance/dispatch-and-reply-format.md
(§2 那段就是被注入的那份共通規定本體——只有一份,改那裡等於改所有派工)

實測(離線、不打網路、不花錢):
  hooks/tests/dispatch-format-guard.test.sh   19/19
  hooks/tests/reply-identity.test.sh          11/11
測資裡的 B⑨ 是真跡:產生 ISEP#30 這條線的那一次派工,一字未改。
另 4 份 leo 點名的違規派工拿不回來了——它們住在 prompt 裡,agent 一停就沒了,
這件事本身就是這條規則的證據(見 hooks/tests/fixtures/README.md,不用想像的例子替補)。

升版 0.4.0 → 0.5.0(產物按版本號分資料夾,不升版新閘不會被載入)。
tag 照慣例打在 merge commit 上,所以這條分支上 check-version-consistency.sh 是紅的。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 11:35:03 +08:00

5.3 KiB
Raw Blame History

派工單與交件回覆的格式(共通規定)

leo 2026-08-27inkstone/ISEP#30 comment 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.shPreToolUse TaskAgent)—— 【工單】 以外還有實質內容就擋,並指出那些內容該搬去哪一格。 判準是**「這一行是不是【工單】欄位」**(在不在),不是「它寫了什麼」。


2. 共通規定(每一次派工由機器自動注入給收工方)

你收到的派工單只有一個票號。任務全文在票上。

  1. 第一個動作是去讀那張票(含每一則 comment)。派工單不會再給你別的東西—— 這是刻意的:票活得比任何一個 agent 久。
  2. 票上的脈絡不夠 ⇒ 回票上問,不要憑猜測動手,也不要回頭問派工的人要細節。
  3. 交件=貼回那張票scripts/ticket say <owner/repo#N> -F <檔>), 不是只在對話裡回報。回覆第一行必須是身份欄,見下。
  4. 回覆第一行一律是 【身份】subagent<owner/repo><你的分支> 角色三選一:總管subagentleo
  5. 不准 push 到 mainmaster,也不准部署 prod。做在自己的分支上,交回分支名。
  6. Gitea 的 org 是 inkstone(不是 Leo);gh 打不到 Gitea 標籤是 s/* 不是 status/*
  7. 先讀你要動的那個 repo 的 CLAUDE.mdsystem-dev/wiki/,照它的慣例走, 不要照你自己習慣的做法。
  8. 交出去之前,你要知道它能不能用——貼實測輸出,不是「我測過了」。 有一格沒驗 ⇒ 那是 report 不是 deliver,講清楚哪一格。
  9. 改了會被載入的東西(pluginworkerbundle)就要升版, 否則產物按版本號分資料夾,你的改動到不了任何人手上。

🔴 這一段是「每次都一樣」的唯一真相源。 想在派工單裡加一句叮嚀之前,先問:它換一張票還成立嗎? 成立就加在這裡(改一次,全機生效),不要加在那一次的 prompt 裡。


3. 交件回覆 第一行表明身份

【身份】subagentinkstone/ISEPfeat/ticket-carries-the-task
  • 角色是三選一的允許清單總管subagentleo
  • 第二格是你動的 repo,第三格是分支(沒有就寫 -

為什麼

2026-08-27 實害:多條線並行時票上的留言看不出身份, 總管寫的診斷被當成 subagent 的結論,而其中一則是錯的

🔴 這條管所有人,不是只管 subagent。 總管寫在票上的東西同樣要標 【身份】總管/…——leo 要分得出哪一則是誰寫的。

機械閘(兩道,因為這個動作有兩條路)

正門 scripts/ticket say / decide 腳本內建檢查,貼上去之前就擋
側門 直接打 Gitea API 貼 comment hooks/reply-identity-guard.shPreToolUse Bash

只封正門的閘等於沒封——ticket-api-bypass-guard.sh 的檔頭已經記過這一課: 「規範有、閘也有,但閘長在『工具』上,而那個動作有兩條路,只封了一條。」


4. 這份規範自己怎麼被驗

bash hooks/tests/dispatch-format-guard.test.sh     派工單閘:該擋的與不該擋的
bash hooks/tests/reply-identity.test.sh            身份欄:正門與側門兩道

測資裡放的是真的發生過的那幾份違規派工單,不是想像出來的例子。