FR-193 紀錄

append-only;每棒追加一個 block,寫完不改

§1

第 1 棒 2026-11-10(首腦:需求釐清 → 設計 → 開卡 → 驗收 → 真包首裝修正)

做了什麼

從決策者一句「本體跟 agent docker 都有改成 rootless 嗎」開始。查出 FR-180 只做到容器內非 root、引擎仍是 root,當時刻意不做 rootless。決策者改判要做。寫討論稿、design v1.0~v1.4、十張卡、Agent 主機手冊(Claude Docs)。工作坊跑完 A0~A8。之後首腦親手:分析正式發版各關卡、合回 ws-CM-3804、在 188 出測試包、協助決策者在 192 用真包首裝、把 16 個維運指令逐一驗過並修掉抓到的問題。

commits

BE(feature/FR-172,都已推):80ff36282 討論稿 → 49ee845c6 D4 → 8976dabd6 design v1.0+cards.py → 5363671c1 v1.1(D1 改 8443)→ 9c5f3091b v1.2(D8)→ da36b6d20 → cbe63588f 開卡 → 495d360e6 → bf9e8cdb6 v1.3(A0 實測後 D9~D11)→ ce2b1cf56 v1.4(A4~A6 合併)。

Agent(feature/FR-172,都已推):工作坊的 A1~A7(0d466da bcae205 e6d261d 9a9588f 88a19ac 0dbcd41 46b4703 87b712c bdb2ea5 cd9fbdf f73d523)、合回 62c23c3,之後首腦親手六個:

commit 修什麼 起因
710467e 部署/演練/健康檢查三支腳本找對 Docker daemon FR-193 漏掉
3ff9fa9 映像落地檢查在完整輸出裡找通過訊息,不只看最後 30 行 FR-188 讓啟動輸出變長
9d6f577 包內容檢查不用「大輸出 | grep -q」(pipefail 下 141) FR-193 讓清單變長
753e4e2 離線裝 Docker 不碰主機 systemd:不用 machinectl、包內不帶 systemd 一組、裝前驗相依 真包首裝第一步就失敗
cd54c60 沒填主系統時完成步驟不空等 90 秒 註冊改到主控台後沒收乾淨
b0efcba 升級驗章改用 docker cp,不掛主機目錄 rootless 下 daemon 進不了 root 700 目錄

未 commit:deploy/install.sh 的 uninstall 修正(見 STATE §1)。

決策

  • D9(A0 實測後改):維運指令用 root 的 CLI 加 DOCKER_HOST 指到 guidant 的 daemon,不用 sudo -u guidant。後者讓 image_read_file、升級 docker load、--env-file 都 permission denied。
  • D10:維運程式改讀主機副本 /usr/local/lib/guidant-agent/install-lib,不以 root 去 source guidant 映像庫裡的腳本。
  • D11:NET_RAW 保留,另加容器 sysctl ping_group_range。拿掉 NET_RAW 連 ping 都起不來。
  • 切換帳號不用 machinectl,改 sudo -u 加 XDG_RUNTIME_DIR 與 bus 位址。192 實測可行,重開機後不登入也會自己起來。
  • 包內不帶 Source 是 systemd 的套件。它們彼此要求同版號,「只裝主機缺的」會把新版配到舊版上。用 systemd 開機的主機一定已有。
  • A4/A5/A6 合成一張(決策者問「為什麼不是全部開發完再測」)。同一支 install.sh、同一次裝機,分三張等於做三次且每次驗不完整。
  • 出包走 188 手動 build_all.sh --with-compose --internal-test,不走 GitLab 直接部署。後者會自動裝到 190/188/189,那三台系統層 Docker 上有主系統,新版安裝程式會拒絕。

教訓

  • 卡的驗收寫「不必跑完整流程、留給總驗」就會漏。 CM-3809 的升級驗收只放假升級單看單元起得來,真正的升級走不到底,到決策者要求全跑指令才抓到。換檔進舊包驗 ≠ 真包驗:離線裝 Docker 與從新映像存主機副本這兩段只有真包走得到,第一次真包安裝就死在前者。
  • D9 只驗了「CLI 自己讀檔」,沒想到「把主機路徑交給 daemon 掛載」。 定一個身分模型時要把「誰讀檔」的每一種形式列完。
  • 「只裝主機缺的套件」遇到 = 版號 的相依會壞。 連網機用 apt 驗不出來,apt 會連帶升級。離線路徑一定要離線驗。
  • 建置檢查用 tail -N 或「大輸出 | grep -q」是定時炸彈。 輸出變長才壞,而且壞的時候產物是好的。
  • 載入 lib 的條件要跟著儲存位置一起改。 lib 搬到主機副本後,cmd_uninstall 仍把載入綁在「讀得到映像名稱」上,wipe 之後整段還原沒執行卻印「移除完成」。
  • 沒先看程式就對決策者下判斷,錯了兩次。 說 ping 在 rootless 不可用(實際是 sysctl);說 wipe 之後容器應該還在(實際設計就是整個刪)。
  • 不帶路徑的 git stash 把別人未提交的檔一起收走。 當場用完整 hash apply、比對一致後才 drop,沒有遺失。檢查用的指令不要夾帶會改工作區的動作。
  • 在這台 Mac 上 bats 的中文測試名跑不了(bash 3.2),本機也沒有 dpkg。 要到 188 跑。Agent 的 venv 本機沒有,全量測試要在 188。188 的 .venv 建置中不可動,另開獨立環境時記得指定 Python 3.11,預設會拿到 3.13。

推翻了什麼

  • FR-180 design 與 SR-09.5 的「不做 rootless」。
  • design v1.2 的 dk() 用 sudo -u guidant、「compose 拿掉 NET_RAW」、uid 換算「起點 + N」(實際是起點 + N − 1)。
  • design v1.0~v1.3 的 D1「預設 443 貼 rootlesskit 能力」→ PM 裁一律 8443。
  • CM-3806 A1 用 machinectl shell 啟用 rootless、必備套件含 systemd-container。
  • 首腦當天說過的「rootless 下 ping/traceroute 不可用」「Docker 套件還沒裝上」「192 上的資料夾是空的」「wipe 之後容器應該還在」。

怎麼不重編譯重出包(188,/opt/guidant-ai-agent,先 git pull、pgrep -af 'nuitka|build_all' 確認沒人在建)

改動只在 scripts/、deploy/、test/ 時編譯產物不變(git diff --name-only <編譯時的 commit> HEAD 核對)。安裝程式會被簽章並進映像,所以從打包那步重跑:

B=/opt/guidant-ai-agent/.build
export FE_CONSOLE_DIST=$B/console/dist-console
scripts/build/build_agent_bundle.sh $B/dist/evidence-agent-1.7.4
scripts/build/smoke_agent_bundle.sh $B/bundle/guidant-agent-1.7.4.tar.gz
scripts/build/build_all.sh --compose-only --internal-test

約 12 分鐘。fetch_docker_debs.sh 有改動時要先把 $B/docker-debs 移開,否則沿用舊快取。主控台前端要重建時:FE_REF=feature/FR-172 NPMRC_FILE=/home/jedi/.npmrc scripts/build/build_console_dist.sh $B/console。

正式發版各關卡(2026-11-10 實測)

套件弱點稽核通過;前端測試通過;映像弱點關卡通過(用更新後的判定程式,Agent 只擋 Critical,白名單 7 條命中、30 天內無到期);編譯、簽章、冒煙六項通過;全量測試 2555 過但環境是 Python 3.13。會擋的:版號仍是已發過的 1.7.4、版本說明未寫。

16 個維運指令在 192(rootless)的結果

正常:status logs fingerprint --help stop start restart reset-admin-password console-allow-cidr configure-storage。可執行但沒有主系統只驗到「失敗時不弄壞」:set-base-url re-enroll。upgrade 原本壞、已修(b0efcba)。console-login 壞、未修、非 rootless 造成(CM-3868)。wipe 正常。uninstall 在 wipe 之後不還原主機設定,修正未驗完。

補記(同一棒,22:40)

交接文件寫完後把做到一半的 uninstall 修正收掉:Agent 3ea579d,已推。

第一次請決策者在 192 重跑 uninstall 時被 resolve_installed_paths 擋下(安裝目錄已被前一次刪掉,「看起來還沒安裝過」),沒走到修正。這暴露修正本身的漏洞:印「移除未完成,請再跑一次」但再跑會被拒。補 host_leftovers_exist,主機有殘留就放行只清殘留。第二次重跑後上機核對,帳號、rootless Docker、設定檔、升級單元、主機副本都清掉,系統層 Docker 恢復 enabled。

教訓:修「失敗時叫人重試」的訊息前,先確認重試那條路走得通。 以及確認題可以用答 no 的方式試跑到提示出現為止,不必真的刪,就能驗「有沒有走到那一段」。

本棒 Agent 親手 commit 共七個:710467e 3ff9fa9 9d6f577 753e4e2 cd54c60 b0efcba 3ea579d。