feat(hooks): 新增部署後強制重啟閘門,接線與冒煙同步到八支 hook #33

Merged
admin merged 5 commits from feat/skillset-governance/restart-gate into feat/skillset-governance/main 2026-08-27 08:38:50 +00:00
Member

feat(hooks): 新增部署後強制重啟閘門,接線與冒煙同步到八支 hook

摘要

  • 需求描述:使用者 15 條規則第三群「技能組治理」的 R15 在 jsc-hooks 的落地。部署後強制重啟:狀態檔 $JSC_HOME/restart-required 存在時擋下技能呼叫,逃生門 JSC_RESTART_GATE=off,豁免九支(jsc-cli:deploy、jsc-hooks:hooks-install、jsc-gitea:wiki、jsc-log:worklog、jsc-log:learn、jsc-meta:*、jsc-ask:ask、jsc-git:pr、jsc-git:commit)。狀態檔由 jsc-cli:deploy 收尾寫入、由新工作階段開始時清除。決策紀錄在 wiki knowledges/QUESTION 的 QUESTION_FB8DF0B5。
  • 計畫名稱:無
  • 計畫頁:無
  • 分析頁:無

變更內容

檔案 為什麼改
hooks/restart-gate.sh 新增第八支 hook。hook 模式擋下未重啟就繼續呼叫的技能,另有 require、clear、report 三個子命令供 jsc-cli 與 session-timer.sh 呼叫
hooks/hooks.json 閘門要有判定點才擋得下來。接到 PreToolUse(Skill),排在 version-guard.sh 前面
hooks/session-timer.sh 閘門要放得下來才不會自鎖。start 與 restart 判定為新工作階段時轉呼叫 restart-gate.sh clear
tools/wire-cli.sh 新 hook 不點名就漏得掉、不自備狀態檔就只走得到捷徑。smoke 補八條判定路徑與清除機制,claude 的接線驗證與 status 各加一個檢查點,四個 CLI 的 degraded 說法補上重啟閘門接不上
skills/hooks-install/SKILL.md 技能是接線的對外說法。hook 清單七支改八支,降級說明補上四個 CLI 的後果,備註補 restart-gate.sh 與 session-timer.sh 的清除職責
README.md、AGENTS.md 狀態檔格式是與 jsc-cli 的介面,只寫在腳本註解裡另一邊看不到。新增 hook 列、「部署後重啟狀態檔」一節、JSC_RESTART_GATE 環境變數列,覆蓋範圍改八支
plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 行為變更要升版,各 CLI 才知道要更新;0.2.3 升到 0.2.4,三份同版號

設計重點

  • 判定看檔案在不在,欄位只用在訊息上。 狀態檔存在就是「還沒重啟過」的證據;at、mode、domains、cli 四個欄位缺了只讓擋人訊息少幾個字,不影響判定。格式是純文字 key=value,與工作包狀態檔同一套,jsc-hooks 與 jsc-cli 兩邊各自實作也對得上。
  • 只擋確定違規,其餘一律放行。 比照 version-guard.sh:狀態檔讀不到、技能名取不到、工具名不是 Skill、技能不是 jsc-*:*,四種都 exit 0。沒有證據時擋下等於停掉每一次技能呼叫。
  • 閘門不自鎖。 九支豁免的理由收斂成同一件事:部署後還有技能組異動報告(R13)與工作日誌(R1)要收尾,整批擋下去「先重啟」與「先寫完報告」互相打死。後三支(jsc-ask:ask、jsc-git:pr、jsc-git:commit)自己不是收尾規則的主體,是為了讓前六支走得完才補進來的——清單認的是技能名,不是呼叫鏈,豁免技能轉呼叫的下一層若不在清單上照樣會被擋。
  • 清除的判準留在 session-timer.sh,狀態檔留在 restart-gate.sh。 新舊工作階段的判準(sessions/{sid}.start 在不在)只有 session-timer.sh 知道,兩邊各寫一份就會漂移。續接同一階段(SessionStart 再觸發、resume、compact)走不到那一段,閘門就一路留到真的重新啟動。
  • 子命令與標準輸入的分界。 require、clear、report 都不讀標準輸入,只有 hook 模式讀;工具端呼叫一律補 </dev/null。理由與 sdlc-gate.sh 相同:標準輸入是管線又沒人關閉時,讀取會一直等,呼叫端整支卡死。
  • 接線順序排在 version-guard.sh 前面。 還沒重啟的舊版比落後一個版號更該先攔;先問「你在跑的是不是舊版」,再問「磁碟上的是不是最新版」。
  • 覆蓋範圍據實回報。 只有 claude 有 pre-tool hook,八支全接得上;codex、copilot、antigravity、kiro 上這道閘門一次技能呼叫都擋不下來,狀態檔照樣寫、下次工作階段開始照樣清,只是中間沒有判定點,重啟得靠 /jsc-cli:deploy 收尾的提示自己動手。這一句在 wire-cli.sh、技能與 README 三處都寫一致。

測試結果

  • tools/ste100-lint.sh 對本存取庫全綠;三份 manifest 版本一致為 0.2.4。
  • restart-gate.sh 判定路徑逐條實測:狀態檔不存在放行(exit 0);狀態檔存在且技能為 jsc-sdlc:implement 或 jsc-sdlc:plan 擋下(exit 2);九支豁免技能全放行(exit 0);JSC_RESTART_GATE=off 放行;技能名取不到放行。
  • 清除機制實測:同一個 session id 續接不清、換 session id 清、restart 子命令清。
  • tools/wire-cli.sh smoke claude → status=ok,14 項判定路徑全部「與預期相同」。
  • 跨 repo 對齊實測:jsc-cli 的 deploy.sh update 經 restart-gate.sh require 寫出的狀態檔是 key=value 四欄,本端讀得懂並正確擋下。修正前 jsc-cli 自己寫四欄 TSV、本端讀 key=value,兩邊對不上;改成由 jsc-cli 轉呼叫 require 後才通過。
  • 四個 domain(meta、cli、hooks、gitea)的 ste100-lint.sh 全綠,三份 manifest 版本各自一致。

前置 Push Request

  • 無
# feat(hooks): 新增部署後強制重啟閘門,接線與冒煙同步到八支 hook ## 摘要 - 需求描述:使用者 15 條規則第三群「技能組治理」的 R15 在 `jsc-hooks` 的落地。部署後強制重啟:狀態檔 `$JSC_HOME/restart-required` 存在時擋下技能呼叫,逃生門 `JSC_RESTART_GATE=off`,豁免九支(`jsc-cli:deploy`、`jsc-hooks:hooks-install`、`jsc-gitea:wiki`、`jsc-log:worklog`、`jsc-log:learn`、`jsc-meta:*`、`jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`)。狀態檔由 `jsc-cli:deploy` 收尾寫入、由新工作階段開始時清除。決策紀錄在 wiki `knowledges/QUESTION` 的 `QUESTION_FB8DF0B5`。 - 計畫名稱:無 - 計畫頁:無 - 分析頁:無 ## 變更內容 | 檔案 | 為什麼改 | | --- | --- | | `hooks/restart-gate.sh` | 新增第八支 hook。hook 模式擋下未重啟就繼續呼叫的技能,另有 `require`、`clear`、`report` 三個子命令供 `jsc-cli` 與 `session-timer.sh` 呼叫 | | `hooks/hooks.json` | 閘門要有判定點才擋得下來。接到 PreToolUse(Skill),排在 `version-guard.sh` 前面 | | `hooks/session-timer.sh` | 閘門要放得下來才不會自鎖。`start` 與 `restart` 判定為新工作階段時轉呼叫 `restart-gate.sh clear` | | `tools/wire-cli.sh` | 新 hook 不點名就漏得掉、不自備狀態檔就只走得到捷徑。`smoke` 補八條判定路徑與清除機制,claude 的接線驗證與 `status` 各加一個檢查點,四個 CLI 的 degraded 說法補上重啟閘門接不上 | | `skills/hooks-install/SKILL.md` | 技能是接線的對外說法。hook 清單七支改八支,降級說明補上四個 CLI 的後果,備註補 `restart-gate.sh` 與 `session-timer.sh` 的清除職責 | | `README.md`、`AGENTS.md` | 狀態檔格式是與 `jsc-cli` 的介面,只寫在腳本註解裡另一邊看不到。新增 hook 列、「部署後重啟狀態檔」一節、`JSC_RESTART_GATE` 環境變數列,覆蓋範圍改八支 | | `plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` | 行為變更要升版,各 CLI 才知道要更新;0.2.3 升到 0.2.4,三份同版號 | ## 設計重點 - **判定看檔案在不在,欄位只用在訊息上。** 狀態檔存在就是「還沒重啟過」的證據;`at`、`mode`、`domains`、`cli` 四個欄位缺了只讓擋人訊息少幾個字,不影響判定。格式是純文字 `key=value`,與工作包狀態檔同一套,`jsc-hooks` 與 `jsc-cli` 兩邊各自實作也對得上。 - **只擋確定違規,其餘一律放行。** 比照 `version-guard.sh`:狀態檔讀不到、技能名取不到、工具名不是 `Skill`、技能不是 `jsc-*:*`,四種都 exit 0。沒有證據時擋下等於停掉每一次技能呼叫。 - **閘門不自鎖。** 九支豁免的理由收斂成同一件事:部署後還有技能組異動報告(R13)與工作日誌(R1)要收尾,整批擋下去「先重啟」與「先寫完報告」互相打死。後三支(`jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`)自己不是收尾規則的主體,是為了讓前六支走得完才補進來的——清單認的是技能名,不是呼叫鏈,豁免技能轉呼叫的下一層若不在清單上照樣會被擋。 - **清除的判準留在 `session-timer.sh`,狀態檔留在 `restart-gate.sh`。** 新舊工作階段的判準(`sessions/{sid}.start` 在不在)只有 `session-timer.sh` 知道,兩邊各寫一份就會漂移。續接同一階段(SessionStart 再觸發、resume、compact)走不到那一段,閘門就一路留到真的重新啟動。 - **子命令與標準輸入的分界。** `require`、`clear`、`report` 都不讀標準輸入,只有 hook 模式讀;工具端呼叫一律補 `</dev/null`。理由與 `sdlc-gate.sh` 相同:標準輸入是管線又沒人關閉時,讀取會一直等,呼叫端整支卡死。 - **接線順序排在 `version-guard.sh` 前面。** 還沒重啟的舊版比落後一個版號更該先攔;先問「你在跑的是不是舊版」,再問「磁碟上的是不是最新版」。 - **覆蓋範圍據實回報。** 只有 claude 有 pre-tool hook,八支全接得上;codex、copilot、antigravity、kiro 上這道閘門一次技能呼叫都擋不下來,狀態檔照樣寫、下次工作階段開始照樣清,只是中間沒有判定點,重啟得靠 `/jsc-cli:deploy` 收尾的提示自己動手。這一句在 `wire-cli.sh`、技能與 README 三處都寫一致。 ## 測試結果 - `tools/ste100-lint.sh` 對本存取庫全綠;三份 manifest 版本一致為 0.2.4。 - `restart-gate.sh` 判定路徑逐條實測:狀態檔不存在放行(exit 0);狀態檔存在且技能為 `jsc-sdlc:implement` 或 `jsc-sdlc:plan` 擋下(exit 2);九支豁免技能全放行(exit 0);`JSC_RESTART_GATE=off` 放行;技能名取不到放行。 - 清除機制實測:同一個 session id 續接不清、換 session id 清、`restart` 子命令清。 - `tools/wire-cli.sh smoke claude` → `status=ok`,14 項判定路徑全部「與預期相同」。 - 跨 repo 對齊實測:`jsc-cli` 的 `deploy.sh update` 經 `restart-gate.sh require` 寫出的狀態檔是 `key=value` 四欄,本端讀得懂並正確擋下。修正前 `jsc-cli` 自己寫四欄 TSV、本端讀 `key=value`,兩邊對不上;改成由 `jsc-cli` 轉呼叫 `require` 後才通過。 - 四個 domain(meta、cli、hooks、gitea)的 `ste100-lint.sh` 全綠,三份 manifest 版本各自一致。 ## 前置 Push Request - 無
jiantw83 added 5 commits 2026-08-27 08:34:28 +00:00
What:新增第八支 hook `hooks/restart-gate.sh`。hook 模式在 `$JSC_HOME/restart-required` 存在時以 exit 2 擋下 jsc 技能呼叫,另有三個子命令:`require {模式} [{domain}...]` 寫入狀態檔掛上閘門、`clear` 清除狀態檔、`report` 印出狀態檔內容。`hooks/hooks.json` 把它接到 PreToolUse(Skill),排在 `version-guard.sh` 前面。`hooks/session-timer.sh` 的 `start` 與 `restart` 在判定為新工作階段時轉呼叫 `clear`。

Why:部署換掉的是磁碟上的技能檔,正在跑的 CLI 行程載入的還是舊版——SKILL.md、hook 腳本與 tools 都在啟動當下讀進記憶體。這段落差期間跑技能,改動看起來沒生效,人會以為部署失敗又重跑一次。所以部署收尾要求重新啟動,這道閘門負責讓「還沒重啟就繼續用技能」擋在門外。

How:判定看的是「檔案在不在」,欄位只用在擋人訊息上——欄位缺了只讓訊息少幾個字,不影響判定。狀態檔用純文字 `key=value`(`at`、`mode`、`domains`、`cli`),格式與工作包狀態檔同一套,`jsc-hooks` 與 `jsc-cli` 兩邊各自實作也對得上。放行原則比照 `version-guard.sh`:只擋確定違規,狀態檔讀不到、技能名取不到、工具名不是 `Skill`、技能不是 `jsc-*:*` 一律 exit 0,沒有證據時擋下等於停掉每一次技能呼叫。九支豁免(`jsc-cli:deploy`、`jsc-hooks:hooks-install`、`jsc-gitea:wiki`、`jsc-log:worklog`、`jsc-log:learn`、`jsc-meta:*`、`jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`)的理由是同一件事:部署後還要寫得完技能組異動報告與工作日誌,整批擋下去「先重啟」與「先寫完報告」會互相打死。後三支自己不是收尾規則的主體,是為了讓前六支走得完才補進來的——清單認的是技能名,不是呼叫鏈。清除交給 `session-timer.sh`,不由本檔自己判:新舊工作階段的判準(`sessions/{sid}.start` 在不在)只有那支腳本知道,兩邊各寫一份就會漂移;續接同一階段走不到那一段,閘門就一路留到真的重新啟動。`require`、`clear`、`report` 都不讀標準輸入,只有 hook 模式讀,工具端呼叫一律再補 `</dev/null`,理由與 `sdlc-gate.sh` 相同。接線排在 `version-guard.sh` 前面:還沒重啟的舊版比落後一個版號更該先攔。不認得的子命令一律安靜 exit 0,不中斷宿主 CLI。擋人訊息依實際 CLI 給重啟方式,並附上豁免清單與逃生門 `JSC_RESTART_GATE=off`。

Who:部署後的第一次技能呼叫,以及 `jsc-cli:deploy` 收尾寫入狀態檔的那一端。
What:`tools/wire-cli.sh` 四處增修。`smoke` 加跑 `restart-gate.sh`,另外自備一份暫時的 `$JSC_HOME`,把八條判定路徑(狀態檔不存在、狀態檔存在且技能為 `jsc-sdlc:implement`、四支豁免技能各一條、逃生門、取不到技能名)各跑一次並比對結束碼,再驗一次清除機制真的清得掉;claude 的接線驗證與 `status` 盤點各加一個 `restart-gate` 檢查點;codex、copilot、antigravity、kiro 四支的 degraded 說法補上「部署後重啟閘門也接不上」;檔頭與各處「七支」一律改為「八支」。

Why:新的 hook 接進 `hooks.json` 只代表宣告在檔案裡。接線驗證不點名就漏得掉——`comment-scope.sh` 與 `lang-guard.sh` 當初就是為同一個原因各列一項。冒煙測試也一樣:不自備狀態檔,只走得到「狀態檔不存在」與「取不到技能名」兩條捷徑,擋人與豁免那幾條一次都跑不到,判定寫了卻沒驗等於沒寫。四個 CLI 的降級說法不補,回報就會暗示每個 CLI 都擋得下來。

How:驗的是判定結果本身,不只是腳本跑得完——每一條路徑都給定預期結束碼,對不上就計入失敗並印出前 200 字的輸出。狀態檔放在暫時目錄,冒煙測試不該把使用者真正的 `$JSC_HOME/restart-required` 拆掉。清除機制那一段刻意走 `session-timer.sh start` 這條真實路徑,不直接呼叫 `clear`:要驗的是「新工作階段會不會清」,不是「`clear` 這個子命令能不能刪檔」。每次呼叫都接 `</dev/null`:hook 模式會讀標準輸入,管線沒人關閉時整支會卡死。建不出暫存目錄也算失敗,不能靜悄悄跳過。四支非 claude 的 CLI 除了 degraded 一句,另外逐支印一行講明後果:一次技能呼叫都擋不下來,狀態檔照樣寫、下次工作階段開始照樣清,只是中間沒有判定點,重啟要靠 `/jsc-cli:deploy` 收尾的提示自己動手。

Who:`jsc-hooks:hooks-install` 的接線、冒煙與盤點三個子命令。
What:`skills/hooks-install/SKILL.md` 四處增修。`description` 與目標段落的 hook 清單加入 `restart-gate.sh`,七支改八支;降級說明補上「四個 CLI 連部署後重啟閘門也接不上」與後果一句;第 4 步的冒煙說明補上重啟閘門的每條判定路徑;備註新增 `restart-gate.sh` 一條,並在 `session-timer.sh` 那一條補上「`start` 與 `restart` 會清除閘門」。

Why:技能是接線這件事的對外說法。hook 加了一支、技能還寫七支,回報就會少一項,而且降級說法不補會暗示每個 CLI 都擋得下來——這正是準則明文禁止的。`session-timer.sh` 那條也要補:清除閘門掛在那兩個事件上,接線少了它們,閘門會一路擋到使用者自己下逃生門。

How:降級那一段把後果講明白,不只說「接不上」:在那四個 CLI 上一次技能呼叫都擋不下來,狀態檔照樣寫、下一個工作階段開始照樣清,重啟本身只靠 `jsc-cli:deploy` 的收尾訊息。備註那一條寫出豁免清單與逃生門,並講明豁免的理由是異動報告與工作日誌要寫得完,讓接線的人知道哪些技能在閘門升起時仍然叫得動。

Who:`/jsc-hooks:hooks-install` 的接線流程與對使用者的回報。
What:`README.md` 五處增修:hook 表新增 `hooks/restart-gate.sh` 一列,`session-timer.sh` 那一列補上會清除閘門、`wire-cli.sh` 那一列補上冒煙新增的判定路徑;覆蓋範圍那段的「七支」改「八支」並補上四個 CLI 接不上重啟閘門的後果;新增「部署後重啟狀態檔」一節,用表列出四個欄位與範例,寫明誰寫誰讀;環境變數表新增 `JSC_RESTART_GATE` 一列;`hooks-install` 段落同步。`AGENTS.md` 的 domain 一句話說明補上「部署後強制重啟閘門」。

Why:狀態檔的格式是 `jsc-hooks` 與 `jsc-cli` 兩邊的介面。介面只寫在腳本註解裡,另一邊改的時候看不到,格式一走鐘閘門就掛不上——這次就真的發生過:`jsc-cli` 寫四欄 TSV、這邊讀 `key=value`,狀態檔存在卻解不出欄位。覆蓋範圍也要據實寫,八支 hook 只有 claude 全接得上。

How:格式壓到最簡的純文字 `key=value`,一行一欄位,順序不拘,不認得的鍵一律忽略,與工作包狀態檔同一套寫法,兩邊各自實作也對得上。表只寫欄位與範例,判定規則寫在表底下:判定看檔案在不在、欄位只用在擋人訊息上、寫的一律是 `jsc-cli:deploy`、清的一律是 `session-timer.sh`,而且判準留在 `session-timer.sh`、狀態檔留在 `restart-gate.sh`,兩邊都不抄對方那一半。

Who:讀 `jsc-hooks` 說明的人,以及 `jsc-cli` 那一側寫 `deploy.sh` 的人。
What:`plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 的 `version` 由 0.2.3 改為 0.2.4,`description` 的 hook 清單從五支補齊到八支,補上註解範圍守門、繁中編碼守門與部署後強制重啟。

Why:本次新增第八支 hook `restart-gate.sh` 並接進 `hooks.json`,`session-timer.sh` 多了清除閘門這件事,接線與冒煙也跟著改,屬於行為變更,版本要跟著往上走,各 CLI 才知道要更新。`description` 原本只列到「版本前置檢查」,而註解範圍與繁中編碼兩支早就在跑了,`README.md` 與 `AGENTS.md` 也都寫了八支——manifest 是各 CLI 安裝時唯一看得到的說明,落後就會讓人以為這個外掛只有五支 hook。

How:三份只改 `version` 與 `description` 兩個欄位,其餘內容不動,三份保持同一版號與同一段描述。

Who:`jsc-hooks` 外掛的套件描述檔。
admin merged commit fe7a3abe2a into feat/skillset-governance/main 2026-08-27 08:38:50 +00:00
admin deleted branch feat/skillset-governance/restart-gate 2026-08-27 08:38:50 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Reference: plugins/hooks#33