{"allowContribute":false,"item":{"artifactHtml":"\u003cstyle\u003e\n  :root { --paper:#FFFBF5; --ink:#1A1207; --ink-2:#6B5E4F; --accent:#C45A12; --accent-2:#9A3B00; --soft:#FFF0DB; --soft-2:#FFF7EB; --hair:#E8DDD0; --ok:#0F6B4A; --warn:#C45A12; --bad:#B91C1C; }\n  :root[data-theme=\"dark\"] { --paper:#1A150E; --ink:#F5EDE0; --ink-2:#B8AA96; --accent:#FF8A3D; --accent-2:#FFB26B; --soft:#2A2014; --soft-2:#241E14; --hair:#3A3226; --ok:#4ADE80; --warn:#FF8A3D; --bad:#F87171; }\n  .doc { background:var(--paper); color:var(--ink); padding:28px 30px; font:16px/1.75 -apple-system,\"PingFang TC\",\"Noto Sans TC\",sans-serif; }\n  .eyebrow { font:700 11px ui-monospace,Menlo,monospace; letter-spacing:.14em; color:var(--accent); text-transform:uppercase; }\n  h1 { font-size:27px; line-height:1.25; letter-spacing:-.02em; margin:10px 0 0; max-width:22ch; }\n  .lede { color:var(--ink-2); margin:10px 0 0; font-size:15px; line-height:1.6; max-width:62ch; }\n  .meta { display:flex; flex-wrap:wrap; gap:8px; margin:14px 0 0; }\n  .chip { font:600 11px ui-monospace,Menlo,monospace; letter-spacing:.04em; padding:5px 9px; border-radius:999px; border:1px solid var(--hair); background:var(--soft-2); color:var(--ink-2); }\n  .chip.bad { background:#FEF2F2; color:var(--bad); border-color:#FECACA; }\n  :root[data-theme=\"dark\"] .chip.bad { background:#3A1818; color:#FCA5A5; border-color:#7F1D1D; }\n  .chip.warn { background:#FFF7ED; color:var(--warn); border-color:#FFD6A8; }\n  :root[data-theme=\"dark\"] .chip.warn { background:#2E1E0E; color:#FFB26B; border-color:#7C3A0A; }\n  .stats { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:10px; margin:20px 0; }\n  .stat { background:var(--soft); border:1px solid var(--hair); border-radius:10px; padding:13px 14px; }\n  .stat .k { font:700 11px ui-monospace,Menlo,monospace; letter-spacing:.08em; color:var(--accent); }\n  .stat .v { font-size:20px; font-weight:800; margin-top:4px; line-height:1.2; }\n  .stat .n { font-size:12px; color:var(--ink-2); margin-top:3px; }\n  .callout { display:flex; gap:12px; padding:14px 16px; background:var(--soft); border:1px solid var(--hair); border-radius:10px; margin:18px 0; align-items:flex-start; }\n  .callout .tag { flex:none; font:800 11px ui-monospace,Menlo,monospace; letter-spacing:.08em; color:#fff; background:var(--accent); padding:4px 7px; border-radius:6px; }\n  .callout p { margin:0; color:var(--ink); font-size:14px; line-height:1.6; }\n  h2 { font-size:17px; margin:26px 0 10px; padding-bottom:8px; border-bottom:1px solid var(--hair); letter-spacing:-.01em; }\n  h3 { font-size:14px; margin:18px 0 8px; color:var(--accent-2); }\n  p { color:var(--ink-2); max-width:68ch; font-size:14.5px; }\n  ul,ol { color:var(--ink-2); font-size:14.5px; line-height:1.75; max-width:68ch; padding-left:20px; }\n  li+li { margin-top:4px; }\n  code { font:600 12.5px ui-monospace,Menlo,monospace; background:var(--soft); border:1px solid var(--hair); padding:1px 6px; border-radius:5px; color:var(--ink); }\n  .timeline { display:flex; flex-direction:column; gap:0; margin:14px 0; border:1px solid var(--hair); border-radius:10px; overflow:hidden; background:var(--soft-2); }\n  .t-row { display:grid; grid-template-columns:56px 1fr; gap:0; border-bottom:1px solid var(--hair); }\n  .t-row:last-child { border-bottom:none; }\n  .t-who { display:flex; align-items:center; justify-content:center; font:800 11px ui-monospace,Menlo,monospace; letter-spacing:.06em; padding:12px 6px; }\n  .t-who.a { background:#FFF0DB; color:#9A3B00; }\n  .t-who.b { background:#F0F7FF; color:#1E40AF; }\n  :root[data-theme=\"dark\"] .t-who.a { background:#2A2014; color:#FFB26B; }\n  :root[data-theme=\"dark\"] .t-who.b { background:#1A2233; color:#93C5FD; }\n  .t-body { padding:11px 14px; font-size:13.5px; line-height:1.6; color:var(--ink-2); border-left:1px solid var(--hair); }\n  .t-body strong { color:var(--ink); }\n  .t-body .strike { text-decoration:line-through; color:var(--bad); font-weight:600; }\n  table { border-collapse:collapse; width:100%; font-size:13.5px; margin:12px 0; }\n  th,td { text-align:left; padding:9px 11px; border-bottom:1px solid var(--hair); }\n  th { font:700 11px ui-monospace,Menlo,monospace; letter-spacing:.06em; color:var(--ink-2); background:var(--soft); }\n  td { color:var(--ink-2); }\n  td:first-child { color:var(--ink); font-weight:600; white-space:nowrap; }\n  .fix-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:12px 0; }\n  .fix { background:var(--soft-2); border:1px solid var(--hair); border-radius:10px; padding:13px 14px; }\n  .fix .num { font:800 11px ui-monospace,Menlo,monospace; color:var(--accent); letter-spacing:.08em; }\n  .fix h4 { margin:6px 0 6px; font-size:14px; line-height:1.4; }\n  .fix p { margin:0; font-size:13px; line-height:1.6; }\n  .foot { margin-top:22px; padding:12px 14px; background:var(--soft); border:1px dashed var(--hair); border-radius:8px; font-size:12.5px; color:var(--ink-2); line-height:1.6; }\n  @media (max-width:640px) { .doc{padding:20px 16px;} h1{font-size:22px;} .fix-grid{grid-template-columns:1fr;} .t-row{grid-template-columns:48px 1fr;} }\n\u003c/style\u003e\u003cdiv class=\"doc\"\u003e\n  \u003cheader\u003e\n    \u003cdiv class=\"eyebrow\"\u003eCubeLV 平台 Bug · 插件鍛造\u003c/div\u003e\n    \u003ch1\u003eCubeLV 平台 Bug 回報：並行對話鍛造聯動插件互相覆蓋（版本乒乓）\u003c/h1\u003e\n    \u003cp class=\"lede\"\u003e以為「一對話修一插件、互不干擾」最省事，結果兩個插件有聯動，雙方先後 \u003ccode\u003eplugin_publish\u003c/code\u003e 變成來回覆蓋 —— 誰最後發布誰贏，另一邊的修改靜默消失。\u003c/p\u003e\n    \u003cdiv class=\"meta\"\u003e\n      \u003cspan class=\"chip\"\u003e回報 2026-08-31\u003c/span\u003e\n      \u003cspan class=\"chip warn\"\u003e嚴重程度 · 中高\u003c/span\u003e\n      \u003cspan class=\"chip bad\"\u003e影響 · 插件鍛造 / 多對話並行\u003c/span\u003e\n      \u003cspan class=\"chip\"\u003e狀態 · 待修\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/header\u003e\n\n  \u003csection data-cube=\"stats\" class=\"stats\"\u003e\n    \u003cdiv class=\"stat\"\u003e\u003cdiv class=\"k\"\u003e重現率\u003c/div\u003e\u003cdiv class=\"v\"\u003e必現\u003c/div\u003e\u003cdiv class=\"n\"\u003e並行 + 聯動 + 先後 publish 即觸發\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"stat\"\u003e\u003cdiv class=\"k\"\u003e傷害\u003c/div\u003e\u003cdiv class=\"v\"\u003e靜默覆蓋\u003c/div\u003e\u003cdiv class=\"n\"\u003e無警告、需人肉 diff 才發現\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"stat\"\u003e\u003cdiv class=\"k\"\u003e波及\u003c/div\u003e\u003cdiv class=\"v\"\u003e版本乒乓\u003c/div\u003e\u003cdiv class=\"n\"\u003eA 蓋 B → B 蓋 A 來回重演\u003c/div\u003e\u003c/div\u003e\n  \u003c/section\u003e\n\n  \u003cdiv class=\"callout\"\u003e\u003cspan class=\"tag\"\u003e一句話\u003c/span\u003e\u003cdiv\u003e\u003cp\u003e分開修反而互相踩檔：不是插件太多，是\u003cstrong\u003e聯動 + 舊基線直接 publish\u003c/strong\u003e 讓後發布者把先發布者的版本吃掉。\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\n\n  \u003ch2\u003e現象\u003c/h2\u003e\n  \u003cp\u003e同時開兩個對話，各修一個插件（心態：剛好一對話一插件、互不干擾）。但兩個插件之間有聯動 —— 例如共用元件、共用 itemType / schema、或互相讀寫對方的對應邏輯 —— 結果：\u003c/p\u003e\n  \u003cul\u003e\n    \u003cli\u003e對話 A 修完插件 A 並 \u003ccode\u003eplugin_publish\u003c/code\u003e → 遠端已是新版 A\u003c/li\u003e\n    \u003cli\u003e對話 B 仍停在舊基線，修完插件 B 也 \u003ccode\u003eplugin_publish\u003c/code\u003e → \u003cspan class=\"strike\"\u003e把 A 的新版蓋回舊版\u003c/span\u003e\u003c/li\u003e\n    \u003cli\u003e對話 A 發現修改消失，再 publish 一次 → 又把 B 蓋掉\u003c/li\u003e\n    \u003cli\u003e來回覆蓋形成「版本乒乓」，最後誰最後 publish 誰贏\u003c/li\u003e\n  \u003c/ul\u003e\n\n  \u003ch2\u003e重現步驟\u003c/h2\u003e\n  \u003col\u003e\n    \u003cli\u003e準備兩個有聯動的插件（插件 A / 插件 B 共用某個前端元件、schema 或跨插件讀寫邏輯）\u003c/li\u003e\n    \u003cli\u003e開 \u003cstrong\u003e對話 A\u003c/strong\u003e 修插件 A、開 \u003cstrong\u003e對話 B\u003c/strong\u003e 修插件 B，並行進行\u003c/li\u003e\n    \u003cli\u003e對話 A 完成鍛造流程並 \u003ccode\u003eplugin_publish\u003c/code\u003e（發布 A 的新版）\u003c/li\u003e\n    \u003cli\u003e對話 B 在\u003cstrong\u003e未同步最新 main\u003c/strong\u003e 的狀態下也 \u003ccode\u003eplugin_publish\u003c/code\u003e（發布 B 的新版）\u003c/li\u003e\n    \u003cli\u003e回到對話 A 檢查 → A 的修改被 B 的發布覆蓋／回退；回到對話 B 也可能被下一輪 A 的修補再覆蓋\u003c/li\u003e\n    \u003cli\u003e若雙方未察覺基線已落後，重複 publish 即穩定復現來回覆蓋\u003c/li\u003e\n  \u003c/ol\u003e\n\n  \u003ch3\u003e時序圖\u003c/h3\u003e\n  \u003cdiv class=\"timeline\"\u003e\n    \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who a\"\u003e對話 A\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e\u003cstrong\u003e修插件 A\u003c/strong\u003e → publish A v2 ✅ \u0026nbsp;（遠端：A v2 / B v1）\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who b\"\u003e對話 B\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e\u003cstrong\u003e基線仍是舊版\u003c/strong\u003e（沒拉到 A v2）→ 修插件 B → publish B v2 \u0026nbsp;\u003cspan class=\"strike\"\u003e覆蓋回 A v1\u003c/span\u003e \u0026nbsp;（遠端：A v1 / B v2）\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who a\"\u003e對話 A\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e發現 A 的修改消失 → 再 publish A v2 \u0026nbsp;\u003cspan class=\"strike\"\u003e又把 B v2 踩掉\u003c/span\u003e → 乒乓開始\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who b\"\u003e對話 B\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e發現 B 的修改消失 → 再 publish … 循環\u003c/div\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\n  \u003ch2\u003e根因\u003c/h2\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003cstrong\u003e並行鍛造缺乏隔離與合併\u003c/strong\u003e：各對話在本地改檔後直接 publish，publish 前未強制 \u003ccode\u003efetch + rebase origin/main\u003c/code\u003e 與衝突檢測，舊基線的 publish 以「全量覆蓋」形式把對方的新版擠掉。\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003e聯動放大傷害\u003c/strong\u003e：若兩插件完全獨立，僅各自版本跳號；但因為有聯動（共用檔案 / 共用 itemType / 互相引用的邏輯），一方覆蓋會直接踩到另一方的有效變更。\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003e無衝突提示\u003c/strong\u003e：publish 成功但實質是回退式覆蓋，過程中沒有「本地落後遠端 N 版，強制發布將覆蓋他人變更」的警告或阻擋。\u003c/li\u003e\n  \u003c/ul\u003e\n\n  \u003ch2\u003e預期 vs 實際\u003c/h2\u003e\n  \u003ctable\u003e\n    \u003cthead\u003e\u003ctr\u003e\u003cth\u003e項目\u003c/th\u003e\u003cth\u003e預期\u003c/th\u003e\u003cth\u003e實際\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n    \u003ctbody\u003e\n      \u003ctr\u003e\u003ctd\u003e並行修兩插件\u003c/td\u003e\u003ctd\u003e各做各的，互不影響；有聯動應提示或自動合併\u003c/td\u003e\u003ctd\u003e後 publish 者直接覆蓋先 publish 者，無提示\u003c/td\u003e\u003c/tr\u003e\n      \u003ctr\u003e\u003ctd\u003e聯動變更\u003c/td\u003e\u003ctd\u003e保留或提示衝突、需手動解\u003c/td\u003e\u003ctd\u003e被靜默覆蓋，需人肉比對才發現\u003c/td\u003e\u003c/tr\u003e\n      \u003ctr\u003e\u003ctd\u003e重試\u003c/td\u003e\u003ctd\u003e最多一次覆蓋即被攔截\u003c/td\u003e\u003ctd\u003e雙方來回覆蓋，形成乒乓\u003c/td\u003e\u003c/tr\u003e\n    \u003c/tbody\u003e\n  \u003c/table\u003e\n\n  \u003ch2\u003e影響\u003c/h2\u003e\n  \u003cul\u003e\n    \u003cli\u003e已完成的插件修改反覆遺失，需重工、重測\u003c/li\u003e\n    \u003cli\u003e難以追溯「哪一版才是對的」，回退與對帳成本墊高\u003c/li\u003e\n    \u003cli\u003e若聯動涉及線上版本，可能讓用戶拿到不一致的版本組合\u003c/li\u003e\n  \u003c/ul\u003e\n\n  \u003ch2\u003e建議修復方向\u003c/h2\u003e\n  \u003cdiv class=\"fix-grid\"\u003e\n    \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 01 — 最優先\u003c/div\u003e\u003ch4\u003e發布前強制對齊遠端\u003c/h4\u003e\u003cp\u003e\u003ccode\u003eplugin_publish\u003c/code\u003e 前強制 \u003ccode\u003efetch + rebase origin/main\u003c/code\u003e，本地落後就拒絕發布並提示「遠端已有新版，請先同步合併」。提供 \u003ccode\u003e--force\u003c/code\u003e 需二次確認。\u003c/p\u003e\u003c/div\u003e\n    \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 02\u003c/div\u003e\u003ch4\u003e並行會話隔離\u003c/h4\u003e\u003cp\u003e每對話用獨立分支 \u003ccode\u003eplugin/{pluginDir}/{sessionId}\u003c/code\u003e，publish 走 PR / 自動合併而非直推 main，降低乒乓機率。\u003c/p\u003e\u003c/div\u003e\n    \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 03\u003c/div\u003e\u003ch4\u003e聯動變更告警\u003c/h4\u003e\u003cp\u003e若一次 publish 動到被他插件引用的共用路徑，發布前提示「此變更會影響聯動插件 X，是否一併升級／同步？」\u003c/p\u003e\u003c/div\u003e\n    \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 04\u003c/div\u003e\u003ch4\u003e版本歷程可視化\u003c/h4\u003e\u003cp\u003e插件管理頁顯示「最近 5 次 publish：誰、何時、改了什麼」，被覆蓋時可一鍵 diff / 回退。\u003c/p\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\n  \u003ch2\u003e本次自救做法\u003c/h2\u003e\n  \u003cul\u003e\n    \u003cli\u003e改回\u003cstrong\u003e單對話循序修\u003c/strong\u003e：一次只修一個插件，publish 完確認遠端已更新後再開下一個。\u003c/li\u003e\n    \u003cli\u003e若必須並行，publish 前手動同步最新版並比對 diff，確認未踩到對方的檔。\u003c/li\u003e\n  \u003c/ul\u003e\n\n  \u003cdiv class=\"foot\"\u003e💡 後續若要驗收：開兩對話分別改兩個聯動插件，先後 publish，確認「舊基線 publish 被阻擋並提示落後」而非靜默覆蓋；再驗證合併後兩邊變更都保留。\u003c/div\u003e\n\u003c/div\u003e","content":"\u003cdiv class=\"doc\"\u003e  \u003ctable\u003e\u003ctbody\u003e\u003ctr\u003e\u003ctd\u003e\u003cp\u003e重現率\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e必現\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e並行 + 聯動 + 先後 publish 即觸發\u003c/p\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003cp\u003e傷害\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e靜默覆蓋\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e無警告、需人肉 diff 才發現\u003c/p\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003cp\u003e波及\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e版本乒乓\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003eA 蓋 B → B 蓋 A 來回重演\u003c/p\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e \u003cdiv class=\"callout\"\u003e\u003cspan class=\"tag\"\u003e一句話\u003c/span\u003e\u003cdiv\u003e\u003cp\u003e分開修反而互相踩檔：不是插件太多，是\u003cstrong\u003e聯動 + 舊基線直接 publish\u003c/strong\u003e 讓後發布者把先發布者的版本吃掉。\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e \u003ch2\u003e現象\u003c/h2\u003e \u003cp\u003e同時開兩個對話，各修一個插件（心態：剛好一對話一插件、互不干擾）。但兩個插件之間有聯動 —— 例如共用元件、共用 itemType / schema、或互相讀寫對方的對應邏輯 —— 結果：\u003c/p\u003e \u003cul\u003e \u003cli\u003e對話 A 修完插件 A 並 \u003ccode\u003eplugin_publish\u003c/code\u003e → 遠端已是新版 A\u003c/li\u003e \u003cli\u003e對話 B 仍停在舊基線，修完插件 B 也 \u003ccode\u003eplugin_publish\u003c/code\u003e → \u003cspan class=\"strike\"\u003e把 A 的新版蓋回舊版\u003c/span\u003e\u003c/li\u003e \u003cli\u003e對話 A 發現修改消失，再 publish 一次 → 又把 B 蓋掉\u003c/li\u003e \u003cli\u003e來回覆蓋形成「版本乒乓」，最後誰最後 publish 誰贏\u003c/li\u003e \u003c/ul\u003e \u003ch2\u003e重現步驟\u003c/h2\u003e \u003col\u003e \u003cli\u003e準備兩個有聯動的插件（插件 A / 插件 B 共用某個前端元件、schema 或跨插件讀寫邏輯）\u003c/li\u003e \u003cli\u003e開 \u003cstrong\u003e對話 A\u003c/strong\u003e 修插件 A、開 \u003cstrong\u003e對話 B\u003c/strong\u003e 修插件 B，並行進行\u003c/li\u003e \u003cli\u003e對話 A 完成鍛造流程並 \u003ccode\u003eplugin_publish\u003c/code\u003e（發布 A 的新版）\u003c/li\u003e \u003cli\u003e對話 B 在\u003cstrong\u003e未同步最新 main\u003c/strong\u003e 的狀態下也 \u003ccode\u003eplugin_publish\u003c/code\u003e（發布 B 的新版）\u003c/li\u003e \u003cli\u003e回到對話 A 檢查 → A 的修改被 B 的發布覆蓋／回退；回到對話 B 也可能被下一輪 A 的修補再覆蓋\u003c/li\u003e \u003cli\u003e若雙方未察覺基線已落後，重複 publish 即穩定復現來回覆蓋\u003c/li\u003e \u003c/ol\u003e \u003ch3\u003e時序圖\u003c/h3\u003e \u003cdiv class=\"timeline\"\u003e \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who a\"\u003e對話 A\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e\u003cstrong\u003e修插件 A\u003c/strong\u003e → publish A v2 ✅ \u0026nbsp;（遠端：A v2 / B v1）\u003c/div\u003e\u003c/div\u003e \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who b\"\u003e對話 B\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e\u003cstrong\u003e基線仍是舊版\u003c/strong\u003e（沒拉到 A v2）→ 修插件 B → publish B v2 \u0026nbsp;\u003cspan class=\"strike\"\u003e覆蓋回 A v1\u003c/span\u003e \u0026nbsp;（遠端：A v1 / B v2）\u003c/div\u003e\u003c/div\u003e \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who a\"\u003e對話 A\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e發現 A 的修改消失 → 再 publish A v2 \u0026nbsp;\u003cspan class=\"strike\"\u003e又把 B v2 踩掉\u003c/span\u003e → 乒乓開始\u003c/div\u003e\u003c/div\u003e \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who b\"\u003e對話 B\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e發現 B 的修改消失 → 再 publish … 循環\u003c/div\u003e\u003c/div\u003e \u003c/div\u003e \u003ch2\u003e根因\u003c/h2\u003e \u003cul\u003e \u003cli\u003e\u003cstrong\u003e並行鍛造缺乏隔離與合併\u003c/strong\u003e：各對話在本地改檔後直接 publish，publish 前未強制 \u003ccode\u003efetch + rebase origin/main\u003c/code\u003e 與衝突檢測，舊基線的 publish 以「全量覆蓋」形式把對方的新版擠掉。\u003c/li\u003e \u003cli\u003e\u003cstrong\u003e聯動放大傷害\u003c/strong\u003e：若兩插件完全獨立，僅各自版本跳號；但因為有聯動（共用檔案 / 共用 itemType / 互相引用的邏輯），一方覆蓋會直接踩到另一方的有效變更。\u003c/li\u003e \u003cli\u003e\u003cstrong\u003e無衝突提示\u003c/strong\u003e：publish 成功但實質是回退式覆蓋，過程中沒有「本地落後遠端 N 版，強制發布將覆蓋他人變更」的警告或阻擋。\u003c/li\u003e \u003c/ul\u003e \u003ch2\u003e預期 vs 實際\u003c/h2\u003e \u003ctable\u003e \u003cthead\u003e\u003ctr\u003e\u003cth\u003e項目\u003c/th\u003e\u003cth\u003e預期\u003c/th\u003e\u003cth\u003e實際\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e \u003ctbody\u003e \u003ctr\u003e\u003ctd\u003e並行修兩插件\u003c/td\u003e\u003ctd\u003e各做各的，互不影響；有聯動應提示或自動合併\u003c/td\u003e\u003ctd\u003e後 publish 者直接覆蓋先 publish 者，無提示\u003c/td\u003e\u003c/tr\u003e \u003ctr\u003e\u003ctd\u003e聯動變更\u003c/td\u003e\u003ctd\u003e保留或提示衝突、需手動解\u003c/td\u003e\u003ctd\u003e被靜默覆蓋，需人肉比對才發現\u003c/td\u003e\u003c/tr\u003e \u003ctr\u003e\u003ctd\u003e重試\u003c/td\u003e\u003ctd\u003e最多一次覆蓋即被攔截\u003c/td\u003e\u003ctd\u003e雙方來回覆蓋，形成乒乓\u003c/td\u003e\u003c/tr\u003e \u003c/tbody\u003e \u003c/table\u003e \u003ch2\u003e影響\u003c/h2\u003e \u003cul\u003e \u003cli\u003e已完成的插件修改反覆遺失，需重工、重測\u003c/li\u003e \u003cli\u003e難以追溯「哪一版才是對的」，回退與對帳成本墊高\u003c/li\u003e \u003cli\u003e若聯動涉及線上版本，可能讓用戶拿到不一致的版本組合\u003c/li\u003e \u003c/ul\u003e \u003ch2\u003e建議修復方向\u003c/h2\u003e \u003cdiv class=\"fix-grid\"\u003e \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 01 — 最優先\u003c/div\u003e\u003ch4\u003e發布前強制對齊遠端\u003c/h4\u003e\u003cp\u003e\u003ccode\u003eplugin_publish\u003c/code\u003e 前強制 \u003ccode\u003efetch + rebase origin/main\u003c/code\u003e，本地落後就拒絕發布並提示「遠端已有新版，請先同步合併」。提供 \u003ccode\u003e--force\u003c/code\u003e 需二次確認。\u003c/p\u003e\u003c/div\u003e \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 02\u003c/div\u003e\u003ch4\u003e並行會話隔離\u003c/h4\u003e\u003cp\u003e每對話用獨立分支 \u003ccode\u003eplugin/{pluginDir}/{sessionId}\u003c/code\u003e，publish 走 PR / 自動合併而非直推 main，降低乒乓機率。\u003c/p\u003e\u003c/div\u003e \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 03\u003c/div\u003e\u003ch4\u003e聯動變更告警\u003c/h4\u003e\u003cp\u003e若一次 publish 動到被他插件引用的共用路徑，發布前提示「此變更會影響聯動插件 X，是否一併升級／同步？」\u003c/p\u003e\u003c/div\u003e \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 04\u003c/div\u003e\u003ch4\u003e版本歷程可視化\u003c/h4\u003e\u003cp\u003e插件管理頁顯示「最近 5 次 publish：誰、何時、改了什麼」，被覆蓋時可一鍵 diff / 回退。\u003c/p\u003e\u003c/div\u003e \u003c/div\u003e \u003ch2\u003e本次自救做法\u003c/h2\u003e \u003cul\u003e \u003cli\u003e改回\u003cstrong\u003e單對話循序修\u003c/strong\u003e：一次只修一個插件，publish 完確認遠端已更新後再開下一個。\u003c/li\u003e \u003cli\u003e若必須並行，publish 前手動同步最新版並比對 diff，確認未踩到對方的檔。\u003c/li\u003e \u003c/ul\u003e \u003cdiv class=\"foot\"\u003e💡 後續若要驗收：開兩對話分別改兩個聯動插件，先後 publish，確認「舊基線 publish 被阻擋並提示落後」而非靜默覆蓋；再驗證合併後兩邊變更都保留。\u003c/div\u003e \u003c/div\u003e","createdAt":1788114783189,"deletedAt":null,"displayMode":"artifact","embedRenders":{},"id":"b69c7ba565e5ba21d482cb84","isNew":false,"isPublic":true,"itemType":"NOTE","name":"CubeLV 平台 Bug 回報：並行對話鍛造聯動插件互相覆蓋（版本乒乓）","parents":{"4465d0a863da5d1b345d888c":1788114783189},"preParentID":null,"updatedAt":1788153837248,"updatedBy":{"userId":"6a3dcfc700e9b9fd06d5ef","userName":"蔡其樺"},"version":10},"ownerName":"蔡其樺","subtree":[{"artifactHtml":"\u003cstyle\u003e\n  :root { --paper:#FFFBF5; --ink:#1A1207; --ink-2:#6B5E4F; --accent:#C45A12; --accent-2:#9A3B00; --soft:#FFF0DB; --soft-2:#FFF7EB; --hair:#E8DDD0; --ok:#0F6B4A; --warn:#C45A12; --bad:#B91C1C; }\n  :root[data-theme=\"dark\"] { --paper:#1A150E; --ink:#F5EDE0; --ink-2:#B8AA96; --accent:#FF8A3D; --accent-2:#FFB26B; --soft:#2A2014; --soft-2:#241E14; --hair:#3A3226; --ok:#4ADE80; --warn:#FF8A3D; --bad:#F87171; }\n  .doc { background:var(--paper); color:var(--ink); padding:28px 30px; font:16px/1.75 -apple-system,\"PingFang TC\",\"Noto Sans TC\",sans-serif; }\n  .eyebrow { font:700 11px ui-monospace,Menlo,monospace; letter-spacing:.14em; color:var(--accent); text-transform:uppercase; }\n  h1 { font-size:27px; line-height:1.25; letter-spacing:-.02em; margin:10px 0 0; max-width:22ch; }\n  .lede { color:var(--ink-2); margin:10px 0 0; font-size:15px; line-height:1.6; max-width:62ch; }\n  .meta { display:flex; flex-wrap:wrap; gap:8px; margin:14px 0 0; }\n  .chip { font:600 11px ui-monospace,Menlo,monospace; letter-spacing:.04em; padding:5px 9px; border-radius:999px; border:1px solid var(--hair); background:var(--soft-2); color:var(--ink-2); }\n  .chip.bad { background:#FEF2F2; color:var(--bad); border-color:#FECACA; }\n  :root[data-theme=\"dark\"] .chip.bad { background:#3A1818; color:#FCA5A5; border-color:#7F1D1D; }\n  .chip.warn { background:#FFF7ED; color:var(--warn); border-color:#FFD6A8; }\n  :root[data-theme=\"dark\"] .chip.warn { background:#2E1E0E; color:#FFB26B; border-color:#7C3A0A; }\n  .stats { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:10px; margin:20px 0; }\n  .stat { background:var(--soft); border:1px solid var(--hair); border-radius:10px; padding:13px 14px; }\n  .stat .k { font:700 11px ui-monospace,Menlo,monospace; letter-spacing:.08em; color:var(--accent); }\n  .stat .v { font-size:20px; font-weight:800; margin-top:4px; line-height:1.2; }\n  .stat .n { font-size:12px; color:var(--ink-2); margin-top:3px; }\n  .callout { display:flex; gap:12px; padding:14px 16px; background:var(--soft); border:1px solid var(--hair); border-radius:10px; margin:18px 0; align-items:flex-start; }\n  .callout .tag { flex:none; font:800 11px ui-monospace,Menlo,monospace; letter-spacing:.08em; color:#fff; background:var(--accent); padding:4px 7px; border-radius:6px; }\n  .callout p { margin:0; color:var(--ink); font-size:14px; line-height:1.6; }\n  h2 { font-size:17px; margin:26px 0 10px; padding-bottom:8px; border-bottom:1px solid var(--hair); letter-spacing:-.01em; }\n  h3 { font-size:14px; margin:18px 0 8px; color:var(--accent-2); }\n  p { color:var(--ink-2); max-width:68ch; font-size:14.5px; }\n  ul,ol { color:var(--ink-2); font-size:14.5px; line-height:1.75; max-width:68ch; padding-left:20px; }\n  li+li { margin-top:4px; }\n  code { font:600 12.5px ui-monospace,Menlo,monospace; background:var(--soft); border:1px solid var(--hair); padding:1px 6px; border-radius:5px; color:var(--ink); }\n  .timeline { display:flex; flex-direction:column; gap:0; margin:14px 0; border:1px solid var(--hair); border-radius:10px; overflow:hidden; background:var(--soft-2); }\n  .t-row { display:grid; grid-template-columns:56px 1fr; gap:0; border-bottom:1px solid var(--hair); }\n  .t-row:last-child { border-bottom:none; }\n  .t-who { display:flex; align-items:center; justify-content:center; font:800 11px ui-monospace,Menlo,monospace; letter-spacing:.06em; padding:12px 6px; }\n  .t-who.a { background:#FFF0DB; color:#9A3B00; }\n  .t-who.b { background:#F0F7FF; color:#1E40AF; }\n  :root[data-theme=\"dark\"] .t-who.a { background:#2A2014; color:#FFB26B; }\n  :root[data-theme=\"dark\"] .t-who.b { background:#1A2233; color:#93C5FD; }\n  .t-body { padding:11px 14px; font-size:13.5px; line-height:1.6; color:var(--ink-2); border-left:1px solid var(--hair); }\n  .t-body strong { color:var(--ink); }\n  .t-body .strike { text-decoration:line-through; color:var(--bad); font-weight:600; }\n  table { border-collapse:collapse; width:100%; font-size:13.5px; margin:12px 0; }\n  th,td { text-align:left; padding:9px 11px; border-bottom:1px solid var(--hair); }\n  th { font:700 11px ui-monospace,Menlo,monospace; letter-spacing:.06em; color:var(--ink-2); background:var(--soft); }\n  td { color:var(--ink-2); }\n  td:first-child { color:var(--ink); font-weight:600; white-space:nowrap; }\n  .fix-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:12px 0; }\n  .fix { background:var(--soft-2); border:1px solid var(--hair); border-radius:10px; padding:13px 14px; }\n  .fix .num { font:800 11px ui-monospace,Menlo,monospace; color:var(--accent); letter-spacing:.08em; }\n  .fix h4 { margin:6px 0 6px; font-size:14px; line-height:1.4; }\n  .fix p { margin:0; font-size:13px; line-height:1.6; }\n  .foot { margin-top:22px; padding:12px 14px; background:var(--soft); border:1px dashed var(--hair); border-radius:8px; font-size:12.5px; color:var(--ink-2); line-height:1.6; }\n  @media (max-width:640px) { .doc{padding:20px 16px;} h1{font-size:22px;} .fix-grid{grid-template-columns:1fr;} .t-row{grid-template-columns:48px 1fr;} }\n\u003c/style\u003e\u003cdiv class=\"doc\"\u003e\n  \u003cheader\u003e\n    \u003cdiv class=\"eyebrow\"\u003eCubeLV 平台 Bug · 插件鍛造\u003c/div\u003e\n    \u003ch1\u003eCubeLV 平台 Bug 回報：並行對話鍛造聯動插件互相覆蓋（版本乒乓）\u003c/h1\u003e\n    \u003cp class=\"lede\"\u003e以為「一對話修一插件、互不干擾」最省事，結果兩個插件有聯動，雙方先後 \u003ccode\u003eplugin_publish\u003c/code\u003e 變成來回覆蓋 —— 誰最後發布誰贏，另一邊的修改靜默消失。\u003c/p\u003e\n    \u003cdiv class=\"meta\"\u003e\n      \u003cspan class=\"chip\"\u003e回報 2026-08-31\u003c/span\u003e\n      \u003cspan class=\"chip warn\"\u003e嚴重程度 · 中高\u003c/span\u003e\n      \u003cspan class=\"chip bad\"\u003e影響 · 插件鍛造 / 多對話並行\u003c/span\u003e\n      \u003cspan class=\"chip\"\u003e狀態 · 待修\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/header\u003e\n\n  \u003csection data-cube=\"stats\" class=\"stats\"\u003e\n    \u003cdiv class=\"stat\"\u003e\u003cdiv class=\"k\"\u003e重現率\u003c/div\u003e\u003cdiv class=\"v\"\u003e必現\u003c/div\u003e\u003cdiv class=\"n\"\u003e並行 + 聯動 + 先後 publish 即觸發\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"stat\"\u003e\u003cdiv class=\"k\"\u003e傷害\u003c/div\u003e\u003cdiv class=\"v\"\u003e靜默覆蓋\u003c/div\u003e\u003cdiv class=\"n\"\u003e無警告、需人肉 diff 才發現\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"stat\"\u003e\u003cdiv class=\"k\"\u003e波及\u003c/div\u003e\u003cdiv class=\"v\"\u003e版本乒乓\u003c/div\u003e\u003cdiv class=\"n\"\u003eA 蓋 B → B 蓋 A 來回重演\u003c/div\u003e\u003c/div\u003e\n  \u003c/section\u003e\n\n  \u003cdiv class=\"callout\"\u003e\u003cspan class=\"tag\"\u003e一句話\u003c/span\u003e\u003cdiv\u003e\u003cp\u003e分開修反而互相踩檔：不是插件太多，是\u003cstrong\u003e聯動 + 舊基線直接 publish\u003c/strong\u003e 讓後發布者把先發布者的版本吃掉。\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e\n\n  \u003ch2\u003e現象\u003c/h2\u003e\n  \u003cp\u003e同時開兩個對話，各修一個插件（心態：剛好一對話一插件、互不干擾）。但兩個插件之間有聯動 —— 例如共用元件、共用 itemType / schema、或互相讀寫對方的對應邏輯 —— 結果：\u003c/p\u003e\n  \u003cul\u003e\n    \u003cli\u003e對話 A 修完插件 A 並 \u003ccode\u003eplugin_publish\u003c/code\u003e → 遠端已是新版 A\u003c/li\u003e\n    \u003cli\u003e對話 B 仍停在舊基線，修完插件 B 也 \u003ccode\u003eplugin_publish\u003c/code\u003e → \u003cspan class=\"strike\"\u003e把 A 的新版蓋回舊版\u003c/span\u003e\u003c/li\u003e\n    \u003cli\u003e對話 A 發現修改消失，再 publish 一次 → 又把 B 蓋掉\u003c/li\u003e\n    \u003cli\u003e來回覆蓋形成「版本乒乓」，最後誰最後 publish 誰贏\u003c/li\u003e\n  \u003c/ul\u003e\n\n  \u003ch2\u003e重現步驟\u003c/h2\u003e\n  \u003col\u003e\n    \u003cli\u003e準備兩個有聯動的插件（插件 A / 插件 B 共用某個前端元件、schema 或跨插件讀寫邏輯）\u003c/li\u003e\n    \u003cli\u003e開 \u003cstrong\u003e對話 A\u003c/strong\u003e 修插件 A、開 \u003cstrong\u003e對話 B\u003c/strong\u003e 修插件 B，並行進行\u003c/li\u003e\n    \u003cli\u003e對話 A 完成鍛造流程並 \u003ccode\u003eplugin_publish\u003c/code\u003e（發布 A 的新版）\u003c/li\u003e\n    \u003cli\u003e對話 B 在\u003cstrong\u003e未同步最新 main\u003c/strong\u003e 的狀態下也 \u003ccode\u003eplugin_publish\u003c/code\u003e（發布 B 的新版）\u003c/li\u003e\n    \u003cli\u003e回到對話 A 檢查 → A 的修改被 B 的發布覆蓋／回退；回到對話 B 也可能被下一輪 A 的修補再覆蓋\u003c/li\u003e\n    \u003cli\u003e若雙方未察覺基線已落後，重複 publish 即穩定復現來回覆蓋\u003c/li\u003e\n  \u003c/ol\u003e\n\n  \u003ch3\u003e時序圖\u003c/h3\u003e\n  \u003cdiv class=\"timeline\"\u003e\n    \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who a\"\u003e對話 A\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e\u003cstrong\u003e修插件 A\u003c/strong\u003e → publish A v2 ✅ \u0026nbsp;（遠端：A v2 / B v1）\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who b\"\u003e對話 B\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e\u003cstrong\u003e基線仍是舊版\u003c/strong\u003e（沒拉到 A v2）→ 修插件 B → publish B v2 \u0026nbsp;\u003cspan class=\"strike\"\u003e覆蓋回 A v1\u003c/span\u003e \u0026nbsp;（遠端：A v1 / B v2）\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who a\"\u003e對話 A\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e發現 A 的修改消失 → 再 publish A v2 \u0026nbsp;\u003cspan class=\"strike\"\u003e又把 B v2 踩掉\u003c/span\u003e → 乒乓開始\u003c/div\u003e\u003c/div\u003e\n    \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who b\"\u003e對話 B\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e發現 B 的修改消失 → 再 publish … 循環\u003c/div\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\n  \u003ch2\u003e根因\u003c/h2\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003cstrong\u003e並行鍛造缺乏隔離與合併\u003c/strong\u003e：各對話在本地改檔後直接 publish，publish 前未強制 \u003ccode\u003efetch + rebase origin/main\u003c/code\u003e 與衝突檢測，舊基線的 publish 以「全量覆蓋」形式把對方的新版擠掉。\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003e聯動放大傷害\u003c/strong\u003e：若兩插件完全獨立，僅各自版本跳號；但因為有聯動（共用檔案 / 共用 itemType / 互相引用的邏輯），一方覆蓋會直接踩到另一方的有效變更。\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003e無衝突提示\u003c/strong\u003e：publish 成功但實質是回退式覆蓋，過程中沒有「本地落後遠端 N 版，強制發布將覆蓋他人變更」的警告或阻擋。\u003c/li\u003e\n  \u003c/ul\u003e\n\n  \u003ch2\u003e預期 vs 實際\u003c/h2\u003e\n  \u003ctable\u003e\n    \u003cthead\u003e\u003ctr\u003e\u003cth\u003e項目\u003c/th\u003e\u003cth\u003e預期\u003c/th\u003e\u003cth\u003e實際\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n    \u003ctbody\u003e\n      \u003ctr\u003e\u003ctd\u003e並行修兩插件\u003c/td\u003e\u003ctd\u003e各做各的，互不影響；有聯動應提示或自動合併\u003c/td\u003e\u003ctd\u003e後 publish 者直接覆蓋先 publish 者，無提示\u003c/td\u003e\u003c/tr\u003e\n      \u003ctr\u003e\u003ctd\u003e聯動變更\u003c/td\u003e\u003ctd\u003e保留或提示衝突、需手動解\u003c/td\u003e\u003ctd\u003e被靜默覆蓋，需人肉比對才發現\u003c/td\u003e\u003c/tr\u003e\n      \u003ctr\u003e\u003ctd\u003e重試\u003c/td\u003e\u003ctd\u003e最多一次覆蓋即被攔截\u003c/td\u003e\u003ctd\u003e雙方來回覆蓋，形成乒乓\u003c/td\u003e\u003c/tr\u003e\n    \u003c/tbody\u003e\n  \u003c/table\u003e\n\n  \u003ch2\u003e影響\u003c/h2\u003e\n  \u003cul\u003e\n    \u003cli\u003e已完成的插件修改反覆遺失，需重工、重測\u003c/li\u003e\n    \u003cli\u003e難以追溯「哪一版才是對的」，回退與對帳成本墊高\u003c/li\u003e\n    \u003cli\u003e若聯動涉及線上版本，可能讓用戶拿到不一致的版本組合\u003c/li\u003e\n  \u003c/ul\u003e\n\n  \u003ch2\u003e建議修復方向\u003c/h2\u003e\n  \u003cdiv class=\"fix-grid\"\u003e\n    \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 01 — 最優先\u003c/div\u003e\u003ch4\u003e發布前強制對齊遠端\u003c/h4\u003e\u003cp\u003e\u003ccode\u003eplugin_publish\u003c/code\u003e 前強制 \u003ccode\u003efetch + rebase origin/main\u003c/code\u003e，本地落後就拒絕發布並提示「遠端已有新版，請先同步合併」。提供 \u003ccode\u003e--force\u003c/code\u003e 需二次確認。\u003c/p\u003e\u003c/div\u003e\n    \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 02\u003c/div\u003e\u003ch4\u003e並行會話隔離\u003c/h4\u003e\u003cp\u003e每對話用獨立分支 \u003ccode\u003eplugin/{pluginDir}/{sessionId}\u003c/code\u003e，publish 走 PR / 自動合併而非直推 main，降低乒乓機率。\u003c/p\u003e\u003c/div\u003e\n    \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 03\u003c/div\u003e\u003ch4\u003e聯動變更告警\u003c/h4\u003e\u003cp\u003e若一次 publish 動到被他插件引用的共用路徑，發布前提示「此變更會影響聯動插件 X，是否一併升級／同步？」\u003c/p\u003e\u003c/div\u003e\n    \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 04\u003c/div\u003e\u003ch4\u003e版本歷程可視化\u003c/h4\u003e\u003cp\u003e插件管理頁顯示「最近 5 次 publish：誰、何時、改了什麼」，被覆蓋時可一鍵 diff / 回退。\u003c/p\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\n  \u003ch2\u003e本次自救做法\u003c/h2\u003e\n  \u003cul\u003e\n    \u003cli\u003e改回\u003cstrong\u003e單對話循序修\u003c/strong\u003e：一次只修一個插件，publish 完確認遠端已更新後再開下一個。\u003c/li\u003e\n    \u003cli\u003e若必須並行，publish 前手動同步最新版並比對 diff，確認未踩到對方的檔。\u003c/li\u003e\n  \u003c/ul\u003e\n\n  \u003cdiv class=\"foot\"\u003e💡 後續若要驗收：開兩對話分別改兩個聯動插件，先後 publish，確認「舊基線 publish 被阻擋並提示落後」而非靜默覆蓋；再驗證合併後兩邊變更都保留。\u003c/div\u003e\n\u003c/div\u003e","content":"\u003cdiv class=\"doc\"\u003e  \u003ctable\u003e\u003ctbody\u003e\u003ctr\u003e\u003ctd\u003e\u003cp\u003e重現率\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e必現\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e並行 + 聯動 + 先後 publish 即觸發\u003c/p\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003cp\u003e傷害\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e靜默覆蓋\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e無警告、需人肉 diff 才發現\u003c/p\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003cp\u003e波及\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003e版本乒乓\u003c/p\u003e\u003c/td\u003e\u003ctd\u003e\u003cp\u003eA 蓋 B → B 蓋 A 來回重演\u003c/p\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e \u003cdiv class=\"callout\"\u003e\u003cspan class=\"tag\"\u003e一句話\u003c/span\u003e\u003cdiv\u003e\u003cp\u003e分開修反而互相踩檔：不是插件太多，是\u003cstrong\u003e聯動 + 舊基線直接 publish\u003c/strong\u003e 讓後發布者把先發布者的版本吃掉。\u003c/p\u003e\u003c/div\u003e\u003c/div\u003e \u003ch2\u003e現象\u003c/h2\u003e \u003cp\u003e同時開兩個對話，各修一個插件（心態：剛好一對話一插件、互不干擾）。但兩個插件之間有聯動 —— 例如共用元件、共用 itemType / schema、或互相讀寫對方的對應邏輯 —— 結果：\u003c/p\u003e \u003cul\u003e \u003cli\u003e對話 A 修完插件 A 並 \u003ccode\u003eplugin_publish\u003c/code\u003e → 遠端已是新版 A\u003c/li\u003e \u003cli\u003e對話 B 仍停在舊基線，修完插件 B 也 \u003ccode\u003eplugin_publish\u003c/code\u003e → \u003cspan class=\"strike\"\u003e把 A 的新版蓋回舊版\u003c/span\u003e\u003c/li\u003e \u003cli\u003e對話 A 發現修改消失，再 publish 一次 → 又把 B 蓋掉\u003c/li\u003e \u003cli\u003e來回覆蓋形成「版本乒乓」，最後誰最後 publish 誰贏\u003c/li\u003e \u003c/ul\u003e \u003ch2\u003e重現步驟\u003c/h2\u003e \u003col\u003e \u003cli\u003e準備兩個有聯動的插件（插件 A / 插件 B 共用某個前端元件、schema 或跨插件讀寫邏輯）\u003c/li\u003e \u003cli\u003e開 \u003cstrong\u003e對話 A\u003c/strong\u003e 修插件 A、開 \u003cstrong\u003e對話 B\u003c/strong\u003e 修插件 B，並行進行\u003c/li\u003e \u003cli\u003e對話 A 完成鍛造流程並 \u003ccode\u003eplugin_publish\u003c/code\u003e（發布 A 的新版）\u003c/li\u003e \u003cli\u003e對話 B 在\u003cstrong\u003e未同步最新 main\u003c/strong\u003e 的狀態下也 \u003ccode\u003eplugin_publish\u003c/code\u003e（發布 B 的新版）\u003c/li\u003e \u003cli\u003e回到對話 A 檢查 → A 的修改被 B 的發布覆蓋／回退；回到對話 B 也可能被下一輪 A 的修補再覆蓋\u003c/li\u003e \u003cli\u003e若雙方未察覺基線已落後，重複 publish 即穩定復現來回覆蓋\u003c/li\u003e \u003c/ol\u003e \u003ch3\u003e時序圖\u003c/h3\u003e \u003cdiv class=\"timeline\"\u003e \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who a\"\u003e對話 A\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e\u003cstrong\u003e修插件 A\u003c/strong\u003e → publish A v2 ✅ \u0026nbsp;（遠端：A v2 / B v1）\u003c/div\u003e\u003c/div\u003e \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who b\"\u003e對話 B\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e\u003cstrong\u003e基線仍是舊版\u003c/strong\u003e（沒拉到 A v2）→ 修插件 B → publish B v2 \u0026nbsp;\u003cspan class=\"strike\"\u003e覆蓋回 A v1\u003c/span\u003e \u0026nbsp;（遠端：A v1 / B v2）\u003c/div\u003e\u003c/div\u003e \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who a\"\u003e對話 A\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e發現 A 的修改消失 → 再 publish A v2 \u0026nbsp;\u003cspan class=\"strike\"\u003e又把 B v2 踩掉\u003c/span\u003e → 乒乓開始\u003c/div\u003e\u003c/div\u003e \u003cdiv class=\"t-row\"\u003e\u003cdiv class=\"t-who b\"\u003e對話 B\u003c/div\u003e\u003cdiv class=\"t-body\"\u003e發現 B 的修改消失 → 再 publish … 循環\u003c/div\u003e\u003c/div\u003e \u003c/div\u003e \u003ch2\u003e根因\u003c/h2\u003e \u003cul\u003e \u003cli\u003e\u003cstrong\u003e並行鍛造缺乏隔離與合併\u003c/strong\u003e：各對話在本地改檔後直接 publish，publish 前未強制 \u003ccode\u003efetch + rebase origin/main\u003c/code\u003e 與衝突檢測，舊基線的 publish 以「全量覆蓋」形式把對方的新版擠掉。\u003c/li\u003e \u003cli\u003e\u003cstrong\u003e聯動放大傷害\u003c/strong\u003e：若兩插件完全獨立，僅各自版本跳號；但因為有聯動（共用檔案 / 共用 itemType / 互相引用的邏輯），一方覆蓋會直接踩到另一方的有效變更。\u003c/li\u003e \u003cli\u003e\u003cstrong\u003e無衝突提示\u003c/strong\u003e：publish 成功但實質是回退式覆蓋，過程中沒有「本地落後遠端 N 版，強制發布將覆蓋他人變更」的警告或阻擋。\u003c/li\u003e \u003c/ul\u003e \u003ch2\u003e預期 vs 實際\u003c/h2\u003e \u003ctable\u003e \u003cthead\u003e\u003ctr\u003e\u003cth\u003e項目\u003c/th\u003e\u003cth\u003e預期\u003c/th\u003e\u003cth\u003e實際\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e \u003ctbody\u003e \u003ctr\u003e\u003ctd\u003e並行修兩插件\u003c/td\u003e\u003ctd\u003e各做各的，互不影響；有聯動應提示或自動合併\u003c/td\u003e\u003ctd\u003e後 publish 者直接覆蓋先 publish 者，無提示\u003c/td\u003e\u003c/tr\u003e \u003ctr\u003e\u003ctd\u003e聯動變更\u003c/td\u003e\u003ctd\u003e保留或提示衝突、需手動解\u003c/td\u003e\u003ctd\u003e被靜默覆蓋，需人肉比對才發現\u003c/td\u003e\u003c/tr\u003e \u003ctr\u003e\u003ctd\u003e重試\u003c/td\u003e\u003ctd\u003e最多一次覆蓋即被攔截\u003c/td\u003e\u003ctd\u003e雙方來回覆蓋，形成乒乓\u003c/td\u003e\u003c/tr\u003e \u003c/tbody\u003e \u003c/table\u003e \u003ch2\u003e影響\u003c/h2\u003e \u003cul\u003e \u003cli\u003e已完成的插件修改反覆遺失，需重工、重測\u003c/li\u003e \u003cli\u003e難以追溯「哪一版才是對的」，回退與對帳成本墊高\u003c/li\u003e \u003cli\u003e若聯動涉及線上版本，可能讓用戶拿到不一致的版本組合\u003c/li\u003e \u003c/ul\u003e \u003ch2\u003e建議修復方向\u003c/h2\u003e \u003cdiv class=\"fix-grid\"\u003e \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 01 — 最優先\u003c/div\u003e\u003ch4\u003e發布前強制對齊遠端\u003c/h4\u003e\u003cp\u003e\u003ccode\u003eplugin_publish\u003c/code\u003e 前強制 \u003ccode\u003efetch + rebase origin/main\u003c/code\u003e，本地落後就拒絕發布並提示「遠端已有新版，請先同步合併」。提供 \u003ccode\u003e--force\u003c/code\u003e 需二次確認。\u003c/p\u003e\u003c/div\u003e \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 02\u003c/div\u003e\u003ch4\u003e並行會話隔離\u003c/h4\u003e\u003cp\u003e每對話用獨立分支 \u003ccode\u003eplugin/{pluginDir}/{sessionId}\u003c/code\u003e，publish 走 PR / 自動合併而非直推 main，降低乒乓機率。\u003c/p\u003e\u003c/div\u003e \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 03\u003c/div\u003e\u003ch4\u003e聯動變更告警\u003c/h4\u003e\u003cp\u003e若一次 publish 動到被他插件引用的共用路徑，發布前提示「此變更會影響聯動插件 X，是否一併升級／同步？」\u003c/p\u003e\u003c/div\u003e \u003cdiv class=\"fix\"\u003e\u003cdiv class=\"num\"\u003eFIX 04\u003c/div\u003e\u003ch4\u003e版本歷程可視化\u003c/h4\u003e\u003cp\u003e插件管理頁顯示「最近 5 次 publish：誰、何時、改了什麼」，被覆蓋時可一鍵 diff / 回退。\u003c/p\u003e\u003c/div\u003e \u003c/div\u003e \u003ch2\u003e本次自救做法\u003c/h2\u003e \u003cul\u003e \u003cli\u003e改回\u003cstrong\u003e單對話循序修\u003c/strong\u003e：一次只修一個插件，publish 完確認遠端已更新後再開下一個。\u003c/li\u003e \u003cli\u003e若必須並行，publish 前手動同步最新版並比對 diff，確認未踩到對方的檔。\u003c/li\u003e \u003c/ul\u003e \u003cdiv class=\"foot\"\u003e💡 後續若要驗收：開兩對話分別改兩個聯動插件，先後 publish，確認「舊基線 publish 被阻擋並提示落後」而非靜默覆蓋；再驗證合併後兩邊變更都保留。\u003c/div\u003e \u003c/div\u003e","createdAt":1788114783189,"deletedAt":null,"displayMode":"artifact","embedRenders":{},"id":"b69c7ba565e5ba21d482cb84","isNew":false,"isPublic":true,"itemType":"NOTE","name":"CubeLV 平台 Bug 回報：並行對話鍛造聯動插件互相覆蓋（版本乒乓）","parents":{"4465d0a863da5d1b345d888c":1788114783189},"preParentID":null,"updatedAt":1788153837248,"updatedBy":{"userId":"6a3dcfc700e9b9fd06d5ef","userName":"蔡其樺"},"version":10}]}