# FR-060 檢測基準內容管理 — STATE（living，永遠只描述「此刻」）

> **這是 living 檔，就地 Edit 覆寫。** 決策脈絡與每棒紀錄在 `fr060-LOG.md`（append-only）。
> 最後更新：2026-08-07（第 11 棒，**全案結案**）
> 收尾彙整見同資料夾 `2026-08-03-fr060-arc-SUMMARY.md`。

---

## §0 讀序（冷接必讀，依序）

1. **本檔全部**（尤其 §1 原始需求、§4 下一步）
2. **Notion CM-1054 母卡全文** — https://app.notion.com/p/3b1346da4cd081efbab5e7fa0f7e3640
   決策 D1–D19 一句話版、三個必讀陷阱、階段拆分、開工前三件待拍板
3. **`design.md`**（同資料夾，1030 行）— **建卡只需讀 §3 決策定案 + §7 拆分 + §8 驗收**；真正開工實作時才需要 §4 現況接入點 + §5 詳細設計
4. 三張已建的子需求卡（CM-1055/1056/1057）— 子任務拆法已寫在各卡「範圍」表格

**硬 gate**：讀完 §1 若答不出「為什麼一個瀏覽功能要順帶拆資料表」，回頭讀 CM-1054 母卡的「任務摘要」段。

---

## §1 🧭 原始需求（先懂這個，其餘都是手段）

決策者原話：

> 「目前只有項目，user 完全不知道這個東西會驗證什麼，我怎麼知道我要選什麼？哪些基準可以參考，我可以先請負責人去判斷、提前先處理，而不是等報告出來才去處理。」

FR-059 做完了「掃描設定檔管理」，但**平台把 profile 當成不透明的二進位檔，完全不理解內容**——使用者在管理頁與任務下拉看到的只有一個名字。

**支撐急迫性的實測數字**：`TWGCB-01-014` 共 234 條控制項，其中 **199 條是人工待判項**（`impact 0.0`，執行時直接 skip），實際自動檢查只有 35 條、覆蓋率 15%。使用者選了、掃了、拿到一份 85% 是 skip 的報告才發現——等於白等一輪掃描。

**三個需求**：①瀏覽 profile 檢驗內容（核心）②修改基本資料含改名 ③分類體系。

**為什麼一個瀏覽功能要拆資料表**：現行單表用 `(detection_tool_id, tenant_id, name)` 當唯一索引——**拿 name 當身分**，這正是「改名改不動、唯一改名途徑是 fork 複製」的根因；而分類三軸是「這支 profile 是什麼」的屬性、不是「這一版是什麼」的屬性。**現在是最便宜的時機**：DEV 僅 13 筆、FR-059 上線才兩天、沒有租戶真的在用。

---

## §2 當前狀態（2026-08-07 第 11 棒實查）

### 🎯 一句話：**全案結案（2026-08-07）**。功能與上版均完成；POC 12 支公版控制項抽取為結案後 follow-up（見 Notion CM-1104），不影響結案判定。

### 三環境 DB 上版已完成，registry 對齊（第 11 棒協調者唯讀實查）

```
DEV: cm1047 / fr060-1  / fr060-2 / fr060-3(param) / fr060-3(dpv)
STG: cm1047 / fr060-1b / fr060-2 / fr060-3(param) / fr060-3(dpv)
POC: cm1047 / fr060-1b / fr060-2 / fr060-3(param) / fr060-3(dpv)
```

差異僅在 DEV 用拆表 migration 原檔 `fr060-1`、STG／POC 用環境自適應版本 `fr060-1b`（原檔斷言寫死 DEV 筆數 13，另開一支環境自適應版本供 STG／POC 使用）——**這是設計如此，不是落差**。

🔴 **POC 12 支公版控制項抽取仍是 follow-up**：`extraction_status` 全部 `pending`，`config.detection_profile_controls` 為 0 筆。STG 已完成（10 支 succeeded／4,133 條控制項）。子表「開始解析」入口已就緒（CM-1096），管理員可在 POC UI 上直接逐支觸發，無需額外開發。追蹤卡：**CM-1104**（`Not started`，作業人員 `雷門`）。原因：登入 POC 需 captcha／Cloudflare Turnstile，AI 助手不處理密碼與人機驗證。

### ✅ 工作區已清乾淨（第 6 棒的警語已解除）

第 6 棒記載的「CM-1075 實作中異動卡在工作區、不可 `git add -A`」**已解除**——CM-1075 於 2026-08-04 完成並 commit（BE `6964c89f`／FE `209bf88`）。

工作區目前**已追蹤的修改只有交接文件與卡片 md**（第 9-10 棒的 `cards/CM-1077-2-edit-and-delete.md`＋`fr060-STATE.md`＋`fr060-LOG.md`，第 10 棒收尾時一併 commit），其餘皆為無關的 docx／png／其他 arc 的 handoff 等未追蹤檔——**絕不可 `git add -A`**。

⚠️ 但**多 session 併發仍是本專案常態**，commit 一律顯式 `git add <檔名>`、禁用 `-am` 的紀律不變。

### 設計階段：✅ 已收口

- D1–D19 **全數拍板**，`design.md` 1030 行已 commit（`7e3e7b97`）
- 討論稿 `discussion.md` + build 產物 `discussion.html` 已 commit（`7124a473` 補 D19）

### 實作進度：✅ 主線七張 ＋ 延伸五張全數落地

| 卡 | 內容 | commit |
|---|---|---|
| CM-1060 | T-1.1 三表 DDL ＋ RLS 八段 ＋ 13 筆搬遷 ＋ 舊表 rename | BE `d7cf9abc` |
| CM-1061 | T-1.2+1.3 資料層三組 ＋ domain/app service ＋ 讀取端 API | BE `01d0909b` |
| CM-1062 | T-1.4 派工鏈兩處破口 ＋ 迴歸測試 | BE `40fcc38d` |
| CM-1063 | T-2.1+2.2 分類 enum 表 ＋ seed ＋ 13 筆補登 ＋ PUT 編輯 ＋ 刪除保護 | BE `a2f2512b`／FE `28f061f` |
| CM-1065 | T-3.2+3.3 抽取器註冊表 ＋ InSpec extractor ＋ 非同步管線 | BE `ec4e70b7` |
| CM-1064 | T-2.3+2.4 管理頁重構（1,239→639，拆六支）＋ 下拉分組 ＋ fallback 攤平 | FE `97f9ac8` |
| CM-1066 | T-3.4 控制項詳細頁 ＋ 摘要區 ＋ 抽取狀態輪詢 | FE `bbcb882` |
| **CM-1072** | url 型基準支援內容抽取（BE 代下載 ＋ SSRF 防護），**延伸納入本 arc** | BE `25045758` |
| **CM-1075** | url 型開明細頁比對 ETag，有變更才重抽（甲案，先實測後實作） | BE `6964c89f`／FE `209bf88` |
| **CM-1082**（`.1`） | 使用判定服務（BE 唯讀，三來源 UNION ALL ＋ 軟刪穿透） | BE `17e977eb` 8 檔 +540 |
| **CM-1083**（`.2`） | 修正來源 ＋ 硬刪（BE 寫入端）＋ 三個 409 error code | BE `dddfe7cf` 9 檔 +790／FE `d6e71e1`（i18n 三語系） |
| **CM-1084**（`.3`） | 兩動作接進操作 menu（FE） | FE `88f2e13` 8 檔 +818/-31 |
| **CM-1093** | `.2` 迴歸測試整批跑 6 條 fail — 補 patch logger | BE `fe615d3e` 1 檔 +17 |

**分頁預設 10 → 20**（決策者口頭交辦，**無 Notion 卡**）：FE `247b6f5`，`DetectionProfileManageView.vue:77` 的 `pageSize = ref(10)` → `ref(20)`。該頁 DataTable 本來就有 `:rows-per-page-options="[10, 20, 50]"`（L711），20 在清單內故 paginator 選擇器正常顯示。
⚠️ **未經畫面驗證**——下手當時 FE 工作區有 `.3` 做到一半的未 commit 內容，開頁只會看到半成品且可能干擾對方，故未驗。留給決策者手測時一併看。

手測後三支 FE 修正：`07b8a7d` 列表分類欄拆三欄／`e4e7967` 中繼資料標籤中文化／`7274d13` 修多列展開只剩最後一筆有資料。

✅ **第 3 棒記載的「BE 目前是壞的」已解除**——CM-1061（`01d0909b`）完成資料層改寫後恢復正常。

### DEV 狀態（2026-08-04 15:42 第 10 棒實查）

```
主檔               11
版本               11
控制項           4,841
分類字典           13（target_type 8 ＋ benchmark_family 5）
抽取 succeeded     11   全數抽完
抽取 failed         0   ← 原「SSRF」基準 v1/v2 已被 CM-1083 刪除驗收時刪乾淨
抽取 pending        0
```

🔴 **這組數字正在變動中，不要當基準**。第 10 棒同一分鐘內連查三次得到 14 → 12 → 11（決策者正在手測 CM-1084 的刪除功能）。**要用數字時一律現查**：

```bash
# 帳號 cmmgr，密碼查 BE .env 的 DB_SECRET
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -tAc "
SELECT '主檔='||count(*) FROM config.detection_profiles
UNION ALL SELECT '版本='||count(*) FROM config.detection_profile_versions
UNION ALL SELECT '控制項='||count(*) FROM config.detection_profile_controls
UNION ALL SELECT 'extr:'||extraction_status||'='||count(*) FROM config.detection_profile_versions GROUP BY extraction_status;"
```

✅ 第 7 棒記載的 2 支 `failed`（決策者測 SSRF 防護時建的「SSRF」基準 v1/v2）**已不存在**——它們正是 CM-1083 的具體驗收目標（引用 0 次、要能被完整刪乾淨），已於 `.2` 實作時刪除並驗證三張表零殘留。

🔴 **舊版本文件寫的「url 型無法抽取、`failed` 是設計內終態」已被 CM-1072（`25045758`）推翻**：抽取時由 BE 代為下載（`common/util/safe_http_fetch.py`，SSRF 五道防護），拿到 bytes 後完全共用 file 型解析路徑。三條不變式——①**掃描路徑完全不動**（agent 仍原樣把 URL 交給 `cinc-auditor exec`，抓的是即時內容不是快照）②**下載內容不落儲存**（`source_type` 維持 `url`、`file_id`／`sha256` 維持 `NULL`）③**下載必須在 `session_scope()` 之外**（網路 IO 最多 60 秒）。完整脈絡見 SUMMARY §2.4。

人工待判 vs 自動檢查（收尾日實查，這正是本需求的 WHY）：

| 基準 | 總數 | 人工待判 | 自動檢查 | 自動覆蓋率 |
|---|---:|---:|---:|---:|
| TWGCB-01-014 Ubuntu 22.04 | 234 | 199 | 35 | **15%** |
| TWGCB-01-007 Win Server 2016 | 699 | 159 | 540 | 77% |
| TWGCB-Windows-2025 | 708 | 150 | 558 | 79% |

舊表 `config.detection_tool_profiles_deprecated_20260803` **未 DROP**（觀察期後另案，`design.md §8.3`）。

🔴 **STG／POC 完全未動**——收尾日實查兩環境 `config.detection_profile%` 新表數皆 **0**，三支 migration 只套 DEV。

### Notion 卡片樹

> 🔴 **本表是 2026-08-04 第 10 棒的實查快照，不是即時狀態。** Notion 是多方（決策者手測、其他 session、實作 runner）都會改的外部真相——**派工或驗收前一律現查，不要照抄本表**。
> 第 9 棒實查發現本表前一版有大面積漂移（CM-1055~1057／CM-1060~1066 已全 `Done`，STATE 卻仍寫「子需求卡」「修正待驗證」「尚未回寫」），並據此寫進派工單交代 subagent「兄弟卡停在修正待驗證別動」——**那份資訊本身就是過期的**。

| 層級 | Case No | 狀態（2026-08-04 第 10 棒實查） |
|---|---|---|
| 母案 FR-060 | **CM-1054** | ✅ **Done**（第 9 棒收口，內容全量重寫五段，含明列的「尚未做」七項） |
| FR-060.1 資料模型重構 | CM-1055 | ✅ Done |
| FR-060.2 分類體系＋編輯 | CM-1056 | ✅ Done |
| FR-060.3 抽取＋瀏覽 | CM-1057 | ✅ Done |
| T-1.1 migration | CM-1060 | ✅ Done |
| T-1.2+1.3 資料層／服務層 | CM-1061 | ✅ Done |
| T-1.4 派工鏈破口 | CM-1062 | ✅ Done |
| T-2.1+2.2 分類 BE | CM-1063 | ✅ Done |
| T-2.3+2.4 管理頁 FE | CM-1064 | ✅ Done |
| T-3.2+3.3 抽取 BE | CM-1065 | ✅ Done |
| T-3.4 詳細頁 FE | CM-1066 | ✅ **Done**（且**早已完整回寫**，含四支 commit 與 PrimeVue 展開列根因） |
| url 型支援內容抽取（延伸） | **CM-1072** | ✅ **Done**（第 9 棒收，決策者手測通過；已補三條不變式與 SSRF 說明） |
| url 型 ETag 比對（甲案） | **CM-1075** | 修正待驗證（待決策者手測） |
| 8 支基準批次抽取控制項 | **CM-1076** | ✅ Done |
| 版本／基準的修正與刪除（未用過才可改可刪） | **CM-1077** | **Not started**（母卡；三張子卡皆已落地，待決策者手測通過後才收） |
| ├─ CM-1077.1 使用判定服務（BE 唯讀） | **CM-1082** | **修正待驗證**（BE `17e977eb`，11 條單元測試綠；⚠️ 卡內三條 DEV 實測斷言待手測） |
| ├─ CM-1077.2 修正來源 ＋ 刪除（BE 寫入） | **CM-1083** | **修正待驗證**（BE `dddfe7cf` 9 檔 +790／FE `d6e71e1` i18n；19 條測試） |
| └─ CM-1077.3 兩動作接進操作 menu（FE） | **CM-1084** | **修正待驗證**（FE `88f2e13` 8 檔 +818/-31，小弟已在 DEV 走完 7 條手測） |
| `.2` 迴歸測試整批跑 6 條 fail（補 patch logger） | **CM-1093** | **修正待驗證**（BE `fe615d3e` 1 檔 +17，整批 failed 80 → 回基準 74） |
| DB schema 命名體系整理（母案） | **CM-1094** | **Not started**（🔴 記錄用，**現階段不做**；決策者裁示「之後會再做一次大整理」） |
| └─ config schema 更名為 detection（`FR-060.2`） | **CM-1095** | **Not started**（🔴 **隨 FR-060 上版一起做**，不單獨排；詳見 §4 第 4 項） |
| 清單操作按鈕 7 顆收進 menu | **CM-1078** | 修正待驗證（第 8 棒完成並收尾；操作欄最終是**單一 kebab**，讀取類也收進去了） |
| 專案軟刪除後無法檢視或復原（follow-up） | **CM-1079** | Not started（🔴 決策者裁示：優先權很後面，**現階段不做，僅記錄**） |
| 全部展開 ＋ 展開列視覺降階（兩頁） | **CM-1080** | 修正待驗證（第 8 棒，決策者當場追加） |

🔴 **CM-1081 不屬於 FR-060，它是「升級迴歸」那條 arc 的母案**（子卡 CM-1085~1092＝T-0~T-6）。所以 CM-1077 三張子卡的 Case No 從 **1082 起跳**，不接在 1080 後面。
⚠️ **Notion 上現有兩條 arc 交錯編號，看卡號時不可依編號連續性推斷歸屬**——1082/1083/1084 是 FR-060，1085~1092 是升級迴歸，1093 又回到 FR-060。

建卡採**兩階段**：先一次 `create-pages` 建薄卡鎖住連號，再逐張 `update-page` 填完整內容——這是第 7 棒本體一次送三份長內容撞 `API Error` 的對策，第 9 棒四次呼叫全成功。

~~🔴 CM-1077 與 CM-1078 動到同一個檔，CM-1078 要先做~~ → **已解除**：CM-1078 已落地並收尾，CM-1077.3 可直接加 menu item，不需重構。

🔴 **CM-1077.3 加 menu item 時必須照 CM-1078 定下的規則**（見 spec §4 gate 表）：**這一列語意上沒有的動作才不放；存在但不能做的一律放 ＋ `disabled`**，整列唯讀的原因由選單頂端說明列（`#start` slot）講一次，個別缺 capability 才在該項 label 後綴。組裝點是 `DetectionProfileManageView.vue::buildActionMenuItems()`。

**子任務卡採乙案共 7 張（CM-1060~1066），與執行 session 一對一**——非第 2 棒規劃的 12 張。原 T-3.1（裝 CINC）未單獨建卡，安裝屬環境動作由決策者決定時機，程式碼與文件部分併入 CM-1065。

> 🔴 **CM-1072 原被劃在 FR-060 之外，2026-08-04 由決策者裁定納入本 arc**——它改的就是 FR-060.3 的抽取服務、程式碼已在裡面、DEV 已生效。

### Git（✅ 第 11 棒實查：BE／FE 均已 push，0 未推送）

BE／FE 兩 repo 於 `feature/e2e-env-build` 上均已 push，工作區乾淨（`git rev-list origin/..HEAD` 為 0）。第 10 棒記載的「BE 未 push 8 支／FE 未 push 3 支」已由決策者全數 push 完畢。

**工作區未 commit**：本檔與 `fr060-LOG.md`（第 11 棒收尾），收尾時一併 commit。

### 舊表 13 筆的原始形態（供上版比對，id 為舊表 id）

```
id 17-24  tool=8(gcb)     t=1    SYSTEM  TWGCB-01-007~014 八支      file  v1 cur/act=true
id 25-26  tool=6(inspec)  t=1    SYSTEM  dev-sec Linux/Windows 基準  url   v1 cur/act=true
id 32     tool=8(gcb)     t=102  TENANT  twgcb-02-003-google-chrome file  v1 cur=true  act=true
id 33     tool=4(openscap) t=102 TENANT  twgcb-02-003-google-chrome file  v1 cur=false act=false  ← 孤兒主檔來源
id 34     tool=8(gcb)     t=102  TENANT  TWGCB-Windows-2025          file  v1 cur/act=true
```

🔴 **id 32 與 33 同 tenant、同 name、同 file_id，但屬不同工具**（tool 8 vs tool 4）——這是搬遷 JOIN 必須三欄的實據，也是 STG 測不出來的形態。

### 環境座標

| 環境 | Host:Port | DB | 本案可否動 |
|---|---|---|---|
| DEV | `192.168.50.188:25432` | `guidant_ai_dev` | ✅ 唯一可套 migration 的環境 |
| STG | `192.168.50.188:25432` | `guidant_ai_stg` | ❌ 已服役、10 筆真資料（全 SYSTEM） |
| POC | `192.168.50.189:25432` | `guidant_ai_poc` | ❌ 等同 production；第 9 棒已**唯讀**盤點（12 筆，新表 0），未做任何寫入 |

帳號 `cmmgr`，密碼查 BE `.env` 的 `DB_SECRET`。

---

## §3 🔴 行為規範（每棒都適用）

### 環境紀律（最高優先）

**開發期間所有 migration 只套 DEV。** STG / POC 一律等決策者**當次明確指示**。

**拆表 migration 比加欄位危險一個量級**——它 rename 的是一張 STG 正在服役的表。若派工單或交接文件寫「三環境都套」，**該指令本身可能就是錯的，停下問決策者**。

### 通用紀律

- **不切 branch**，就在 `feature/e2e-env-build` 上做
- commit **顯式 `git add <檔名>`，禁用 `-am`**
- **不 push**（等 user 明示）
- 收尾類動作（spec / SUMMARY / memory / Notion 母卡回寫）**等 user 明確下令**
- 卡住兩次就停下回報，不要第三次重試
- 憑證禁入版控

### 建卡專用紀律

- Case No 是 auto_increment，**靠建立順序保證連號**——同一批要一次 `create-pages` 建完
- Properties：專案 `CM`／來源 `雷門`／作業人員 `小弟`／狀態 `Not started`／需求編號 `FR-060.x`
- **子任務卡內容要自足到新 session 不讀其他文件能開工**

---

## §4 下一步（**建議清單，不是執行授權**）

> 🎯 **全案已結案（2026-08-07）**。以下項目均已完成，保留原文供回顧；唯一剩餘的 follow-up 見「📋 結案後 follow-up」段。

### ✅ 已完成（原「立即可做」）

1. ~~四張卡待決策者手測~~ → **已手測通過**，CM-1082／1083／1084／1093 皆已收。手測重點（原文保留）：

   | 面向 | 怎麼測 |
   |---|---|
   | **硬刪（放行路徑）** | 拿一支**引用 0 次**的基準 → 刪除 → 確認框應寫出**具體的版本數與控制項數**，刪完三張表零殘留、列表不再出現 |
   | **改來源（放行路徑）** | 同一支未使用基準 → 「修正來源」換網址送出 → **版號不變**（不會多出 v2）、狀態回「待解析」、稍後自動變「解析完成」且控制項換成新來源的 |
   | **409 守門（擋下路徑）** | 拿已被使用過的基準 → 「修正來源」應灰、最後一項應是**停用**而非刪除，選單頂端說明**帶得出專案名稱** |
   | **分頁 20 筆** | 進頁預設顯示 20 筆，paginator 的每頁筆數選擇器正常顯示 `10/20/50` |

### ✅ 已完成（原「待排」）

2. ~~上版 STG／POC~~ → **已完成**。四支 migration 已套三環境（registry 對齊見 §2），DEV 用原檔 `fr060-1`，STG／POC 用環境自適應版本 `fr060-1b`。

### 📋 結案後 follow-up（唯一剩餘項目）

**POC 12 支公版控制項抽取尚未觸發** —— 追蹤卡 **CM-1104**（`Not started`，作業人員 `雷門`）。詳見 §2「三環境 DB 上版已完成」段與 CM-1104 卡內容。這不是阻塞項，是管理員自行操作的待辦（子表「開始解析」入口已就緒，逐支點擊 12 次即可，非開發工作）。

### ⚠️ 未確認狀態（收尾時未查證，不要假設已完成）

3. **CM-1095「`config` schema 更名為 `detection`」執行狀態未經第 11 棒確認**。第 10 棒裁示「隨 FR-060 上版一起做」，但第 11 棒協調者查到的三環境 registry（見 §2）只列出 `cm1047` / `fr060-1(b)` / `fr060-2` / `fr060-3` 五筆，**沒有看到對應 CM-1095 的 migration 記錄**——這個 schema 改名是否真的隨本次上版執行，**下一手需另外查證**（`\dn` 看 schema 是否已是 `detection`，或查 `schema_migrations` 有無 CM-1095 對應列）。

   背景（原文保留）：`config` schema 本身**在 STG／POC 都已服役**（FR-056 v1.11.0 帶進去的），兩環境各 5 張表（`detection_tools`／`tenant_detection_tool_configs`／`detection_tool_param_schemas`／`detection_tool_profiles`／`job_execution_detection_tools`）。成本盤點：跨 schema FK 零／DB 端一行 `ALTER SCHEMA`／程式碼 8 個 model 檔的 `__table_args__`／舊 migration 檔不改。

4. **CM-1079 專案軟刪除後無法檢視或復原**——🔴 決策者裁示「優先權很後面，現在不建議做，僅記錄」。
   ⚠️ **未來任何人要判「專案是否已刪除」，一律用 `project_extensions.deleted_at`，不可用 `status='archived'`。**

5. **CM-1094 DB schema 命名體系整理（母案）**——🔴 決策者裁示「之後會再做一次大整理」，**現階段只記錄**，子卡 CM-1095 例外（見上方第 3 項）。

### ✅ 收尾類（已完成，2026-08-07 決策者下令收尾）

6. **Notion 母卡 CM-1054 已收 `Done`**（第 9 棒內容全量重寫五段；第 11 棒追加「全案結案」段，含三環境 registry、push 狀態、POC follow-up 標記）。
   CM-1077 母卡狀態：本次未查證，交接時未特別要求動它（母派工單指示「CM-1077 已是 Done，不用動它」）。
7. **SPEC 與使用手冊**：`docs/specs/current/system-admin/detection-profile-manage.md`、`docs/user-manual/scan-profile-guide.md` 已隨早期 commit 完成，CM-1075／CM-1083／CM-1084 三批行為是否已補寫，第 11 棒未查證，留給下一手。

### 其他 follow-up（未變動，供未來查閱）

8. **三個欄位的「值」仍是英文**，決策者已知悉、**未決定是否翻譯**：檢查類型（`kernel_module` 等 14 種）／適用角色（`baseline`/`dc`/`dns`/`web`）／狀態（`pending-mapping`）。
9. **一支端點不在任何規格書裡**：`POST /api/1.0/detection-tool-profile-versions/<uid>/extraction`（手動重抽），FE 已在用。同路徑 `GET` 是抽取狀態輪詢。
10. **舊表未 DROP**（`detection_tool_profiles_deprecated_20260803`）——觀察期後另案，`design.md §8.3`。
11. **一般 succeeded 態仍無重抽入口**——CM-1075 只在「來源已過期」時給按鈕。若使用者想主動重抽一支正常的基準，UI 上沒有路徑（BE 端點是有的）。
12. **整批測試曾有 74 failed ＋ 9 個 collection error**（既有問題、非 FR-060 範圍），第 11 棒未重查現況。

**上述項目除 CM-1104（已開卡追蹤）外，其餘每一項都要 user 當次單獨發令才動。**

---

## §5 開工前決策者要拍板的三件事（2026-08-04 第 6 棒狀態）

1. ✅ **BE 主機安裝 CINC Auditor** — **四台皆已安裝**：開發機（本機 macOS，7.1.7 於 `/usr/local/bin/cinc-auditor`）＋ 三台部署機（188／189／190）。`docs/claude/host-dependencies.md` 第三項已補齊（授權紅線／路徑解析順序／缺了會怎樣／各主機狀況表）。
   ⏳ 剩下的是**各機 `CINC_AUDITOR_CMD` 路徑解析實測**，仍列在上版前條件內。
   ⚖️ **授權紅線：必須 CINC Auditor（Apache 2.0），絕不可換官方 InSpec 6+ 商業 binary 或 `inspec-core` gem**（均受 Chef EULA）。
   路徑解析走 `CINC_AUDITOR_CMD` 環境變數比照 LibreOffice 慣例，**不要硬編** `/usr/bin/cinc-auditor`。
2. ✅ **POC 環境唯讀確認** — **第 9 棒完成**（純 `SELECT`，未動任何狀態）。上版前的基準值：

   | 環境 | `config.detection_tool_profiles` | 筆數 | `config.detection_profile%` 新表數 |
   |---|---|---:|---:|
   | POC | 存在 | **12** | **0** |
   | STG | 存在 | **10** | **0** |

   → **四支 migration 只套 DEV 的結論不變**。上版時這兩組筆數就是「搬遷前應有幾筆」的對帳基準。
3. ✅ **`.1` 開工時機** — 已完成。拆表 migration 於 2026-08-03 套 DEV（`d7cf9abc`），DEV 驗收通過；**STG／POC 那張服役中的表未被動過**，改名風險尚未實際發生，上版時才會遇到。

---

## §6 座標（收尾日實查，勿照抄 design.md）

### FE（🔴 CM-1064 已大改，管理頁拆成一頁 ＋ 六支子元件）

```
src/views/detection-profile/
├── DetectionProfileManageView.vue        961 行（原 1,239 → CM-1064 拆成 688 → CM-1078/1080/1084 後 961）
│     ├── pageSize = ref(20)              L77   ← 分頁預設（`247b6f5`，原 10）
│     ├── buildActionMenuItems(row)             主檔層 menu 組裝點
│     └── :rows-per-page-options="[10,20,50]"   L711  ← 改預設分頁時新值必須在此清單內
├── DetectionProfileControlsView.vue      632 行（控制項詳細頁）
├── profileDisplay.js                      顯示名解析共用
└── components/
    ├── ProfileFormDialog.vue             433
    ├── ProfileVersionTable.vue           431（CM-1084 +185/-21，版本列也收成 menu）
    ├── ControlExtractionSummary.vue      279
    ├── ProfileSourceDialog.vue           207  ← CM-1084 新增（修正來源對話框）
    ├── ProfileNewVersionDialog.vue       175
    ├── ProfileDetailDialog.vue           170
    └── ProfileCopyDialog.vue             149

src/composables/useProfileUsage.js                        122 行  ← CM-1084 新增（問判定 ＋ 組訊息）
src/service/DetectionToolProfileService.js                243 行（CM-1084 +77，五支新方法）
src/components/detection-tools/DetectionConfigField.vue   269 行（下拉分組）
src/views/device/DeviceManage.vue
src/config/api/api.js                                     controls／extraction／taxonomies ＋ CM-1084 四支端點常數
src/config/locales/i18n/{zh-tw,en}/detection-profile-manage.json   CM-1084 各 +21 條
src/config/locales/i18n/{zh-tw,zh-cn,en}/error-code.json          CM-1083 各 +3 條（409009~409011）
```

**CM-1084 的三條 UX 規則**（實作已落地，改這頁前務必先讀）：
① 不可用的動作**灰掉 ＋ 說明原因**，訊息要帶「被用了幾次 ＋ 被哪些專案用 ＋ 該怎麼辦」；專案清單為空但 `in_use=true` 時要自然降級成不帶專案名（**不可印出空括號**）
② **「刪除」與「停用」不同時出現**——不是兩項都放然後刪除灰掉，而是只出現其中一項（未用過→刪除／用過→停用）
③ 刪除二次確認框要列出「將一併刪除 N 個版本、M 條控制項」的**具體數字**，不是只寫「確定刪除嗎」
④ 判定拿不到時**保守當作「不能刪」**而非放行（判定回錯方向會刪掉不該刪的）

⚠️ **要改詳細頁的展開列，先讀 LOG 第 4 棒「PrimeVue 3 展開列會整個銷毀重建」那條**——加 `:key` 或改具名函式 ref 都擋不住。

**另一處事實更正**：design.md §4.3 寫「orchestration 的**三處**呼叫點改指 `get_version_with_profile()`」，實查 `_profile_domain.get_by_uid` 只有**兩處**：

- `app/detection_tools/service/detection_orchestration_service.py:224`（`_load_profile()`，破口①）
- 同檔 `:794`（`_humanize_profile_param()`，破口②）

建 T-1.4 卡時寫兩處。

**再一處事實更正（2026-08-03，CM-1060 實查）**：design.md §8.1 寫「既有 **10 筆** soft-ref 引用零轉換仍可解析」，**這個描述不精確**。實際形態是 `job_execution_detection_tools.tool_params` 那 4 筆 4/4 全部指向 profile 的 uid，但 `agent_tasks.params` 那 6 筆裡**只有 2 筆（url 型）指向 profile 的 uid**，另外 4 筆（file 型）的 `_profile.uid` 存的是 `upload_files` 的 uid、本來就不指向 profile 表。

→ **寫迴歸測試時該斷言的是 6 筆**（`tool_params` 4 ＋ `agent_tasks.params` url 型 2），file 型 4 筆走 `upload_files` 路徑要分開驗。已寫進 CM-1062 卡頭。

### BE 檔案（🔴 舊的 `detection_tool_profile*` 命名已全數改為 `detection_profile*`）

```
infra/detection_tools/model/       detection_profile{,_version,_control,_taxonomy}.py
infra/detection_tools/mapper/      detection_profile{,_version,_control,_taxonomy}_mapper.py
infra/detection_tools/repository/  detection_profile{,_version,_control,_taxonomy}_repo_impl.py
                                   detection_profile_usage_query.py     124 行  ← CM-1082 三來源 UNION ALL（find_refs）
domain/detection_tools/entity/     detection_profile{,_version,_control,_taxonomy}_{entity,query_entity}.py
domain/detection_tools/repository/ detection_profile{,_version,_control,_taxonomy}.py
                                   detection_profile_version_repo_impl.py  214 行（CM-1083 +49）
domain/detection_tools/service/    detection_profile_domain_service.py（含 get_version_with_profile；CM-1083 +41）
                                   detection_profile_control_domain_service.py
                                   detection_profile_taxonomy_domain_service.py
app/detection_tools/service/       detection_profile_service.py         1,119 行（CM-1082 +98／CM-1083 +171）
                                   detection_profile_taxonomy_service.py
                                   detection_profile_extraction_service.py
api/detection_tools/routes/        detection_profile_route.py            582 行（15 個 Resource）
                                   detection_profile_taxonomy_route.py
api/detection_tools/serializers/   detection_profile.py／detection_profile_taxonomy.py
common/util/profile_extractor/     __init__.py 71／base.py 95（註冊表＋抽象基底）／inspec.py 439
common/util/safe_http_fetch.py     473 行  ← CM-1072 代下載（SSRF 五道防護）＋ CM-1075 probe_url_validators()
common/util/detection_profile_ref.py    76 行  ← §4.4 確認零影響，未動
common/code/detection_tools_error_code.py    FR-060 共 13 條（CM-1083 +3：409009/409010/409011）
test/  test_detection_profile_{version_alloc,dispatch,taxonomy,archive}.py
       test_detection_profile_usage.py                                  204 行 11 條  ← CM-1082
       test_detection_profile_edit_delete.py                            367 行 19 條  ← CM-1083（+CM-1093 補 autouse patch logger）

scripts/sql/2026-08-03-fr060-1-detection-profile-split.sql              662 行
scripts/sql/2026-08-03-fr060-2-detection-profile-taxonomies.sql         272 行
scripts/sql/2026-08-03-fr060-3-param-schema-extraction-declaration.sql   91 行
scripts/sql/2026-08-04-fr060-3-dpv-source-validators.sql                 79 行  ← CM-1075（第四支）
```

**CM-1082 新增的兩支唯讀端點**（註冊在 `/<string:uid>` 之前，才不會被當成 uid 吃掉；沿用既有 `referencing-*` 命名慣例）：

```
GET /api/1.0/detection-tool-profiles/<uid>/referencing-usage           主檔層
GET /api/1.0/detection-tool-profile-versions/<uid>/referencing-usage   版本層
回傳 {in_use, projects: [{uid, name}], version_refs}
```

app service 兩支方法：`get_profile_usage()` / `get_version_usage()`（`detection_profile_service.py`，共用 `_build_usage()`）；
domain service 新增 `list_all_versions()`（不分頁，判定專用）。

🔴 `_build_usage()` 有一條 **fail-open fallback**（`if self._usage_query is None: return {"in_use": False, ...}`），會讓「守門有效」與「守門根本沒被呼叫」在測試裡長得一模一樣。DI 已 wiring，真實環境不會走到；但**寫新的守門測試時必須斷言 `in_use=true` 那條路徑確實走到**，不可 mock 掉 `usage_query`。

**CM-1083 新增的三支寫入端點**：

```
PATCH  /api/1.0/detection-tool-profile-versions/<uid>/source   改來源（版號不動、重抽控制項）
DELETE /api/1.0/detection-tool-profile-versions/<uid>          刪版本（非 current 且未使用過）
DELETE /api/1.0/detection-tool-profiles/<uid>                  刪主檔（硬刪，CASCADE 帶走版本與控制項）
```

三個 error code（`common/code/detection_tools_error_code.py`）：

| 常數 | 代碼 | 用途 |
|---|---|---|
| `DETECTION_PROFILE_VERSION_IN_USE` | `DETECTION_TOOLS_409009` | 版本已被使用過，不可改來源／刪除 |
| `DETECTION_PROFILE_IN_USE` | `DETECTION_TOOLS_409010` | 主檔底下有版本被使用過，不可刪除 |
| `DETECTION_PROFILE_CURRENT_VERSION_UNDELETABLE` | `DETECTION_TOOLS_409011` | 不可單獨刪除當前版本 |

🔴 **沒有任何「強制執行」參數或旗標可以繞過 409**——已被使用過的版本一律凍結，這是決策者裁定的稽核可信度問題，不是 UX 取捨（不提供「確定要更新嗎」的硬改選項）。
**無新 migration**：CASCADE 在 FR-060.1 拆表時就已設好。

### CM-1077 三張子卡的派工內容（第 7 棒產出 `c3b10b32`，第 9 棒上 Notion）

```
docs/features/FR-060-2608-detection-profile-content-management/cards/
├── CM-1077-1-usage-judgement.md    202 行  → Notion CM-1082（✅ 已實作 `17e977eb`）
├── CM-1077-2-edit-and-delete.md    235 行  → Notion CM-1083（✅ 已實作 `dddfe7cf`；第 9 棒 185→235 補「前置卡 .1 提供的介面」節）
└── CM-1077-3-fe-actions.md         193 行  → Notion CM-1084（✅ 已實作 FE `88f2e13`）
```

三份都自足到「新 session 不讀其他文件就能開工」，含 DEV 實測值可直接當驗收斷言。
`.2` 卡經第 9 棒擴充後，決策者拍板的四節（規則／邊界／作業紀律／改完要能重抽）**未動**，只追加前置介面與第 13 條完成定義。

---

## §7 三個必讀陷阱（來自母卡，實作時逐條對照）

### ① 人工待判項判準必須是 `impact == 0.0`

用 `tags.check_type == 'pending'` 會**漏 6 條**（`twgcb_01_014_0079`~`0084` 標 `check_type: 'service'` 但實為 impact 0.0 且只有 skip）。`impact` 是 InSpec 原生語意，外來 profile 也一定有。**不要依賴任何自訂 tag 做這個判定。**

⚠️ 但表欄位名**不叫 `impact`**（D19）——用 `severity_raw` ＋ `severity_norm`，判準寫在各格式的 extractor 裡。

### ② 派工鏈兩處破口（原以為零影響，實 grep 推翻）

- **破口①** `_load_profile()` 的 `is_active` 守門——拆表後在主檔。從檔 entity 有屬性但預設 None 則 `None is False` 為假 → **守門靜默失效、已停用的 profile 照樣派得出去**（無聲授權漏洞，比 500 更糟）
- **破口②** `_humanize_profile_param()` 的 `getattr(profile,"name",None)` 有 default 不報錯，只讓通知信「掃描設定」**靜默退化成 `profile:<uuid>`，且目前無任何測試會抓到**
- 解法：domain service 新增 `get_version_with_profile(uid)` 回主＋從合成 entity

### ③ 「STG 跑得順」不能當驗收依據

STG 的 10 筆**全 SYSTEM、無 TENANT**。搬遷 SQL 在 STG **測不到租戶路徑、測不到 32/33 三欄 JOIN 陷阱、測不到停用列造成的孤兒主檔**。**驗收必須在 DEV 做。**

---

## §8 冷接自檢題（答不出來代表沒讀懂，回頭讀）

1. 為什麼一個「瀏覽 profile 內容」的需求要順帶拆資料表？（提示：唯一索引拿什麼當身分）
2. 搬遷時舊表那 13 個 uid 要給主檔還是從檔？為什麼？
3. 從檔為什麼一定要自己掛 RLS，不能靠主檔的 FK 繼承？
4. 為什麼「STG 跑得順」不能當驗收依據？
5. 本案至今寫過幾行 production code？

> 答案分散在 §1、§2、§7，以及 CM-1054 母卡的 D3／D4 段。
