拿 build 好的 guidant-ai-1.20.0b1 安裝包,在兩台乾淨機器上各走一條路,比對終態是否一致。
--upgrade結論:兩路終態實質一致,但發現一處真落差(內建稽核流程範本的 scope),詳見第 5 節。該落差會讓新裝客戶的租戶看不到兩個內建流程範本。依卡片第 ④ 條不自行修復,回寫等首腦裁示。
| 項目 | 值 |
|---|---|
| 新版包 | guidant-ai-1.20.0b1.tar.gz(933 MB) |
| SHA256 | daa49224cef4ff7f7e5a773956e10986a929b1de1d6be34a6b14bfa4aad52f43(與派工單一致) |
| 來源 commit | 4a7be93f(兩路 BE 實跑回報的 commit 也是這串) |
| 舊版包 | guidant-ai-1.19.0.tar.gz(2026-09-04 正式版) |
取包方式為 scp 唯讀取檔,未對 188 做任何變更。
兩台「乾淨主機」都是本機 docker 內自建的 Debian 12 容器(amd64、跑自己的 dockerd),互相獨立、各自空白。
| 項目 | 實際狀況 | 是否打折 |
|---|---|---|
| 主機 OS/架構 | Linux x86_64(容器內) | ⚠️ 是。實體機是 macOS arm64,amd64 由 Docker Desktop 模擬。安裝包的 check_environment 硬性要求 Linux + x86_64,此模擬讓檢查通過。CPU 指令集層面與真實 x86 主機不完全等價 |
| 起始 image 數 | 0(兩台都是) | 否。包自帶的 6 顆 image 全部靠自己載入 |
| 外部網路 | 實際切斷(docker network disconnect,容器內 DNS 解不到任何外部網域) |
否。是真封閉網路,不是模擬 |
| 記憶體 | 7 GB(建議值 8 GB) | ⚠️ 安裝程式印警告後照常通過。不影響功能,可能影響大檔匯入速度 |
| 主機時區 | Etc/UTC(部署設 Asia/Taipei) | ⚠️ 安裝程式印警告後照常通過 |
| 對外 port | 80/443,未改埠 | 否 |
| 授權匯入 | 未驗 | ⚠️ 是。本包以 --skip-prod-key-check 產出(無 PROD 授權簽發鑰),依派工單指示跳過此項 |
其餘各項(安裝流程、DB、權限、選單、檔案)皆為真實執行,無替代或跳過。
| 步驟 | A 路(新裝) | B 路(升級) |
|---|---|---|
| 環境檢查 | 通過(2 項警告:記憶體、時區) | 同左 |
| 安裝/升級 | install.sh --config → exit 0 |
先 1.19.0 → exit 0;再 --upgrade → exit 0 |
| 映像載入 | 6 顆全數載入 | 6 顆重新載入並通過完整性驗證 |
| migration | init 三輪建庫 | 主線 24 支 + 套件 46 支 = 實際套用 70 支,能力點 upsert 115 筆 |
| 系統檔灌入 | 10 個 | 補灌 10 個(不重複) |
| 服務健康 | 6 個容器全 healthy | 同左 |
| 跑的映像 | guidant-ai-be/fe:1.20.0b1 |
完全相同 |
/api/1.0/version |
1.20.0b1 / commit 4a7be93f |
完全相同 |
升級前 B 路造的客戶資料:1 個租戶、1 個專案、1 筆正常稽核輪次、1 筆孤兒稽核輪次(父專案不存在,用來驗孤兒清理)、1 位專案參與者、1 個使用者上傳檔。
| 比對項目 | 方法 | 結果 |
|---|---|---|
| DB 結構 | pg_dump --schema-only 全文 diff |
16904 行完全相同。唯一差異是 5 條 CHECK 的陣列轉型寫法(ARRAY[(x)::text,…] vs (ARRAY[x,…])::text[])與 pg_dump 每次隨機的 restrict token——逐條取 pg_get_constraintdef 核對,語意等價 |
| 表數量 | information_schema.tables |
兩路皆 169 |
| 權限(表+序列+預設 ACL) | 全量列出排序 diff | 4188 行,0 差異 |
| RLS 開關與 policy | pg_class + pg_policies 全量 |
461 條 policy、165 張表旗標,0 差異 |
| CM-1800 的 13 張表 | 派工單指定查詢 | 兩路皆 13 張全部 relrowsecurity=t、每張 4 條 policy |
| CM-1800 哨兵 | schema_migrations |
兩路皆有 2026-09-14-fr094-cm1800-rls-project-scoped-tables.sql |
| migration 清單 | 全 211 筆檔名排序 diff | 0 差異(兩路皆 211 筆) |
| 索引 | pg_indexes 全量 |
0 差異 |
| 函式 | pg_proc 全量 |
0 差異(83 個) |
| enum 型別 | 全部 12 個型別與標籤 | 0 差異 |
| 能力點 | 127 筆,名稱/resource_type/action/is_platform | 0 差異 |
| 路由↔︎能力點、角色↔︎能力點綁定 | 366 行全量 diff | 0 差異 |
| 選單路由 | 48 筆,含層級/url/排序/啟用 | 0 差異 |
| fail-open 路由 | 未綁能力點的葉節點 | 兩路皆 恰好 3 筆(license-status/my-tasks/project-dashboard,即 CM-1760 白名單) |
| 登入後的實際選單 | 建租戶管理員 → 登入 → /user/web-menu |
兩路皆 http 200,10 個節點、結構/網址/能力點名稱完全相同(僅自動編號流水號不同,屬正常) |
| 檢測基準 | detection_profiles |
兩路皆 10 套;版本 10、控制項 4133、分類 13、工具 8,皆 0 差異 |
| 框架 | oscal.frameworks |
兩路皆 1 個(CMMC_2),版本 2、catalog 2 |
| 稽核階段字典 | stage_objects |
兩路皆 6 筆 |
| storage-seed 檔案 | bucket 內容逐檔比對 | 10 個系統檔,檔名與大小完全相同 |
| 框架 PDF 可讀性 | 從 filer 實際下載 | 兩路皆 http 200、2272014 bytes、%PDF- 開頭 %%EOF 結尾——不是空白檔 |
| 租戶管理員登入 | 設定精靈建帳號 → 登入 → 打 API | 兩路皆成功,/oscal-frameworks/menu 200、/users/menu 200,無 403 |
| 項目 | 結果 |
|---|---|
| 孤兒稽核輪次清理 | 升級前 2 筆(1 正常 + 1 孤兒)→ 升級後孤兒 0 筆、正常那筆完好。清理生效且沒誤刪 |
| 舊資料保留 | 租戶、專案、參與者、上傳檔全部還在 |
| 客戶上傳檔 | cm1778-user.txt 升級後仍在 bucket 內,未被 storage-seed 補灌覆蓋 |
| 新裝路 | 升級路 | |
|---|---|---|
完整稽核流程 / 自我評估稽核 的 scope |
TENANT |
SYSTEM |
| 非 1 號租戶的使用者看得到幾個內建範本 | 0 個 | 2 個 |
實測方式:以 cm_app(受 RLS 約束的業務帳號)模擬一個新租戶的 session,查 compliance.flow_templates。
為什麼會這樣(追到根因):
scope='SYSTEM' 是 RLS policy 裡讓「原廠公版全租戶可讀」的那條分支(flow_templates_select 第一條件)。2026-09-14-fr093-4d-flow-templates-stage-objects.sql 插入,該 INSERT 沒有指定 scope 欄位,吃資料表預設值 TENANT。2026-09-14-fr094-cm1790-shared-scope-subtree.sql——它有一句 UPDATE … SET scope='SYSTEM' WHERE is_builtin=true。SYSTEM。99-stamp.sql 把 CM-1790 蓋章為「已收斂進基線」所以不跑;但範本是蓋章之後才由 4d migration 插入的 → 沒有任何語句把它改成 SYSTEM,停在預設值。關鍵佐證:188 基線庫本身這兩筆是 SYSTEM(唯讀查證,正確)。所以問題不在基線資料,而在「蓋章跳過的 migration,其回填對象卻在蓋章後才被另一支 migration 產生」這個順序交錯。
客戶端症狀:新安裝的客戶,建了自己的租戶後,流程範本清單是空的,看不到原廠附的兩個內建稽核流程;升級上來的舊客戶則正常看得到。且不會有錯誤訊息——RLS 過濾是靜默的。
未修復(依派工單第 ④ 條)。可能修法有幾種(把 4d 的 INSERT 補上 scope='SYSTEM'/基線重產時連帶處理/99-stamp 不蓋 CM-1790 的章),屬決策者裁示範圍,runner 不自行選。
public.schema_version 兩路不同(不影響功能,但仍是不一致)| 新裝路 | 升級路 | |
|---|---|---|
schema_version.version |
1.20.0b1 |
1.19.0b1(停在舊值) |
schema_migrations 的基線哨兵 |
__init_baseline_v1.20.0b1__ |
__init_baseline_v1.19.0b1__ |
原因:schema_version 只在新裝時由 99-stamp.sql 寫入,升級路的 migrate.sh 完全不碰這張表。
影響評估:查過全 codebase 與 install.sh/guidant 維運腳本,沒有任何程式或腳本讀 public.schema_version(Python 側同名字串都是 SSP 匯入的欄位,無關)。升級判定實際讀的是 /srv/guidant-ai/.env 的 GUIDANT_VERSION,兩路都正確是 1.20.0b1。所以目前不影響任何功能,但「同一版的兩台機器 DB 自報版號不同」對日後除錯與盤點是個誤導源。
基線哨兵那筆屬同源現象:升級路的庫當初是 1.19.0 裝的,該筆是歷史事實,不宜也不必改寫。
| 觀察到的差異 | 判定 |
|---|---|
| 派工單寫「能力點 115」,實測 127 | 非缺陷。115 是套件宣告數(升級日誌「upsert 115 筆能力點宣告」),127 是總數(含主專案)。出貨 seed 04-seed-core.sql 本身即 127 筆,兩路一致 |
| 派工單寫「新裝 enum 應為小寫」,實測有 6 個大寫 | 非缺陷。大寫的是流程引擎狀態 enum(jobstatus/taskstatus 等,值為 TODO,PROCESSING,…),與 CM-1756 Q2 講的 asset enum 不同組。asset 相關 enum 在此庫中不存在(無對應表)。兩路 12 個 enum 完全相同,不存在兩路不一致問題 |
| 5 條 CHECK 約束的 SQL 文字不同 | 非缺陷,pg_get_constraintdef 核對語意等價,僅 pg_dump 輸出寫法差異 |
| 選單能力點的數字 id 不同(如 72 vs 80) | 非缺陷,自動編號,兩路產生順序不同所致;名稱層比對 0 差異 |
| 升級路多 1 個租戶、多 1 個上傳檔 | 預期,是驗收時刻意造的客戶資料,正是「舊資料沒弄丟」的證據 |
schema_version 升級不更新:要不要讓 migrate.sh 也寫這張表,或確認它已無用途可考慮廢除。