跨外掛路徑改實體解析,修好失效的版本閘門,並讓接線腳本不再可能把連結指向自己 #82

Merged
admin merged 2 commits from fix/physical-path-resolution-across-plugins into develop 2026-09-03 04:40:20 +00:00
Member

摘要

  • 需求描述:版本閘門對 11 個 domain 全部回「查詢失敗」,等於完全失效,而且是無聲的。根因是跨外掛路徑解析帶著 .. 穿過符號連結,邏輯解析與實體解析的答案不同。同一個根因還有另一種發作方式:接線腳本會把共用連結改成指向自己。兩處都改成實體解析。
  • 計畫名稱:無
  • 計畫頁:無
  • 分析頁:無

變更內容

檔案 為什麼改
hooks/lib.sh 新增 jsc_abs_path() 把路徑正規化成不含 .. 的實體路徑;jsc_gitea_sh() 的四條候選改成尾端統一正規化,正規化失敗退回原樣路徑,「找得到」的判準不變
tools/wire-cli.sh HERE 與 ROOT 改成 cd -P 加 pwd -P。ROOT 會被 ln -sfn 當成目標,邏輯解析會讓它等於連結自己
references/behaviors.md 第 10 列的括號說明改寫:從實體根目錄跑 wire-cli.sh 的規定保留,但性質從必要條件降成多一層保險
plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 版號 0.4.2 升到 0.4.3

設計重點

  • 同一個根因,兩種症狀。 .. 穿過符號連結時,核心與 [ -f ] 用實體解析、跟著連結走;shell 的 cd 用邏輯解析、純文字消去 ..。兩邊答案不同,所以 [ -f ] 檢查通過的路徑,cd 進不去。
  • 症狀一:版本閘門失效。 jsc_gitea_sh 交出帶 .. 的路徑,gitea.sh 的 cd 失敗、回結束碼 2 與空輸出,呼叫端判成查不到。表現是 report 照印表格、每一列都寫「查詢失敗」,而 recommend 回 unverifiable 不回 update——沒有人會被擋,也沒有人知道閘門已經不作用。
  • 症狀二:連結指向自己。 wire-cli.sh 常常經由 current/jsc-hooks 被叫起來,邏輯解析讓 ROOT 等於那條連結,ln -sfn "$ROOT" "$_link" 於是把連結指向自己,之後每一支 hook 的接線路徑都解不開,全機器 hook 一起失效。這件事實際發生過。
  • 選 cd -P 加 pwd -P,不選 readlink -f。 兩者都是 shell 內建,不必在 PATH 上找執行檔。這些函式會在 cron 那種只剩幾段 PATH 的環境下跑,少一個外部相依就少一個解不出來的理由。
  • 把文件規則換成程式保證。 原本擋住症狀二的是兩道防線:事後用 [ -f ] 檢查連結通不通,以及文件要求呼叫端先解出實體根目錄再跑。前者要等連結已經被寫壞才攔得到,後者靠人記得。改成實體解析之後,這個失敗模式不可能成立。文件那條規定保留為保險,因為舊版腳本還在別的機器上跑。

測試結果

  • 驗證在同形佈局做,不是真實安裝版面。 要驗真實版面就得把改好的 lib.sh 寫進外掛快取,那一步被權限機制擋下,沒有繞過。改用暫存區搭一套形狀相同的連結農場:實體版本目錄、旁鄰的 jsc-gitea、一條指過去的 current/jsc-hooks 連結,退三層穿過連結的形狀與真實安裝版面一致。
  • 同一套佈局先塞原版 lib.sh 跑一次,十一列「查詢失敗」一字不差地重現,jsc_gitea_sh 回的正是帶 .. 的路徑,gitea.sh 回 cd: can't cd to ... 與結束碼 2。
  • 換成改過的 lib.sh 再跑一次:十一個 domain 全部查得到遠端版本,behind 0,結束碼 0。
  • 症狀二單獨驗過:用邏輯路徑的 ROOT 跑 ln -sfn,連結被寫成指向自己、session-timer.sh 讀不到;用實體路徑的 ROOT 跑,連結指向實際目錄、可用。失敗模式重現得出來,也確認消掉了。
  • 四條候選路徑各有冒煙測試:JSC_GITEA_TOOLS 帶 .. 的值會被正規化、開發並排版面照舊解得到、四條全落空時 jsc_gitea_sh 回 1 而 version-guard.sh 仍安靜放行結束碼 0。
  • lint-scripts.sh、check-behaviors.sh、ste100-lint.sh 對這個存放庫都回結束碼 0。
  • 真實環境的 current 連結與外掛快取全程沒有動過,wire-cli.sh status claude 仍回 status=wired。
  • 真實安裝版面要等這份修正部署下去才會好。

前置 Push Request

  • 無
## 摘要 - 需求描述:版本閘門對 11 個 domain 全部回「查詢失敗」,等於完全失效,而且是無聲的。根因是跨外掛路徑解析帶著 `..` 穿過符號連結,邏輯解析與實體解析的答案不同。同一個根因還有另一種發作方式:接線腳本會把共用連結改成指向自己。兩處都改成實體解析。 - 計畫名稱:無 - 計畫頁:無 - 分析頁:無 ## 變更內容 | 檔案 | 為什麼改 | | --- | --- | | `hooks/lib.sh` | 新增 `jsc_abs_path()` 把路徑正規化成不含 `..` 的實體路徑;`jsc_gitea_sh()` 的四條候選改成尾端統一正規化,正規化失敗退回原樣路徑,「找得到」的判準不變 | | `tools/wire-cli.sh` | `HERE` 與 `ROOT` 改成 `cd -P` 加 `pwd -P`。`ROOT` 會被 `ln -sfn` 當成目標,邏輯解析會讓它等於連結自己 | | `references/behaviors.md` | 第 10 列的括號說明改寫:從實體根目錄跑 `wire-cli.sh` 的規定保留,但性質從必要條件降成多一層保險 | | `plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` | 版號 0.4.2 升到 0.4.3 | ## 設計重點 - **同一個根因,兩種症狀。** `..` 穿過符號連結時,核心與 `[ -f ]` 用實體解析、跟著連結走;shell 的 `cd` 用邏輯解析、純文字消去 `..`。兩邊答案不同,所以 `[ -f ]` 檢查通過的路徑,`cd` 進不去。 - **症狀一:版本閘門失效。** `jsc_gitea_sh` 交出帶 `..` 的路徑,`gitea.sh` 的 `cd` 失敗、回結束碼 2 與空輸出,呼叫端判成查不到。表現是 `report` 照印表格、每一列都寫「查詢失敗」,而 `recommend` 回 `unverifiable` 不回 `update`——沒有人會被擋,也沒有人知道閘門已經不作用。 - **症狀二:連結指向自己。** `wire-cli.sh` 常常經由 `current/jsc-hooks` 被叫起來,邏輯解析讓 `ROOT` 等於那條連結,`ln -sfn "$ROOT" "$_link"` 於是把連結指向自己,之後每一支 hook 的接線路徑都解不開,全機器 hook 一起失效。這件事實際發生過。 - **選 `cd -P` 加 `pwd -P`,不選 `readlink -f`。** 兩者都是 shell 內建,不必在 PATH 上找執行檔。這些函式會在 cron 那種只剩幾段 PATH 的環境下跑,少一個外部相依就少一個解不出來的理由。 - **把文件規則換成程式保證。** 原本擋住症狀二的是兩道防線:事後用 `[ -f ]` 檢查連結通不通,以及文件要求呼叫端先解出實體根目錄再跑。前者要等連結已經被寫壞才攔得到,後者靠人記得。改成實體解析之後,這個失敗模式不可能成立。文件那條規定保留為保險,因為舊版腳本還在別的機器上跑。 ## 測試結果 - **驗證在同形佈局做,不是真實安裝版面。** 要驗真實版面就得把改好的 `lib.sh` 寫進外掛快取,那一步被權限機制擋下,沒有繞過。改用暫存區搭一套形狀相同的連結農場:實體版本目錄、旁鄰的 `jsc-gitea`、一條指過去的 `current/jsc-hooks` 連結,退三層穿過連結的形狀與真實安裝版面一致。 - 同一套佈局先塞原版 `lib.sh` 跑一次,十一列「查詢失敗」一字不差地重現,`jsc_gitea_sh` 回的正是帶 `..` 的路徑,`gitea.sh` 回 `cd: can't cd to ...` 與結束碼 2。 - 換成改過的 `lib.sh` 再跑一次:十一個 domain 全部查得到遠端版本,`behind 0`,結束碼 0。 - 症狀二單獨驗過:用邏輯路徑的 `ROOT` 跑 `ln -sfn`,連結被寫成指向自己、`session-timer.sh` 讀不到;用實體路徑的 `ROOT` 跑,連結指向實際目錄、可用。失敗模式重現得出來,也確認消掉了。 - 四條候選路徑各有冒煙測試:`JSC_GITEA_TOOLS` 帶 `..` 的值會被正規化、開發並排版面照舊解得到、四條全落空時 `jsc_gitea_sh` 回 1 而 `version-guard.sh` 仍安靜放行結束碼 0。 - `lint-scripts.sh`、`check-behaviors.sh`、`ste100-lint.sh` 對這個存放庫都回結束碼 0。 - 真實環境的 `current` 連結與外掛快取全程沒有動過,`wire-cli.sh status claude` 仍回 `status=wired`。 - 真實安裝版面要等這份修正部署下去才會好。 ## 前置 Push Request - 無
jiantw83 added 2 commits 2026-09-03 04:39:36 +00:00
版本閘門對 11 個 domain 全部回「查詢失敗」,等於完全失效——它擋不下任何版本落後的技能呼叫,而且是無聲的:report 照印表格,只是每一列都寫查詢失敗。

根因是 jsc_gitea_sh 回傳的路徑帶著 ..,而那些 .. 要穿過 current/jsc-hooks 這條符號連結。兩種解析方式對它的答案不同:核心與 [ -f ] 用實體解析、跟著連結走,判定檔案存在;shell 的 cd 用邏輯解析、純文字消去 ..,落到一個不存在的目錄。所以 [ -f ] 檢查通過、路徑交了出去,gitea.sh 的 cd 卻失敗,回結束碼 2 與空輸出,呼叫端就判成查不到。

新增 jsc_abs_path,用 cd -P 加 pwd -P 把路徑正規化成不含 .. 的實體路徑。挑這個做法是因為兩者都是 shell 內建,不必在 PATH 上找執行檔——這些函式會在 cron 那種只剩幾段 PATH 的環境下跑,少一個外部相依就少一個解不出來的理由。jsc_gitea_sh 的四條候選改成尾端統一正規化,正規化失敗就退回原樣路徑,「找得到」的判準不變。

wire-cli.sh 的 HERE 與 ROOT 一併改成實體解析。那是同一個根因的另一種發作方式:ROOT 會被 ln -sfn 當成目標,而這支腳本常常就是經由那條連結被叫起來的,邏輯解析會讓 ROOT 等於連結自己,連結被改成指向自己,全機器 hook 一起失效。這件事實際發生過。原本靠兩道防線擋著:事後的 [ -f ] 檢查,以及文件要求呼叫端先解出實體根目錄。前者要等連結已經被寫壞才攔得到,後者靠人記得。改成實體解析之後這個失敗模式不可能成立。

行為契約第 10 列跟著改:從實體根目錄跑 wire-cli.sh 的規定保留,但性質從必要條件降成多一層保險,並寫明保險為什麼還值得買——舊版腳本還在別的機器上跑。

驗證用同形佈局做:暫存區搭一套一樣形狀的連結農場,先塞原版重現失敗、再塞改版確認修好。真實環境的連結與快取全程沒有動過。
路徑解析的修正要靠版號才傳得到機器端,版本前置檢查才會要求更新。

三份 manifest 由 sync-skill-manifest.sh 同步,只動版本欄位。
admin merged commit 2704a7fa8f into develop 2026-09-03 04:40:20 +00:00
admin deleted branch fix/physical-path-resolution-across-plugins 2026-09-03 04:40:20 +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#82