現象
同一次執行裡,Phase 0.3 判定「有東西要同步、繼續」,Phase 0.5 隨即判定「沒東西可推、exit 0」。兩者條件可同時成立,且沒有任何一方知道對方的存在。
證據
plugins/harness-devtools/skills/plugin-update/SKILL.md:
- L246 Case C —
當 MP_DRIFT=yes OR SHELL_RECENT_TOUCHES 非空:正常 sync use case,print 簡短摘要後進入 Phase 0.5
- L349 Case A —
divergence 0 0 AND git status --short 空 → echo "✗ Nothing to push…"; exit 0
SHELL_RECENT_TOUCHES 問的是「過去 30 天有沒有 commit 碰過這個 plugin」,Case A 問的是「此刻有沒有未推送或未提交的東西」。一個看歷史、一個看當下,兩者正交 —— 所以「30 天內改過 ∧ 現在乾淨」是完全正常的狀態,而它同時滿足兩個互斥的裁決。
根本原因:0.5 假設了一個 0.3 不成立的前提
Phase 0.5 的設計前提是「使用者已自行改好並 commit,來這裡決定要不要 push」。但 Phase 2 的編輯(bump plugin.json、鏡像 marketplace.json)是 skill 自己要做的,在 0.5 執行的當下還沒發生。
於是最乾淨的起始狀態 —— 剛 pull 完、樹是乾淨的、正要來跑一次正常的版本同步 —— 反而觸發 exit 0。gate 在它最該放行的情況下擋住。
實例
2026-08-31 對 che-apple-mail-mcp 出貨 v3.0.0:
Phase 0.3 → Case C(plugin/ 30 天內 8+ 檔變動)→ 繼續
Phase 0.5 → 0 0 divergence + 乾淨樹 → Case A → exit 0
當時是判斷「gate 的目的(別推不相干或做到一半的東西)在 clean tree 下已最大程度滿足」而繼續執行,但那是繞過規則,不是遵守規則。
額外的不一致
Case A 的訊息建議「bypass plugin-update 並直接跑 claude plugin marketplace update」。這在此情境下是錯的指引 —— marketplace.json 的版本此刻還沒 bump,直接刷 cache 只會把舊版本再拉一次。
方向(不指定作法)
問題在「0.5 用『現在有沒有待推送的東西』代理『這次執行結束後會不會有東西要推』」。skill 自己會產生變更這件事,讓這個代理失效。
可能的方向(擇一或其他):
- 讓 0.5 知道 0.3 的裁決 —— Case C 通過時,clean+0 unpushed 應視為合法的起始狀態而非 abort 條件。
- 把 0.5 移到 Phase 2 之後 —— 讓它 gate 的是 skill 已經產生的變更,那才是它文案在講的東西。
- 把 Case A 的 abort 收窄成「0.3 也判定 nothing to sync」時才成立。
順帶:Case A 的建議指令在 0.3 判定 Case C 的情況下應該改掉,理由如上。
Current Status
Phase: verified
Last updated: 2026-09-15 by idd-verify (R4 PASS)
Key Decisions
- R4 PASS(0 CRITICAL/HIGH,16 MEDIUM / 12 LOW / 7 INFO)— 報告:https://github.com/PsychQuant/che-plugin-devtools/issues/19#issuecomment-5674491363;tag
idd-19-verified = 937d40e(已 push)
- R4:Phase 2 Step 4 push 明寫 remote +
HEAD:refs/heads/<branch> 並在 push 後驗 @{u} == HEAD;以 @{u}..HEAD 決定是否 push;Phase 0.3 先解 @{u},解不到即 abort
- R3:Case A fence 自驗 divergence 雙零後 pass-through;E' fence 印新狀態、每次 invocation 最多一次;Phase 5 新增最終 report
- R2:Phase 0.3 對所有 plugin 執行(binary 信號對純 shell 為空);Phase 0.5 只 gate 樹上既有的未提交 / 未推送;Case A 的 bypass 建議改為條件式
- 採 issue 方向 1+3、不採 2:0.5 的價值在動手前攔既有髒東西,不搬到 Phase 2 之後
Scope Changes
Blocking
- (none) — 等使用者
/idd-close #19(idd-all 永不 auto-close)
Commits
現象
同一次執行裡,Phase 0.3 判定「有東西要同步、繼續」,Phase 0.5 隨即判定「沒東西可推、
exit 0」。兩者條件可同時成立,且沒有任何一方知道對方的存在。證據
plugins/harness-devtools/skills/plugin-update/SKILL.md:當 MP_DRIFT=yes OR SHELL_RECENT_TOUCHES 非空:正常 sync use case,print 簡短摘要後進入 Phase 0.5divergence 0 0 AND git status --short 空→echo "✗ Nothing to push…"; exit 0SHELL_RECENT_TOUCHES問的是「過去 30 天有沒有 commit 碰過這個 plugin」,Case A問的是「此刻有沒有未推送或未提交的東西」。一個看歷史、一個看當下,兩者正交 —— 所以「30 天內改過 ∧ 現在乾淨」是完全正常的狀態,而它同時滿足兩個互斥的裁決。根本原因:0.5 假設了一個 0.3 不成立的前提
Phase 0.5 的設計前提是「使用者已自行改好並 commit,來這裡決定要不要 push」。但 Phase 2 的編輯(bump
plugin.json、鏡像marketplace.json)是 skill 自己要做的,在 0.5 執行的當下還沒發生。於是最乾淨的起始狀態 —— 剛 pull 完、樹是乾淨的、正要來跑一次正常的版本同步 —— 反而觸發
exit 0。gate 在它最該放行的情況下擋住。實例
2026-08-31 對
che-apple-mail-mcp出貨 v3.0.0:當時是判斷「gate 的目的(別推不相干或做到一半的東西)在 clean tree 下已最大程度滿足」而繼續執行,但那是繞過規則,不是遵守規則。
額外的不一致
Case A 的訊息建議「bypass plugin-update 並直接跑
claude plugin marketplace update」。這在此情境下是錯的指引 —— marketplace.json 的版本此刻還沒 bump,直接刷 cache 只會把舊版本再拉一次。方向(不指定作法)
問題在「0.5 用『現在有沒有待推送的東西』代理『這次執行結束後會不會有東西要推』」。skill 自己會產生變更這件事,讓這個代理失效。
可能的方向(擇一或其他):
順帶:Case A 的建議指令在 0.3 判定 Case C 的情況下應該改掉,理由如上。
Current Status
Phase: verified
Last updated: 2026-09-15 by idd-verify (R4 PASS)
Key Decisions
idd-19-verified=937d40e(已 push)HEAD:refs/heads/<branch>並在 push 後驗@{u}== HEAD;以@{u}..HEAD決定是否 push;Phase 0.3 先解@{u},解不到即 abortScope Changes
${VAR}全檔掃描(bash 把全形標點併入變數名)與 plugin-debug 兩處殘留一併修,已記 CHANGELOGBlocking
/idd-close #19(idd-all 永不 auto-close)Commits
937d40efix(plugin-update): R4 — refspec push + @{u} 驗證、Phase 0.3 先解 @{u}、Phase 5 report (plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19)3c92e6efix(plugin-update): R3 — Case A 自驗、E' fence、Phase 2 版本驗證 (plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19)e326bd5fix(plugin-update): R2 — Phase 0.3 全 plugin、Phase 0.5 clean-start 通過 (plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19)acfa22afix(plugin-update): Phase 0.3/0.5 判準正交修復 (plugin-update: Phase 0.3 與 Phase 0.5 可對同一次執行給出互相矛盾的裁決 #19)