Problem
Original text (使用者,2026-09-12,剛用 /harness-devtools:plugin-update che-keychain 把 che-keychain 0.3.0 上架之後):
「這個skill有需要改嗎??」
— Source: Claude Code session, che-keychain 0.3.0 release
實跑紀錄(che-keychain 是 self-hosted marketplace:root .claude-plugin/marketplace.json 的 entry 是 source: "./plugin";plugin/.claude-plugin/plugin.json 用 "binaryVersion";wrapper 是 plugin/bin/che-keychain,變數名 REPO=;session-start hook 不 curl、委派給 wrapper):
Phase
實際發生
原因
0.1
find_plugin_marketplace che-keychain → rc=1 → 照 skill 該 abort「不在任何 marketplace」
佈局假設 — #18
0.3 Step 1
IS_BINARY_BACKED=false
(a) PLUGIN_DIR 錯(#18 );(b) 就算路徑對,偵測只認 bin/*-wrapper.sh 含 GITHUB_REPO= 或 hook 內 api.github.com.*releases — che-keychain 三個信號全 miss(本 issue 第 2 點 )
0.3 Step 2
BINARY_VERSION='' → Signal 2 永不觸發
讀 binary_version,檔案寫 binaryVersion(第 1 點 )
0.3 Step 2
MP_DRIFT=yes「剛好」
marketplace entry 原本沒有 version 欄位 → '' ≠ 0.2.0;Phase 2 Step 1 的 p['version'] 對同一份檔案會 KeyError(第 4 點 )
0.5
clean + 0 unpushed → Case A「Nothing to push」
release 剛 push 完、binaryVersion 還是 0.2.0 而 latest tag 已是 v0.3.0;沒有任何 phase 負責「latest release 比 pin 新 → bump」,Phase 1.5 只有反方向的 Signal 2(main 超前 release)(第 3 點 ;abort 本身是 #19 )
5
claude plugin list 顯示 0.3.0 就算完成
沒驗 wrapper 是否真的把新 binary 抓到 ~/bin、.che-keychain.version 是否等於 binaryVersion(第 5 點 )— 我是手動跑 wrapper --version 才確認的
全部都是安靜 的:沒有一個 phase 報錯,skill 自己在 Phase 1.5 就寫過「沉默與『檢查過沒問題』不可區分」(#17 )。本 issue 只收 #18 / #19 沒有 涵蓋的五點;#18 (PLUGIN_DIR / resolver 佈局)與 #19 (0.3 與 0.5 互斥)各自處理。
Type
bug
Expected
欄位名 :binaryVersion(camelCase)與 binary_version(snake_case)二選一為 canonical,另一個在讀取時視同 alias(本機 20 個 binary-backed plugin.json:17 個 binary_version、3 個 binaryVersion;che-keychain / che-transport-mcp 的 wrapper 讀的是 binaryVersion)。Phase 0.3 Step 2 與 Phase 1.5 Signal 2 都改用同一個讀取 helper。
binary-backed 偵測 :改成「bin/ 下任何檔案(不限 *-wrapper.sh)含 releases/(download|latest|tags) 或 gh release download」— 與 issue-driven-dev idd-close Step 6.5 的 has_binary_wrapper 同一判準;hook 委派給 wrapper 的佈局不再漏判。GITHUB_REPO= / REPO= / 其他變數名都能抓到 repo(從 URL pattern 抽,不靠變數名)。
Release 之後的正向信號 :新增「latest release tag(或 --binary-version 引數)比 binaryVersion pin 新」的偵測 → AskUserQuestion「bump pin 到 vX.Y.Z 並同步?」。這是 common-release-flow「release 完自動跑 plugin-update」鏈唯一需要的動作,目前只能手動改 plugin.json。與 plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19 的關係:plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19 讓 0.5 不再對 clean tree abort,本點提供 0.3 判定 sync intent 的正向依據。
marketplace entry 缺 version :視為「缺 → 由 plugin.json 補上」而非 KeyError / 巧合 drift;Phase 2 Step 1 用 .get('version')。
Phase 5 驗證 binary :binary-backed plugin 更新後跑 wrapper --version(或讀 ~/bin/.<name>.version),對比 binaryVersion;不一致就報出來(wrapper 下載失敗常見於 release 漏上傳 asset,Phase 1.5 Signal 1 只在 update 前看一次)。
每一點都要有 fixture 級測試(self-hosted 佈局、binaryVersion 拼法、無 version 的 entry),比照 scripts/test-resolve-marketplace.sh 的做法。
Actual
如上表:五個 phase 在 che-keychain 這類佈局上全部沒有作用,且不報錯;使用者靠手動改 plugin.json、手動跑 wrapper 才完成 0.3.0 上架。
Impact
三個 ./plugin 佈局的 marketplace(che-keychain、che-apple-mail-mcp、che-ical-mcp)與所有 binaryVersion 拼法的 plugin,每次 release 後的 plugin-update 都是手動流程;binary 沒換到新版時沒有任何訊號。
Refs #18 , #19 (同一支 skill 的另外兩個缺陷;三者一起修時共用 plugins/harness-devtools/skills/plugin-update/SKILL.md 與 scripts/resolve-marketplace.sh,需序列化)。
Current Status
Phase : implemented
Last updated : 2026-09-16 by idd-implement (R8)
Key Decisions
R8:E-auto staging 逐檔檢查 + index 一致、最後一次賦值須為字面或 manifest 讀取、partial 讀取不印值、TMPDIR 解析失敗即關座標、Phase 5 PEND 行全狀態、Step 3 checked 只算比對過、Case B no-pin 模板改 wrapper pin 模型、Phase 0.5 abort 提醒
R7:writer 改 mkstemp + 保留 mode(修 R6 掉 exec bit 的回歸)、寫入後失敗獨立出口、helper 載入 fail-closed、測試座標限系統 temp 樹、行內覆寫 / jq 讀法 / 移動 ref 都 rc 4、pin rollback 守門、Signal 1 checked 只算查到的
R6:repo 逐檔分級再跨檔合併、*_REPO= 賦值須被用進下載行、字面 pin 須為最後一個賦值、寫入階段失敗明說已改檔案、E-auto 寫後失敗印 AUDIT、seam 三條件;plugin-update: #22 verify 遺留 — --binary-version 引數、plugin-health KeyError、Step 3 執行門檻、E-auto 跳幅、sidecar 拼法、_pbm_json_field 非字串值 #26 已開、第 1 點 errata 已貼
R5:asset 預檢狀態只升不降、所有 Case E 拒絕走 refuse()(unattended 印 AUDIT 回 75)、同 manifest 多 key 單一 buffer、多 repo 不進 Case E 且 bump 拒絕、Case B 加 no-wrapper、Phase 5 分 PENDING-FETCH / MISMATCH、helper 註解行對稱、BINARY_NAME 只在用於下載時算 asset
R4:key regex 認 macdoc 寫法(非註解行 "<key>"[^"]*:)、Signal 1 先查 v<pin> tag、hook-only 有 pin 的佈局用 hooks 列當下載器、共用 pin 的 wrapper 全部同改且寫後重讀 rc、repo 以 bin/ 為準、asset 只認宣告、E-auto 加 asset 預檢、version 來源不做預設
R3:pin 逐 wrapper 從三種封閉來源讀(wrapper 字面 DESIRED_VERSION / wrapper grep 的 manifest key / none),不再有 manifest 全域 version 回退;plugin_binary_version_effective 刪除
R3:Phase 1.5 Step 2 只算 bin/ 列、hooks 列交 Step 3(有 bin wrapper 的 plugin 跳過);unattended Case E 專用來源 → E-auto(樹乾淨且 0 0 才 commit+push)、version 來源不 bump
R3:Phase 5 逐 bin wrapper 對各自 pin,任一 unresolved 即 UNVERIFIED k/N;收尾移到 Step 1.9
R2:逐 wrapper 驗證、repo 抽取分級、Case E 不 abort unattended(verify R1 6 HIGH)
Plan tier(hard-gate:shared abstraction):新 helper scripts/plugin-binary-meta.sh 收斂 binary metadata 讀取,SKILL.md 各 phase 只消費 helper;Case E 優先於 C
版本 bump 2.5.0 + root marketplace.json 鏡像留到 verify PASS 後最後一個 commit(idd-22-verified 即 2.5.0 的 SHA)
Conflict Class C:與 plugin-update: PLUGIN_DIR 寫死 aggregator 佈局,self-hosted marketplace 會讓 binary gate 靜默失效 #18 / plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19 序列化(同一支 SKILL.md)
Scope Changes
Blocking
verify Round 8 進行中(R1 6 HIGH、R2 8 HIGH、R3 8 HIGH、R4 2 HIGH、R5 2 HIGH、R6 3 HIGH、R7 3 HIGH,皆已修)
Commits
17ac698 fix(plugin-update): E-auto staging 逐檔檢查、最後一次賦值須可讀、partial 不印值、TMPDIR 解析、Phase 5 全狀態、Case B 模板 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22 )
ad265e7 fix(plugin-update): writer 保留 mode 且用 mkstemp、寫入後失敗獨立出口、helper 載入 fail-closed、座標限 temp 樹、pin 讀法邊界、rollback 守門 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22 )
2695ccf fix(plugin-update): repo 逐檔分級再合併、賦值須被用進下載、字面 pin 須為最後一個賦值、寫入失敗不宣稱零變更、seam 三條件 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22 )
e3729d9 fix(plugin-update): Case E asset 預檢單調、拒絕一律 AUDIT、同 manifest 多 key 單一 buffer、多 repo 拒絕、no-wrapper、PENDING-FETCH、註解行對稱 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22 )
9389c9c fix(plugin-update): key regex 認 macdoc 寫法、Signal 1 查 v 前綴 tag、hook-only 有 pin 可達、共用 pin 全部同改、repo 以 bin 為準 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22 )
475f905 fix(plugin-update): pin 逐 wrapper 從三種來源讀、hooks 列分流、unattended Case E-auto commit+push、Phase 5 不再假 all-N (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22 )
e5c0a27 fix(plugin-update): 逐 wrapper 驗證、repo 抽取分級、version 回退顯式化、Case E 不 abort unattended (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22 )
72996f8 feat(plugin-update): binary metadata 收斂到 plugin-binary-meta.sh、Phase 0.3 加 release-ahead Case E、Phase 5 驗 binary 到位 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22 )
Problem
實跑紀錄(che-keychain 是 self-hosted marketplace:root
.claude-plugin/marketplace.json的 entry 是source: "./plugin";plugin/.claude-plugin/plugin.json用"binaryVersion";wrapper 是plugin/bin/che-keychain,變數名REPO=;session-start hook 不 curl、委派給 wrapper):find_plugin_marketplace che-keychain→ rc=1 → 照 skill 該 abort「不在任何 marketplace」IS_BINARY_BACKED=falsebin/*-wrapper.sh含GITHUB_REPO=或 hook 內api.github.com.*releases— che-keychain 三個信號全 miss(本 issue 第 2 點)BINARY_VERSION=''→ Signal 2 永不觸發binary_version,檔案寫binaryVersion(第 1 點)MP_DRIFT=yes「剛好」version欄位 →''≠0.2.0;Phase 2 Step 1 的p['version']對同一份檔案會 KeyError(第 4 點)binaryVersion還是 0.2.0 而 latest tag 已是 v0.3.0;沒有任何 phase 負責「latest release 比 pin 新 → bump」,Phase 1.5 只有反方向的 Signal 2(main 超前 release)(第 3 點;abort 本身是 #19)claude plugin list顯示 0.3.0 就算完成~/bin、.che-keychain.version是否等於binaryVersion(第 5 點)— 我是手動跑 wrapper--version才確認的全部都是安靜的:沒有一個 phase 報錯,skill 自己在 Phase 1.5 就寫過「沉默與『檢查過沒問題』不可區分」(#17)。本 issue 只收 #18 / #19 沒有涵蓋的五點;#18(PLUGIN_DIR / resolver 佈局)與 #19(0.3 與 0.5 互斥)各自處理。
Type
bug
Expected
binaryVersion(camelCase)與binary_version(snake_case)二選一為 canonical,另一個在讀取時視同 alias(本機 20 個 binary-backed plugin.json:17 個binary_version、3 個binaryVersion;che-keychain / che-transport-mcp 的 wrapper 讀的是binaryVersion)。Phase 0.3 Step 2 與 Phase 1.5 Signal 2 都改用同一個讀取 helper。bin/下任何檔案(不限*-wrapper.sh)含releases/(download|latest|tags)或gh release download」— 與 issue-driven-devidd-closeStep 6.5 的has_binary_wrapper同一判準;hook 委派給 wrapper 的佈局不再漏判。GITHUB_REPO=/REPO=/ 其他變數名都能抓到 repo(從 URL pattern 抽,不靠變數名)。--binary-version引數)比binaryVersionpin 新」的偵測 → AskUserQuestion「bump pin 到 vX.Y.Z 並同步?」。這是 common-release-flow「release 完自動跑 plugin-update」鏈唯一需要的動作,目前只能手動改 plugin.json。與 plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19 的關係:plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19 讓 0.5 不再對 clean tree abort,本點提供 0.3 判定 sync intent 的正向依據。version:視為「缺 → 由 plugin.json 補上」而非 KeyError / 巧合 drift;Phase 2 Step 1 用.get('version')。--version(或讀~/bin/.<name>.version),對比binaryVersion;不一致就報出來(wrapper 下載失敗常見於 release 漏上傳 asset,Phase 1.5 Signal 1 只在 update 前看一次)。每一點都要有 fixture 級測試(self-hosted 佈局、
binaryVersion拼法、無version的 entry),比照scripts/test-resolve-marketplace.sh的做法。Actual
如上表:五個 phase 在 che-keychain 這類佈局上全部沒有作用,且不報錯;使用者靠手動改 plugin.json、手動跑 wrapper 才完成 0.3.0 上架。
Impact
三個
./plugin佈局的 marketplace(che-keychain、che-apple-mail-mcp、che-ical-mcp)與所有binaryVersion拼法的 plugin,每次 release 後的 plugin-update 都是手動流程;binary 沒換到新版時沒有任何訊號。Refs #18, #19(同一支 skill 的另外兩個缺陷;三者一起修時共用
plugins/harness-devtools/skills/plugin-update/SKILL.md與scripts/resolve-marketplace.sh,需序列化)。Current Status
Phase: implemented
Last updated: 2026-09-16 by idd-implement (R8)
Key Decisions
*_REPO=賦值須被用進下載行、字面 pin 須為最後一個賦值、寫入階段失敗明說已改檔案、E-auto 寫後失敗印 AUDIT、seam 三條件;plugin-update: #22 verify 遺留 — --binary-version 引數、plugin-health KeyError、Step 3 執行門檻、E-auto 跳幅、sidecar 拼法、_pbm_json_field 非字串值 #26 已開、第 1 點 errata 已貼"<key>"[^"]*:)、Signal 1 先查v<pin>tag、hook-only 有 pin 的佈局用 hooks 列當下載器、共用 pin 的 wrapper 全部同改且寫後重讀 rc、repo 以 bin/ 為準、asset 只認宣告、E-auto 加 asset 預檢、version來源不做預設DESIRED_VERSION/ wrapper grep 的 manifest key / none),不再有 manifest 全域version回退;plugin_binary_version_effective刪除version來源不 bumpUNVERIFIED k/N;收尾移到 Step 1.9scripts/plugin-binary-meta.sh收斂 binary metadata 讀取,SKILL.md 各 phase 只消費 helper;Case E 優先於 Cidd-22-verified即 2.5.0 的 SHA)Scope Changes
--binary-versionCLI 引數替代方案不實作(issue 寫「或」)→ follow-up issue*-wrapper.sh掃描 → plugin-debug / plugin-deploy 也寫死 plugins/<name> 佈局 (sister concern from #18) #23;has_binary_wrapper的bin/*.sh→ issue-driven-development#344Blocking
Commits
17ac698fix(plugin-update): E-auto staging 逐檔檢查、最後一次賦值須可讀、partial 不印值、TMPDIR 解析、Phase 5 全狀態、Case B 模板 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22)ad265e7fix(plugin-update): writer 保留 mode 且用 mkstemp、寫入後失敗獨立出口、helper 載入 fail-closed、座標限 temp 樹、pin 讀法邊界、rollback 守門 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22)2695ccffix(plugin-update): repo 逐檔分級再合併、賦值須被用進下載、字面 pin 須為最後一個賦值、寫入失敗不宣稱零變更、seam 三條件 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22)e3729d9fix(plugin-update): Case E asset 預檢單調、拒絕一律 AUDIT、同 manifest 多 key 單一 buffer、多 repo 拒絕、no-wrapper、PENDING-FETCH、註解行對稱 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22)9389c9cfix(plugin-update): key regex 認 macdoc 寫法、Signal 1 查 v 前綴 tag、hook-only 有 pin 可達、共用 pin 全部同改、repo 以 bin 為準 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22)475f905fix(plugin-update): pin 逐 wrapper 從三種來源讀、hooks 列分流、unattended Case E-auto commit+push、Phase 5 不再假 all-N (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22)e5c0a27fix(plugin-update): 逐 wrapper 驗證、repo 抽取分級、version 回退顯式化、Case E 不 abort unattended (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22)72996f8feat(plugin-update): binary metadata 收斂到 plugin-binary-meta.sh、Phase 0.3 加 release-ahead Case E、Phase 5 驗 binary 到位 (plugin-update: binary-backed detection misses binaryVersion, non-wrapper.sh wrappers, missing marketplace version, and the post-release pin bump; Phase 5 never verifies the binary #22)