# FR-167 交接現況（STATE）

> **這份是 living 檔**：每棒就地 Edit，只描述「此刻」。歷史脈絡看同資料夾的 `fr167-LOG.md`。

| 項目 | 值 |
|------|-----|
| 最後更新 | 2026-11-01 15:5x（第 9 棒交接：七 repo `feature/FR-139` 已由決策者合回 `main`、`ws-CM-3241` worktree 與 branch 全刪、母卡 Done；下一棒見 §4） |
| branch | **七 repo 皆在 `main`、與 origin 同步、工作樹乾淨**（決策者 11-01 15:xx 把 `feature/FR-139` 合回 main，首腦未切）。BE `4f9cc336d`、FE `78e4f535`、Agent `2b78d00`、update-center `f4d2cd0`、LC `4cfa423`、test `80e8087`、jedi `7842c01b`。版號 BE／FE 1.27.10、Agent 1.4.8、jedi-detection 1.7.2。主線含批 32 修正與重試 412 修正，**未出包** |
| 母卡 | CM-3241 https://app.notion.com/p/FR-167-Agent-35-8-408346da4cd0815eaf38ffbe607f448e （狀態 Not started，母卡狀態不代表進度） **Done（2026-11-01 收口）** |
| 子卡 | 批 1～32 共 93 張：除 CM-3312（e2e 暫緩）、CM-3377（裁不做）、CM-3378（結案出包等 FR-171／172）外全部 Done |
| 工作坊 | 工作坊 30 已跑完 95 張（含批 32），無在跑的卡；七 repo 的 `.worktrees/CM-3241` 與 `ws-CM-3241` 已刪（11-01，決策者令，`branch -d` 全放行＝內容都在主線） |
| 本棒角色 | 首腦（第 9 棒，2026-11-01 12:5x～15:5x）：60.167 真因磁碟滿並清兩台磁碟、P12 收口卡與批 32 合回、技術文件站、重試 412 修、母卡收 Done、七 repo 雜檔整理、worktree 清除。細節 LOG 第 9 棒 |
| 接手前必讀 | §0 |

## 🧭 原始需求 / WHY

- 客戶主機升級要「絕不出事」：`guidantai upgrade` 在資料庫複本上做、每步有時限、寫進度檔，失敗／卡住／斷電自動退回，現用那份資料庫從頭到尾沒動。
- 把搬檔與敲指令拿掉：主系統每天問更新站有沒有新版 → 管理員確認後才下載、驗簽 → 有權限的人按立即或排程 → 主系統只寫更新單、主機 systemd 呼叫 `guidantai upgrade`。離線客戶自己上傳。Agent 在主系統升完後跟進，先一台再其餘。
- 版本只有一種、任何版本都不自動裝、授權管能用什麼不管能不能裝。更新站（Cloudflare Worker＋D1＋R2）沒有私鑰，只轉交決策者在自己電腦蓋章的清單。
- 決策者 2026-10-29 裁：開發期無人值守（小弟只驗本機能驗的），測試期首腦操作 190、決策者看畫面；小弟可 push 自己的 ws branch、套件發 beta，正式版留結案卡。
- 完整決策 D1～D31 在 `../design.md` §3；為什麼這樣決定在 `../discussion.md`。

## §0 接手讀序

1. 本檔 🧭、**§1 當前狀態**、待辦 56～60、§4、§5 最新兩條（不再用心跳帶訊息、push 常態授權）
2. `fr167-LOG.md` **第 8 棒** 兩個 block（推翻了什麼、教訓）；`docs/draft_requirement_spec/2026-11-01-agent-independent-update.md`
3. PM 簡報 https://claude.ai/artifact/1HqiRVDJkGsJCsKeGiEBfU 與機制報告 https://claude.ai/artifact/AmUhtjZVptzoqExGEUhjhx（repo 副本在 `../briefing/`；機制問答已交給另一個 session，見 `qa-session-handoff.md`；決策者與 PM 的共識在這兩份；報告 Q5／Q20 備份、Q15 PG 大版是已知缺口）
4. 雛形 v2：`../prototype/update-center-v2.html`
5. `../design.md` §5.1／§5.4／§5.8／§7（**D16 原文已過時**，以 §3「Agent 跟進可選」為準）
6. 要看某張卡：`python3 /Users/chouraymond/Projects/Jedicogy/agent-workshop/scripts/notion_case.py get CM-xxxx`（BE repo 目錄跑；每張隔幾秒，連撈會 429）
7. 發布工具：`~/Projects/Billows/Audit-Manager/.fr167-dev/publish/publish-12710.sh <版>`（改檔內 `S=` 為你的 scratchpad 路徑；要 `notes-<版>.json`）；188 出包腳本 `/home/jedi/fr167-build-12710.sh`（Agent＋主系統，先把主控台前端產物 scp 到 `/tmp/fr167-fe-console/dist-console`）

## §1 當前狀態（2026-11-01 12:50，第 8 棒交接）

**✅ 此刻：更新站最新 1.27.10（內含 Agent 1.4.8）。189 POC 已線上升到 1.27.10；190／188 仍 1.27.8，沒升。189 的 Agent：121（ubuntu-lab-01）1.4.8 成功、60.167（60-ubuntu-2604-lab-03）推送失敗仍 1.4.7。**

| 主機 | 主系統 | Agent |
|---|---|---|
| 190 測試機 | 1.27.8 | 本機一台，未推 |
| 188 STG | 1.27.8 | — |
| 189 POC | **1.27.10**（12:14:55～12:22:23 線上升級八步全成功，system_logs 已補寫） | 121 **1.4.8**（12:31:50 主機升完、12:36:30 主系統收成功、12:37:07 回報更新站）；60.167 **1.4.7 失敗**「無法讀取新映像」 |

- **更新站**：`product_releases` 1.27.8／1.27.9／1.27.10 皆 published；`instance_version_reports` 189 為 1.27.10；**`agent_version_reports` 第一次有真資料**（121 succeeded 1.4.7→1.4.8、60.167 failed）＝P11-1／P11-6 真環境驗過。
- **1.27.10 內容**：jedi-detection 1.7.2（心跳 Profile 清單帶 `tool`、`name`）＋隨包 Agent 1.4.8（容器版 Office 預覽修正：Agent 接上 jedi-file-upload 設定讀取 port＋image `LIBREOFFICE_CMD`；主控台 CINC／GCB Profile 下拉，依工具過濾）。開包核過：Agent 映像 env、BE 執行檔 `tool_code`、主控台新字串、storage-seed 11 檔。
- **60.167 失敗點**：Agent 主機升級單第 0 步 `docker create --network none guidant-ai-agent:1.4.8` 失敗（映像已 load 且 inspect 得到）。121 同包同步驟過 → 疑該機 docker／核心（同 190 昨天的「核心裝了沒重開」）。首腦連不到該機，**決策者在該機跑三行查**（uname／reboot-required、手動 docker create、`journalctl -u guidant-agent-upgrade-order`）。包已在該機下載過，修好後 Agent 區勾 60.167 再推不必重下。
- **121 手放的 dev-sec**：`/data/content/cache/manual/` 兩支（見待辦 61），CINC 在 Agent 主控台貼路徑用。
- **121 的應急捷徑已消失**：今早 10:4x 首腦在 121 容器 `ln -s guidant-soffice soffice` 讓 demo 能預覽 Word，12:31 換成 1.4.8 新容器後由正式修正接手。
- **188 build 機**：`.build/bundle/` 刪了 1.27.5～1.27.7 本機副本（已在 R2），留 1.27.8／1.27.9／1.27.10；磁碟約 38G→（出 1.27.10 後）再查。build 腳本 `/home/jedi/fr167-build-12710.sh`（Agent＋主系統一起出）；主控台前端產物要從本機 build 後 scp 到 188 `/tmp/fr167-fe-console/dist-console`（188 沒 node）。
- **發布工具**：`~/Projects/Billows/Audit-Manager/.fr167-dev/publish/publish-12710.sh`（`S=` 指本棒 scratchpad，下一棒要改）＋`notes-1.27.10.json`。
- **briefing/**：PM 簡報原檔、機制問答第 9 版（加升級接力 sequence 圖）、`handoff/qa-session-handoff.md`（機制問答交給另一個 session）。

## §1 歷史狀態（第 6 棒交接，2026-10-31 18:20）

**✅ 此刻：更新站 1.27.3～1.27.7 五版 `published`；190 在 1.27.6、前端已熱換成含 e1849538 修正的 image、正在升 1.27.7（incoming 已有 .part）**
- 今天發版軌跡：1.27.3（P9 全部修正＋bugsink 2.6.2）→ 1.27.4（只換版號，驗第 0 步秒級／維護頁當次換新）→ 1.27.5（FE 三修正：Agent 收合 `a4126e2b`、目標版號 `13ef3ecc`、遮罩 `30addad1`）→ 1.27.6（只換版號，驗遮罩）→ **1.27.7（FE `e1849538`：輪詢看「有沒有未結束的更新單」不看畫面狀態、Agent 區無本輪紀錄不畫格）**。1.27.4／1.27.6 是 188 直出沒回主線版號；主線 BE `f1d87a12b`／FE `d81b08a4` 已是 1.27.7。
- **為什麼要 1.27.7（根因，不要再補狀態格）**：access log 證明前端最後一次 status 在下載完成前 16 秒；`pollMs` 只在 ACTIVE_VIEWS 才輪詢，下載完後端先寫 `ready`（不在集合內）→ 前端停止輪詢 → 一秒後後端寫單、主機驗包 59 秒、開跑，前端全程沒再問。13／19／37 三次都在修「問到之後怎麼畫」，沒碰「要不要問」。修法 `pollIntervalMs(status)`：`latest_job` 不在終態（succeeded／failed／rolled_back／cancelled／skipped）就 3 秒問；scheduled 且未到 30 秒；`watch(status)` 不是 `watch(view)`。
- **190 前端熱換**（決策者 18:00 令「直接去替換 190 的 docker」）：188 用 FE `e1849538` 重 build `guidant-ai-fe:1.27.6` 同 tag、`docker save` 傳 190 `load`、`compose -f /srv/guidant-ai/docker-compose.yml up -d --no-deps guidant-fe`。現在跑的 image `8a89cef33901`（舊 `36f11968b0a4`），用壓縮後仍在的字面值序列核過（`rolled_back,cancelled,skipped` 新 1／舊 0）。api／db 沒動。**注意**：190 的 `bundles/guidant-ai-1.27.6/` 已被清理，compose 要用 `/srv/guidant-ai/docker-compose.yml`，不是容器標籤寫的 bundles 路徑；`guidantai restart` 不會重建容器。
- **update-center 已部署**（`8e6ea6e`，Version `5dd8c372`）：蓋章帶 `ops_sha256`／`ops_size`、`download-url` 認 `artifact: "ops"`。正式 CLI publish 路徑（register-draft → D1 → `guidant-release publish`）還沒帶 ops 兩欄，測試期用 `~/Projects/Billows/Audit-Manager/.fr167-dev/publish/publish-1273.sh <版>` 直接從 188 的 release.json 簽（含 ops），要另開 update-center 卡。
- **發布工具**：`publish-1273.sh <版>`（支援 `STATUS=draft`）、`notes-<版>.json` 三段說明；188 出包腳本範本 `build1277.sh`（be／init 沿用上一版 retag、FE 重 build、build_bundle、prune）。FE image 現在 20 秒（P9-11 分層快取生效）；整包 ~11 分；R2 上傳 ~5 分。

**✅ 188 STG／189 POC 都已升到 1.27.7（第 7 棒，2026-10-31）**
- 188：18:33 `sudo ./install.sh --upgrade` → 18:39 完成，七步全 ok，164 支 migration，清掉 1.27.5／1.27.6 殘留映像；磁碟 86%／54G。log `/srv/guidant-ai/log/upgrade-20261031-183353.log`。
- 189：包從 188 scp 到 `/home/jedi/`（sha256 OK）→ 19:21 開跑 → 19:33 完成（約 12 分，比 188 慢，資料庫複製與弱點庫解開較久），七步全 ok，164 支 migration，api／worker／socketio／fe 1.27.7 healthy，版本 API 回 1.27.7，`ui_routes` 無幽靈列；磁碟 73%／52G。log `/srv/guidant-ai/log/upgrade-20261031-192137.log`。
- **189 的兩台 Agent 已手動升 1.4.2 → 1.4.7**（決策者 21:05 令「都是 POC，幫我升」）：50.121（ubuntu-lab-01）21:22、60.167（60-ubuntu-2604-lab-03）21:34，各用 `sudo guidant-agent-compose upgrade /home/jedi/guidant-agent-compose-1.4.7.tar.gz`，兩台 agent＋seaweedfs 皆 healthy，189 `remote_agents` 回報 1.4.7。**為什麼手動**：1.4.2 沒有遠端更新能力（1.4.4 才加），更新中心推不動；升到 1.4.7 後下一版起可從 189 推（但要 189 `upgrade/bundles/guidant-ai-<版>/agent/` 有包，這次走 install.sh 升的沒有）。**包怎麼搬**：主機之間沒有免密 SSH（189↔121、188→121 都拒），經 mac 中轉兩趟 1.8G。**順手發現**：兩台的 SeaweedFS 升級前都 unhealthy、`can't fork`，PID 數 15,360／18,478（Agent 本體才 6），容器沒設 pids 上限、1.4.2 跑三週程序洩漏；升級重建容器後 PID 22／18 恢復。要盯 1.4.7 會不會再長（待辦 53）。兩台都提示「本機管理員密碼尚未設定」，屬既有狀態。
- **188／189／190 bucket 已補灌 CRA PDF**（決策者 21:50 令「你先做吧」）：來源 venv `jedi_oscal_v2/assets/CRA Regulation (EU) 2024-2847 OJ L EN.pdf`（sha256 e3ecaabd… 與 009 migration 一致），照 install.sh `load_storage_seed` 同一條路（docker cp 進 seaweedfs 容器 → `weed filer.copy … /buckets/guidant-ai/`），key `20261014000000_b81c42ff-….pdf`，三台 `fs.ls` 皆 1,782,351 bytes。不用重啟。待辦 47 的「當下補救」半做完，出包守門那半在 P10-4。
- 兩台 compose 工作目錄現在都是 `/home/jedi/guidant-ai-1.27.7/`。`/home/jedi/guidant-ai-1.26.0/` 已刪（決策者 21:55 令；刪前核 working_dir／bind mount／config_files／install.conf／symlink 皆 0 引用），各回收 6.1G；`guidant-ai-1.26.0.tar.gz` 留著。188 磁碟 85%／60G、189 69%／59G。
- **兩台都還沒做 §4-1 第 6 步**（`guidant.env` 加 `UPDATE_SITE_URL`，讓之後走線上更新）——改 STG／POC 設定，等決策者令。
- 升級後決策者在 188 看到三件事，已記待辦 45～47（側欄幽靈選單＝瀏覽器存檔，登出再登入即消失；product-update 能力點缺翻譯；CRA PDF 沒進包）。189 同樣會有待辦 47（bucket 沒有 CRA PDF）。
- 簡報：決策者 19:00 要「給 PM 向資安背景老闆報告」的簡報，已產 12 張 https://claude.ai/artifact/1HqiRVDJkGsJCsKeGiEBfU （私人，要分享才能開）。

**188 STG／189 POC 升級前現況（歷史，19:35 前）**
- 兩台都 **1.26.0**（沒有 `guidantai upgrade`、沒有更新中心、不會問更新站），compose 工作目錄 `/home/jedi/guidant-ai-1.26.0/`、資料目錄 `/srv/guidant-ai`、`GUIDANT_VERSION=1.26.0`，`guidant.env` 沒 `UPDATE_SITE_URL`，沒裝 Agent。
- **seccomp errno 524 已解**（待辦 38 根因＝apt 裝了新核心沒重開）：兩台今天重開進 5.15.0-202，`no-new-privileges` 能起、exec 能進、全部容器 healthy。188 STG api 三容器（10-30 第 4 棒 smoke `docker rm -f` 掉的）已起回；**188 `/home/jedi/guidant-ai-1.26.0` 是首腦 12:xx 誤刪後從同目錄 tar.gz 重建的**（compose 與 `/srv` 那份 cmp 相同，資料庫 named volume 未動：58 專案／2 使用者／311 migration）。
- 磁碟：188 82%／68G 可用；189 61%／75G 可用（今天各清 ~70G／~50G）。1.27.7 包 5.26G＋解開 ~8G＋資料庫複本（<1G）夠。
- 189 升了 containerd.io 2.4.2（無效但無害）；188 沒升。
- 1.27.7 包只在 188 `/opt/guidant-ai-be/.build/bundle/guidant-ai-1.27.7.tar.gz`（＋`.sha256`／`.ops.tar.gz`／`.release.json`），189 沒有。

## §1 歷史狀態（第 5 棒）（2026-10-30 23:52）

**測試期第一步（2026-10-30 17:40，進行中）**
- **測試章換新**：原 DEV 公鑰 `d6c8513a4ea604d9` 的私鑰隨小弟暫存清掉、遺失。首腦在決策者 mac 用 openssl 產新測試章 `b91819f86eb4b4f8`，私鑰在 `~/.guidant-release-test/`（600、無 passphrase、不入 repo，只供內部測試），公鑰換進 `scripts/installer/release-keys/`（BE `daf3fa35b`）。正式章仍待決策者 `init-keys`。
- **簽章工具**：測試期簽清單用首腦 scratchpad 的 `sign-release.ts`（import update-center `cli/envelope.ts` 的 buildPayload／signRelease／serializeEnvelope，不另寫簽章邏輯）＋ BE `scripts/build/make_offline_bundle.sh` 產單一離線檔。端到端驗過：BE `verify_manifest`／`verify_package_file` 收下、竄改簽章被拒。scratchpad 會清，下一棒要用就照這段重寫（三十行）。
- **出包漏洞**：10/29 晚之後 pip-audit 新增 81 項 CVE，擋在 verify。已全清（本機 audit_deps.sh 0 項）：
  - 發 Nexus：jedi-license-runtime 1.1.7（beta 轉正）、jedi-remote-agent 1.10.1（補事件碼 6230～6236）、jedi-common 1.3.8（opentelemetry 1.45.1）、jedi-ai-gateway 0.1.5（litellm 1.103.1；**不取 1.104.0**，它加 filelock<4 會把 filelock 壓回有洞的 3.32.7）、jedi-file-protect 0.2.2（pyzipper 0.4）、jedi-evidence-classification 1.6.9（沙箱 pillow 13／pypdf 6.21.4 等）。
  - BE 釘上述六支，Pillow 上限改 <14，升 aiohttp／brotli／certifi／dnspython／filelock／setuptools／opentelemetry 全家（BE `04889d086`、`43b3469c9`）。
  - 分類沙箱 `relock.sh` 加參數 `./relock.sh <套件…>` 轉 `--upgrade-package`（原版 pip-compile 不會自己升）。
- **版號**：BE `pyproject.toml` 1.27.0、FE `package.json` 1.27.0。**這是內部測試包**，release note／spec 快照／tag 等測完正式進版再做。
- **188**：`/opt/jedi-python-package` 由 `main` 切到 `feature/FR-139`（決策者 2026-10-30 授權）。原因：CI 只在當下 branch `git pull`，`main` 的分類沙箱停在 1.6.6，與 BE 鎖的版號不符會停在 build_classifier_image。BE／FE clone 本來就在 `feature/FR-139`。
- **pipeline 3007809601**（e2e、rehearse_base 1.26.0）：清 190 → 裝 1.26.0 → 灌 STG 唯讀 dump → 用 1.27.0 包升級比筆數。package 段預期紅（無 PROD 章、無更新站 CI 變數），不影響 190。
- **剩下**：190 升好後出 1.27.1（BE／FE 改版號、188 手動 build_all＋build_bundle `--skip-prod-key-check`）→ 測試章簽清單 → 離線檔 → 決策者在 190 更新中心上傳、按更新。線上更新站要決策者 `npx wrangler login`（在 update-center 資料夾，先 `npm install`）＋告知 Access 信箱，第二輪做。

**🔴 190 上有一個測試期繞道（修好升級程式後要刪）**
- `/etc/systemd/system/guidant-upgrade-order.service.d/fr167-self-replace-timeout.conf`：`Environment=UPG_LIMIT_SELF_REPLACE=600`（2026-10-30 22:40 加、已 daemon-reload）。原因見下：第 0 步 60 秒讀不完 5.25GB 包，實測抽兩個小檔要 62 秒。**正式修正在下一版升級程式；出新版後刪這個檔再 `systemctl daemon-reload`**，否則會掩蓋升級程式有沒有真的修好。

**✅ 此刻（2026-10-31 10:20）：190 已線上升級 1.27.1 → 1.27.2 成功，繞道已刪**
- 決策者 10:04 按更新到 1.27.2 → 下載 5.25GB → 10:11:58 主機開始 → 10:19:41 完成（約 7 分 40 秒）。第 0 步 2 分 19 秒；第 4 步 post-migrate 的錯誤追蹤站與弱點庫兩關都過（正式包、非手補）；redis 全程 healthy、`/tls` 5 檔在；健康檢查實打版本／登入／3 支 API 通過。
- 結果：api／worker／socketio／fe 皆 1.27.2 healthy；清理刪 1.27.0 三顆映像與第一次升級留下的 named volume，資料庫快照只留 `pgdata-prev-1.27.1`；磁碟 38%→37%。`role-manage` 已被第三輪清掉；**`tool-plugin-manage` 仍在**（待辦 28 未修，1.27.3 才清）。
- **190 的 systemd 繞道已刪**（`fr167-self-replace-timeout.conf`＋`daemon-reload`，`Environment=` 已空）；190 現在跑 1.27.2 升級程式，第 0 步時限自己依包大小算（`upg_self_replace_limit`）。
- 升級中畫面的修正（自動跳維護頁、維護頁白話步驟名、退回失敗終態）要 **1.27.2 → 下一版** 才看得到（升級過程由舊版前門顯示）；平常畫面的修正這次即生效。
- 下一步：Agent 區推 1.4.5，驗主系統主動通知（不等 5 分鐘心跳）。

**✅ 此刻（2026-10-31 02:15）：1.27.2 已發布，190 可直接測**
- **更新站**：D1 有 1.27.1、1.27.2 兩列皆 `published`；1.27.2 `kid=b91819f86eb4b4f8`、size 5,251,570,709、`r2_key v1/1.27.2/…`。R2 三檔齊、大小與 188 包一致。依更新站 `planPath`，1.27.1 客戶端路徑＝`[1.27.2]` 一跳（min_base 1.0.0）。
- **驗章**：用 BE 真實 `verify_manifest`＋出貨公鑰，以 1.27.1 為基準驗過；190 `/etc/guidant/release-keys/` 有此 kid。
- **包內確認**（188 解開目錄 grep＋FE image 內 grep）：待辦 1／7／17／18／20／21／23 的主機 lib 修正、install.sh `ensure_bugsink_env` 在 case 前、Agent compose 1.4.5、維護頁 `self_replace`／`rollback_failed`／`?upgraded=1`、更新中心 `submitted` 與白話步驟名，全在。
- **沒驗到**：190 從自己的身分實際「立即檢查」看到 1.27.2（產品 image 是 Nuitka 無 python，容器內無法直呼 client；要決策者登入按「立即檢查」）。
- **決策者早上做**：190 更新中心「立即檢查」→ 看到 1.27.2 →「更新到 1.27.2」。🔴 這次停服務那刻**仍要手動重新整理一次**（自動跳轉在新前端，1.27.2→下一版才生效）；第 0 步跑的是 190 上手補的 lib（同一套修正），時限仍有 600 秒繞道。升過後 Agent 區推 1.4.5 可驗「主動通知、不等 5 分鐘」。
- **之後首腦做**：190 升過 → 刪 `/etc/systemd/system/guidant-upgrade-order.service.d/fr167-self-replace-timeout.conf`＋`daemon-reload`。

**🌙 夜間無人值守計畫（2026-10-31 00:50 起，決策者：「我希望早上來看到我是可以直接測試升級的」「交給你了」）**
- **工作坊 30 是 auto 模式**（`dispatch_mode=auto`）：每批全驗完＋全合回 `ws-CM-3241` 就自己開下一批，不需人按。00:40 時批 18 五張 CM-3336～3340 全過驗全合回；批 19 CM-3341／3342 已派。之後批 20（CM-3343 文件）→ 批 21（CM-3344 P8-9 結案出包）自動接續。
- **P8-9（CM-3344）00:55 派工前補卡縮範圍**：小弟只做本機驗新裝＋驗升級＋BE 版號 1.27.2，**不去 188 出包**（原因：188 pull 的是 `feature/FR-139`，修正此刻只在 `ws-CM-3241`，照原卡出的包不含修正）。首腦掛 Monitor 盯工作坊 log，看到 `#CM-3344 合回 ws-CM-3241：merged` 或「沒有下一批」即接手：
  1. 合回主線：BE／FE／agent `feature/FR-139` ← `ws-CM-3241`（`merge-tree` 零衝突才 `merge --no-ff`）；FE `package.json` → 1.27.2，**並補 16 個 `PRODUCT_UPDATE_*` 錯誤碼翻譯**；守衛測試；push（常態授權）。00:52 預檢：三 repo 試合皆零衝突（BE ws 領先 8／落後 5，落後的是首腦今晚推的文件與 lib 修正）；主工作區各有一支別 session 的未提交檔（BE FR-170 `parent-cards.json`、agent `CLAUDE.md`），不在合回路徑、不 add。
  1b. **Agent 要升 1.4.5**：P8-5 改了 Agent 程式但版號仍 1.4.4，與 190 現行同號，重出會被判「已是最新」而送不到。合回後 agent `pyproject.toml` → 1.4.5、`RELEASE_NOTES.md` 加 `## 1.4.5` 兩條（主系統推送後立刻開始、升級解包有進度），commit＋push，再出 compose 包。
  2. 188 出包：`/opt/guidant-ai-be`、`/opt/guidant-ai-fe`、`/opt/guidant-ai-agent` `git pull` 到剛推的 HEAD（先 `pgrep -f 'nuitka|build_all'`）；Agent compose 若有新版先 `build_all.sh --with-compose`；BE `scripts/build/build_all.sh --all` → `build_bundle.sh --skip-prod-key-check`（約一小時）。開包 grep `dbc_restore_item`、`ensure_bugsink_env` 在 case 前、維護頁 `step_self_replace`。
  3. 簽章：本 session scratchpad `sign/sign-release.ts`（從上一 session 複製、import update-center `cli/envelope.ts`，私鑰 `~/.guidant-release-test/`），`UC_ROOT=~/Projects/Billows/Audit-Manager/update-center node --experimental-strip-types sign-release.ts <unsigned> <out>`。
  4. 上傳 R2：在 188 用 `rclone`（憑證 `/home/jedi/.fr167-r2.env`），key `v1/1.27.2/` 三檔（tar.gz／.sha256／.release.json），照 `scripts/build/publish_draft.sh` 的 key 形狀。
  5. D1：照昨晚 `insert.sql`（範本在本 session scratchpad `rel1271-reference/`）寫 1.27.2 一列，`status=published`、`kid=b91819f86eb4b4f8`、三段說明沿用 1.27.1；`npx wrangler d1 execute guidant-update-center --remote --file`。
  6. 驗：`curl https://guidantai-update.jedicotech.com` 客戶端端點以 190 身分問得到 1.27.2；190 更新中心「立即檢查」看到 1.27.2（唯讀，不按更新）。
  7. 停：不在 190 按更新、不刪 190 繞道。STATE 寫結果、桌面通知。
- **卡住或驗不過**：寫進本段下方、不出半套、通知決策者。
- **00:56 CM-3343 小弟停下問 → 工作坊暫停**：問的是 FE `error-code.json` 翻譯不在它工作目錄（首腦開卡把 FE 檔塞進 BE 卡，違反一卡一 repo）。首腦裁：本卡縮成三份文件（`6aed60bbf` 已完成），卡上寫裁示、改「修正待驗證」、`POST /api/workshops/30/resume`（要帶 `Content-Type: application/json`），00:57 已送驗。**FE 16 個 `PRODUCT_UPDATE_*` 翻譯改由首腦在合回 FE 時補**（zh-tw＋en `error-code.json`，中文取 `common/code/product_update_error_code.py`）。

**🌙 夜間執行進度（2026-10-31 01:20）**
- 工作坊 30 批 18～21 九張全過驗全合回 `ws-CM-3241`（01:09「64 張卡都做完了，沒有下一批」）。CM-3344 本機驗：新裝 186 支／升級 164 支皆 0 錯誤、終點 475、audit_deps 0、守衛 119 綠。
- 首腦補：SPEC skipped 三處（`638361333`）、FE 14 個錯誤碼翻譯＋版號（`bc78ddf6`；實數 14，卡寫 13、小弟報 16 皆不準）、Agent 1.4.5（`99722ff`）。
- **已合回並 push**（遠端＝本地已核）：BE `abd6fe721`、FE `f908f7a2`、Agent `99722ff`。合回後 BE 測試 200 綠、FE vitest 58 綠＋`vite build` 0、Agent 87 綠。
- 🔴 **Mac git 對 gitlab.com 憑證失效**（keychain 空，`could not read Username`）。glab 仍登入，push 用一次性 helper：`git -c credential.helper= -c 'credential.helper=!glab auth git-credential' push origin feature/FR-139`（不改全域設定、不存新憑證）。188 用 SSH 不受影響。
- 188：三 clone pull 到上述三個 commit。主控台前端暫存副本 `/tmp/fr167-fe-console` 是純檔案（非 git、188 的 `/opt/guidant-ai-fe` 無 node），用 `git archive` 從 f908f7a2 刷新後 `npm run build:CONSOLE`（nvm node 22），01:15 新產物——**昨晚那份是 19:25 舊 FE，沿用會讓 Agent 1.4.5 帶舊主控台**。
- 01:15 Agent `build_all.sh --with-compose`（`FE_CONSOLE_DIST=/tmp/fr167-fe-console/dist-console`）背景跑，log `/tmp/fr167-agent-145-build.log`。
- BE 出包已排在 188：`/tmp/fr167-be-after-agent.sh` 等 Agent `EXIT=0` 才跑 `build_all.sh --all` → `AGENT_COMPOSE_BUNDLE=…1.4.5.tar.gz build_bundle.sh --skip-prod-key-check`（明確指定，不靠 mtime 挑；Agent 失敗就不跑）。log `/tmp/fr167-be-127-2-build.log`。兩個 Nuitka 不並跑（188 會 linker crash）。
- 188 磁碟 92%（32G 可用），估本次約 20G，未刪任何舊包（1.27.0 在 190、1.27.1 在更新站上）。
- 01:35 Agent 1.4.5 完成 `EXIT=0`（Nuitka 8 分、smoke 六項、Trivy＋Grype 弱點關卡過、compose 1.79GB，notes 帶兩條新說明）。
- 🔴 **01:35 BE 出包 3 秒就失敗在 ⓪b audit_deps：`找不到 pip-audit`**——不是漏洞。`audit_deps.sh` 以裸名呼叫 pip-audit，它只在 `/opt/guidant-ai-be/.venv/bin/`；昨晚是互動 shell（venv 已 activate）所以過，`nohup setsid` 的非互動 shell 沒有。**無人值守出包一律先 `export PATH=/opt/guidant-ai-be/.venv/bin:$PATH`**（同理 FE 主控台要 source nvm）。01:37 用 `/tmp/fr167-be-build.sh` 重排，log `/tmp/fr167-be-127-2-build.log`（失敗那次留在 `.fail1.log`）。
- 🔴 **01:38 第二次又停在 ⓪ DB 前置斷言**（STG 落後 116＋48 支）——這是常態不是錯（memory `feedback_release_smoke_db_assert_use_pipeline_tempdb_not_stg`）：`build_all.sh` 沒有「斷言 exit 3 → `smoke_tempdb.sh`」分流，**那段只寫在 `.gitlab-ci.yml` build-be job**，所以在 188 手跑 `build_all.sh` 永遠出不了正式包。**絕不為了出包去升 STG schema**。01:39 改用 `/tmp/fr167-be-build.sh` 照 CI 順序手跑：sbom→audit_deps→`build_release`→`build_prompt_guard_onnx`→`assert_db_current`（rc 3 走 `smoke_tempdb`：STG 唯讀 pg_dump→188:25432 臨時庫→migrate→smoke→刪庫）→`build_image`→`build_fe_image`→`build_init_image`→`build_classifier_image`→`build_bundle --skip-prod-key-check`（CI 的 `internal_test` 路徑才准這個旗標；正式 package 段禁止）。失敗 log 留 `.fail1.log`（pip-audit）、`.fail2.log`（DB 斷言）。
- 01:39～01:57 照 CI 順序跑完：`build_release` 12 分、`smoke_tempdb` PASS 9／FAIL 0（臨時庫 `guidant_ai_smoke_1272` 已刪）、BE／FE／init 三顆 1.27.2 image＋classifier 沿用 1.6.9。
- 🔴 **01:57 第三次停在 `build_bundle`：`GUIDANT_BUGSINK_ADMIN_PASSWORD 未設`**——CI 是遮罩變數，手跑要自己 export。值取 188 `/home/jedi/fr167-test-bugsink-admin.txt`（雜湊與 1.27.1 包內 `bugsink-admin.secret` 相同，即 190 錯誤追蹤站建站那組）。01:58 只重跑打包 `/tmp/fr167-be-bundle-only.sh`（image 不重建；密碼經環境變數帶入、不上命令列不入 log），log `/tmp/fr167-be-127-2-bundle.log`。
- **188 手跑出包的三道 CI 專屬前提**（下次照抄，都寫在 `/tmp/fr167-be-build.sh` 與 `-bundle-only.sh`）：①PATH 加 `/opt/guidant-ai-be/.venv/bin`（pip-audit）；②DB 斷言 rc 3 走 `smoke_tempdb.sh`（只在 `.gitlab-ci.yml`）；③export `GUIDANT_BUGSINK_ADMIN_PASSWORD`。另 Agent 要 `FE_CONSOLE_DIST` 指新建的主控台產物、FE 主控台要 source nvm。
- 發布腳本已備：本 session scratchpad `publish-1272.sh`（188 取未簽清單→Mac 測試章簽→188 rclone 用 env 臨時遠端上傳 `v1/1.27.2/` 三檔，不寫 rclone.conf→wrangler 寫 D1 一列）。已驗：wrangler 仍登入正確帳號、D1 只有 1.27.1、R2 憑證能列出 1.27.1 三檔、簽章模組可載入。

**✅ 此刻（2026-10-30 23:32）：190 線上更新第四次實測成功，1.27.0 → 1.27.1**
- 全程 4 分 30 秒（23:27:36 收單 → 23:32:03 前門重建）：第 0 步 skipped（已裝維運指令＝目標版）、1～7 全 ok；健康檢查實打版本／登入／3 支 API；換接後 `.env` 版號 1.27.1、`GUIDANT_PGDATA_DIR=/srv/guidant-ai/pgdata`，升級前那份留在 named volume `guidant_guidant-pgdata`（`prev_kind=volume`，第一次升級的既定契約）。容器 api／worker／socketio／fe 1.27.1 healthy，redis／seaweedfs／bugsink 全程沒停。Agent 這次沒勾跟進（`with_agents=false`），仍 1.4.3。
- **跑的是手補 lib**（待辦 16／18／20／21／23 的修正直接套在 190 `/usr/local/lib/guidant/` 與解開的包；備份 `/home/jedi/fr167-lib-bak-20261030-232552/`、`/home/jedi/fr167-install.sh.bak-20261030-230145`）。正式包出來後 190 要再升一次才算驗過出貨品。
- 今晚四次實測軌跡：①22:30 第 0 步 60 秒逾時（繞道 600 秒）②22:51 第 4 步 bugsink 變數未設、自動退回成功 ③23:04 第 4 步 vulndb 因 redis unhealthy、**自動退回也失敗**、`guidantai recover` 救回 ④23:27 成功。每次的根因與修法在待辦 1／16／21。
- **Agent 手動推送 1.4.3→1.4.4 成功**（23:34 按推送 → 23:38 心跳下發 → 23:38:50 下載驗過寫單 → 23:42:5x 容器 1.4.4 healthy）：Agent 側六步全過。中間兩段空窗要修：心跳等待 4 分（待辦 26）、解包無進度 3 分（待辦 27）。
- **未驗**：真重開機的開機檢查、跨版兩段、排程模式、Agent 自動跟進（勾「接著更新 Agent」那條）。
- 190 的 systemd 繞道（`UPG_LIMIT_SELF_REPLACE=600`）仍在；待辦 1 修正進包後刪。

**🔴 第一次實測（2026-10-30 22:35）：失敗在第 0 步，系統未動**
- 決策者在 190 更新中心「檢查更新 → 預先下載 → 更新到 1.27.1（勾接著更新 Agent、立即）」。下載 5.25GB 與驗章都成功（22:28），主機收單、驗包通過、22:30:14 開始升級。
- **第 0 步 `self_replace` 60 秒逾時（退出碼 124）**→「升級未進行，系統沒有任何變動」。190 仍 1.27.0 照常；`product_update_jobs` id 1 = `failed`。
- **根因**：`lib/upgrade_runner.sh:36` `UPG_LIMIT_SELF_REPLACE` 預設 60 秒；`lib/self_replace.sh` `sr_extract` 用 `tar -xzf` 從整包抽 VERSION／guidantai／lib／release-keys 幾個小檔，gzip 無法隨機存取、必須讀完整包。1.27.1 包 5.25GB（含 Agent compose 1.79GB）讀不完 60 秒。**下一棒第一件事**：時限改依包大小（或改 60→300），或 self_replace 改讀包外已解好的檔／先把小檔放在包最前面並抽到即停。修完重出 1.27.1、重放更新站（D1 列與 R2 物件換新、版號可不變，見下）。
- **更新站現況**：`https://guidantai-update.jedicotech.com`（Cloudflare Worker，帳號 Raymond.jedicotech、D1 `guidant-update-center` id 219dbab7…）。Access 守後台（team `curly-water-b543.cloudflareaccess.com`、AUD 在 update-center `.env`）；Worker secret 五個（ACCESS_*×3、R2_*×2）。已發布 **1.27.1**（直接寫 D1，略過 Access／內部先裝，決策者裁示僅測試用）、R2 `v1/1.27.1/` 三檔。測試章 `b91819f86eb4b4f8`，私鑰 `~/.guidant-release-test/`。簽章工具在首腦 scratchpad `sign/sign-release.ts`（import update-center `cli/envelope.ts`），scratchpad 會清，要用就重寫。
- **190 現況**：1.27.0 全新安裝（`/srv/guidant-ai`），Agent 1.4.3 已裝已核准、心跳正常（`/srv/guidant-ai-agent`，名 `guidant-190-agent`）。`guidant.env` 加了 `UPDATE_SITE_URL`（有 `.bak-fr167-*` 備份）。授權由 189 LC 開通（**無身分票**，開通早於 189 LC 更新，送原文＋warning）。安裝設定碼 `c3ca9258…`；Bugsink 測試密碼在 188 `/home/jedi/fr167-test-bugsink-admin.txt`；Agent 本機管理員密碼在 190 `/home/jedi/fr167-agent-local-admin.txt`。
- **188 磁碟（第 6 棒 12:40）**：79%、83G 可用。已刪自家舊包／舊映像／兩個 home 殘留目錄；build cache 60G 全是被現存 image 引用的 shared 層（`builder prune --filter until` 清不掉），只能等刪舊 image 或 P9-2 的 `--keep-storage` 收；第三方映像、dist、Prompt Guard 模型一律不清（決策者 10/31 明示）。FR-133 留下的兩個 `until pgrep build_all` 等待迴圈已 kill。
- **188 現況**：BE／FE／jedi／agent clone 皆 `feature/FR-139`（jedi、agent 本棒由 main 切過來，決策者授權）。`.build/bundle/` 有 1.27.0、1.27.1（含 Agent 1.4.4）；舊 1.27.1（含 1.4.2）在 `/home/jedi/fr167-bundle-old/`。Agent compose 1.4.3、1.4.4 在 `/opt/guidant-ai-agent/.build/compose/`。主控台前端暫存 build 在 `/tmp/fr167-fe-console/dist-console`（`FE_CONSOLE_DIST` 指它）。R2 憑證 `/home/jedi/.fr167-r2.env`（600，用完可刪）。
- **189 LC**：已更新到 `feature/FR-139` `14fc8eb`（product_major＋update_ticket，alembic a7c3e19d52b0），決策者授權；備份 `/home/jedi/lc-poc-before-fr167-20261030-195811.dump`。**189 現在跑 feature 分支**，合回 main 前要記得。
- **本棒修掉、已 commit 已 push**：IEC 62443 migration 空操作（升級擋）、fr144 選單驗證只驗自己六項（新裝擋）、六支套件發版（license-runtime 1.1.7／remote-agent 1.10.1／common 1.3.8／ai-gateway 0.1.5／file-protect 0.2.2／evidence-classification 1.6.9）＋12 支第三方升版清 81 CVE、測試章換新、CI `internal_test` input、更新站部署＋改公司網域、Agent SeaweedFS 加密、Agent build 三個過時檢查（LDAP 路徑／smoke https／就緒判定）、Agent 四支掃描工具升版＋兩條 Go CVE 進白名單（agent 限定、至 2026-12-31）、Agent 1.4.4 版本說明、檢查更新改每 6 小時＋啟動先問一次。
- **本棒發現、未修（交給下一棒或第二期）**：
  1. **self_replace 60 秒逾時**（上面，擋線上更新，最優先）。🔴 **第 0 步跑的是主機上已裝的舊版升級程式**（`/usr/local/lib/guidant/upgrade_runner.sh`），不是新包裡的——所以這個 bug 在「被升級的那一版」，新包不必重打。修法擇一或並用：① 時限依包大小算（例：每 GB 60 秒、下限 300）；② `self_replace` 不從整包抽檔：出包時把 `VERSION`／`guidantai`／`lib/`／`release-keys/` 另放包外一份（或放在 tar 最前面、抽到即停）；③ 第 0 步改讀已解好的 bundles 目錄。**注意**：修好的升級程式要先裝到舊版主機上才生效——客戶從 1.27.0 升時跑的仍是 1.27.0 的程式，所以 1.27.0 已出貨的客戶仍會撞這個逾時，要評估是否要靠更新單帶時限參數、或 1.27.x 補一版只換升級程式。
  2. **FE「立即」文案錯**：`mode_now_hint`「先提醒在線使用者 5 分鐘再開始」、`apply_now_done`「5 分鐘後開始更新」，後端與 design 都是立刻開始（not_before＝now）。使用者會以為有 5 分鐘存檔。改文案或真的做 5 分鐘緩衝，要決策者裁。
  3. 全新安裝多一個沒翻譯的選單 `lang.menu.role-manage`（jedi-iam 001 在第二輪插回 FR-132 刪掉的選單）。
  4. 全新安裝角色詳細頁 500（ui-routes `capability 缺 license_module`）。
  5. 1.26.0 舊包在全新機器裝不起來（fr114 backfill 在能力點建立前查），CI 排練從零重灌 1.26.0 必紅；排練基底要換或改流程。
  6. Agent 單獨更新（不下載 5G 整包）未做，design 寫第一版不做；Agent 修正頻繁，建議第二期。
  7. 預檢要按更新才跑，預先下載後卡片仍「還沒跑過」。
  8. SPEC `system-admin/product-update.md:242` 仍寫 cron 03:00（已改每 6 小時＋啟動）。
  9. 13 個 `PRODUCT_UPDATE_*` 錯誤碼 FE 沒翻譯（舊待辦）。
  10. **升級失敗後前端一直轉圈**：第 0 步逾時、主機放棄（系統未動），但更新中心頁卡在「更新中」輪詢、使用者以為整站壞了（決策者 22:35 實測，重新整理即恢復；伺服器全程正常、無 500）。失敗要正確顯示「失敗、系統未變動」並停止輪詢；查 `product_update_order_sync` 寫回 failed 後 FE `deriveView` 是否仍判 running。
  11. **第 0 步逾時被記成 `failed`，該記 `skipped`**：`failed` 的語意是「驗證失敗、包已刪、要重下載」，但第 0 步逾時時包還在 `incoming/`、系統未動。後果：「重試」只收 `rolled_back`／`skipped` 用不了；畫面退回「有新版可更新」且預先下載鈕重新出現，使用者以為包被刪了（決策者 22:49 實測）。**不會重下載**：下載程式開跑前看 incoming 有包就重驗雜湊與簽章、驗過直接 ready（第 5 棒核程式碼＋190 實測 26 秒），第 4 棒寫「會重新下載 5.25GB」是錯的。修 host 寫進度檔時，第 0～2 步（系統未動）逾時一律寫 `skipped`。
  12. **出新版前要做的事（決策者 2026-10-30：晚點修正後出一版新的）**：上面 1、2、3、4、7、10、11、13～27 修完一次打包（16、18、20、21、23 第 5 棒已修已 commit、待進包；其餘未動），不要修一項打一次（一包約 1 小時）。出包前在本機先驗：新裝用出貨 init image 空庫 init＋migrate、升級用 STG 唯讀 dump＋migrate.sh（本棒做法，見 LOG）。出新版後刪 190 的 systemd 繞道（上面）。「檢查更新每 6 小時＋啟動先問」（`7774e7345`）已 commit，下一版才生效。
  13. **按「更新到 X」後畫面跳回起始畫面**（決策者 22:50 實測）：沒包時 apply 走「先下載、就緒後寫單」，從下載驗證完（22:50:07）到主機寫回 running（22:51:45）約 1 分半，更新單狀態是「已送單等主機」，FE `deriveView` 不認這個狀態就退回 `available`（只是最近一筆不再 failed 所以少了預先下載鈕）。使用者以為沒送出會再按一次。修：按下到主機接手的每個中間狀態（下載中／驗證中／已送單／主機已收單）都要有對應畫面，不可退回起始畫面；順便核 status 端點有沒有把「已送單」這個狀態露出來。
  14. **預檢結果排版**（決策者 22:54 實測）：預檢通過的 detail 是用分號串成一行的長字串，更新中心「更新前檢查」區整段擠成一行。修：主機寫 detail 時一項一列（或用固定分隔符），FE 拆成清單一列一項（設定檔／版本／簽章／路徑／docker／資料庫／空間／停機預估）。
  15. **停服務那一刻更新中心跳「網路無法回應」**（決策者 22:54 實測）：第 3 步停 api 容器到前門切 503 維護頁之間，原頁面輪詢 status 失敗，SPA 當一般網路錯誤彈訊息。維護頁本身有進度條與步驟清單（每 3 秒讀 `/upgrade-progress.json`），決策者手動重新整理後就看到了（22:57），**缺的只是自動跳轉**。修：更新中心在 view=running 時輪詢遇 503／連線失敗不彈錯，直接 `location.reload()` 讓前門送維護頁；維護頁完成後已會自動回原頁。決策者 23:03 按「重試更新」再次點名「一樣沒有跳轉」——立即、排程、重試三條路都要跳。
  16. **🔴 190 第二次線上更新失敗在第 4 步 post-migrate：`bugsink.sh: line 101: BUGSINK_DB_PASSWORD: unbound variable`**（22:57，自動退回成功、1.27.0 以原資料庫起來、前門 200）。第 0～3 步全過（self_replace 2 分 22 秒、migrate 0 支待套＋能力點 243 筆）。根因：升級的設定補齊分四段各自 `bash install.sh --upgrade-phase <段>`（`lib/upgrade_apply_on_copy.sh:39 aoc_phase`），`config` 段 `ensure_bugsink_env` 把密碼讀進 shell 變數，`post-migrate` 段是**新行程**、`setup_bugsink → bugsink_create_db` 直接用 `$BUGSINK_DB_PASSWORD`，`set -u` 炸。手動 `install.sh --upgrade` 同一行程跑完所以 CI 排練與 STG 升級都沒撞到。修：`run_upgrade_phase` 每段開頭都先 `bugsink_load_env`（或 `setup_bugsink` 自己從 `$ENV_FILE` 讀），並盤 post-migrate／post-switch／ops-cli 段其他「靠前一段 shell 變數」的函式（`seed_ai_provider_keys`、`upgrade_backfill_storage_snapshot`、`install_vulndb`）。**這支在新包裡（第 4 步跑的是解開的新包 install.sh），所以要重打包才會好；驗法＝本機用 `--upgrade-phase` 四段分四個行程跑，不是跑一次 `--upgrade`。** 第 5 棒已修 repo（`run_upgrade_phase` case 前統一 `ensure_bugsink_env`，冪等）並把同一 patch 套到 190 解開的包（備份 `/home/jedi/fr167-install.sh.bak-20261030-230145`），決策者按「重試更新」驗第 5～7 步；**190 跑的是手補包，正式包出來後 190 要再升一次。**
  17. **失敗訊息裡的步驟名是程式代號**（決策者 22:59 實測）：「第 4 步 apply_on_copy 未通過，已退回 1.27.0」「第 0 步 self_replace 未通過」——使用者看不懂。修：主機 `upgrade_runner.sh` 寫進度檔 error 時用白話步驟名（0 換成新版維運指令／1 預檢／2 停接新工作／3 複製資料庫／4 在複本上升級／5 健康檢查／6 換接／7 清理／8 退回），FE 與維護頁顯示步驟也走同一份對照。**維護頁的步驟對照表漏了第 0 步**，畫面直接印 `self_replace`（決策者 23:07 截圖，其餘 1～7 步都有白話名）；維護頁與更新中心的預檢 detail 同樣一行擠在一起（待辦 14 兩邊都要修）。
  18. **自動退回後 `guidant-bugsink` 容器停在 Exited**（22:58 退回完 `docker ps -a` 看到，升級前是 Up）：退回第 8 步「以升級前那份資料庫啟動 1.27.0」只起主服務清單，沒含 bugsink。修：`upgrade_rollback.sh` 起服務時加 `$(bugsink_services)` 或照 install.sh 2139 行同一份清單；順便核換接（第 6 步）起服務的清單有沒有同樣漏。
  19. **按下更新／重試後到主機開跑之間畫面沒反應**（決策者 23:04：「看起來都沒反應，user 會覺得有問題」）：按下到第 1 步開始約 2 分鐘（後端重驗包 26 秒 → 寫單 → systemd 收單 → 主機再驗包 50 秒），期間畫面沒有任何變化。修：按下後立即切成「已送出」卡片，依序顯示「驗證更新包中」「已交給主機」「主機驗包中」「開始升級（即將看到維護頁）」，每個中間狀態都有字與轉圈；主機收單與驗包階段要寫進度檔或更新單讓 status 端點看得到。與 13、15 合併成一條「從按下到維護頁的完整狀態鏈」一起修。
  20. **重試仍從第 0 步 self_replace 跑**（23:04 實測，又花 2 分多抽檔）：設計與 `upgrade_order.py:330` 註解都說重試「從預檢重跑」，但主機 runner 收單一律從第 0 步。第 0 步是把包內維運指令裝到主機，重試時已裝過（`/usr/local/lib/guidant/VERSION` 已是目標版）。修：runner 第 0 步前比對已裝維運指令版本＝目標版就記 skipped 直接進第 1 步；或更新單帶 `retry=1` 讓 runner 跳過。
  21. **🔴 第三次實測：第 4 步 vulndb 匯入「guidant-redis unhealthy」失敗 → 自動退回也失敗，190 服務停擺**（23:08～23:09，`guidantai recover` 待跑）。bugsink 修正已生效（④-2 過）。根因：第 3 步 `dbc_config_snapshot` 把 `DBC_CONFIG_ITEMS=(.env guidant.env docker-compose.yml certs pki …)` 整個目錄留快照、第 4 步 config 段 `internal_tls.sh` 重寫 `certs/internal`、退回 `dbc_config_restore` 整個目錄換回——目錄 inode 變了，但 **redis／seaweedfs 容器升級期間沒停、bind mount 仍指舊 inode**，容器內 `/tls` 變空（實查 `docker exec guidant-redis ls /tls` 為空），healthcheck 讀不到 CA 變 unhealthy，之後任何 `compose up` 都因依賴不健康起不來：第 4 步 vulndb-import 炸、退回起舊版 api 也炸。第二次實測沒撞到是因為那次退回前 config 段沒換過目錄。修（擇一或並用）：① `certs`／`pki` 這類被容器 bind mount 的目錄，快照與換回改**目錄內逐檔 rsync 覆寫**，不搬目錄；② 第 3 步停服務把 redis／seaweedfs 一起停、退回與換接時一起 recreate；③ 退回起服務用 `--force-recreate`。**第 5 棒已修（repo commit 見 LOG）**：`dbc_config_restore` 目錄改 `dbc_restore_item` 在原目錄內 `rsync -a --delete`（無 rsync 退 find＋cp）；190 以拋棄式容器實證舊法掛載變空、新法仍看得到檔；bats 87 綠。同一批一起修了 18（bugsink 偵測改先存變數再 grep，避開 pipefail）、20（第 0 步已裝版本＝目標版就記 skipped，`UPG_FORCE_SELF_REPLACE=1` 可強制）、23（診斷包 chmod 644）。四支檔已套 190 主機 lib（備份 `/home/jedi/fr167-lib-bak-20261030-232552/`）與解開的包。**仍要進正式包**；1.27.0 已裝的客戶主機跑舊 lib 同待辦 1 問題。
  22. **維護頁在「退回失敗」終態顯示錯**（決策者 23:10 截圖）：進度檔 `status=failed, result=rollback_failed` 時，頁面仍標「更新中」、進度條卡一半、底下「更新進行中，請勿關閉主機」；步驟列 `rollback` 沒翻譯（併待辦 17）；第 4 步失敗 detail 把最後一條 migration 進度與錯誤串成一行（「設定補齊失敗 · migration 2/2 …」）。修：維護頁 `renderProgress` 的 state 要多一個 `rollback_failed`（紅色、標題「升級失敗，自動退回也失敗」、顯示 `sudo guidantai recover` 指引、停掉「勿關機」提示）；失敗步驟 detail 只顯示錯誤、不接前一條進度；更新中心同一終態也要對應（待辦 10 一起）。
  23. **下載診斷包 500**（決策者 23:17 實測）：主機 `guidantai diag` 以 root 產出 `upgrade/diag/<job>/guidant-diag-*.tar.gz`（0.3MB 產出成功），api 容器輪詢到檔案存在後讀檔 `PermissionError: [Errno 13]`（容器內非 root、檔案 root 600 類權限）。修：`upgrade_order.sh` 產完診斷包 `chmod 644`（目錄 755）或 chown 給容器使用者；api 端讀不到時回 4xx 帶原因而非 500。
  24. **升級成功後更新中心仍顯示「1.27.1 → 1.27.1 有新版本」＋「已排程在 23:27 更新／取消這次更新」**（決策者 23:32 截圖）。兩個來源：①更新單同步排程一分鐘一次，前門重建（23:32:03）到紀錄同步 succeeded（23:32:50）之間畫面拿到的仍是 scheduled；②升級後 session 失效（status 23:33:04 回 401），頁面留著升級前那份回應在畫，而 `last_check` 仍是「以 1.27.0 身分問到 1.27.1」那筆（BE `available_from` 與 FE `latestRelease` 都有擋 latest＝current，所以不是判定漏、是舊 payload）。修：換接完成時主機或後端把 `last_check` 清掉或立刻重查一次；維護頁偵測後端回來跳回時要帶 `?upgraded=1` 讓更新中心強制重載並處理 401 導登入；更新單同步在換接後要立刻觸發一次而非等排程。
  25. **Agent 推送後最長要等一個心跳週期（5 分鐘）才開始動**（決策者 23:34 按推送、23:38 心跳才下發）：rollout 建了但 Agent 要下一次心跳才拿到更新單，期間畫面無變化、使用者以為沒反應（與待辦 19 同一類）。修：①畫面在 `dispatched` 狀態明示「已推送，Agent 最晚 N 分鐘內（下次心跳）開始」並倒數；②評估主系統推送時主動通知 Agent（Agent 有 8xxx 本機 API 可敲、或 socketio）讓它立刻心跳一次，不改 5 分鐘預設。
  26. **🔴 推送 Agent 要主動通知，不等心跳**（決策者 23:36 裁：「這太蠢了，後來是有通道可以進去的」，取代待辦 25 的「畫面倒數」方案）。通道就是 FR-039 的主系統→Agent 資料面（mTLS＋短效 JWT，`infra/remote_agent/agent_credential_client.py` 的 `_send` 是先例、`RemoteAgentAdapter._request` 是 canonical），Agent 端守門 `core/data_plane_auth.py` 的 `_PROTECTED_PREFIXES`。做法：①Agent 加 `POST /update/notify`（前綴加進 `_PROTECTED_PREFIXES`，與 `/detection` 同級守門），handler 只呼叫既有 `core/heartbeat_wake.request()`（vulndb／profile 立即檢查已在用的原語）讓心跳迴圈立刻跳一次；②主系統 `agent_rollout_service.push()` 與自動跟進 `_rebatch` 寫完 rollout 後，對每台照 credential_client 的 auth 打一次 notify，**連不到一律靜默降級回等心跳**（Agent 離線、客戶沒開入站埠都還能用），不報錯。190 的 `AGENT_AUTH_MODE=full`，Agent 8443 對主機開；api 容器要能連到 agent 的 `base_url`（同機時是主機 IP，先驗）。Agent 側改動屬 evidence-agent repo，要另開卡（Repo 欄）。
  27. **Agent 升級解包階段沒進度**（23:38:46～23:4x，主機單元 `activating` 一分多鐘、進度檔還沒建、畫面與 journal 都無變化）：`guidant-agent-compose upgrade-host order` 先從 1.79GB 包 `tar -xzf` 抽 install.sh／compose／映像再寫進度檔，抽檔分鐘級。修：收單後先寫進度檔「第 0 步 解開更新包」再抽，與主系統第 0 步同一個問題（待辦 1）。
  28. **全新安裝多一個沒翻譯的選單 `lang.menu.tool-plugin-manage`**（決策者 2026-10-31 10:02 截圖，190「合規與稽核」底下，在「Agent 掃描工具設定」下面）。**與待辦 3（`role-manage`，P8-7 已修）同一類病、換一個選單**：主線 `2026-10-29-fr166-1-tool-page-agent-credentials.sql` 把 `tool-plugin-manage` **改名**成 `agent-tool-credentials`；但 jedi-detection 套件 `005-detection-ui-routes.sql` 是「`WHERE NOT EXISTS name='tool-plugin-manage'` 就插」——改名後舊名不在了，套件在第二輪（主線之後跑）**又插回一列舊名**，`enable=1`，FE 沒有這個 key 的翻譯就印原文。實證 190（全新安裝 1.27.0）：`ui_routes` id 20＝`agent-tool-credentials`、id 120＝`tool-plugin-manage`（兩列並存）；`schema_migrations` 套用順序 fr166-1 19:48:41 → hide 19:48:46（此時舊名已不存在，空跑）→ 套件 005 19:48:50（插回）。DEV 只有改名後那一列，因為 DEV 從沒重放套件 005——**只有全新安裝會中**，CM-3344 驗新裝時抽查的是 `role-manage` 沒查這列所以沒抓到。**1.27.2 包也會帶這個 bug**（套件 005 沒改），190 從 1.27.1 升上去不會清掉既有那列。修法（擇一，照 P8-7 先例）：①改套件 jedi-detection `005` 的插入條件，改名後的 `agent-tool-credentials` 存在也不插（根治、要發套件版）；②在 `scripts/init/migrate-capability-grants.sql` (e) 段（第三輪，唯一排在套件之後的掛點，P8-7 清 role-manage 就放這裡）加一行冪等 `DELETE FROM ui_routes WHERE name='tool-plugin-manage' AND EXISTS(SELECT 1 FROM ui_routes WHERE name='agent-tool-credentials')`；第三輪每次升級都跑，190 升級時就會清掉。建議①＋②都做：①防新裝、②清已裝客戶（190、及任何從 1.27.x 全新裝起來的）。順手掃一次「主線改名或刪除過、但某支套件仍以舊名 `NOT EXISTS` 插入」的所有 ui_routes 列，一次收乾淨。
  29. **`role-manage` 幽靈選單（另一個 session 2026-10-31 轉來，請出版首腦統一處理）——1.27.2 已處理，剩根治與 190 現存一列**。另一 session 查實：jedi-iam `001-iam-ui-routes.sql`（第 25～26 行插、第 51 行起綁 route_capabilities）以 `WHERE NOT EXISTS` 在全新安裝的套件那輪插回；只有全新安裝會中，188／189 舊版升上來的順序剛好對。**此根因第 4 棒已記為待辦 3，P8-7（CM-3342，`ee697bab8`）已修**：`scripts/init/migrate-capability-grants.sql` (e) 段在第三輪（唯一排在套件之後的掛點）`DELETE FROM ui_routes WHERE name='role-manage' AND url='/auth/role-manage'`；首腦 10:15 實查 **1.27.2 init image 的模板與實際執行的 `packages/_capability_grants.sql` 都已含這行**，所以 1.27.2 全新安裝不會再長出來，190 從 1.27.1 升 1.27.2 時第三輪也會清掉它現存那列（id 121，route_capabilities 隨刪）。**剩下要做的**：①根治套件——改 jedi-iam `001` 拿掉 role-manage 的 INSERT 與綁定（另四個選單不動），發新版、主專案改釘、重跑 `flatten_pkg_migrations.sh`；第三輪那行 DELETE 留著當保險（已套環境改內容不重跑，靠它清）。②與待辦 28 一起盤：所有「主線改名／刪除過、套件仍以舊名 `NOT EXISTS` 插回」的 ui_routes 列（另一 session 點名 user-manage、tenant-manage、department-manage、ldap-config 待查；待辦 28 的 `tool-plugin-manage` 已是一例），第三輪 (e) 段一次補齊清理。③驗收：修好的包在乾淨環境全新安裝，`ui_routes` 無 role-manage／tool-plugin-manage，側欄只有「存取管理 → 角色／指派」與「Agent 掃描工具設定」。
  30. **「更新前檢查」顯示上一次升級的舊結果，看起來像這次的**（決策者 2026-10-31 10:12 截圖：要升 1.27.1→1.27.2，區塊卻打勾「現行版本 1.27.0」「出貨包雜湊相符（1.27.1）」「1.27.0 → 1.27.1」，底下小字「2026-10-30 23:28 跑的結果」）。根因：FE `preflightResult(progress)` 只找進度檔裡的 preflight 步，**沒比對進度檔的 `from`／`to` 是不是這次要升的版本**，進度檔留的是上一次升級那份就照樣顯示成「通過」。決策者另點出：預檢要按更新才跑、放在開頭像是一進來就有資料，問為什麼不跟進度放一起或改彈窗。**建議**：①FE 判定加「`progress.from==現行版` 且 `progress.to==目標版`」，對不上一律當「還沒跑過」；②平常畫面不放這個展開區，改到「更新到 X」確認視窗裡顯示（P8-2 已做「下載完自動先跑一次只讀預檢」，確認視窗開啟時就有這次的結果；沒有就顯示「按下更新時第一步會跑」）；③升級中預檢就是步驟清單第 1 步，不另列。**理由**：預檢的意義是「按下去之前告訴你會不會過」，放在決定要不要按的那個視窗才對得上；放在頁面上常駐，不是空的就是過期的。同一張截圖還有：「預計停機 約 3 秒」（拿資料庫複製秒數當停機，實際 190 這次停機約 4 分半，要改成依步驟加總估，或照既有規範「不寫數字、寫會看到維護頁」）；「已排程在 10:10 更新」但其實已在跑（待辦 13／19，1.27.2 前端已修，這次看到的是 1.27.1 前端）。
  31. **按下更新後畫面停 3 分多鐘才開始「預檢」轉圈**（決策者 2026-10-31 10:15，190 升 1.27.2 實測）。時間線：10:10:57 收單 → 10:11:58 重驗 4.9G 包通過（1 分）→ 10:11:58～10:14:17 第 0 步 self_replace（**2 分 19 秒**）→ 10:14:18 第 1 步預檢才開始。畫面在前 3 分多鐘完全不動。**1.27.2 只修了維護頁一半**：維護頁 `KNOWN_STEPS` 已含 `self_replace`（待辦 17），但更新中心頁 `productUpdateState.js:14 STEP_NAMES` 仍從 `preflight` 開始、**漏第 0 步**，進度檔寫了第 0 步 running、畫面沒有對應列就像卡住（第 5 棒先前回報「待辦 17 已修」只對維護頁成立，更正）。修：①`STEP_NAMES` 第一列加 `self_replace`（白話「準備新版升級程式」），running 時顯示已經過秒數；②收單後重驗包那 1 分鐘也露出來（併待辦 19 狀態鏈）；③**根治＝縮短第 0 步本身**：2 分 19 秒全花在 gzip 要讀完 5G 才抽得出幾個小檔，出包時把 `guidantai`／`lib/`／`release-keys`／`VERSION` 另放一份在包外（或 tar 最前面、抽到即停），第 0 步改讀那份，秒級完成——即待辦 1 修法②，這次要真做，不只靠時限放寬。
  32. **Agent 區要有每台的逐步進度（step）**（決策者 2026-10-31 10:25：「至少要讓 user 知道現在跑去哪了，不然他等很久很白痴」）。現況 1.27.2 每台只一個標籤（排隊中→下發中→成功／失敗），升級那幾分鐘一直「下發中」。Agent 主機進度檔（`/srv/guidant-ai-agent/upgrade/upgrade-progress.json`）已有 `steps[]`（n／name／status／started／ended／limit_s），只是心跳只帶最終 `upgrade_result`。**定案做法**：每台 Agent 下展開 8 格，**重用 `UpgradeStepList`**（不另刻）：0 等 Agent 接手（已推、等心跳領單）→1 下載更新包（1.8G，帶已下載量）→2 解開更新包（P8-5 的 extract）→3 預檢→4 停接新工作→5 換上新版→6 啟動新版、健康檢查→7 清理；running 轉圈＋已經過秒數、ok 打勾、failed 打叉＋原因。**0、1 兩格最久最像當掉（等心跳最多 5 分、下載 1.8G），一定要列**，不能只列 Agent 本機那 6 步。改三處：①Agent `core/update_sync.py` 心跳多帶 `upgrade_progress`（讀既有進度檔＋下載 .part 大小），**升級期間心跳加快到 5 秒**；②BE `agent_update_rollouts` 加 `progress jsonb`，心跳收進來寫入，`agent_rollout_service.overview()` 的 `_agent_item` 回傳；③FE `AgentRolloutPanel.vue` 每列可展開、下發中那台預設展開。**限制**：回報進度的是 Agent 自己，要 Agent 先升到有此功能的版本，下一次推送才看得到。
  33. **維護頁只有暗色**（決策者 2026-10-31 10:20）：`public/maintenance/maintenance.css` 只照抄 `src/assets/_theme-overrides.scss` 的暗色 `:root` token，主系統選亮色、升級時跳到維護頁仍是黑底。修：①`maintenance.js` 最前面讀 `localStorage['cm_color_scheme']`（主系統 `layout.js` 存的鍵，同源可讀）設 `document.documentElement.dataset.theme`，沒有值就跟 `prefers-color-scheme`；②`maintenance.css` 補 `:root[data-theme="light"]` 一套 token，值照抄 `_theme-overrides.scss` 的 light 段（兩邊改 token 要一起改，檔頭已註明）；③程式放既有外掛 js（CSP 不放行 inline script）。
  34. **證據上傳要有類型白名單**（決策者 2026-10-31 10:55）：實查 BE 沒任何類型關卡、FE input 無 accept；預覽只有 pdf／html／png／jpg／gif／webp 直開＋十種 Office 轉 PDF；**影片完全沒預覽**。白名單與擋下清單寫在 P9-9（CM-3354）。**決策者 11:15 裁：eml／msg 收**（只下載不預覽），已回寫 CM-3354。影片播放器留第二期。
  35. **Agent 區跑成功後 8 格仍常駐展開**（決策者 10-31 16:28）：✅ 已修 FE `a4126e2b`（成功自動收起、失敗保留），進 1.27.5。
  36. **更新中心目標版號被上一張已成功的更新單蓋住**（決策者 10-31 16:38：查到 1.27.4 畫面與按鈕仍 1.27.3）：✅ 已修 FE `13ef3ecc`（`targetVersionOf`：單還在走才以單為準），進 1.27.5。
  37. **下載完畫面退回起始、按下到主機開跑之間沒反應**（決策者 10-31 16:47）：✅ 已修 FE `30addad1`（scheduled 且時間已到＝submitted；整張卡蓋遮罩＋五格狀態鏈），進 1.27.5；升級前那版前端顯示，1.27.5→1.27.6 才看得到。
  38. **🔴 客戶主機 apt 裝了新核心沒重開 → docker 29 預設 seccomp 在舊核心 errno 524**：帶 `no-new-privileges` 的容器起不來、exec 全炸、exec 式健康檢查全 unhealthy。188 STG（跑 -187）／189 POC（跑 -171）都中、已裝 -202、`reboot-required` 亮著；190 跑 -198 正常是對照組。**升 containerd.io／runc 無效**（189 實升 2.4.2／runc 1.5.2 無效），**重開機即解**（189 17:31 重開後 9 容器全 healthy；188 17:36 重開）。**要進**：①升級預檢加「`reboot-required` 存在或跑的核心 < 已裝核心 → 擋並指路先重開」＋「實測 `docker run --security-opt no-new-privileges --entrypoint true <base>`」；②手冊主機層依賴與升級章加「安裝／升級前 apt 更新到底並重開一次」；③`check_environment` 同一條。memory `feedback_seccomp_errno524_is_stale_kernel_reboot_not_runc`。開卡時機：P9 收尾後另開。
  39. **Bugsink 管理員密碼全客戶同一組**（FR-133 D5 刻意取捨；決策者 10-31 17:0x 問起）：出包時從 CI 變數烙進包內 `bugsink-admin.secret`，每台裝同一組，任何一台外洩等於全部外洩。建議改每台隨機產生、原廠取得走不印明文的路（一次性重設或 LC 通道）。屬方案取捨，另開卡，不擋本輪。
  40. **誤刪 188 `/home/jedi/guidant-ai-1.26.0`**（首腦 10-31 12:xx 清磁碟時，以為是解開的舊包，實為 STG compose 工作目錄）：已從同目錄 1.26.0 tar.gz 重建（compose 與 /srv 那份 cmp 相同），資料庫未受影響。教訓入 memory：刪目錄前先 `docker inspect` 全部容器的 working_dir／bind mount。
  41. **Agent 列 expander 箭頭在沒東西可展開時仍顯示**（決策者 10-31 18:05）：PrimeVue expander 欄整欄固定畫箭頭；rows 空時要用 `#body` slot 依 `stepRowsOf(data).length` 決定畫不畫（BodyCell 的 `rowtogglericon` slot 可參考）。決策者裁「先不要動」，demo 後再修。
  42. **【第二期】使用者刪掉 `bundles/<版>/agent/` 後推 Agent**（決策者 10-31 18:10 問，裁併第二期）：現況安全——overview `bundle_ready=false`、按鈕全灰、412008；下載途中被刪 Agent 拿 404 記失敗。不夠好：①文案「這一版的更新包沒有附 Agent 安裝包」把「被刪」說成「沒附」，要分兩句；②沒有復原路徑（同版重下被擋），要讓現行版能單獨重抓 Agent 包——與待辦 6「Agent 單獨更新」同一件事，第二期一起做。
  43. **Agent 升級 cleanup 沒清舊映像**：190 留 guidant-ai-agent 1.4.3～1.4.7 五顆各 4.23G（第 7 棒 18:30 `docker images` 核實）。demo 後收。
  44. **🔴 下載 100% 到遮罩之間 37 秒空窗，「更新到 X」按鈕仍是活的**（決策者 10-31 18:25：「為什麼不能 100% 就直接開遮蔽」「中間還是會被 user 到處按、可以再按一次升級」「停了很長一段時間我以為你又沒做好」）。第 7 棒用 190 的檔案 mtime＋DB 列＋journal 對出時間軸（1.27.6→1.27.7）：18:17:00 下載完 → 18:17:17 worker 算完第 1 次整包 sha256 寫 `ready`（畫面退回就緒卡、按鈕亮）→ 18:17:37 `apply_after_download` 寫單前**再算一次**整包 sha256（`_enqueue`→`verify_ready_path`→`verify_package_file`）才寫單變 scheduled／submitted（遮罩才出現）→ 18:18:33 主機撿單驗包第 3 次 → 18:18:59 預檢第 4 次。輪詢沒斷（第 6 棒修正有效），洞在**後端把 `ready` 當中途狀態寫 DB、前端照字面畫就緒卡，沒看這張單是「更新到 X」建的（`applied_by` 有值）**。修法三處：①FE `deriveView`：`ready` 且 `applied_by` 有值、無 error、無 cancelled_at → 視為 `submitted`，遮罩從 100% 那刻蓋上（可熱換 190 驗）；②BE `apply`：同條件再按回 412「已在處理」，不寫第二張單；③BE `run_download`：下載完剛驗過，寫單前不重算雜湊（把驗過結果傳給 `_enqueue`），主機撿單與預檢兩次也可併一次，「按下到開跑」約 80 秒可縮到約 30 秒。決策者 18:35 裁「記錄或開卡」→ 先記這裡，demo 後開卡。
  45. **側欄殘留 `lang.menu.tool-plugin-manage`**（決策者 10-31 18:50，188 升 1.27.7 後截圖）：**伺服器端乾淨**——第 7 棒用 blsadmin 實打 188／190 `/user/web-menu`，都只回 `agent-tool-credentials`，`ui_routes` 也沒有舊列（P9-5／P9-7 有效）。殘留在瀏覽器：FE `userStore.permissions`（側欄樹）隨 `cm_store` 進 localStorage，只在登入／OTP／切租戶／開通授權時重抓 `API.WEB_MENU`，F5 與升級後 `?upgraded=1` 重載都拿存檔那份。修法：頁面載入時比照 `ensureCapabilities` 重抓一次 `WEB_MENU`（或把 `permissions` 排除在 persist 外）。當下解法：登出再登入。
  46. **角色頁能力點顯示 `system.product-update.read／update` 原始 key**（決策者 10-31 18:52）：FE `permission-tree.json`（zh-tw／en）的 `capability.system`／`permission_resource.system`／`capability_how.system` 三段都沒有 `product-update`，`capabilityLabel` 查不到就回 name。補三段各兩筆（中文取 `core/host_capabilities.py:214-215`）。
  47. **🔴 CRA 框架頁「原始 PDF 檔已遺失」**（決策者 10-31 18:55）：1.27.7 包 `storage-seed/` 只有 10 檔（兩支 CMMC PDF＋八支 TWGCB），少 `20261014000000_b81c42ff-….pdf`。根因：188 `/opt/guidant-ai-be/.build/storage-seed/` 是 **8 月 21 日的舊殘留**（10 檔），`build_bundle.sh` ⑥ 段「已存在且非空時不動」讓它贏過 10 月 30 日攤平的 `.build/pkg-assets/`（11 檔、含 CRA）。套件 jedi-oscal-v2 2.9.0 的 `assets/storage-seed.map` 三行都對，問題只在 build 機殘留。188／190 bucket 都沒有這支（188 升級補灌 10 個、190 同）。修法：①188 刪 `.build/storage-seed/` 讓它從 pkg-assets 重建，`build_bundle.sh` 改成「pkg-assets 比 storage-seed 新就覆蓋」或乾脆每次重建（守門改 fail-fast：map 列數 ≠ 攤平檔數就擋）；②188／189／190 當下補救＝把那支 PDF 丟進 bucket（對 STG／POC 是寫入，等令）；③下一包 1.27.8 帶齊。
  48. **🔴 拿掉發布清單 90 天有效期檢查**（決策者 10-31 20:00 裁「就 1」）：現況 `package_verifier.py` `MANIFEST_VALID_DAYS=90`，簽發超過 90 天的清單主機拒收；客戶拖三個月或離線檔放著就裝不了，原廠得每 90 天重蓋章。決策者裁拿掉，理由：重放舊版已由「版號只能往上＋主版號相同＋主機公鑰目錄」三道擋住，要廢某版走更新站 withdraw 或輪換公鑰。**改兩處要一起**：主系統 `verify_manifest` 的有效期分支（含 `EXPIRED` 錯誤碼與測試）＋主機 `scripts/installer/lib/release_keys.sh`／預檢第 1 步若有同一條檢查；update-center `planPath` 若有看 issued_at 也查。`issued_at` 欄位保留（紀錄用）。demo 後開卡。
  49. **主系統沒打更新站 `POST /v1/report`**（第 7 棒 20:10 grep 證實，決策者裁記待辦）：更新站 `handleReport` 與 `instance_version_reports.last_upgrade_*` 欄位都做好了，後台版本頁也用它算「幾台退回／失敗」，但 BE `update_site_client.py` 只有 `check`／`download_url`／`ops_download_url`，沒有 report。現況原廠只能從 `check` 看到客戶「現在幾版」（每 6 小時），看不到「升級成功還是退回」。補法：`update_site_client.report(license_text, fingerprint, ticket, result, from, to, at)`，在 `upgrade_order.after_switch`／`sync_from_progress` 寫回終態那一刻打一次（成功、rolled_back、failed、skipped 都報），連不到只 warning 不擋。demo 後開卡。
  50. **PM 簡報收進 FR 資料夾**（決策者 10-31 20:45：「這個簡報到時候也把它放到我們的 FR 裡面」）：簡報 https://claude.ai/artifact/1HqiRVDJkGsJCsKeGiEBfU 10 張（封面、做了什麼、架構、出版流程圖、簽章圖、五件確認、升級八步圖、狀態、資料不掉、服務架構圖；第 11 安全對照與第 12 待補已拿掉，內部知道就好）。原始檔在本 session scratchpad `artifact-files/0259ebe7-…/project/`（scratchpad 會清，要收時從 artifact `read` 回來）。落點建議 `docs/features/FR-167-2610-online-update-channel/briefing/`，照 `doc-site-build` skill 進需求站；收尾一併做，等令。
  51. **🔴 Agent 區落後三版仍顯示「已是最新」**（決策者 10-31 20:48，189 截圖：兩台 Agent 1.4.2、包是 1.4.7）：根因＝走 `install.sh --upgrade` 時包解在 `/home/jedi/guidant-ai-1.27.7/`，`<資料目錄>/upgrade/bundles/` 不存在，`agent_rollout_service._bundle` 找不到 → `target_version=None` → `is_lagging` 看到目標空回 False → 畫面全「已是最新」（§3 契約早寫「install.sh --upgrade 時 Agent 不會跟進」，但畫面不該說最新）。修法：目標版未知時 `lagging` 回 `null`、`bundle_ready=false` 時畫面顯示「無法比對（這一版的 Agent 安裝包不在升級目錄）」並說明怎麼補；順手修待辦 42 ①文案（「被刪」≠「沒附」）。188／189 當下要能推 Agent：把 `/home/jedi/guidant-ai-1.27.7` 搬或 symlink 到 `/srv/guidant-ai/upgrade/bundles/guidant-ai-1.27.7`（改 STG／POC，等令）。
  52. **CRA 繁中舊版本列 `2024/2847-zh-TW` 出現在框架清單**（決策者 10-31 20:46）：全新安裝沒有（基線庫、DEV、init seed 都只有英文版）；升級路徑有——jedi_oscal_v2 007（10/13 建繁中版）→ 011（10/15 併進英文版翻譯表、繁中版標 `deprecated` **不刪**）→ 188／189／190 三台都留一列 deprecated，前端清單沒濾。修法：①migration 驗無資源庫／專案 catalog 掛在那列（011 已改掛英文版）後刪 framework_version＋catalog；②API／FE 清單一律濾 `deprecated` 當保險。
  53. **Agent 1.4.2 的 SeaweedFS 程序洩漏**（第 7 棒 21:15 升 189 兩台 Agent 時發現）：跑三週後 seaweedfs 容器 PID 15,360／18,478、健康檢查 `can't fork: Resource temporarily unavailable`，容器 `PidsLimit` 未設。升 1.4.7 重建後 PID 22／18。要查：①是 seaweedfs 4.48 本身還是 Agent 打它的方式（心跳每次開連線不關？）；②compose 加 `pids_limit`；③一週後回看 121／167 的 PID 有沒有再長。屬 Agent repo，另開卡。
  54. **三台主機指紋相同**（第 7 棒 23:21 看更新站回報表）：188／189／190 授權編號各異，`fingerprint` 都是 `ff793254d00c…`。三台是同一範本複製的 VM，指紋若取 machine-id／MAC 之類會撞（memory `feedback_container_machine_id_empty_fallback_mac_drifts` 相關）。影響：更新站統計與封鎖名單按指紋會誤殺；授權檔本身綁指紋也會互通。要查指紋算法與這三台的來源欄位，屬 jedi-license-runtime，另開卡。
  55. **PM 會議 2026-10-31 機制問答報告**：https://claude.ai/artifact/AmUhtjZVptzoqExGEUhjhx （Q6／7／8／9／11～19、Q5＋20 備份現況、待討論四條；私人，分享給 PM 要從 Share）。與簡報（待辦 50）一起收進 FR 資料夾 `briefing/`，收尾做。PM 會議 B 類（私鑰保管、本體與 Agent 分開更新、Agent 主控台更新中心、PG 引擎大版升級）決策者裁「先不討論」，報告末段已列。
  57. **Agent 升級結果回報更新站（P11-1／P11-6）本輪驗不到真環境**（第 8 棒 02:20）：三台 Agent 都是 1.4.7、1.27.9 包內 Agent 也是 1.4.7，更新中心沒有新版可推（同版 412 擋）。要驗 `agent_version_reports` 得等下一版 Agent 包（或出一個 1.4.8 只改版號）。程式面已核：欄位名、指紋 regex、終態值兩邊一致；BE `test_fr167_agent_rollout.py` 97 行含送達才寫 `reported_at`。
  58. **Agent 主控台「檔案」頁把預覽 PDF 快取當證據列出、名稱只顯示一串 ID**（決策者 2026-11-01 11:2x 在 121 看到兩列 `45a7b531-…`、`89fc9235-…`）：根因在 jedi-file-upload `minio_adapter.convert_to_pdf`——轉好的 PDF 另寫一列 `upload_files`，`file_name` 填存檔名去掉 `.pdf`（＝原檔存檔 ID，不是原檔名）、`storage_scope` 走預設 `customer`，只有 `ref_id` 指回原檔。Agent 主控台檔案清單（`guidant_agent/files/adapters/customer_files_repo.py` 只濾 `storage_scope=customer`）照列，總數與佔用量也多算。原檔都還在（121 實查 `20261101011429_<id>.docx` 兩個 + 兩個 `.pdf` 快取）。主系統證據畫面走關聯表、不受影響。**修**：①Agent 三個查詢（清單、數量、佔用）加 `ref_id IS NULL`，Agent 一處即可；②（選）套件把 `file_name` 改「原檔名.pdf」，牽動主系統共用套件，另發版。決策者裁「先不用，記錄下來」。
  59. **🔴 主系統與 Agent 要分開更新**（決策者 2026-11-01 12:2x 裁：「這邊之後要改成主系統跟 agent 要分開更新」，在 189 推 Agent 1.4.8 時說）：現況 Agent 包只能隨主系統包出，修一個 Agent bug（本日 1.4.8 Office 預覽）也得出整包主系統 1.27.10、客戶要先停機升主系統才推得到 Agent。原本列在 PM 會議 B 類「先不討論」、design 標第二期，**決策者已改判要做**，待開討論稿／開卡。要碰的：更新站多一類成品（Agent 版本線、R2 `agent/` 路徑、清單簽章 payload_type）、主系統更新中心 Agent 區「檢查 Agent 新版／下載 Agent 包」不經主系統升級、`bundles/<主系統版>/agent/` 改成獨立的 `agent-bundles/<Agent 版>/`、`min_agent_version`／相容矩陣兩向檢查、離線單一檔也要能單出 Agent。**決策者 2026-11-01 12:4x 補需求五點並裁「下階段要討論，也要開始做」**：①主系統與 Agent 不再包在同一包 ②各別更新 ③Agent 也要有更新中心（離線上傳、或向主系統發動更新需求）④R2 安裝包分開 ⑤不再用心跳帶資料。需求紀錄：`docs/draft_requirement_spec/2026-11-01-agent-independent-update.md`（現況、心跳夾帶 8 種盤點、每條要碰的地方）。
  60. **Agent 區升級途中 8 格永遠停在第 1 格「等 Agent 接手」**（決策者 2026-11-01 12:28 在 189 推 1.4.8 時看到）：BE P9-6（`180b30904`）把拉到的進度放在 overview 每台 agent 的 `item["progress"]`，FE P9-10（`fc08e549`）讀 `agent.rollout?.progress`，兩張同批平行做、欄位位置從一開始就沒對上；成功時 FE 直接全打勾不看進度，所以 10-31 在 190 看結果時沒發現。升級、結果、回報不受影響（121 成功、60.167 失敗都正確記錄並回報）。**修**：FE `agentRolloutState.js` 改讀 `agent.progress`（一行），或 BE 把 progress 放進 rollout；加一條契約測試讓兩邊對同一份形狀。順帶：60.167 失敗「無法讀取新映像」＝主機升級單第 0 步 `docker create --network none guidant-ai-agent:1.4.8` 失敗（映像已 load、121 同包同步驟過），疑主機 docker／核心問題，決策者在該機查。
  61. **Agent 主控台 CINC 的 Profile 下拉是空的**（決策者 2026-11-01 12:5x，121 實測）：主系統只同步「檔案型」Profile 給 Agent；CINC 兩支 dev-sec 在 189 登記為「網址型」（GitHub），沒有檔可同步，121 本機只有 GCB 8 支 TWGCB。GCB 下拉正常、CINC 退回文字框（文案「這台還沒收到同步的 Profile」對 CINC 是準的）。兩條路：A 主控台也列網址型（心跳／新通道帶名稱與網址，要出版）；B 網址型改由主系統先下載再同步（封閉網路也能跑，但主系統要連外，改設計）。決策者裁「先不動，要 demo」，併入 Agent 獨立更新那輪討論。**補查（12:5x）**：①189 主系統登記網址型時**有下載過**（解析出控制項 Linux 59／Windows 431，`extraction_status=succeeded`），但解析完不留檔（`file_id` 空），所以不同步——合理修法是網址型解析時順便存檔、當檔案型同步給 Agent（B 方向，不需主系統常態連外）。②決策者記得「以前可以下拉」：查 FE 歷史，**Agent 主控台這欄從 FR-166 P2-3（10-27 `6c608601`）出生就是文字框**，下拉一直只在**主系統檢測頁**（FR-059 08-01 `78f8e073`），不是 FR-170 重構弄壞的。③demo 應急：首腦把 dev-sec 兩支放到 121 Agent `/data/content/cache/manual/{linux,windows}-baseline`（`/data/content` 唯讀、cache 可寫、為資料卷，重建容器不掉），`cinc-auditor check` Valid；主控台 CINC 欄貼該路徑可跑，不進下拉。這兩個目錄是手放的、不在同步清單，日後修好待辦 61 後可刪。
  62. **升級換接後 redis／seaweedfs／bugsink 的 compose 標籤仍指舊目錄**（第 9 棒 11-01 清 188 時發現）：線上升級不重建這三個，`working_dir`／`config_files` 標籤留在升級前的解包目錄（188 現指 `/home/jedi/guidant-ai-1.27.7`），舊目錄一刪下次 compose 操作就變孤兒。修：第 6 步換接或第 7 步清理對不重建的服務也 `compose -f <新目錄> up -d --no-deps`（不重啟、只換標籤），或清理時把舊目錄留到下一次升級。併下一版。
  63. **更新站後台「Agent 在版／Agent 退回／Agent 失敗」三欄永遠 0**（文件棒 11-01 查到，首腦核過 `update-center/src/releases.ts:378-380`）：拿 Agent 版號（1.4.x）比主系統版號（1.27.x），兩條版本線永遠不相等。回報本身正確，只有後台三格不準。**決策者裁：記待辦、等 FR-172（Agent 獨立版本線）一併處理**，不另開卡。
  64. **收尾小碎片（文件棒 11-01 核對時查到，收尾一起做）**：①BE `PUT /system/product-update/auto-check` 與 status 的 `auto_check_enabled` FE 沒接，留或拔待裁；②FE i18n `product_update.intro`（寫「每天一次」，實際每 6 小時）與 `offline_hint`（舊兩檔說法）兩個鍵已無人引用，可刪；③錯誤碼 `PRODUCT_UPDATE_400002`（清單過期）驗證已不拋，BE 與 FE 翻譯可一起拿掉；④`onprem/README.md`、`agent/README.md` 手冊變更紀錄各補一行（這次只改章節頁）；⑤正式 publish 路徑不帶 `ops_sha256`／`ops_size`（CI register-draft 丟掉、D1 無欄）且 publish 完仍印「90 天到期」提示，正式出版前要補。
  65. **更新單 `failed`＋進度檔 `result=unchanged` 時前端有重試鈕、後端回 412**（文件棒 11-01 查到）：FE `canRetry` 對這組合顯示重試，BE `retry` 只收 `rolled_back`／`skipped`。會落到這組合的是開機檢查或 recover 判「斷在第 3 步之前」（`upgrade_rollback.sh:306-311`）。修：BE `retry` 放行 `failed` 且 `result=unchanged`（系統沒動過、包還在）或 FE 這組合改顯示「重新下載」。併下一版。
  56. （本棒即時回饋收集區：決策者 2026-10-30 指示「聽到回饋先記錄、晚點一起修」，新回饋往下加編號，不單獨修。）

**190 全新安裝 1.27.0 後看到的問題（決策者裁：先測更新，功能問題晚點處理）**
- **多一個沒翻譯的選單 `lang.menu.role-manage`**（租戶與組織 → 存取管理旁）。根因：主線 `2026-10-07-fr132-access-management-menu.sql` 刪掉舊 `role-manage`，但 jedi-iam 套件 `001-iam-ui-routes.sql` 以「不存在才插」在第二輪又插回來；全新安裝才會出現，DEV 與既有安裝沒重放 001 所以看不到。修法待定：套件那支不再插 role-manage，或主線補一支空操作刪除在套件之後。
- **角色詳細頁打開就錯**（`/access/roles/<uid>`）：`GET /api/1.0/ui-routes` 500，`ValueError: capability 缺 license_module，宣告漏填（不可退回 resource_type）`。全新安裝有能力點的 license_module 是空的；同屬「全新安裝才有」的資料缺口。角色本身資料正確（role_capability_patterns 與範本一致；role_capabilities 是退役表，0 筆正常）。
- 這兩件與 1.27.0／1.27.1 的更新流程無關，第二輪換版後一起查。

**開發期全部收完，已合回 feature 分支**
- 批 14～17 十張功能卡（含工作坊首腦-3 補開的 CM-3316 更新站 `min_agent_version`、CM-3317 Agent compose 包旁 `.notes.json`）全過驗、全合回；決策者 16:10 按完 Done。四 repo `merge --no-ff` 零衝突。
- CM-3312（test e2e 綁 v2）暫緩：程式已寫完 commit，只差「對 v2 版面實跑 @product-update」，留 190 測試期。
- BE 守衛 `test_module_boundaries` 兩紅（`jedi_password` 無層號／未接 plugins）是 FR-170 合回帶進來的既有紅（合回前同 commit 一樣紅），屬 FR-170 CM-3315 範圍，不是本案。
- 新 migration `2026-10-30-fr167-agent-follow-optional.sql`（CM-3306）DEV 已套已登記；**出貨基線待重產清單再加這一支**。

**DEV 測試環境（首腦起在背景，下一棒沿用即可）**
- DEV 授權是**落地版**（`config.tenant_licenses` id 5087，本機 LC DEV 簽、綁本機 Mac 指紋、帶身分票），**決策者裁維持，不改回 SaaS**（§5）。原 SaaS 照 id 43 停用。
- BE 8000 與 worker **16:15 已重啟到合回後的程式**，都帶 `UPDATE_SITE_URL=http://127.0.0.1:8788`、`GUIDANT_RELEASE_KEYS_DIR=~/Projects/Billows/Audit-Manager/.fr167-dev/release-keys`、`GUIDANT_UPGRADE_DIR=~/Projects/Billows/Audit-Manager/.fr167-dev/upgrade`。測試資料夾已從 scratchpad 複製到 `~/Projects/Billows/Audit-Manager/.fr167-dev/`（不會被清；含假站 log、授權換照 SQL、公鑰 `7fa0a0746b2f8327.pub`）。**重啟任何一支都要帶這三個變數**，worker 漏帶會讓下載驗章失敗。stdout 在 BE `log/fr167-api.out`／`log/fr167-worker.out`。
- 假更新站：test repo `site-regression/support/update-site-stub.js` 在 8788（每次重啟換一把臨時章，要清舊 `.pub` 再重塞版本）；目前塞 1.27.0。License Center DEV 在 5062（`PYTHONPATH=src` 才起得來）。
- DEV `product_update_jobs` 現在最新一筆是 `ready`（1.27.0 包在 `fr167-dev/upgrade/incoming/`），決策者可直接按「更新到 1.27.0」試排程與立即；立即後畫面會停在已送出，mac 沒有主機升級程式，正常。

**FE**：已接新契約（CM-3311），`ProductUpdateService.apply` 直接送 `with_agents`／`agent_uids`，`SEND_AGENT_FOLLOW` 旗標已拆掉。決策者 FE dev 要自己重啟才吃到。

**未 push**：BE 領先 origin 29、FE 4、update-center 3、evidence-agent 6（含 FR-170 的）、jedi 18（FR-170 的）、test 1。

**已知紅燈**：`test_module_boundaries.py` 在另一 session 重產基線後已全綠（`2abba2b3d`）。BE 13 個 `PRODUCT_UPDATE_*` 錯誤碼 FE 仍沒翻譯。

**雜項**：BE repo `stash@{0}`（CM-3273 security-report 產物）仍在，確認不需要可 drop（`stash@{1}` 是 FR-075 的，別動）。Agent 側卡的 venv 改用 `evidence-agent-knWYPLiu-py3.13`（FR-170 P0 後，舊 7jojxvJC import 會失敗）；FR-170 的 CM-3314 會把 FR-167 的 `core/update_sync.py` 改成 WorkerComponent，本案不再碰那支與 `core/enroll.py` 心跳迴圈。

## §2 批次總表

| 批 | 卡 | repo | 做什麼 | 狀態 |
|---|---|---|---|---|
| 1 | CM-3242 P1-1 | BE | 升級骨架：`guidantai upgrade／recover`、先換成新版的自己、lib 空殼、進度檔 | Done |
| 1 | CM-3243 P1-9a | BE | 出包產未簽的 release.json | Done |
| 1 | CM-3244 P1-3a | jedi | jedi-remote-agent 心跳契約（dispatch_gate、update、upgrade_result）；發 `1.10.0b1` | Done |
| 1 | CM-3245 P3-1 | BE | 能力點、更新中心選單路由、預設角色授權（migration） | Done |
| 1 | CM-3246 P1-8 | FE | 前門維護頁（nginx 503 → maintenance.html 讀進度檔） | Done |
| 1 | CM-3247 P2-1 | update-center | TS Worker 骨架、D1 六表、客戶端三端點、WebCrypto 驗授權檔 | Done |
| 1 | CM-3248 P2-5 | license-center | 授權檔 payload 加 `product_major` | Done |
| 2 | CM-3249 E-1 | BE | 主 repo 釘 `jedi-remote-agent==1.10.0b1` | Done |
| 2 | CM-3250 P1-2 | BE | 預檢：空間、版本路徑、DB、驗清單簽章 | Done |
| 2 | CM-3251 P1-3c | BE | draining 旗標、輪詢進行中數、時段結束放棄 | Done |
| 2 | CM-3252 P3-2a | BE | `product_update_jobs` 表、模組骨架、DI | Done |
| 2 | CM-3253 P2-2 | update-center | Access 守門後台端點、CI token 只能登記草稿、後台頁 | Done |
| 2 | CM-3254 P2-3 | update-center | CLI `guidant-release`：init-keys 一正一備、publish、withdraw、rotate-key | Done |
| 3 | CM-3255 P1-3b | BE | 主系統接 draining：worker 不撿單、心跳不派任務 | Done |
| 3 | CM-3256 P1-4 | BE | 停服務、複製資料庫到 `pgdata-next`、compose 卷吃變數 | Done |
| 3 | CM-3257 P1-5 | BE | 在複本上升級：載映像驗雜湊、migrate 進度轉寫 | Done |
| 3 | CM-3258 P2-4 | BE | CI 出包後 register-draft、傳 R2 | Done |
| 3 | CM-3259 P2-6 | BE | DEV 公鑰隨包、裝到 `/etc/guidant/release-keys/` | Done |
| 3 | CM-3260 P3-2b | BE | 發現新版與下載：更新站 client、每日排程、五道驗章、離線上傳 | Done |
| 3 | CM-3261 P3-3 | FE | 更新中心頁 | Done |
| 4 | CM-3262 P1-6 | BE | 健康檢查、換接（複本轉正、原份留快照、前門最後重建） | Done |
| 4 | CM-3263 P3-2c | BE | 更新單端點、systemd path＋service、排程、全站提醒端點 | Done |
| 4 | CM-3264 P3-4 | FE | 有新版橫幅、更新前 30／5 分提醒 | Done |
| 5 | CM-3265 P1-7 | BE | 自動退回、開機檢查、`guidantai recover` | Done |
| 5 | CM-3266 P1-10 | BE | 收尾清理、診斷包 | Done |
| 5 | CM-3267 P3-2d | BE | 跨版兩段 | Done |
| 5 | CM-3268 P4-5 | agent | Agent 升級每步時限、進度檔、自動退回；邏輯進 `install-lib/` | Done |
| 6 | CM-3269 P1-9b | BE | CI 跨版排練（190 裝舊版＋STG dump 再升級） | Done |
| 6 | CM-3270 P4-2 | BE | Agent 跟進主系統側：`agent_update_rollouts`、先推 1 台、心跳下發 | Done |
| 7 | CM-3271 P4-3 | agent | Agent 跟進 Agent 側：下載驗 sha、升級單、開機退回 | Done |
| 7 | CM-3272 P4-4 | FE | 更新中心 Agent 跟進區；notice 全站提醒、診斷包 gzip、錯誤碼翻譯 | Done |
| 8 | CM-3273 P5-1 | BE | 手冊升級章、防火牆放行、資安例外登記、預檢補 python3 | Done（已合回） |
| 8 | CM-3274 P5-2 | BE | SPEC `system-admin/product-update.md` | Done |
| 8 | CM-3275 P5-3 | test | e2e 更新中心頁（離線上傳標 `@known-bug`） | Done |
| 8 | CM-3276 P5-5 | BE | 結案：發正式版、釘正式版、PROD 印樣守門、網域 | Done |
| 9～13 | CM-3293～3302 | 多 repo | 開發期收完後的修正（更新站身分票 A 案等，見 LOG 第 3 棒） | Done |
| 14 | CM-3306 P7-1 | BE | Agent 跟進可選（apply 多 `with_agents`／`agent_uids`）、手動推送可排程、稍後提醒 7 天；migration agent-follow-optional | Done |
| 14 | CM-3307 P7-2 | BE | 「更新到 X」沒包也能送：apply 先下載、就緒後才寫更新單；下載中／排程中再按回 412005 | Done |
| 14 | CM-3308 P7-3 | BE | release.json 加 `min_agent_version`；`make_offline_bundle.sh` 產單一離線檔 | Done |
| 14 | CM-3309 P7-4 | agent | `gen_agent_manifest` 讀 RELEASE_NOTES.md 寫 manifest notes | Done |
| 15 | CM-3310 P7-5 | BE | 上傳收單一離線檔、status 帶最近成功升級時間、Agent 概覽帶最低相容版／說明／提醒、紀錄合併 | Done |
| 16 | CM-3311 P7-6 | FE | 接新契約：必須更新紅色路徑、Agent 排程與稍後提醒改後端、離線單一檔、紀錄合併 | Done |
| 16 | CM-3313 P7-8 | BE | design D16 改可選、SPEC 更新中心頁照 v2、手冊離線改單一檔 | Done |
| 17 | CM-3312 P7-7 | test | e2e 頁物件改綁 v2（程式已 commit `0f292b9`，差實跑） | **暫緩** |
| 17 | CM-3316 P7-9 | update-center | `min_agent_version` 進 register-draft／D1 0003／publish 蓋章 | Done |
| 17 | CM-3317 P7-10 | agent | compose 包旁產 `guidant-agent-compose-<版>.notes.json` | Done |
| 18 | CM-3336 P8-1 | BE | 主機升級程式：第 0 步時限依包大小、0～2 步失敗記 skipped、步驟白話名、預檢分列、換接後寫 last-switch.json | Done |
| 18 | CM-3337 P8-2 | BE | 後端：status 露出 submitted、換接立即同步＋重查新版清 last_check、診斷包讀不到 412、預先下載後先跑預檢 | Done |
| 18 | CM-3338 P8-3 | FE | 更新中心頁：按下到維護頁狀態鏈、輪詢失敗自動跳維護頁、失敗終態停輪詢、步驟白話、預檢分列、?upgraded=1 重載、「立即」文案問決策者 | Done |
| 18 | CM-3339 P8-4 | FE | 維護頁：rollback_failed 終態、self_replace／rollback 白話、detail 分列、跳回帶 ?upgraded=1 | Done |
| 18 | CM-3340 P8-5 | agent | `POST /update/notify`（guidant_agent/sync/，守門同 /detection）＋升級解包先寫進度 | Done |
| 19 | CM-3341 P8-6 | BE | rollout 轉 dispatched 時走 FR-039 mTLS 主動打 Agent notify，連不到靜默回等心跳 | Done |
| 19 | CM-3342 P8-7 | BE | 全新安裝缺口：role-manage 幽靈選單、能力點 license_module 回填、排練基底 1.26.0→1.27.0（問決策者） | Done |
| 20 | CM-3343 P8-8 | BE | 文件同步：SPEC／手冊／design §5、13 個錯誤碼 FE 翻譯 | Not started |
| 21 | CM-3344 P8-9 | BE | 結案：本機驗新裝＋升級 → 版號 1.27.2 → 188 出包（簽清單／發布／190 升級／刪繞道由首腦做） | Done |
| 22 | CM-3346 P9-1 | BE | B：出包另產 ops.tar（維運指令＋維護頁），第 0 步秒級抽、維護頁隨第 0 步換新（A 主機半） | 驗收通過待確認（已合回 feature、已 push） |
| 22 | CM-3347 P9-2 | BE | D：build_all `--reuse-images` 快速出包、三道 CI 前提進腳本、版號不烙 C 碼 | 驗收通過待確認（已合回 feature、已 push） |
| 22 | CM-3348 P9-3 | FE | A FE 半＋30／31／33：步驟補第 0 步、預檢進確認視窗比對版本、停機不寫秒、維護頁亮色＋nginx try_files | 驗收通過待確認（已合回 feature、已 push） |
| 22 | CM-3349 P9-4 | agent | Agent 開唯讀 `GET /update/progress` 回升級逐步進度與下載量（守門同 notify）；心跳不改（決策者 10/31 裁改拉） | 驗收通過待確認（已合回 feature、已 push） |
| 22 | CM-3350 P9-5 | jedi | jedi-iam 001 不插 role-manage、jedi-detection 005 不插 tool-plugin-manage；發 1.11.1／1.7.1 | 驗收通過待確認（已合回 feature、已 push） |
| 22 | CM-3356 P9-11 | FE | Dockerfile 分層：npm install 獨立一層在 COPY . . 前、加 .dockerignore（不排 .git）；只改程式不重裝套件（第 6 棒補開，另一 session 清 188 時發現） | 驗收通過待確認（已合回 feature、已 push） |
| 22 | CM-3354 P9-9 | BE | 證據上傳只收文件與圖片：副檔名＋檔頭白名單（zip／影片／執行檔擋），清單放 jedi-file-upload port，FE 讀端點 | 驗收通過待確認（已合回 feature、已 push） |
| 23 | CM-3351 P9-6 | BE | overview 對下發中那幾台並行拉 `GET /update/progress` 回 progress（記憶體快取、不加表不加 migration）；FE 8 格另開 fe 卡 | 驗收通過待確認（已合回 feature、已 push） |
| 23 | CM-3355 P9-10 | FE | Agent 區每台展開 8 格（重用 UpgradeStepList，吃 P9-6 progress）；五個證據上傳入口 accept 讀 P9-9 端點、新錯誤碼翻譯（第 6 棒補開） | 驗收通過待確認（已合回 feature、已 push） |
| 23 | CM-3352 P9-7 | BE | 改釘兩套件重攤平、第三輪刪 tool-plugin-manage、出包前幽靈選單比對、升級後只留安裝包；build_all 出包成功後 `builder prune --keep-storage 20G`（從 P9-2 移來） | 驗收通過待確認（已合回 feature、已 push） |
| 24 | CM-3353 P9-8 | BE | 結案：本機驗 → 1.27.3／1.4.6 正式包 → `--reuse-images` 出 1.27.4／1.4.7 第二包 | Done（決策者 10-31 15:45） |
| 25 | CM-3358 P10-1 | BE | 待辦 44 BE 半：寫單前不重算整包雜湊、「更新到 X」建的單就緒後再按回 412 | Done |
| 25 | CM-3359 P10-2 | FE | 待辦 44 FE 半＋45＋46＋51 FE 半：下載 100% 即蓋遮罩、側欄每次載入重抓、product-update 能力點翻譯、Agent 目標版未知顯示「無法比對」 | Done |
| 25 | CM-3361 P10-4 | BE | 待辦 47：出包 storage-seed 每次從套件 assets 重建＋筆數守門 | Done |
| 25 | CM-3362 P10-5 | jedi | 待辦 52：jedi-oscal-v2 027 刪 CRA 繁中 deprecated 列（驗無掛載）＋清單濾 deprecated；發 2.9.1 | Done |
| 26 | CM-3360 P10-3 | BE | 待辦 48＋38：拿掉清單 90 天有效期；預檢加核心沒重開檢查＋手冊（與 P10-1 同改 error_code 檔，故下一批） | Done |
| 27 | CM-3363 P10-6 | BE | 待辦 49＋51 BE 半：補打更新站 /v1/report；目標版未知 lagging 回 null | Done |
| 27 | CM-3364 P10-7 | BE | 主專案改釘 jedi-oscal-v2 2.9.1、重攤平帶進 027、DEV 套 | Done |
| 28 | CM-3365 P10-8 | BE | 結案：本機驗 → 1.27.8 → 188 出包（開包驗 storage-seed 11 檔含 CRA PDF）；簽發／三台升級／補灌 PDF 由首腦做 | Done（決策者 01:11） |
| 29 | CM-3366 P11-1 | update-center | PM #2 更新站半：report 收 target=agent＋agent_fingerprint、新表 agent_version_reports、後台 Agent 統計 | Done（2026-11-01） |
| 29 | CM-3367 P11-2 | FE | PM #10 文案「這一版更新了什麼」→「更新內容」 | Done（2026-11-01） |
| 29 | CM-3368 P11-3 | FE | PM #21 支援頁文案精簡、內容清單收進 Accordion | Done（2026-11-01） |
| 29 | CM-3369 P11-4 | BE | PM #17 升級進度檔每步回寫 system_logs（新事件碼段、只寫一次） | Done（2026-11-01） |
| 29 | CM-3370 P11-5 | BE | PM #22 診斷包收 upgrade／install log 最近三支＋last-switch.json | Done（2026-11-01） |
| 30 | CM-3371 P11-6 | BE | PM #2 主系統半：Agent 升級結果打 report（target=agent，reported_at 防重送） | Done（2026-11-01） |
| 31 | CM-3372 P11-7 | BE | 結案：本機驗 → 1.27.9 → 188 出包；UC 部署／簽發／三台升級由首腦做 | Done（2026-11-01） |

| 32 | CM-3374 P12-1 | agent | 升級解包前磁碟預檢、docker 失敗帶真正原因、升級成功清舊映像 | Done（合回 `2b78d00`） |
| 32 | CM-3375 P12-2 | agent | SeaweedFS pids 1024／mem 2g 上限 | Done（合回 `2b78d00`） |
| 32 | CM-3376 P12-3 | FE | 8 格讀 `agent.progress`、無紀錄不畫箭頭 | Done（合回 `d18e98a2`） |
| 32 | CM-3377 P12-4 | BE | Bugsink 密碼每台隨機 | 裁不做（Done 跳過） |
| 33 | CM-3378 P12-5 | BE | 結案出 1.27.11／Agent 1.4.9 | 裁等 FR-171／172 後再出 |

## §3 已落地的契約（與 design 原文不同處，後面一律照這裡）

- **資料庫位置**：第一次升級前資料庫在 docker named volume `guidant_guidant-pgdata`，`mv` 改不了名；換接是把 `pgdata-next` 改名成 `<資料目錄>/pgdata` 並在 `.env` 寫 `GUIDANT_PGDATA_DIR`，named volume 原封留作快照（進度檔 `db.prev_kind=volume`）。之後的升級快照才是 `pgdata-prev-<舊版>` 資料夾（`prev_kind=dir`）。路徑一律走 `lib/upgrade_common.sh` 的 `upg_pgdata_dir`／`upg_pgdata_copy_dir`／`upg_pgdata_main_dir`。P1-10 清理看 `prev_kind` 決定刪 volume 還是刪資料夾。
- **簽好的清單**：v2 信封存在包旁 `guidant-ai-<版>.release.json`；shell 端只能透過 `release_keys_canonical` 讀驗過章的 canonical，不准讀信封外層。主機要有 python3，或含 `JSON::PP`／`Compress::Zlib`／`MIME::Base64` 的 perl；缺了升級預檢會擋。
- **beta 版號**：jedi-remote-agent 開發期是 `1.10.0b1`（不是文件原寫的 `1.9.0b1`，1.9.0 已被 FR-166.1 占用）；正式版應為 `1.10.0`。
- **R2 物件 key**：`v<主版號>/<完整版號>/<檔名>`，只在 Worker `src/releases.ts` 拼；P2-4 卡上 `releases/<版>/` 是筆誤。
- **status 契約**（FE 用）：頂層 `available`、`download`、`last_check`（含 `last_success_at`、`error_code`）、`scheduled_at`、`draining`／`in_flight`、`progress.segment`；失敗記 `failed`（包已刪），`skipped` 只給包還在的情況。全站提醒 `GET /system/product-update/notice` 只驗登入。
- **容器掛載**：api／worker 掛 `upgrade:ro`，`orders:rw`、`incoming:rw` 疊在上面，`/etc/guidant/release-keys:ro`；fe 只掛 `upgrade:ro`。更新單上限 8KB；`orders/` 730、`incoming/` 750。
- **送給更新站的身分**：授權檔原文（含 `issued_to` 客戶名稱）＋指紋＋現行版號＋最近一次升級結果；更新站只記授權編號、指紋、版號、結果。手冊已照實寫。
- **Agent 跟進限制**：只支援 docker compose 形態；從沒有自動跟進能力的舊版要先手動升一次；主機要有 systemd；用 `install.sh --upgrade` 指向 IT 自己解開的目錄升級時 Agent 不會跟進、更新中心也推不了（412008），要改用 `guidantai upgrade <包>`。
- **Agent 跟進可選**（決策者 2026-10-30 定案，取代 D16「一律自動」）：「更新到 X」確認視窗勾「主系統更新完成後，接著更新 Agent」（預設勾，可選哪幾台）＝主系統升完先 1 台再其餘；不勾＝主系統升完 Agent 不動，之後在 Agent 區自己勾、立即或排程，另有「稍後提醒」7 天。apply 多 `with_agents`／`agent_uids`（CM-3306）。
- **更新中心顯示文字**：「這一版更新了什麼／影響範圍／建議動作」來自更新站後台三欄（CM-3295），來源是 release note 檔尾「更新中心顯示文字」段（`version-bump` skill Step 1），3～5 條白話、停機不寫分鐘數，發布前照貼。
- **離線包由原廠提供**：主機連不到外網時更新中心拿不到包；原廠交付，客戶上傳。目前要兩個檔（包＋簽好的清單），CM-3308／3310 改成單一離線檔。
- **停機時間一律不寫死**：畫面與文案寫「升級期間將看到維護頁」，有預檢估算才顯示估算值。
- **wrangler.toml** 的 `database_id` 是全 0 佔位、Access 信箱未填，首腦部署時 `wrangler d1 create` 後填；`UPDATE_SITE_URL` 預設空字串、客戶端預設網址等網域定了才填。

## §4 下一棒要做什麼（第 10 棒）

**4-1 FR-167 本案已收口**：母卡 Done、批 32 合回主線、文件站上線、`main` 已含全部。留在本案的只有：CM-3378 結案出包（等 FR-171／172 一起出 1.27.11）、CM-3312 e2e 暫緩、待辦 62～64（換接後 compose 標籤、後台 Agent 三欄、收尾小碎片）、正式出版路徑（出貨前另開卡）。**沒有要接手的進行中工作**。

**4-2 60.167 重推 1.4.8**：磁碟已清到 31G，決策者在 189 Agent 區勾 60.167 重推即可，包在 189 `bundles/guidant-ai-1.27.10/agent/` 不必重下；推完核更新站 `agent_version_reports` 該指紋變 succeeded（查法 §6）。

**4-3 Agent 獨立更新＝新 FR（建議 FR-172），與 FR-171 同一討論週期**：決策者 11-01 裁「171 先，172 可能一起」。分工：FR-171 管主系統↔Agent 的線（指令直推、心跳只表存活、憑證自動換、守門收斂），FR-172 管 Agent 的貨（包分開出、更新站多一類成品、R2 分路徑、Agent 主控台更新頁、相容矩陣兩向）。重疊只有三處，172 直接用 171 定的通道：主系統叫 Agent 升級、Agent 回報結果與進度、契約版號。實作順序：172 的出包／更新站／主控台頁與 171 平行，172「結果與進度回報」那一兩張等 171 通道卡做完。需求紀錄 `docs/draft_requirement_spec/2026-11-01-agent-independent-update.md`；要搬過去的舊待辦：6、42、58、61、60 的「結果晚 5 分」半邊。

**4-4 本棒查到、沒開卡的事實**
- **60.167 推 1.4.8 失敗真因＝磁碟滿**，不是 docker 或核心：dockerd journal 04:27 `failed to extract layer … no space left on device`；升級同時要放下載包 1.7G＋解開 1.7G＋載入映像 4.3G＋快照 4.3G，該機當時剩 13G；`upgrade_order.sh:305` 把 stderr 丟掉只印「無法讀取新映像」。已清：舊 1.4.2 映像、1.4.7 包副本、舊 compose 專案 `guidant-agent-compose-0230` 三個 volume（15G 舊實驗證據，決策者裁刪）→ 可用 31G。**決策者可去 189 Agent 區勾 60.167 重推**，包在 189 `bundles/guidant-ai-1.27.10/agent/` 不必重下。該機核心 7.0.0-30、已裝 -38 未重開（`reboot-required` 在），這次不是它害的。
- **SeaweedFS 洩漏在 1.4.7 仍在**：121／60.167 跑 24 小時 PID 5,665／5,594（agent 本體 12）；CM-3375 加上限是止血，根因未查。
- **188 磁碟 6.7G→100G**（決策者逐項裁）：刪 24 個無容器引用的舊 tag、build cache 74G→36G（留 20G）、解開的 1.27.8／1.27.9 目錄、Agent 舊包 1.4.1～1.4.6、`/home/jedi/seaweedfs` 獨立 3.99 實驗組（含兩容器）、0.2.28 Agent 孤兒 seaweedfs＋四 volume。**沒刪 `/home/jedi/guidant-ai-1.27.7`（8.4G）**：redis／seaweedfs／bugsink 三容器的 compose `working_dir`／`config_files` 標籤仍指它（線上升級不重建這三個），刪了下次 compose 操作變孤兒＝10-31 誤刪 1.26.0 同款。**升級程式缺口**：換接時應把不重建的服務也 `up -d` 一次讓標籤指到新目錄（或第 7 步清理改指），記為待辦 62，可併 171／172 後的下一版。
- **三台主機指紋相同（待辦 54）更正**：更新站 `instance_version_reports` 主鍵＝`(license_id, fingerprint)`，三台授權各異所以三列都在、統計沒混算，第 7 棒「會算成一台」判斷錯。真正會撞的是 License Center（範本複製 VM 的 `/etc/machine-id` 相同，188／189 實查同值 `d478e208…`，`product_uuid` 不同）；屬 LC 不屬本案，轉記 LC。
- **待辦 2「立即」文案已修**（P8-3 `933aa083`，現為「按下後立刻開始更新」），不開卡。
- **Bugsink 登入不會強制改密碼**（官方映像 `CREATE_SUPERUSER` 建帳號，無首次改密機制），每台客戶同組密碼直到有人跑 `guidantai bugsink-set-password`——待辦 39 的疑慮屬實，決策者裁先不處理。
- **FE 主線多一個 STATE 沒記的 commit**：`85753fdb` 12:41 主控台 favicon，在 1.27.10 出包後才進，下一版才帶到。
- Notion P8 批 18～21、P10 批 25～28 實際全 Done（§2 表仍寫 Not started，收尾時改）。

**4-5 舊清單照舊**：待辦 41（併入 CM-3376）、43（併入 CM-3374）、53（CM-3375 止血）、62（新）；PM 會議 B 類（私鑰保管、PG 大版）；Q20 備份；正式出版路徑；e2e CM-3312 暫緩；189 LC 跑 feature 分支。今天下午 Office 預覽／Profile 下拉那批沒卡，要不要補事後紀錄卡仍待決策者。

## §5 已定裁示（不要重問）

- 開發期小弟只驗本機；測試期首腦操作 190。CRA 兩題先不管。
- 小弟可 push 自己的 ws branch；**套件需要發版就直接發（2026-10-31 決策者改判，不再先確認清單）**，發後卡上留版號與 consumer 清單。
- 更新站用 TypeScript、R2 既有 bucket `guidant-ai-release` 分主版號資料夾、Access 只放行決策者信箱、網域出貨前再定、先用免費方案。
- 不自動裝、不分安全版與功能版、只維護最新版、授權過期仍可升同主版號、換主版號重新授權。
- 救援頁、常駐助手、頻道、取消鈕（服務停掉後）、強制中斷進行中工作、差異包、只換單顆容器都不做。
- **push 自 2026-10-31 起為常態授權**（決策者 00:35「這是常態，不鎖定了」）：首腦可直接推自己當下的 branch（`feature/FR-139`），不推 main、不 force；母卡與子卡 Done 一律決策者。
- **目前以落地版為主**；DEV 授權維持落地版，不改回 SaaS（2026-10-30）。
- 更新中心版面＝雛形 v2；Agent 跟進可選（預設勾、可選台）；「預先下載」名稱不改（＝先下載到主機再決定要不要更新）；按鈕說明用 hover 不常駐；停機不寫分鐘數。
- 版本說明三段不是 release note，是 release note 檔尾抽出的 3～5 條白話，發布前貼更新站後台。
- **🔴 不再用心跳帶訊息**（決策者 2026-11-01 12:3x，189 推 Agent 1.4.8 時：「之後不要再用心跳帶訊息了，這機制真的雷到一個不行」）。心跳只回答「我還活著」，不承載任何狀態或指令。實測暴露的問題：①結果最晚 5 分鐘才回主系統（121 升完 12:31:39，新容器 12:31:29 已先心跳一次，要等 12:36 下一跳）；②下發要等下一跳（推送後最長 5 分才開始，待辦 25／26 已補主動通知）；③容器換掉時心跳中的狀態跟著丟；④升級途中畫面沒有即時資料。**改法方向**：Agent→主系統的結果與進度走專用端點、事件發生當下就送（Agent 已有 outbox 可重送）；主系統→Agent 的指令走既有 FR-039 資料面（mTLS＋短效 JWT，`/update/notify`、`/update/progress` 已是這條）。併入待辦 59「主系統與 Agent 分開更新」一起設計，既有靠心跳的欄位（`update`、`upgrade_result`、`profiles`、`profiles_check`、vulndb）逐一盤點改走專用通道。
- 發布印章私鑰在決策者 mac；開發、CI、STG、POC 都不用蓋章，只有「給客戶」那一刻 `publish` 打一次 passphrase。章放哪台（是否與 LC 同機）決策者「流程面後續再了解」，未裁。

## §6 pre-flight（接手先跑，唯讀）

```bash
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git status --short; git log --oneline -3
for r in . ../compliance-manager-fe ../update-center ../evidence-agent ~/Projects/Jedicogy/module/jedi-python-package ../license_center ../compliance-manager-test; do echo "$(basename $(cd $r; pwd)) ahead=$(git -C $r rev-list --count origin/$(git -C $r rev-parse --abbrev-ref HEAD)..HEAD) ws_ahead=$(git -C $r log --oneline HEAD..ws-CM-3241 2>/dev/null | wc -l)"; done
grep -n "工作坊 3[0-9]" ~/Projects/Jedicogy/agent-workshop/.workshop/logs/workshop.log | tail -15
for n in 3374 3375 3376 3378 3366 3372; do echo -n "CM-$n "; python3 ~/Projects/Jedicogy/agent-workshop/scripts/notion_case.py get CM-$n | grep "^狀態"; sleep 3; done
lsof -nP -iTCP:8000 -iTCP:8788 -iTCP:5062 -iTCP:5180 -sTCP:LISTEN | awk 'NR>1{print $1,$2,$9}'
.venv/bin/pip show jedi-remote-agent jedi-license-runtime jedi-detection | grep -E "^Name|^Version"   # 1.11.0／1.1.7／1.7.2
for h in 190 188 189; do echo -n "$h: "; ssh jedi@192.168.50.$h "curl -sk https://127.0.0.1/api/1.0/version"; echo; done   # 1.27.8／1.27.8／1.27.10
ssh jedi@192.168.50.189 "docker exec guidant-db psql -U cmmgr -d guidant_ai -At -c \"SELECT name, agent_version FROM compliance.remote_agents\""   # 121=1.4.8、60.167=1.4.7（重推後應 1.4.8）
ssh jedi@192.168.50.188 "df -h / | tail -1"   # 11-01 清到 100G
```

## §7 行為規範（本案特有）

- 首腦不開 subagent；改卡內文先改 `cards.py`／`gen_notion_cards.py` 再重產。
- **驗收 shell 卡一律在 Linux 容器跑 bats**（`docker run --rm -v <樹>:/w -w /w ubuntu:24.04`，裝 bats、jq、openssl、rsync、python3）；mac 上會有假紅，小弟說「乾淨 HEAD 也紅」不能信。
- **測合併後的樹不只測小弟 branch**：`git merge-tree --write-tree HEAD ws-CM-3241` → `commit-tree` → `worktree add --detach` 到暫存目錄測，測完 `worktree remove --force`。
- 簽章相關驗收用 P2-3 CLI 真簽出來的信封（`node --experimental-strip-types` 直接 import update-center `cli/envelope.ts`），不用小弟自組的 fixture。
- Notion 一次撈多張卡要隔幾秒，連撈 35 張會 429。
- 只動 DEV；190 是測試期專用；STG／POC 唯讀。
