# 190 升級測試回饋（決策者 2026-11-03，陸續追加）

收齊後開 FR-172 修正卡。檔案定位是首腦查過的。

| # | 決策者原話（摘） | 改什麼 | 在哪 |
|---|---|---|---|
| 1 | 「Guidant AI 1.28.1 可以更新（目前 1.28.0）。還沒有下載，你確認後才會下載與安裝。」後半句移除 | `banner_text` 只留前半句 | FE `src/config/locales/i18n/zh-tw/product-update.json:167`（en 同步） |
| 2 | 更新紀錄 Agent 跟本體是一起的嗎？ | **只回答，不改**：同一張表，「對象」欄分主系統／Agent（Agent 一次推送彙整成一列、帶台數）。若要分開另議 | FE `UpdateHistory.vue` |
| 3 | 「（先下載到主系統，再由主系統送給各台 Agent；Agent 不連外網）」不用寫 | Agent 區「更新包大小」只留大小 | `product-update.json:333` `size` |
| 4 | 「預先下載：先把 Agent 更新包下載到主系統並驗證，不動任何 Agent；下載完再決定要不要推。」不用寫 | 拿掉 `pre_download_desc` 那行 | `product-update.json:315`＋ `AgentRolloutPanel.vue` 引用處 |
| 5 | Agent 區按鈕排版改成跟主系統一致 | 主系統：主按鈕＋「預先下載」「立即檢查」同一排、靠左、在說明上方。Agent 區現在是「預先下載」左下、「立即檢查」右下 | `AgentRolloutPanel.vue` |
| 6 | 同 3 那句也不要（其他出現處） | grep 所有「Agent 不連外網」：Agent 主控台更新頁 `lead` 也有這句，一併拿掉 | `agent-console-update.json:3` |
| 7 | 主系統區塊「可直升」改「相容版本」 | `kv_min_base` 改字 | `product-update.json:241`（`:24` `min_base` 若也在畫面要一起看） |
| 8 | Agent「每台停機：約 1 到 3 分鐘，該台進行中的工作會先做完」不寫時間；改「升級期間會進入維護模式，完成後自動恢復」；label 不叫「每台停機」，主系統「預計停機」也一樣，改叫「注意事項」 | Agent `kv_downtime`＋值；主系統 `kv_downtime`（`:240`、`:30`）＋值 | `product-update.json` |
| 9 | 「影響的台數」看不懂；這樣是一次全部更新嗎？不能跟之前一樣分開勾嗎？ | ①label 改專業說法（首腦建議見下）；②**可以分開勾**，但勾選框只在「已下載」之後才出現（`mode === 'lagging'`），下載前表格沒有勾選欄，看起來像全部一起更新。建議下載前就顯示勾選欄（停用＋提示「下載完成後可選擇要更新的 Agent」），或在表格上方一句說明 | `product-update.json:336` `kv_count`；`AgentRolloutPanel.vue:326` 勾選欄條件 |
| 10 | 主系統「更新到 X」確認視窗廢話太多 | ①預檢框只列檢查項目（專業條列）；②底部「服務停掉之前隨時可以取消…資料不會遺失。」與「只更新主系統，Agent 不會動…」兩段拿掉，改成標題旁 info icon，點開才看到一段專業說明（回復機制、Agent 不受影響） | `product-update.json:46` `preflight_will_run_on_apply`、`:257～262` `confirm_*`、`:284` `with_agents_off`；`UpdateConfirmDialog.vue` |

| 11 | 主系統開始更新時要整個畫面遮罩，不要只遮主系統區塊；下載完後那段也是 | 現在的遮罩 `.pu-mask` 只蓋主系統卡、只在「送出→主機開跑」之間（`inFlight`），主機一開跑就收掉換步驟清單，Agent 區按鈕整段都點得到。改成：從按下更新（含先下載中、下載完等待開跑）到主機切維護頁為止，整個頁面（含 Agent 區、更新紀錄鈕、側欄導覽）蓋遮罩，步驟清單與「取消這次更新」顯示在遮罩上 | `UpdateCenter.vue:301～365`（`inFlight`、`.pu-mask`） |

| 12 | 主系統更新完跳轉回來，中央內容是白的 | **首腦查到的高可能原因（沒開瀏覽器，待重現確認）**：維護頁成功後跳 `/system/product-update?upgraded=1`；`UpdateCenter.vue:150` onMounted 讀到 `upgraded` 就 `router.replace` 拿掉參數。但 `App.vue:17` 頂層 `<component :key="route.fullPath">`，網址一變整個 AppLayout 卸掉重掛，正在掛載的那份還在跑 `pu.load({fresh:true})`，內層 `<router-view>`（`AppLayout.vue:132`）沒 key，重掛途中內容區留白。不是舊 chunk 404：維護頁用整頁跳轉，fe log 這段時間沒有 /assets 404。修法：拿參數改用 `history.replaceState`（不經 router、不換 fullPath），或 `fresh` 旗標改放 sessionStorage；修完在 190 再升一次重現驗 | `UpdateCenter.vue:150～156`、`App.vue:17～19` |
| 13 | Agent 只能預先下載嗎？不能勾選後按「更新到 X 版」就自動下載＋推送＋更新？ | **現況確實不行**：design D9 定成「預先下載 → 更新選取的台」兩步（理由：1.8 GB 誰按、何時下載要有紀錄），勾選框要下載完才出現。主系統則是一顆「更新到 X」自己先下載再升。決策者要 Agent 比照主系統一步完成 → 推翻 D9，要開卡：勾選欄一開始就在；按「更新選取的 N 台到 X」→ 本機沒包就先排下載（照樣寫下載人稽核）→ 下載完自動推選的台（先 1 台、成功再推其餘）；「預先下載」保留成次要按鈕，跟主系統一樣 | `AgentRolloutPanel.vue`、`agent_release_service.start_download`／`agent_rollout_service.push`（後端要能「下載完接著推」，同主系統 `applied_by`） |
| 14 | 主系統更新 log 查得到嗎？還是只有更新紀錄？會在 system_log 嗎？ | **只回答**：①每一步的結果與耗時、整次結果都寫進 `system_logs`（事件碼 6239～6247 對第 0～8 步、6248 整次結果；190 這次 9 筆都在），系統日誌頁查得到；②更新紀錄（更新中心）是一次一列摘要；③主機完整 log 在主機 `/srv/guidant-ai/log/upgrade-*.log`，畫面只有失敗時能「下載診斷包」（`FailurePanel.vue`），成功時沒有入口。Agent 那邊成功也能「下載升級紀錄」（E-3／E-4）。**決策者 2026-11-03 裁「統一」**：主系統比照 Agent，成功與失敗都能在更新紀錄下載該次完整升級 log（主機 `upgrade-*.log`），與 #20 同一個展開區；失敗時的診斷包照留 | `system_logs`、`FailurePanel.vue:75` |

| 15 | Agent 預先下載沒有進度條 | 主系統下載有百分比進度條（`UpdateCenter.vue:228`，讀 status.progress）；Agent 區只有按鈕在轉（`AgentRolloutPanel.vue:376` `:loading`），後端 Agent 下載沒記已下載量。要：後端 `agent_release_service` 下載時寫已下載量（reuse 主系統 `package_downloader` 的進度回呼與 status.progress 形狀），FE Agent 區比照主系統顯示進度條＋取消下載。與 #13 一起做 | `agent_release_service.py` `_fetch`、`AgentRolloutPanel.vue` |

| 16 | Agent 更新期間還可以操作嗎？好像沒看到進入維護階段 | **現況確實沒有維護狀態**：Agent 升級（解包 → 檢查 → 處理執行中工作 → 換設定 → 換容器 → 健康檢查）在「換容器」之前 Agent 照常運作、照常收工作，主控台也沒有「升級中」的畫面；只在換容器那一兩分鐘連不上。**補查（決策者問 Agent 收不收得到通知）**：收得到——主系統直推 `POST /update`，Agent 有 `update_sync.in_progress()`（目標版、下載進度、步驟）；但只有主控台「更新」頁（`console/views/Update.vue`）用它，`Shell.vue` 沒有、其他頁不擋，派工入口也沒看它。所以不需新通知管道，直接用 `in_progress`：Shell 全頁遮罩、派工入口回「升級中」、主系統 Agent 列標「升級中」。主系統那邊升級有維護頁，Agent 沒有對等設計。要：①升級單一寫下 Agent 進入「升級中」：停收新工作（主系統派工時回明確狀態，主系統畫面標「升級中」）；②Agent 主控台顯示升級中遮罩（比照主系統，只留進度）；③#8 的「注意事項」文案就要依這個行為寫 | Agent `update_sync.py`（寫單時設旗標）、派工入口、主控台；主系統 Agent 列狀態 |

| 17 | （首腦查 #16 時發現）190 Agent 的內建儲存 SeaweedFS 一直 unhealthy | **根因找到了（就是 compose 註解寫「根因未查的洩漏」那個）**：健康檢查每 10 秒跑一次 busybox `wget https`，每次留下一個 `ssl_client` 殭屍行程；容器沒開 `init: true`，PID 1 是 weed、不會收殭屍。一天約 8,600 個，正好對上註解「1.4.7 一天五千六」。1.5.0 加了 `pids_limit: 1024` 後，約 3 小時就撞頂：健康檢查本身都 exec 不起來（`procReady not received`），190 實查 1006 個殭屍、連續失敗 2623 次。影響：Agent 的證據儲存可能無法新增檔案；升級的健康檢查步驟會判失敗。修法：SeaweedFS（與 init 容器）加 `init: true`（docker 的 tini 會收殭屍），不再靠 pids 上限救火；在 190 驗一天殭屍數維持 0。**現場紀錄**：190 `/home/jedi/evidence-seaweedfs-zombies-20261103-1010/`（summary、inspect、12 小時 log）；決策者 2026-11-03 10:10 令重啟後健康恢復，30 秒內殭屍又長到 2 個（洩漏照舊，等修） | Agent `deploy/docker-compose.yml:250～312` |

| 18 | Agent 主控台「立即檢查」是什麼意思？（1.5.2 已發布，按了卻顯示「已是最新」） | **現況**：Agent 主控台的「立即檢查」只是問主系統「你上次查到的最新版是哪個」（`GET /agents/update/available` → `available_for_agent` 讀 `last_check`），**不會叫主系統去問更新站**。主系統每 6 小時才自己查一次，所以剛發布的 1.5.2 主控台看不到，顯示「已是最新」是錯的結論。要：①主控台「立即檢查」改成請主系統當下問一次更新站（Agent 線）再回答，注意節流避免多台 Agent 一起打更新站；或②保留現行為但改字為「重新整理」、並顯示「主系統最近一次查更新站：X 時間」，讓人知道資訊多新。首腦建議①。「已是最新」只在主系統剛查過才這樣寫 | Agent `console/app/update_console.py`、BE `available_for_agent`／`agent_update_request_route` |

| 19 | Agent 主控台「更新」頁整體改（決策者 2026-11-03 定案，取代 #18 的二選一） | ①**跟主系統斷線時**不能檢查更新，只給離線上傳（檢查鈕停用＋說明）；②**「立即檢查」= 通知主系統當下去更新站查**，查到有新版就顯示「更新到 X 版」一顆按鈕；**拿掉「請主系統更新這一台」**；③版面跟主系統更新中心一致（版本列、注意事項、更新內容、按鈕列同一排靠左）；④更新期間主控台**全頁鎖定**，只顯示進度（同 #16）；⑤更新結束**自動恢復**、不用重登——**根因已查**：Agent 主控台登入狀態存在記憶體（`identity/app/session.py` `SessionStore`），升級換容器就全部消失，所以一定被登出。要改成重啟後還在（存到 Agent 資料目錄、加密或只存雜湊），或升級前發一張短效「升級後免重登」票；遮罩在新版起來後自動重載回原頁 | Agent `console/app/update_console.py`、`identity/app/session.py`、FE `console/views/Update.vue`、`console/Shell.vue`；BE `agent_update_request_route`（立即檢查要能觸發主系統查站，加節流） |

| 20 | 升級完，Agent 區那台的八格步驟還留在畫面上沒清掉；過程不用特別顯示，放到「更新紀錄」裡展開或另開 Dialog 看，有留就顯示、沒有就不顯示 | **現況**：Agent 列有 rollout 就有展開箭頭，展開顯示上一次升級的步驟（狀態「已完成」），而且出現在下一版（1.5.2）的區塊下，看起來像在跑；`autoExpand` 只在同一個頁面工作階段收起，重整後箭頭與舊步驟仍在。要：①升級結束（成功）後 Agent 列不再帶展開步驟與「下載升級紀錄」，只留狀態；升級中（含失敗待處理）才在列上展開看進度；②「更新紀錄」Dialog 的 Agent 列可展開看每台八格步驟、耗時與「下載升級紀錄」，資料來源 `agent_update_events`（有存就顯示，沒有就不出現展開）。**決策者 2026-11-03 釐清：只指 Agent 區，主系統不改** | FE `AgentRolloutPanel.vue:316～365`、`agentRolloutState.autoExpand`、`UpdateHistory.vue`；BE history 回每台 events |

| 21 | Agent 主控台「更新到 X」確認視窗：不要寫時間；第一條寫專業一點 | 現行三條：「由主系統送更新包過來，紀錄『由 Agent 主控台（{who}）發動』」「進行中的掃描做完後停機 1 到 3 分鐘」「失敗會自動退回 {v}」。改成（首腦建議）：①「更新包由主系統提供，並經發布印章與完整性驗證；本次操作記錄為由 Agent 主控台（{who}）發起」②「更新前會先完成進行中的掃描，更新期間 Agent 暫停服務，完成後自動恢復」③「任一步驟失敗將自動回復至 {v}」 | `agent-console-update.json:34～36`（en 同步） |

| 22 | Agent 主控台更新中：要全視窗遮蔽不讓使用（截圖：更新中左側選單、掃描工具等都還能點）；「1 到 3 分鐘」不要寫 | ①併 #16／#19④：Shell 層全視窗遮罩（含左側選單、頂部列），只留進度條、步驟清單與一句狀態；②**全部拿掉時間**——grep「1 到 3 分鐘」共四處：`agent-console-update.json:21` `downtime_body`、`:35` `confirm_2`（#21 已改寫）、`:39` `updating_hint`、`product-update.json:335` Agent `downtime`（#8 已改寫）。`updating_hint` 建議改「更新期間 Agent 暫停服務，完成後自動恢復；換版時連線會短暫中斷，畫面會自動重新連線」 | `console/Shell.vue`、`console/views/Update.vue`、上列 i18n（en 同步） |

| 23 | 主系統的 Agent 更新檔更新完會刪掉嗎？現在更新兩版就佔兩版空間 | **現況**：主系統 `upgrade/agent-bundles/` 保留規則（design D7）＝「各台現行版中最舊＋最新＋還在推的」，而且**只在下載新包時才清**，推送完成不清。190 實查：1.5.1、1.5.2 各 1.7 GB 都在（1.5.1 已沒有任何一台在用）。Agent 主機側**沒有問題**：解包暫存 `agent-staging` 升級結束會自己清（`_uo_cleanup`，190 升完 1.5.2 後只剩 12K；首腦先前看到的 4 GB 是升級進行中）；舊映像 `guidant-ai-agent:1.5.1` 留一份當退回是設計。要：①主系統推送整批結束（全部成功）就清掉不再需要的 Agent 包，規則改「只留最新那份」（失敗待重推的那版照留）；②Agent 舊映像保留上一版一份、主系統資料庫快照保留上一版一份，寫進「注意事項」讓客戶知道要多留多少空間 | BE `agent_release_service.retain`（規則改、並在 `agent_rollout_service.record_result` 整批 succeeded 時呼叫） |

| 24 | 主系統的更新包升級完會刪嗎？ | **現況**：升級第 7 步清理（`scripts/installer/lib/upgrade_cleanup.sh` `cln_packages`）規則是「`incoming/` 留本次這版的包＋清單＋ops 小檔，到下次升級成功才刪」；`bundles/`（解開的）會刪。190 實查 `incoming/` 還有 1.28.1 的 3.3 GB 包。**留著的理由只有清單**：`agent_rollout_service.host_min_agent_version` 要讀 `incoming/guidant-ai-<現行版>.release.json`（驗過章）算「這版主系統要求 Agent 最低版」；3.3 GB 的 tar.gz 本身升級成功後沒人用（退回用的是舊映像與資料庫快照，不是包）。要：**升級成功**後刪 tar.gz 與 ops 小檔，只留 `release.json`（幾 KB）；**退回或跳過**（還可能「重試」，`upgrade_order.retry` 用 incoming 同一包重跑預檢）時照留整包。189 POC 也一樣：1.27.10 的 5 GB 包還在 `incoming/`。另外資料庫快照 `pgdata-prev-<上一版>` 照留（退回用，190 現在 157 MB，客戶資料大時會很大，寫進注意事項） | `upgrade_cleanup.sh:86～101`；`agent_rollout_service.py:155` 依賴清單不依賴包 |

**首腦建議用字**
- #9 label：「適用 Agent」，值「1 台（在線 1、離線 0）」；或「待更新 Agent」。
- #8 label：「注意事項」，值「升級期間進入維護模式，完成後自動恢復」。
- #10 預檢：
  - 標題「更新前檢查」，條列：磁碟空間、版本相容性、更新包簽章與完整性、資料庫連線；一句「任一項未通過即中止，系統維持現狀」。
  - info icon 內文：「更新過程分段執行並設有時限，任一步驟失敗會自動回復到 {from}，資料不受影響。服務停止前可隨時取消。本次僅更新主系統，Agent 維持現行版本，可於 Agent 區另行更新。」
