hooks-install 的腳本呼叫改用執行期解出的字面絕對路徑,讓無人看管的輪次不再卡在第一支腳本 #80

Merged
admin merged 3 commits from fix/literal-absolute-paths-for-hooks-wiring into develop 2026-09-03 02:52:05 +00:00
Member

摘要

  • 需求描述:無人看管的輪次在第一支腳本就被權限層擋下,整輪沒開始就結束。hooks-install 的腳本呼叫改成執行期解出的字面絕對路徑,並把兩個根目錄的解法固定在一個前置步驟裡;同時補掉 repair 那一列既有的行為清單缺漏,三份 manifest 升版。
  • 計畫名稱:無
  • 計畫頁:無
  • 分析頁:無

變更內容

檔案 為什麼改
skills/hooks-install/SKILL.md 新增路徑守則與前置步驟兩節,九處腳本呼叫改成由執行期解出的兩個根目錄開頭;兩條 readlink 各自用 [ -d ] 查過目錄存在;並寫明存放庫自帶的 hook 設定檔為什麼刻意保留變數寫法
references/behaviors.md hooks-install 四列跟著技能校準;另補 repair 那一列缺的收尾 skill-end 事件,那是既有欠帳,HEAD 的內容也過不了行為清單檢查
plugin.json 版號 0.4.1 升到 0.4.2,帶出這批變更
.claude-plugin/plugin.json 同上,由 manifest 同步工具一併升版
.codex-plugin/plugin.json 同上,由 manifest 同步工具一併升版

設計重點

  • 權限層比對的是還沒展開的字面字串。2026-09-02 到 09-03 以非互動模式比照排程環境實測七種寫法:帶變數或帶波浪號的路徑一律解不出來、一律要人核准,連 readlink 與 ls 都要有自己的規則;路徑中段的萬用字元不匹配,所以帶版本號的快取路徑放不進允許清單。放寬允許清單不是解法,寫成字面絕對值才是。
  • 可攜性挪到執行期,不寫死在文件裡。兩個根目錄各以 readlink 解一次,只在前置步驟解,不重解、不為此新增腳本;主代理人解完把兩條字面路徑交給每一個 sub agent,sub agent 自己不解。
  • 守門要查目錄存在,不能只看「非空且絕對」。JSC_HOME 沒設時 readlink -f "$JSC_HOME/current" 印出 /current、結束碼 0,非空又是絕對路徑,前三項全部放行,而那個目錄並不存在。所以兩條 readlink 各自在同一步用 [ -d ] 查過才算數。
  • 兩個根目錄各有各的用途。連結農場根目錄給跨 domain 呼叫用;jsc-hooks 的實體根目錄只給接線腳本用。後面這一條與權限無關:那支腳本從自身位置推出自己的根目錄,又會改寫自己正踩著的那條連結,走連結跑會讓連結指向自己,全機器的 hook 一起失效,這件事實際發生過。
  • 存放庫自帶的 hook 設定檔保留變數寫法,是刻意的例外。那是檔案內容,由 hook 自己的 shell 在執行當下展開,不經過權限層;改成實體路徑等於把某一台機器的路徑寫死進要發佈的檔案。
  • 請審閱者留意:這張 PR 是必要條件,不是充分條件。機器端還要把允許清單補齊到完整的字面路徑,無人看管的輪次才真的跑得動。

測試結果

  • check-behaviors.sh(工作目錄):結束碼 0,回「對上 2 支技能,五個欄位齊全,跡象欄都寫了收尾事件」。
  • check-behaviors.sh(HEAD 內容的快照):結束碼 1,點名 repair 那一列沒寫到收尾的 skill-end 事件。這一筆用來證明那是既有欠帳,不是這次改壞的。
  • lint-scripts.sh:結束碼 0,20 支腳本過語法、可執行與結束碼宣告,其中 1 支判定為函式庫。
  • lint-frontmatter.sh:結束碼 0。
  • ste100-lint.sh:結束碼 0。
  • 三份 manifest 都解得出 JSON,版本欄位一致為 0.4.2。
  • wire-cli.sh status claude:結束碼 0,回 status=wired,九支 hook 的接線點逐項 present。
  • 尚未實測的部分:無人看管的整輪回歸還沒重跑,因為這台機器的允許清單還沒補齊到完整的字面路徑,補齊之後才驗得出來。

前置 Push Request

  • 無
## 摘要 - 需求描述:無人看管的輪次在第一支腳本就被權限層擋下,整輪沒開始就結束。hooks-install 的腳本呼叫改成執行期解出的字面絕對路徑,並把兩個根目錄的解法固定在一個前置步驟裡;同時補掉 repair 那一列既有的行為清單缺漏,三份 manifest 升版。 - 計畫名稱:無 - 計畫頁:無 - 分析頁:無 ## 變更內容 | 檔案 | 為什麼改 | | --- | --- | | `skills/hooks-install/SKILL.md` | 新增路徑守則與前置步驟兩節,九處腳本呼叫改成由執行期解出的兩個根目錄開頭;兩條 `readlink` 各自用 `[ -d ]` 查過目錄存在;並寫明存放庫自帶的 hook 設定檔為什麼刻意保留變數寫法 | | `references/behaviors.md` | hooks-install 四列跟著技能校準;另補 repair 那一列缺的收尾 skill-end 事件,那是既有欠帳,HEAD 的內容也過不了行為清單檢查 | | `plugin.json` | 版號 0.4.1 升到 0.4.2,帶出這批變更 | | `.claude-plugin/plugin.json` | 同上,由 manifest 同步工具一併升版 | | `.codex-plugin/plugin.json` | 同上,由 manifest 同步工具一併升版 | ## 設計重點 - 權限層比對的是還沒展開的字面字串。2026-09-02 到 09-03 以非互動模式比照排程環境實測七種寫法:帶變數或帶波浪號的路徑一律解不出來、一律要人核准,連 `readlink` 與 `ls` 都要有自己的規則;路徑中段的萬用字元不匹配,所以帶版本號的快取路徑放不進允許清單。放寬允許清單不是解法,寫成字面絕對值才是。 - 可攜性挪到執行期,不寫死在文件裡。兩個根目錄各以 `readlink` 解一次,只在前置步驟解,不重解、不為此新增腳本;主代理人解完把兩條字面路徑交給每一個 sub agent,sub agent 自己不解。 - 守門要查目錄存在,不能只看「非空且絕對」。`JSC_HOME` 沒設時 `readlink -f "$JSC_HOME/current"` 印出 `/current`、結束碼 0,非空又是絕對路徑,前三項全部放行,而那個目錄並不存在。所以兩條 `readlink` 各自在同一步用 `[ -d ]` 查過才算數。 - 兩個根目錄各有各的用途。連結農場根目錄給跨 domain 呼叫用;jsc-hooks 的實體根目錄只給接線腳本用。後面這一條與權限無關:那支腳本從自身位置推出自己的根目錄,又會改寫自己正踩著的那條連結,走連結跑會讓連結指向自己,全機器的 hook 一起失效,這件事實際發生過。 - 存放庫自帶的 hook 設定檔保留變數寫法,是刻意的例外。那是檔案內容,由 hook 自己的 shell 在執行當下展開,不經過權限層;改成實體路徑等於把某一台機器的路徑寫死進要發佈的檔案。 - 請審閱者留意:這張 PR 是必要條件,不是充分條件。機器端還要把允許清單補齊到完整的字面路徑,無人看管的輪次才真的跑得動。 ## 測試結果 - `check-behaviors.sh`(工作目錄):結束碼 0,回「對上 2 支技能,五個欄位齊全,跡象欄都寫了收尾事件」。 - `check-behaviors.sh`(HEAD 內容的快照):結束碼 1,點名 repair 那一列沒寫到收尾的 skill-end 事件。這一筆用來證明那是既有欠帳,不是這次改壞的。 - `lint-scripts.sh`:結束碼 0,20 支腳本過語法、可執行與結束碼宣告,其中 1 支判定為函式庫。 - `lint-frontmatter.sh`:結束碼 0。 - `ste100-lint.sh`:結束碼 0。 - 三份 manifest 都解得出 JSON,版本欄位一致為 0.4.2。 - `wire-cli.sh status claude`:結束碼 0,回 `status=wired`,九支 hook 的接線點逐項 present。 - 尚未實測的部分:無人看管的整輪回歸還沒重跑,因為這台機器的允許清單還沒補齊到完整的字面路徑,補齊之後才驗得出來。 ## 前置 Push Request - 無
jiantw83 added 3 commits 2026-09-03 02:38:14 +00:00
無人看管的輪次會在第一支腳本就被擋下,整輪還沒開始就結束。2026-09-02 到 09-03 以非互動模式比照排程環境實測七種寫法,結論很乾淨:權限層比對的是還沒展開的字面字串,帶變數或帶波浪號的路徑一律解不出來,一律要人核准;路徑中段的萬用字元也不匹配,所以帶版本號的快取路徑放不進允許清單。連 readlink、ls 這種只讀指令都要有自己的規則。放寬允許清單救不了這件事,只有把路徑寫成字面絕對值才跑得動。

技能因此多兩節。路徑守則寫明每一次腳本呼叫都要是字面絕對路徑,並說清楚為什麼不能為了可攜性換回變數寫法——變數寫法買不到可攜性,只買到一輪還沒跑到第一階段就死掉。前置步驟把可攜性挪到執行期:兩個根目錄各以 readlink 解一次,只在這裡解,之後不重解,也不為此新增腳本。主代理人解完把兩條字面路徑交給每一個 sub agent,sub agent 自己不解。技能內九處腳本呼叫都改成由這兩個根目錄開頭。

兩條 readlink 各自在同一步用 [ -d ] 查過印出來的目錄真的存在。JSC_HOME 沒設時第一條會印出 /current、結束碼 0,非空又是絕對路徑,只查前三項擋不下來。

解兩個根目錄不是重複。連結農場根目錄給跨 domain 呼叫用;jsc-hooks 的實體根目錄只給接線腳本用,而這一條的理由與權限無關:那支腳本從自身位置推出自己的根目錄,又會改寫自己正踩著的那條連結。走連結跑下去,連結會被指向自己,全機器的 hook 一起失效——這件事實際發生過。

存放庫自帶的 hook 設定檔刻意保留變數寫法,技能文件也把這個例外寫明。那是檔案內容,由 hook 自己的 shell 在執行當下展開,不經過權限層,也不是誰在提示裡打出來的路徑;改成實體路徑等於把某一台機器的路徑寫死進要發佈的檔案。

行為清單的 hooks-install 四列跟著校準。

受影響的是每一個跑 hooks-install 的人,最直接的是排程觸發、沒有人在旁邊核准的那些輪次。
行為清單檢查拿 HEAD 的內容跑就過不了,它點名 repair 那一列的可驗證跡象沒寫到收尾的 skill-end 事件。這是既有欠帳,跟這一批的路徑處理沒有關係,所以單獨一筆,之後要回退哪一邊都不會牽連另一邊。

補上的內容照準則寫:收尾在本機事件流留下這一輪的 skill-end,status 從 ok、blocked、failed、degraded、aborted 五個裡取一個,中途停下的那幾輪也照寫——只有 start 沒有配對的 end,會被讀成中斷。

受影響的是驗收 repair 有沒有跑完的人,還有把行為清單檢查掛在流程裡的每一個存放庫。
hooks-install 的路徑處理與行為清單都變了,版號要帶得出這批變更,版本前置檢查才會要求機器端更新。

三份 manifest 由 sync-skill-manifest.sh 同步,只動版本欄位,內容一致。

受影響的是靠版號判斷要不要更新的每一台機器。
admin merged commit f0fd92eb33 into develop 2026-09-03 02:52:05 +00:00
admin deleted branch fix/literal-absolute-paths-for-hooks-wiring 2026-09-03 02:52:05 +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#80