技能與 hook 的執行結果寫進本機事件流,供助理排空 #77

Merged
admin merged 2 commits from feat/status-event-stream into develop 2026-09-02 07:48:47 +00:00
Member

摘要

  • 需求描述:所有 skill 與 hook 的執行狀態都要回報給助理。現行紀錄只記「被叫用」,沒有成敗、沒有結束碼;hook 成功時完全不留紀錄。本 PR 建立事件流與 hook 端的記錄,技能端的收尾回報另案。
  • 計畫名稱:無
  • 計畫頁:無
  • 分析頁:無

變更內容

檔案 為什麼改
tools/report-status.sh 新增。事件流的命令列介面:三個記錄子命令加 drain 與 rotate
hooks/lib.sh 新增 emit_event、json_escape、hook_status_of、hook_trace。寫入函式放這裡,hook 才不必在執行期依賴別的 plugin 路徑
九支 hook 加 assistant-gate、heartbeat 各加一行 hook_trace,用 EXIT trap 記錄自己的結束碼
hooks/skill-usage.sh 同時發技能的 start 事件
hooks/version-guard.sh、restart-gate.sh、assistant-gate.sh 走 deny.sh 的路徑補 JSC_EVENT_STATUS=blocked 覆寫
README.md、references/behaviors.md 隨變更對齊
三份 manifest 隨變更升版

設計重點

  • 不直接寫 wiki。 hook 每次提示都跑,網路寫入會拖垮宿主 CLI;失敗的 hook 自我回報會疊出迴圈,既有的錯誤回報因此刻意不接在失敗的 hook 上,這裡沿用同一條線。助理巡檢時 drain 排空。
  • hook 端用 EXIT trap,一支只加一行。 這幾支的 exit 點很多,sdlc-gate.sh 一支就有五十幾個。逐點改要動到每一條判定路徑,而那些路徑正是閘門的判準;為了加一行紀錄去動閘門,風險遠大於收益。trap 涵蓋每一條離開路徑,含 set -e 的中途失敗。
  • 狀態推不出來時由 hook 覆寫。 version-guard.sh 檔頭本來就寫明「exit 0 有兩種意思」:antigravity 走 stdout 的 deny JSON、kiro 只印警告,兩者擋下時結束碼都是 0。不覆寫就會把擋下記成放行。
  • 技能的 start 是免費的。 PostToolUse(matcher=Skill) 在技能指示載入後觸發,那就是開始跑。end 只能由技能自己在收尾寫——hook 觸發時技能的實際工作還在後面的模型輪次,看不到成敗。有 start 沒有配對的 end 就是中止,那正是現行紀錄分不出來的那一種。
  • 記錄失敗一律吞掉,不得改變結束碼。 hook 的結束碼是閘門的判準,被記錄動到就等於閘門行為被記錄改寫。參數檢查是例外,以 2 擋在記錄之前,避免汙染事件流。
  • 每行一次 printf 且壓在 4096 位元組內,五支 CLI 併發時單次 O_APPEND 才不會互相插隊;detail 因此截到 200 字元。
  • drain 用 tail -c +N 不用 dd bs=1 skip=。 後者是一個位元組一次系統呼叫,位移到幾 MB 就是幾百萬次。
  • 輪替不放在每次寫入,那等於每次提示多一次系統呼叫;改由巡檢排空之後呼叫。

測試結果

  • emit_event 四種情況:無 ms 無 detail、有 ms、含雙引號與換行與 tab 的 detail、500 字元 detail。四行都是合法 JSON,跳脫正確,500 字截成 200,最長一行 358 位元組。
  • hook_trace 五種離開路徑:exit 0 → ok、exit 2 → blocked、exit 9 → failed、set -e 中途失敗 → failed 且結束碼 1、覆寫 JSC_EVENT_STATUS=blocked 且 exit 0 → blocked。五種情況下腳本自己的結束碼都原封不動。
  • report-status.sh 參數檢查:非法 status、非法結束碼、缺參數、無子命令,四種都回 2 且不寫任何事件。
  • drain:首次吐 3 筆、再寫一筆後只吐新的 1 筆、沒有新事件回 3;輪替後從頭讀得到。位移 5 MB 之後排空耗時 8 ms。
  • rotate:未達上限不動;超過上限改名成 .1、位移歸零、新檔可續寫可排空。
  • 實跑五支 hook,事件流逐筆出現,含子行程觸發的 restart-gate clear。
  • wire-cli.sh smoke claude 結束碼 0,139 條斷言全部與預期相同,沒有一條「與預期不同」。九支 hook 的結束碼語意完好。
  • lint-scripts.sh(20 支)、check-link-format.sh、check-behaviors.sh、ste100-lint.sh、check-page-name.sh 全部結束碼 0。

前置 Push Request

  • 無
## 摘要 - 需求描述:所有 skill 與 hook 的執行狀態都要回報給助理。現行紀錄只記「被叫用」,沒有成敗、沒有結束碼;hook 成功時完全不留紀錄。本 PR 建立事件流與 hook 端的記錄,技能端的收尾回報另案。 - 計畫名稱:無 - 計畫頁:無 - 分析頁:無 ## 變更內容 | 檔案 | 為什麼改 | | --- | --- | | `tools/report-status.sh` | 新增。事件流的命令列介面:三個記錄子命令加 `drain` 與 `rotate` | | `hooks/lib.sh` | 新增 `emit_event`、`json_escape`、`hook_status_of`、`hook_trace`。寫入函式放這裡,hook 才不必在執行期依賴別的 plugin 路徑 | | 九支 hook 加 `assistant-gate`、`heartbeat` | 各加一行 `hook_trace`,用 EXIT trap 記錄自己的結束碼 | | `hooks/skill-usage.sh` | 同時發技能的 `start` 事件 | | `hooks/version-guard.sh`、`restart-gate.sh`、`assistant-gate.sh` | 走 `deny.sh` 的路徑補 `JSC_EVENT_STATUS=blocked` 覆寫 | | `README.md`、`references/behaviors.md` | 隨變更對齊 | | 三份 manifest | 隨變更升版 | ## 設計重點 - **不直接寫 wiki。** hook 每次提示都跑,網路寫入會拖垮宿主 CLI;失敗的 hook 自我回報會疊出迴圈,既有的錯誤回報因此刻意不接在失敗的 hook 上,這裡沿用同一條線。助理巡檢時 `drain` 排空。 - **hook 端用 EXIT trap,一支只加一行。** 這幾支的 exit 點很多,`sdlc-gate.sh` 一支就有五十幾個。逐點改要動到每一條判定路徑,而那些路徑正是閘門的判準;為了加一行紀錄去動閘門,風險遠大於收益。trap 涵蓋每一條離開路徑,含 `set -e` 的中途失敗。 - **狀態推不出來時由 hook 覆寫。** `version-guard.sh` 檔頭本來就寫明「exit 0 有兩種意思」:antigravity 走 stdout 的 deny JSON、kiro 只印警告,兩者擋下時結束碼都是 0。不覆寫就會把擋下記成放行。 - **技能的 `start` 是免費的。** `PostToolUse(matcher=Skill)` 在技能指示載入後觸發,那就是開始跑。`end` 只能由技能自己在收尾寫——hook 觸發時技能的實際工作還在後面的模型輪次,看不到成敗。有 `start` 沒有配對的 `end` 就是中止,那正是現行紀錄分不出來的那一種。 - **記錄失敗一律吞掉,不得改變結束碼。** hook 的結束碼是閘門的判準,被記錄動到就等於閘門行為被記錄改寫。參數檢查是例外,以 2 擋在記錄之前,避免汙染事件流。 - **每行一次 `printf` 且壓在 4096 位元組內**,五支 CLI 併發時單次 O_APPEND 才不會互相插隊;`detail` 因此截到 200 字元。 - **`drain` 用 `tail -c +N` 不用 `dd bs=1 skip=`。** 後者是一個位元組一次系統呼叫,位移到幾 MB 就是幾百萬次。 - **輪替不放在每次寫入**,那等於每次提示多一次系統呼叫;改由巡檢排空之後呼叫。 ## 測試結果 - `emit_event` 四種情況:無 `ms` 無 `detail`、有 `ms`、含雙引號與換行與 tab 的 `detail`、500 字元 `detail`。四行都是合法 JSON,跳脫正確,500 字截成 200,最長一行 358 位元組。 - `hook_trace` 五種離開路徑:`exit 0` → ok、`exit 2` → blocked、`exit 9` → failed、`set -e` 中途失敗 → failed 且結束碼 1、覆寫 `JSC_EVENT_STATUS=blocked` 且 `exit 0` → blocked。**五種情況下腳本自己的結束碼都原封不動。** - `report-status.sh` 參數檢查:非法 `status`、非法結束碼、缺參數、無子命令,四種都回 2 且不寫任何事件。 - `drain`:首次吐 3 筆、再寫一筆後只吐新的 1 筆、沒有新事件回 3;輪替後從頭讀得到。位移 5 MB 之後排空耗時 8 ms。 - `rotate`:未達上限不動;超過上限改名成 `.1`、位移歸零、新檔可續寫可排空。 - 實跑五支 hook,事件流逐筆出現,含子行程觸發的 `restart-gate clear`。 - **`wire-cli.sh smoke claude` 結束碼 0**,139 條斷言全部與預期相同,沒有一條「與預期不同」。九支 hook 的結束碼語意完好。 - `lint-scripts.sh`(20 支)、`check-link-format.sh`、`check-behaviors.sh`、`ste100-lint.sh`、`check-page-name.sh` 全部結束碼 0。 ## 前置 Push Request - 無
jiantw83 added 2 commits 2026-09-02 07:40:55 +00:00
現行紀錄只記「被叫用」,欄位是 ts、cli、session、skill,沒有成敗也沒有
結束碼。跑完整輪的技能與開場就中止的技能,在紀錄裡長得一模一樣。hook
成功時更是完全不留紀錄,只有錯誤路徑會寫 wiki,而那條路徑刻意不自動觸發。

事件流走本機檔案,不直接寫 wiki。hook 每次提示都跑,網路寫入會拖垮宿主
CLI;失敗的 hook 自我回報還會疊出迴圈,既有的錯誤回報因此不接在失敗的
hook 上,這裡沿用同一條線。助理巡檢時排空、彙整、寫頁。

hook 端用 EXIT trap 接,一支只加一行。這幾支的 exit 點很多,階段閘門一支
就有五十幾個;逐點改要動到每一條判定路徑,而那些路徑正是閘門的判準,為了
加一行紀錄去動閘門,風險遠大於收益。trap 涵蓋每一條離開路徑,含中途失敗。

狀態預設由結束碼推,推不出來的由 hook 自己覆寫。相依版本檢查與兩道閘門有
這種情形:antigravity 走 deny JSON、kiro 只印警告,兩者擋下時結束碼都是 0,
單看結束碼會把擋下記成放行。

技能的 start 由既有的技能用量 hook 順手發,不必改任何技能文件。end 只能由
技能自己在收尾步驟寫——hook 觸發時技能的實際工作還在後面的模型輪次,看不到
成敗。有 start 沒有配對的 end,就是那一輪中止了。
admin merged commit 9adb39d21a into develop 2026-09-02 07:48:47 +00:00
admin deleted branch feat/status-event-stream 2026-09-02 07:48:47 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: plugins/hooks#77