# FR-184 交接紀錄（LOG）

> append-only。每棒收工追加一個 block，寫完不改。現況看 `FR-184-STATE.md`。

---

## 第 1 棒｜首腦｜2026-11-07～11-08

**做了什麼**
- 需求探脈：compose 十服務、installer、FR-065 §4.8 K8s 預留、FR-173 直接部署、saas 模式影響面、189／190 實測記憶體
- 費用實查（不是記憶值）：Google 定價頁前端渲染，用 test repo 的 Playwright 無視窗切台灣區抓；AWS 抓定價頁背後官方 JSON；DO／Hetzner 官方頁。GCP 台灣 16 GB 約 144／月、8 GB 約 82；K8s 正統約 290、高可用約 770
- 討論稿 → 設計 v1.0 → cards.py 驗批 → 開卡 CM-3643～3651（八張六批）；新 repo `guidant-ai-infra` 骨架並登記進工作坊
- 引導決策者完成：GCP 專案、帳單、API、state bucket、Cloudflare 權杖、Brevo、Turnstile 小工具、Origin 憑證、Secret Manager 12 個、SSL 嚴格規則
- T2 親手 apply：24 資源、VM 35.194.213.124，真機驗開機腳本與防火牆，寫 runbook
- 架構圖（mingrammer diagrams，GCP 官方圖示），補上客戶地端 Agent 區塊

**commits**：BE `db989fcc3`～`811a9ba44`；infra `7ec4f3a`～`08c67b6`（含小弟 T1 `93e0287`）。11-08 決策者授權後兩 repo 都已 push（infra 新建遠端分支）。BE 這次 push 夾帶另一 session 的 `01fb1289e`＋`b0cc9b321`（CM-3653 加開關又撤回，淨效果零），推前已核對

**決策**（理由在 design §3）
- VM 不 K8s；8 GB；host 模式；Turnstile 必開；密碼自帶；序號裝完才貼；SSL 只對單一主機名嚴格

**教訓**
- **費用要實查**：首輪憑記憶報價錯四處（DO 沒有 4 核 16 GB Basic、Hetzner 新加坡缺貨且 4 核只到 8 GB、AWS 台北比東京便宜而非貴、GCP 台灣比美國貴 14%）。決策者原話「你要確認啊，不然幹嘛請你評估」
- **Model 欄留空擋不住工作坊**：T2 被派給小弟、等人回答 3 分鐘後工作坊整個暫停。已記 memory，首腦親手的卡要開成「暫緩」
- **第一個 GCP 帳單帳戶被 Google 風控關閉**：重建帳單帳戶與專案才過。apply 前先看 `billingEnabled`
- **Cloudflare 免費方案「設定規則」在下拉選單的「Configuration Rules」**，左側選單的「設定」是另一個功能；欄位要選「主機名稱」不是「完整 URI」，否則規則永遠比對不到
- **`pbpaste` 搭指令時不要用 Cmd+V 貼指令**，剪貼簿會被指令文字蓋掉

**推翻了什麼**
- 討論稿 D2「Turnstile 先關」→ 決策者推翻，改必開（加 F1／T4）
- 討論稿 D3 建議 16 GB → 決策者裁 8 GB
- 首腦一度要決策者預先放 License 序號 → 決策者指出沒機器碼不能開通，更正為裝完才貼
- 設計 §5.2「從快照還原 License 不用重開」→ T1 小弟實查不成立（machine-id 在系統碟、vTPM 換機解不開），列 O1 待解

**交出時**：1.30.0 包 11-08 10:57 已在 188 出好，T3 前置齊了，未開始裝。

---

## 第 2 棒｜首腦｜2026-11-08

**做了什麼**
- 冷接盤點：pre-flight 全綠（GCP、Secret 12 個、VM 空機、網址 521、188 有 1.30.0 包、Terraform 無漂移）
- 決策者問「雲端版也走線上更新嗎」→ 盤點更新中心與直接部署兩條路，建議主路改線上更新（待裁，記 STATE §4）
- 追 Turnstile 時發現原 F1／T4 做法有兩個洞：升級會把 FE 蓋回關閉版，且前門 CSP 根本會擋 Turnstile 的 script 與 iframe
- 決策者裁「有填 key 就開」並要首腦直接改：BE `a108ab505`、FE `f95a85b8`，已 push
- F1 CM-3650、T4 CM-3651 卡上寫取消理由；T3、V1、母卡補說明；design、README、STATE 更新

**決策**
- Turnstile：兩把都填真 key 才開（Cloudflare 測試 key 視同沒填），`TURNSTILE_ENABLED=false` 可強制關；後端驗 token、`/setup/status`、前門 CSP 三處同一判定（`common/util/turnstile_state.py`）
- key 放 compose `.env`（`GUIDANT_TURNSTILE_SITE_KEY`／`_SECRET_KEY`），不放 `guidant.env`：前門要 site key 決定 CSP，compose 只能從 `.env` 插值，且不能把整份 `guidant.env` 掛給前門
- 沿用既有未登入端點 `/setup/status` 帶 Turnstile 狀態，不另開 API（登入頁本來就會打）
- 升級健康檢查：Turnstile 開著時接受 `CAPTCHA_400001`，只在 `/setup/status` 說開著時才放寬
- 舊機器 `guidant.env` 的 `TURNSTILE_ENABLED=false` 升級時不自動刪：客戶可能有意強制關，沒填 key 本來就是關
- admin 後台設定之後另做

**教訓**
- **「要另出一顆 image」的方案先檢查前門擋不擋**：F1／T4 開卡時只查了 FE build 與 BE 開關，沒看 nginx CSP，方案本身就開不起來
- **build 時寫死的開關搭配會被升級覆蓋的產物，等於每次升級都要人補一步**：同一包靠執行期設定切換才是對的形狀
- 首腦這棒自己動手改主產品，是決策者當次明示（「你直接改」），不是常態

**推翻了什麼**
- design v1.0「程式零改動」→ 主產品改了 Turnstile 開關
- design D2「F1 出 `build:CLOUD`、T4 換雲端 FE image、每次升級後重跑」→ 取消，改有填 key 就開
- design §5.2「先寫 `/srv/guidant-ai/.env` 再跑 install.sh」→ 走不通（`install.sh:933` 看到 `guidant.env` 或 `certs/server.key` 就停），改 `install.conf`；這點第 1 棒 STATE §3 已發現，本棒同步進 design

**交出時**：BE、FE 已 push。188 上 10:57 的 1.30.0 包不含 Turnstile 改動，T3 要等重出包。
- 同棒追加：決策者裁「升級走線上更新，出包我自己跑」。design D8 改為平常走更新中心、只收正式發版，P1 直接部署定位救火用；§5.3、§8 第 7 項、兩張架構圖標籤同步。P1 CM-3647、母卡補說明。雲端版機器指紋不登記內部主機清單、不加 `PRODUCT_UPDATE_ALLOW_SAME_VERSION`，寫進 STATE T3 步驟 8
- 同棒追加（13:20～14:05）：**T3 系統裝好**。決策者重出 1.30.0（13:09）→ 開包核過含 Turnstile 改動 → 188 直接 scp 到 VM → Mac 組 install.conf → `--check-only` → 安裝。踩三個坑：①Ubuntu 24.04 要先 apt 裝 tpm2-tools（`--check-only` 不擋、正式才擋，記 §4 另開卡）②vulndb 檔 600，容器 uid 1000 讀不到，第⑥-3 步失敗；根因 188 的 zstd 1.4.8 從 stdin 寫檔一律 600，`457f2964a` 修出包腳本；VM 上 chmod 644 後 `GUIDANT_REUSE_EXISTING_INSTALL=1` 重跑通過 ③手跑出包沒帶 `DEFAULT_UPDATE_SITE_URL`，`guidantai config edit` 補 `UPDATE_SITE_URL`。另：決策者 Secret Manager 的 db-admin 與 db-app 原本同一組，首腦指出後決策者換了 db-app。裝完換 Origin 憑證、外驗 200／Turnstile enabled／CSP 放行／直打 IP 擋；installer 自產九組回寫 Secret Manager；VM 家目錄安裝包刪除（先查過無容器與單元參照）
- 教訓：**手跑出包與 CI 出包有差異**（`DEFAULT_UPDATE_SITE_URL` 只有 CI 帶），用手跑的包裝正式環境要對照 CI 變數補；**讀含祕密的安裝輸出時過濾要以「一行裡的長十六進位」為準**，不要只過濾標籤（設定碼單獨一行，本棒漏過濾印進對話，一次性、已用途內，影響小）
- 同棒追加（14:05～15:30）：**T3 完成**——決策者跑設定精靈（`Billows` tenant 3、另建 `Jedicogy` tenant 4）、License 開通、Brevo 填好測試信寄出成功（`/mail/test` 200）。首腦另做：①出包權限根因查到底——包內 19 檔 600，只有 vulndb 掛給容器讀會壞，`9086e8ad8` 改接既有 `dist_perms.sh` 整包正規化＋斷言（188 實包＋`guidant-ai-be:1.30.0` 端對端驗：600 讀不到、644 讀得到），已 push；決策者要出 1.30.1 帶上 ②預算告警 Terraform（infra `724e9c0`），第一次 USD 回 400——帳單帳戶是 TWD，改 6,500 TWD 成功 ③查清楚雲端連更新站不需 Zero Trust token（Access 只守 `/admin/*`，`/v1/*` 靠 LC 簽的身分票＋指紋驗章）；更新站 `/v1/*` 沒有限流，建議 Cloudflare 加一條（未做） ④郵件與儲存設定的多租戶行為（寫進 STATE §4）
- 教訓：**STATE 裡「等某張卡出新版」這類依賴要開工前實查**——本棒照 STATE 說「Agent 接雲端要等 CM-3654」，被決策者問才去查，修正早已含在 Agent 1.7.0 並發佈；跨 session 的依賴狀態會過期，回報前 `git branch --contains`／`git tag --contains` 一下
- 決策：雲端版 `Billows`（安裝精靈建的總部）留給自家用，外部試用客戶另開租戶——host 模式下總部管理員能改全站寄信／LDAP；根治在 saas FR
- 同棒追加（15:30～16:00）：決策者要求「安裝後手動處理」能進自動化的都做、併 1.30.1 出。主產品 `b3b21a950`：`--check-only` 與正式安裝共用「出貨包 tpm2-tools 適不適用這台」判斷（只修誠實度，不改成自動連網 apt——installer 只裝本地檔）；infra `8194fb0`：開機腳本預裝 tpm2-tools、時區走 metadata。Origin 憑證刻意不進開機腳本（VM 服務帳號不該讀私鑰；升級會保留），寫進 runbook。手動處理清單 10 項，剩重建 VM 時的憑證與 188 known_hosts
- 同棒追加（16:00～17:20）：**1.30.1 經更新中心線上更新成功**（發版 session 發、決策者按更新，首腦監看八步；健康檢查在 Turnstile 開著時通過）。決策者覺得慢，實測拆解：VM 內 API 80～130 ms、外部 0.65～1.3 秒，差額是 Cloudflare 分到 SJC 繞路；升級後記憶體用 6.2／7.75 GB（三個後端各載一份 AI 守門模型約 1.5 GB）、CPU pressure 28%。決策者裁升 **e2-standard-4**（infra `5fe59da`）：先 `guidantai stop` 再 apply，重開後手動 `guidantai start`；VM 內降到 17～24 ms、CPU pressure 4%、可用記憶體 9.4 GB。Cloudflare 靜態檔快取：雜湊檔已在快取、固定檔名的主題檔有 4 小時舊版風險，**決策者裁先不動、看客戶反應**
- 教訓：**升級中與平常的資源數字差很多**——首腦先依決策者在升級中截的 htop（後端全停、851 MB）說「記憶體完全沒壓力」，服務起來後實際 6.2 GB，當場更正。判規格要在服務穩定後量

**第 2 棒 commits 總表（交出時補）**
- BE（本棒）：`a108ab505` Turnstile 有填 key 就開、`457f2964a`＋`9086e8ad8` 出包整包權限正規化、`b3b21a950` installer tpm2-tools 判斷一致；文件 `b8365df40`、`0a6cd990c`、`3fa60590c`、`b250ffca0`、`33272a8e6`、`79049c878`、`7c2c531c7`
- BE（發版 session，1.30.1）：`0134d23f5` 進版、`f2066a58c` 正式發版帶預設更新站網址、`2aeb13257` 弱點白名單
- FE：`f95a85b8` 登入頁問後端決定 Turnstile＋前門 CSP；`d9b1221d` 版號 1.30.1（發版 session）
- infra：`724e9c0` 預算告警、`8194fb0` 開機腳本補時區與 tpm2-tools、`5fe59da` 升 e2-standard-4、`7caae4a` Terraform 導讀

**推翻了什麼（第 2 棒）**
- design D2「F1 出 build:CLOUD、T4 換雲端 FE image」→ 取消，主產品改有填 key 就開
- design D8「升級走 CI 直接部署」→ 平常走更新中心，P1 改救火用
- design D3／T1「e2-standard-2 8 GB」→ 決策者 11-08 裁升 e2-standard-4（三個後端行程各載 AI 守門模型，8 GB 平常就用到 80%）
- 首腦說「CI 正式發版會帶 DEFAULT_UPDATE_SITE_URL」→ 實際 CI 變數名與出包腳本讀的名字沒對上，發版 session 在 1.30.1 才修（`f2066a58c`）；雲端 VM 的 `UPDATE_SITE_URL` 是首腦手動補的、1.30.1 升級後保留
- STATE「Agent 接雲端要等 CM-3654」→ 早已含在 Agent 1.7.0

**交出時**：雲端版 1.30.1、e2-standard-4，決策者先給客戶試用。下一棒 Q&A：決策者要看懂 Terraform 回答 PM，導讀在 infra `docs/terraform-walkthrough.md`。之後 P1 → O1 → V1。
