釋出 jsc-hooks 0.3.7:助理心跳與運行閘門(閘門未接線) #70

Merged
admin merged 5 commits from develop into master 2026-09-01 07:51:20 +00:00
Member

摘要

  • 需求描述:助理的心跳與運行閘門。心跳負責留下「助理還在不在跑」的唯一依據,閘門負責在助理沒跑時擋下 jsc 技能呼叫。閘門這一版刻意不接線,接線檔一個字都沒動。
  • 計畫名稱:無
  • 計畫頁:無
  • 分析頁:無

變更內容

檔案 為什麼改
hooks/heartbeat.sh 新增。四個子命令:write 寫心跳、check 判定新鮮、report 印現況、clear 清除
hooks/assistant-gate.sh 新增。助理運行閘門。尚未接線
README.md hooks 表補兩列(事件欄都標明不接線),環境變數表補心跳門檻與閘門開關
三份 manifest 版本一起提升(心跳那一筆)

設計重點

為什麼不接線

這台機器的穩定路徑指向開發存放庫,寫進接線檔就立刻對五支 CLI 生效。而現在還沒有心跳,接線的那一秒整組技能會全部鎖死,連修的路徑都走不到。接線的前提是助理已經在跑、心跳穩定,那是另一輪的事。

心跳

  • 新鮮的判準是「檔案存在,而且時間戳距現在小於門檻」。門檻預設 300 秒,環境變數可覆寫,壞值退回預設而不報錯——變數打錯字不該讓判定整個歪掉。
  • 絕不看 pid 存活。 五支 CLI 與容器裡的行程互相看不到彼此的 pid,看了也證明不了什麼。
  • check 用結束碼分四種狀態:新鮮、過期、不存在、時間戳壞掉。前三種處置不同,第四種既不是「跑過停了」也不是「沒啟動過」,併進任何一邊都會讓訊息說錯話,而且絕不能退回判成新鮮。
  • 寫入走暫存檔再更名。直接覆寫的話,剛好讀到寫一半的檔案會少掉時間戳,助理活著卻被判成壞了。
  • 判定只有一份,check 與 report 共用同一個探測函式。兩邊各判一次一定會漂移,狀態與訊息就會對不上。

閘門

  • 這是整組 hook 裡唯一一道 fail-closed 的閘門。 其餘的原則都是資料不足就放行,這一道相反。代價是狀態檔寫不進去時全組停擺,所以逃生門與豁免清單不是選配,是能上線的前提。
  • 豁免清單只收解鎖路徑,不收收尾規則。這一點與重啟閘門的方向相反:重啟閘門解鎖靠閘門外的動作(重新啟動),收尾規則要寫得完;這一道解鎖靠跑一支技能,清單收寬了閘門就等於沒有。
  • 清單認技能名不認呼叫鏈,所以豁免技能轉呼叫的下一層也要收。最要緊的是 wiki 那一支:巡檢要先把結果寫上監控頁才寫心跳,只豁免助理自己會做出「沒心跳就擋 wiki、擋了巡檢跑不完、跑不完就還是沒心跳」的自咬環。
  • 心跳判定回「檔案系統問不出來」時放行,不擋。 那條路徑本來就不產生這一碼;而且那一刻正是磁碟或權限壞掉的訊號,擋下去連豁免那幾支也修不動——它們同樣要寫狀態檔。磁碟壞掉要人去清磁碟,不是把技能組鎖起來。時間戳壞掉則照擋,那是確定沒有可信心跳的證據,修法也就在豁免清單裡。
  • 逃生門的判斷擺在載入共用函式庫之前。 函式庫讀不到時 sh 會就地結束並回擋人的那個碼,逃生門會跟著跑不到,人就繞不過去。這一點與重啟閘門不同,因為那一道是 fail-open。
  • 擋人一律經 deny.sh。 三支走標準錯誤加結束碼,另外兩支靠標準輸出的內容擋、結束碼固定是零。自己印訊息會在那兩支上無聲失效。
  • 心跳不存在、過期、時間戳壞掉講三種話。使用者要做的事不一樣:從沒啟動過、跑過停了、檔案壞了要重建。

測試結果

sh -n 兩支都通過;lint-scripts.sh exit 0(19 支);ste100-lint.sh exit 0;檔案無 BOM。

心跳(暫存 JSC_HOME,沒碰真實環境):檔案不存在、寫入後、時間戳改成 400 秒前、門檻覆寫成 600、時間戳空值與非數字、clear 後再 clear、不認得的子命令與無參數——每一種的結束碼都與檔頭宣告一致。另外驗了寫不進去的兩種版面(位置被一般檔案佔住、被非空目錄佔住),都正確回報失敗。

閘門:心跳新鮮放行、不存在擋、過期擋且訊息報出差幾秒、時間戳壞掉擋且講第三種話、逃生門放行、解不出技能名放行、非 jsc 技能放行、11 支豁免技能逐一放行、非豁免技能(工作日誌那一支)確實被擋。

五支 CLI 的擋人形態逐一驗過,這一項最要緊,錯了那兩支就是無聲失效:

CLI 結束碼 輸出走哪
claude、codex、copilot 2 標準錯誤
antigravity 0 標準輸出的 deny JSON
kiro 0 標準輸出的警告

接線檔確認未動:git status 只有新增的腳本與 README;hooks.json、codex-hooks.json、wire-cli.sh 三份檔案裡閘門名稱的出現次數都是 0。

前置 Push Request

  • 無
## 摘要 - 需求描述:助理的心跳與運行閘門。心跳負責留下「助理還在不在跑」的唯一依據,閘門負責在助理沒跑時擋下 jsc 技能呼叫。**閘門這一版刻意不接線**,接線檔一個字都沒動。 - 計畫名稱:無 - 計畫頁:無 - 分析頁:無 ## 變更內容 | 檔案 | 為什麼改 | | --- | --- | | `hooks/heartbeat.sh` | 新增。四個子命令:`write` 寫心跳、`check` 判定新鮮、`report` 印現況、`clear` 清除 | | `hooks/assistant-gate.sh` | 新增。助理運行閘門。**尚未接線** | | `README.md` | hooks 表補兩列(事件欄都標明不接線),環境變數表補心跳門檻與閘門開關 | | 三份 manifest | 版本一起提升(心跳那一筆) | ## 設計重點 ### 為什麼不接線 這台機器的穩定路徑指向開發存放庫,寫進接線檔就立刻對五支 CLI 生效。而現在還沒有心跳,接線的那一秒整組技能會全部鎖死,連修的路徑都走不到。**接線的前提是助理已經在跑、心跳穩定**,那是另一輪的事。 ### 心跳 - 新鮮的判準是「檔案存在,而且時間戳距現在小於門檻」。門檻預設 300 秒,環境變數可覆寫,壞值退回預設而不報錯——變數打錯字不該讓判定整個歪掉。 - **絕不看 pid 存活。** 五支 CLI 與容器裡的行程互相看不到彼此的 pid,看了也證明不了什麼。 - `check` 用結束碼分四種狀態:新鮮、過期、不存在、時間戳壞掉。前三種處置不同,第四種既不是「跑過停了」也不是「沒啟動過」,併進任何一邊都會讓訊息說錯話,而且絕不能退回判成新鮮。 - 寫入走暫存檔再更名。直接覆寫的話,剛好讀到寫一半的檔案會少掉時間戳,助理活著卻被判成壞了。 - 判定只有一份,`check` 與 `report` 共用同一個探測函式。兩邊各判一次一定會漂移,狀態與訊息就會對不上。 ### 閘門 - **這是整組 hook 裡唯一一道 fail-closed 的閘門。** 其餘的原則都是資料不足就放行,這一道相反。代價是狀態檔寫不進去時全組停擺,所以逃生門與豁免清單不是選配,是能上線的前提。 - **豁免清單只收解鎖路徑**,不收收尾規則。這一點與重啟閘門的方向相反:重啟閘門解鎖靠閘門外的動作(重新啟動),收尾規則要寫得完;這一道解鎖靠跑一支技能,清單收寬了閘門就等於沒有。 - **清單認技能名不認呼叫鏈**,所以豁免技能轉呼叫的下一層也要收。最要緊的是 wiki 那一支:巡檢要先把結果寫上監控頁才寫心跳,只豁免助理自己會做出「沒心跳就擋 wiki、擋了巡檢跑不完、跑不完就還是沒心跳」的**自咬環**。 - **心跳判定回「檔案系統問不出來」時放行,不擋。** 那條路徑本來就不產生這一碼;而且那一刻正是磁碟或權限壞掉的訊號,擋下去連豁免那幾支也修不動——它們同樣要寫狀態檔。磁碟壞掉要人去清磁碟,不是把技能組鎖起來。時間戳壞掉則照擋,那是確定沒有可信心跳的證據,修法也就在豁免清單裡。 - **逃生門的判斷擺在載入共用函式庫之前。** 函式庫讀不到時 sh 會就地結束並回擋人的那個碼,逃生門會跟著跑不到,人就繞不過去。這一點與重啟閘門不同,因為那一道是 fail-open。 - **擋人一律經 `deny.sh`。** 三支走標準錯誤加結束碼,另外兩支靠標準輸出的內容擋、結束碼固定是零。自己印訊息會在那兩支上無聲失效。 - 心跳不存在、過期、時間戳壞掉**講三種話**。使用者要做的事不一樣:從沒啟動過、跑過停了、檔案壞了要重建。 ## 測試結果 `sh -n` 兩支都通過;`lint-scripts.sh` exit 0(19 支);`ste100-lint.sh` exit 0;檔案無 BOM。 **心跳**(暫存 `JSC_HOME`,沒碰真實環境):檔案不存在、寫入後、時間戳改成 400 秒前、門檻覆寫成 600、時間戳空值與非數字、`clear` 後再 `clear`、不認得的子命令與無參數——每一種的結束碼都與檔頭宣告一致。另外驗了寫不進去的兩種版面(位置被一般檔案佔住、被非空目錄佔住),都正確回報失敗。 **閘門**:心跳新鮮放行、不存在擋、過期擋且訊息報出差幾秒、時間戳壞掉擋且講第三種話、逃生門放行、解不出技能名放行、非 jsc 技能放行、11 支豁免技能逐一放行、非豁免技能(工作日誌那一支)確實被擋。 **五支 CLI 的擋人形態逐一驗過**,這一項最要緊,錯了那兩支就是無聲失效: | CLI | 結束碼 | 輸出走哪 | | --- | --- | --- | | claude、codex、copilot | 2 | 標準錯誤 | | antigravity | 0 | 標準輸出的 deny JSON | | kiro | 0 | 標準輸出的警告 | **接線檔確認未動**:`git status` 只有新增的腳本與 README;`hooks.json`、`codex-hooks.json`、`wire-cli.sh` 三份檔案裡閘門名稱的出現次數都是 0。 ## 前置 Push Request - 無
jiantw83 added 5 commits 2026-09-01 07:42:17 +00:00
What:
- 新增 hooks/heartbeat.sh,四個子命令:write 寫心跳、check 判定新鮮、report 印現況、clear 清除。
- README 的 hooks 表補一列,事件欄註明不接線;環境變數表補上心跳門檻那一個。

Why:
- 助理是背景行程,別人要知道它還在不在跑,唯一的依據就是它留下的心跳。閘門要判、status 技能要印、巡檢要記,三邊都需要同一份判定。
- 判定散在三個地方一定會漂移,狀態跟訊息就會對不上。所以判定只寫一份,check 與 report 共用同一個探測函式。

How:
- 新鮮的判準是「檔案存在,而且時間戳距現在小於門檻」。門檻預設 300 秒,是心跳週期的五倍,一次網路或磁碟卡頓不會誤判;環境變數可以覆寫,壞值退回預設而不報錯——變數打錯字不該讓判定整個歪掉。
- 絕不看 pid 存活。五支 CLI 與容器裡的行程互相看不到彼此的 pid,看了也證明不了什麼,pid 只當擋人訊息的線索。
- check 用結束碼分四種狀態:新鮮、過期、不存在、時間戳壞掉。前三種的處置各不相同,擋人訊息要說的話也不一樣;第四種既不是「跑過停了」也不是「沒啟動過」,併進任何一邊都會讓訊息說錯話,而且絕不能退回判成新鮮。
- 寫入走暫存檔再更名。直接覆寫的話,剛好讀到寫一半的檔案會少掉時間戳,助理活著卻被判成壞了。
- 這一支不接線,只是被助理與閘門呼叫的工具。接線是後續獨立的一步,先接會在心跳還沒跑起來時就擋死整組技能。

Who:
助理落地的第一塊:先有心跳,閘門才判得動,主體才有東西可寫。
What:
- 三份 manifest 的版本一起提升。
- heartbeat.sh 檔頭引用的技能名由 status 改成 assistant。

Why:
- 上一筆加了 heartbeat.sh 卻沒有動版本號。版本不動,別的 domain 就沒有辦法用相依宣告要求「要有這支腳本的那一版」——宣告寫得出來,卻保證不了內容。助理宣告的下限本來會落在一個不含這支腳本的版本上。
- 檔頭寫的技能名是助理落地初期那一支獨立技能。助理主體把三個操作收攏成一支之後,那個名字就不存在了,照著找會找不到東西。

How:
- 版本由 sync-skill-manifest.sh 同步,三份一致。
- 這一支仍然不接線,只是被助理與閘門呼叫的工具。

Who:
助理主體實作時,從相依宣告那一側回頭抓到的兩個缺口。
What:
- 新增 hooks/assistant-gate.sh:心跳新鮮就放行,心跳不存在、過期或時間戳壞掉就擋下該次技能呼叫。
- 豁免清單十一支、逃生門一個。README 的 hooks 表與環境變數表跟著補。
- 這一版刻意不接線,接線檔一個字都沒動。

Why:
- 助理沒在跑的時候,技能會以為背景有人收尾,實際上沒有。這道閘門把那個落差擋在門外。
- 不接線是因為這台機器的穩定路徑指向開發存放庫,寫進接線檔就立刻對五支 CLI 生效。而現在還沒有心跳,接線的那一秒整組技能會全部鎖死,連修的路徑都走不到。接線的前提是助理已經在跑、心跳穩定。

How:
- 這是整組 hook 裡唯一一道 fail-closed 的閘門。其餘的原則都是資料不足就放行,這一道相反。代價是狀態檔寫不進去時全組停擺,所以逃生門與豁免清單不是選配,是能上線的前提。
- 豁免清單只收解鎖路徑,不收收尾規則。這一點與重啟閘門的方向相反:重啟閘門解鎖靠閘門外的動作,收尾規則要寫得完;這一道解鎖靠跑一支技能,清單收寬了閘門就等於沒有。
- 清單認技能名不認呼叫鏈,所以豁免技能轉呼叫的下一層也要收進來。最要緊的是 wiki 那一支:巡檢要先把結果寫上監控頁才寫心跳,只豁免助理自己會做出「沒心跳就擋 wiki、擋了巡檢跑不完、跑不完就還是沒心跳」的自咬環。
- 心跳判定回「檔案系統問不出來」時放行,不擋。那是判不出事實,不在這道閘門的職權裡;而且那一刻正是磁碟或權限壞掉的訊號,擋下去連豁免那幾支也修不動——它們同樣要寫狀態檔。磁碟壞掉要人去清磁碟,不是把技能組鎖起來。時間戳壞掉則照擋,那是確定沒有可信心跳的證據,而且修法就在豁免清單裡。
- 逃生門的判斷擺在載入共用函式庫之前。函式庫讀不到時 sh 會就地結束並回擋人的那個碼,逃生門也會跟著跑不到,人就繞不過去。
- 擋人一律經 deny.sh 輸出。三支走標準錯誤加結束碼,另外兩支靠標準輸出的內容擋,結束碼固定是零;自己印訊息會在那兩支上無聲失效。
- 心跳不存在、過期、時間戳壞掉三種情況講三種話。使用者要做的事不一樣:一個是從沒啟動過,一個是跑過停了,一個是檔案壞了要重建。

Who:
助理落地的最後一塊。接線與準則那一節另外處理。
Reviewed-on: #68
Reviewed-on: #69
admin approved these changes 2026-09-01 07:51:17 +00:00
admin merged commit af67f79f83 into master 2026-09-01 07:51:20 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: plugins/hooks#70