# FR-058 規劃完成、實作已派出 — 換 session 交接（2026-07-30）

| 項目 | 內容 |
|------|------|
| 緣由 | FR-058（檢測工具擴充，一次接入 ZAP / InSpec·CINC / GCB / Nmap 四個工具）的**規劃階段已 100% 完成並 commit + push**。走完 big-feature-workflow 全部 5 個 step（探脈 → 討論稿 → design.md → Notion 開卡 → 交接 prompt）。第一批的實作已於 2026-07-30 派給**兩個外部 session 並行進行中**。 |
| Branch | `feature/FR-058`（BE），HEAD `a9e9bbdb`，**已 push、與 origin 同步**（`git rev-list --left-right --count origin/feature/FR-058...HEAD` 回 `0 0`） |
| **本棒角色** | **協調與驗收，不是實作。** 等兩個實作 session 回報 → 執行第一批端到端驗收 → 產第二批交接 prompt → 更新討論稿。**不要自己下去寫 connector 或改 BE**，那是已派出去的工作。 |
| 接手前必讀 | 本文 §0 讀序（含硬 gate 與冷接自檢） |
| 預估時間 | 若兩 session 都已回報要跑驗收：60–120 分鐘（重建 agent image + 部署三台 + 等心跳是主要成本）；若只是理解現況等待：15–30 分鐘 |

---

## 🧭 原始需求 / WHY

### 決策者的原始需求（一字不改）

> 「目前的檢測工具管理 `/plugin/tool-plugin-manage` 還需要增加下面工具——OpenSCAP for Windows 解決方案 / Nmap / GCB / SonarQube。請幫我分析跟規劃，開成新的需求放到 Notion。」

### 需求的演變過程（這是理解本案的關鍵，不讀會誤解範圍）

原始四項工具，經過調研與決策者裁示後**變成另外四項**，過程如下：

| 階段 | 發生什麼 | 結果 |
|------|----------|------|
| 起點 | 決策者列出四項：OpenSCAP for Windows / Nmap / GCB / SonarQube | 四項 |
| 中途追加 | 決策者追加 **ZAP**（第五個工具） | 五項 |
| 裁示① | **SonarQube 本期不做**（決策者裁示，日後另議） | 剩四項 |
| 調研發現 | 「OpenSCAP for Windows」**證實不可行**——OpenSCAP 官方已放棄 Windows 支援（`docs/windows.md` 明文標示 no longer usable，引擎層無 WinRM transport、無 Windows OVAL probe）。改由 **InSpec / CINC Auditor** 承擔此需求 | 工具換人，需求不變 |
| 裁示② | **ZAP 優先上線，其餘分批做**（取代原本的三線並行規劃） | 分批出貨 |

**最終四工具**：ZAP（DAST 網頁動態掃描）/ InSpec·CINC Auditor（Windows + Linux 組態檢測）/ GCB（政府組態基準 TWGCB，引擎複用 CINC）/ Nmap（網路埠掃描）。

### 大圖定位（本案在哪一棒）

```
FR-056 檢測工具整合平台（v1.11.0 上線）
   └ tool-agnostic 地基：後端對具體工具零知識、前端純 schema 驅動、加工具原則上只要一條 SQL seed
        ↓
FR-057 OpenSCAP SSH connector（v1.12.0 上線，2026-07-30 全案結案）
   └ 第一個真實工具落地：補齊 SSH 連線型態、setup_guide 欄位、多檔證據回收、兩條「靜默假成功」防護通道
        ↓
FR-058（本案）一次規劃四個工具 + 補平台唯一的結構性缺口
```

### 本案要解決的兩件事

**① 接入四個工具**——涵蓋三種完全不同的連線型態：

| 形態 | 已上線 | 本案新增 |
|------|--------|----------|
| ① 連遠端服務 API | OpenVAS | **ZAP**（FR-058.1） |
| ② 遠端登入目標主機 | OpenSCAP（SSH / Linux） | **InSpec·CINC**（SSH + WinRM，FR-058.2）、**GCB**（複用 .2 引擎，FR-058.3） |
| ③ agent 本機 CLI | 尚無（`connection_type='CLI'` 已定義但沒人用） | **Nmap**（FR-058.4，此型態首個實際使用者） |

**② 補平台唯一的結構性缺口：任務層敏感參數**（FR-058.0）——現有設計把憑證與參數分兩處放，而**只有憑證那一條有加密**：

| 層級 | 存放位置 | 加密 | 適合放什麼 |
|------|----------|------|-----------|
| 租戶層 | `tenant_detection_tool_configs` | Fernet 加密 | ZAP 服務位址與 API key——全租戶共用、設定一次不動 |
| 任務層 | `job_execution_detection_tools.tool_params` | **明文** | 掃描目標、profile 這類非敏感參數 |
| 任務層敏感 | **目前不存在** | — | 「這次掃 A 網站用 A 的帳密，下次掃 B 網站用 B 的帳密」正好落在這一格 |

若直接把被掃網站的登入帳密塞進 `tool_params`，會發生兩件事：① 帳密明文躺在資料庫；② FE 執行紀錄視窗會顯示任務參數——BE 現有的 secret 剝除機制是針對**租戶層**設定的 secret 定義寫的，任務層參數沒有這套保護，**等於帳密直接顯示在畫面上**。這不是 ZAP 專屬問題（Nmap 做認證掃描、GCB 每台主機帳密不同都會撞同一格），因此當成**平台級能力**補上，並且是 ZAP 的硬前置。

敏感參數的生命週期：**資料庫為密文、僅在派工傳輸與 agent 執行期間為明文、agent 用完即丟不落地、FE 一律剝除**。

### 本棒在大圖的位置

規劃已完成並 push，實作已派出。**本棒是協調者與驗收者**——等實作回報、跑端到端驗收、產第二批交接 prompt、更新討論稿。不是實作者。

---

## §0 接手讀序

### 0.1 🔒 先懂需求 gate（全讀，讀完才能碰任何東西）

1. **本文「🧭 原始需求 / WHY」全段**——特別是「需求的演變過程」那張表，不讀會搞不清楚為何最終工具清單跟決策者原話對不上
2. `/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-058-2607-detection-tools-expansion/design.md` **§1（需求背景與目標）+ §2（D1–D11 決策定案）全讀**——D1–D11 是本案所有取捨的依據，實作 session 若回報阻礙要決策，判斷原則就在這裡

### 0.2 現況與待辦

3. 本文 §1（當前狀態盤點）→ §2（前次教訓）→ §3（三種情境與處理方式）→ §4（開工順位）
4. Notion 母案 CM-957：`https://app.notion.com/p/3ad346da4cd081859d2ed8b21ca2697c`——**末段「🚀 分批出貨計畫」是本棒最重要的一段**，含批次安排、session 切分、測試紀律、第一批驗收清單

### 0.3 需要細節才開（不要一開始就全讀，會燒 context）

5. `design.md` §3（現況接入點盤點，含派工鏈路四階段與技術債表）/ §4（詳細設計，四個工具各自的 param_schema 與 connector 要點）/ §5（拆分，1 橫向項 + 5 子需求 × 20 子任務）/ §6（端到端驗收九項）
6. `docs/features/FR-058-2607-detection-tools-expansion/discussion.html`（951 行）——含流程圖與官方資源盤點。**注意：內容停在「四工具一次接入、三線並行」版本，尚未反映分批出貨安排**，讀它時要知道這一點；更新它是本棒的待辦項（見 §4 第 5 步）
7. 前作交接文件（要理解 agent 部署踩過的坑時才開）：`docs/features/FR-057-2607-openscap-ssh-connector/handoff/2026-07-29-fr057-arc-complete-handoff.md` §2.2（跨平台 Docker build 陷阱）與 §2.4（版號第六處同步點）

### 0.4 ✅ 冷接自檢（五問，答不出來回去讀 §0.1，不要碰任何東西）

1. **本案要解決什麼問題？**（提示：不只是「多加四個工具」，還有一個平台級的結構性缺口）
2. **為何 SonarQube 被移出本案？原本編號 D10 現在怎麼處理？**
3. **GCB 的驗收條件是什麼？為何刻意不是「涵蓋多少規則」？**
4. **為何 `_TOOL_ID_TO_CODE` 解耦（D11）要排在最前面？在改成分批出貨後，這個理由換成什麼了？**
5. **本棒的角色是什麼？哪些事情不該做？**

> 第 4 題是陷阱題：原本的理由（三條線搶同一個 factory 檔）在改成序列執行後**已不成立**，新理由寫在 Notion 母案「分批出貨計畫」段。答錯代表沒讀到那一段。

---

## §1 當前狀態盤點

> 本節取代範本的「§1 症狀」。本案不是 bug，沒有症狀，只有狀態。

### 1.1 規劃產出物（已完成、已 commit、已 push）

| 產出 | 位置 | 狀態 |
|------|------|------|
| 討論稿 | `/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-058-2607-detection-tools-expansion/discussion.html`（951 行） | 已 commit。Artifact 線上版：`https://claude.ai/code/artifact/73631a39-e710-42c0-a0d4-34a1fe70b122`。⚠️ **內容停在「四工具一次接入、三線並行」版本，尚未反映分批出貨安排——這是本棒的明確待辦項**（決策者說「等派發出去後再用 subagent 產」） |
| 設計文件 | `/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-058-2607-detection-tools-expansion/design.md`（441 行） | 已 commit，內容為最新 |
| FR 登記 | `/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/README.md` 第 113 行 | 已 commit，**已含分批出貨敘述**（此處是最新的，與 discussion.html 不同步） |
| Notion case 樹 | CM-957〜983（27 張卡，零斷號） | 已建。母案末段含分批出貨計畫 |
| CM-953 | `https://app.notion.com/p/3ac346da4cd0819390d2c5b85c754bf8` | **已結案（Done）**，指向 CM-971（FR-058.2 吸收之） |

### 1.2 Notion 卡片全表（本棒追進度用）

母案：**CM-957** — `https://app.notion.com/p/3ad346da4cd081859d2ed8b21ca2697c`

| 子需求卡 | 標題 | Notion URL | 子任務卡 |
|---------|------|-----------|---------|
| **CM-958** | FR-058.X `_TOOL_ID_TO_CODE` 解耦（D11） | `https://app.notion.com/p/3ad346da4cd08164b0f9f05776a88562` | CM-959（T-X.1 BE）、CM-960（T-X.2 agent） |
| **CM-961** | FR-058.0 任務層敏感參數 | `https://app.notion.com/p/3ad346da4cd0810fae51e7f192d5ad85` | CM-962（T-0.1）、CM-963（T-0.2）、CM-964（T-0.3）、CM-965（T-0.4） |
| **CM-966** | FR-058.1 ZAP | `https://app.notion.com/p/3ad346da4cd081f48410d617dee61ba9` | CM-967（T-1.1）、CM-968（T-1.2）、CM-969（T-1.3）、CM-970（T-1.4） |
| **CM-971** | FR-058.2 InSpec / CINC Auditor | `https://app.notion.com/p/3ad346da4cd0811faa85fd15a38df059` | CM-972（T-2.1）、CM-973（T-2.2）、CM-974（T-2.3）、CM-975（T-2.4 結案 CM-953） |
| **CM-976** | FR-058.3 GCB | `https://app.notion.com/p/3ad346da4cd08120b9f1cf9b1f976847` | CM-977（T-3.1）、CM-978（T-3.2）、CM-979（T-3.3） |
| **CM-980** | FR-058.4 Nmap | `https://app.notion.com/p/3ad346da4cd081159daae65d4b08c80d` | CM-981（T-4.1）、CM-982（T-4.2）、CM-983（T-4.3） |

### 1.3 實作派工狀態（2026-07-30 已派出，進行中）

**分批出貨安排**（決策者 2026-07-30 拍板，取代原三線並行）：

| 批次 | 內容 | 卡號範圍 | 端到端測試 |
|------|------|---------|-----------|
| **第一批（ZAP 上線）** | ID 解耦 + 任務層敏感參數 + ZAP | CM-958〜970 | **批次結束測一次** |
| 第二批 | InSpec / CINC → GCB | CM-971〜979 | 批次結束測一次 |
| 第三批 | Nmap | CM-980〜983 | 併第二批一起測 |

**第一批的兩個實作 session（已派出、並行中）**：

| Session | 負責範圍 | 卡號 | Repo |
|---------|---------|------|------|
| **Session 1（平台側）** | T-X.1 派工 payload 加欄位 + FR-058.0 全部四個子任務 | CM-959、CM-962〜965 | BE + FE |
| **Session 2（agent 側）** | T-X.2 factory 改寫 + ZAP 四個子任務 | CM-960、CM-967〜970 | evidence-agent |

**兩者為何能並行不互卡**：
- Session 2 的 T-X.2 採「**有 code 用 code、無 code 退回 id**」的 fallback 寫法，不會被 Session 1 的 T-X.1 擋住
- **欄位名稱已定死為 `detection_tool_code`**（design.md §4.X 明文），兩邊照此寫即可，**不需要即時協調**
- Session 2 只有最後的 T-1.4（登入後掃描）真正需要等 Session 1 的解密接線（CM-963），前面的 seed、connector 骨架、被動／主動掃描都不必等

### 1.4 git 現況（本棒接手時要重新確認）

| 項目 | 值 |
|------|-----|
| BE branch | `feature/FR-058` |
| BE HEAD | `a9e9bbdb` docs(fr058): 檢測工具擴充四工具接入——討論稿 + design.md + FR 登記 |
| origin 同步 | 已 push，`0 0`（無 ahead / behind） |
| working tree | 有一批**與 FR-058 無關的既有 untracked 檔案**（v1.9-bugfix handoff、v1.8.0 交付文件 docx、ddd-layer-audit 資料夾、`security-audit-report-2026-07-17.html`、`variants.png` 等）+ 一個 modified 的 `CLAUDE.md`。**那些是別的 arc 留下的，不是本 arc 產生，不要清理、不歸本棒管** |

⚠️ **實作 session 的 commits 會陸續進來**，本棒接手時務必先跑 §6 pre-flight 看實際 `git log`，不要拿本文的 HEAD 當現況。

---

## §2 前次教訓（規劃 session 踩過或刻意避開的）

### 2.1 subagent 一次寫大檔會中途斷線

本 session **兩次**遇到同一個現象：subagent 讀完資料、正要 Write 大檔（討論稿 / design.md）時，API 在 stream 中途 stall，agent 就此陣亡。

**解法**：用 `SendMessage` 恢復該 agent 並要求**分段落地**——先 Write 骨架，再多次 Edit append。

**對本棒的意義**：下次派文件產出類 subagent（例如 §4 第 5 步要更新 discussion.html），**直接在派工單就寫明「分段寫檔：先 Write 骨架再多次 Edit append，不要一次寫完整份」**，不要等它斷了才補救。

### 2.2 subagent 一律用 1M context model

呼叫 Agent tool 時**不要帶 `model` 參數**（會繼承主 session 的 1M context model）。曾因省 context 派小模型，導致 context 爆炸（autocompact thrashing）中途陣亡要重拆重派，浪費的 token 與時間比一開始就用大 context model 更多。

純機械小任務（一條 grep、一個檔的小改）才可用小 model，猶豫時預設選 1M。

### 2.3 不信 subagent 自報完成，一律實際抽查

本 session 每次都實際開檔 / fetch Notion 抽查，**並且抓到了問題**：

- README 那一列數字寫錯——寫「16 子任務」，實際是 **20**
- README 排程敘述停在「三線並行」，實際已改**分批出貨**

兩處都是抽查才抓到的。**subagent 自報「已完成」不等於內容正確**。

### 2.4 mermaid init 字串是三次踩坑後的解

每張 mermaid 圖**首行必須內嵌 `%%{init:...}%%`，單獨一行、不可斷行**。原因：Artifact 雲端不執行頁面 script，靠頁面 JS 設定 mermaid theme 的做法在雲端完全失效，只有內嵌 init 字串有用。

discussion.html 內的圖都已照此處理，本棒更新它時**不要動那些 init 行**。

### 2.5 決策者會推翻保守建議，先理解論證再回應

D7（GCB 範圍）原本我建議「先做 Linux GCB 子集」，理由是防守 content 工程的風險。決策者從**產品面**指出應該雙邊一起做——客戶是混合環境，只交付一半等於交付不完整。

最終定案是把兩個層次拆開：**引擎層雙邊一次到位（由 CINC connector 承擔）／content 層本案不做（只留一份 Windows 最小 Demo profile 證明鏈路）**。這比原本任一方案都好。

**教訓**：遇到決策者提出不同角度，**先理解他的論證再回應，不要固守原建議**。他看的是產品與交付，我看的是技術風險，兩者疊起來才是完整判斷。

---

## §3 下一棒的三種可能情境與各自的處理方式

> 本節取代範本的「§3 Root cause 候選」。本案沒有 bug 要追根因，只有情境要判斷。
>
> **判斷起點**：看 Notion 卡狀態是否已改「修正待驗證」（實作 session 完成子任務後會回寫）。

### 情境 A：兩個實作 session 都回報完成 → 執行第一批端到端驗收

**⚠️ 前置動作很貴，先讀 §7 確認齊備再動手。**

#### A.1 前置環境準備（順序不可顛倒）

```bash
# ① 三環境套 migration（DEV → STG → POC，一律用 cmmgr 帳號，密碼請查 .env 或部署文件）
# DEV
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
  --single-transaction -v ON_ERROR_STOP=1 \
  -f /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/<實作 session 產出的 fr058 seed 檔>.sql

# STG（同一台 server，不同 DB）
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_stg \
  --single-transaction -v ON_ERROR_STOP=1 -f <同上檔案>

# POC（不同 server）
psql -h 192.168.50.189 -p 25432 -U cmmgr -d guidant_ai_poc \
  --single-transaction -v ON_ERROR_STOP=1 -f <同上檔案>

# ② 三環境 seed 結果對照（確認 ZAP 進去了、id 一致）
for db in "192.168.50.188 guidant_ai_dev" "192.168.50.188 guidant_ai_stg" "192.168.50.189 guidant_ai_poc"; do
  set -- $db
  echo "=== $2 @ $1 ==="
  psql -h $1 -p 25432 -U cmmgr -d $2 \
    -c "SELECT id, code, connection_type, status FROM config.detection_tools ORDER BY id"
done

# ③ 重建 agent image（⚠️ 開發機是 Mac arm64、目標主機是 Linux amd64，必須指定 --platform）
cd ~/Projects/Billows/Audit-Manager/evidence-agent
DOCKER_BUILDKIT=1 docker build --platform linux/amd64 \
  --secret id=nexus_auth,src=.secrets/nexus.env \
  -t guidant-ai-agent:<新版號> --load .
docker inspect guidant-ai-agent:<新版號> --format '{{.Os}}/{{.Architecture}}'
# 必須回 linux/amd64 才可出貨。回 arm64 就是踩到 FR-057 §2.2 那個坑

# ④ 部署三台 agent（121 POC / 122 STG / 123 DEV）
for h in 192.168.50.121 192.168.50.122 192.168.50.123; do
  docker save guidant-ai-agent:<新版號> | ssh jedi@$h docker load
  # 改該機 /opt/evidence-agent/deploy/.env 的 AGENT_IMAGE
  # 改該機 /opt/evidence-agent/deploy/docker-compose.yml 的 AGENT_VERSION（第六處版號同步點，不會隨 git pull 更新）
  ssh jedi@$h "cd /opt/evidence-agent/deploy && docker compose up -d guidant-ai-agent"
done

# ⑤ 確認三台心跳健康
for h in 192.168.50.121 192.168.50.122 192.168.50.123; do
  echo "=== $h ==="
  ssh jedi@$h "docker ps --filter name=guidant-ai-agent --format '{{.Names}}\t{{.Image}}\t{{.Status}}'"
  ssh jedi@$h "docker logs deploy-guidant-ai-agent-1 --tail 5"
done
# 預期：Up + log 有 heartbeat 200 OK

# ⑥ BE 重啟（改 service code 後必做，必先 kill -9）
cd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be
ps aux | grep -i "main_app.py" | grep -v grep | awk '{print $2}' | xargs -r kill -9
nohup python main_app.py > /dev/null 2>&1 &
sleep 5 && lsof -i :8000 | head -3

# ⑦ FE 部署（由決策者或 FE 部署流程處理）
```

#### A.2 第一批八項驗收清單

| # | 項目 | 驗收條件 |
|---|------|---------|
| 1 | **敏感參數三處剝除** | 任務參數含敏感欄位時：DB 內為密文、agent 收到明文、FE 執行紀錄與 `system_logs` 皆**不出現該值**（整個 key 不出現，不是遮罩成 `****`） |
| 2 | **ZAP 設定測試連線** | 填 ZAP 服務位址與 API key → 測試連線通過；連不到 / API key 錯 / 正常三情境各回清楚訊息 |
| 3 | **ZAP 被動掃描** | 選 ZAP、填 `target_url`、維持預設被動 → 開始執行 → PDF 進證據池、summary 有 alert 統計 |
| 4 | **ZAP 登入後掃描** | 選「登入後主動」並填測試帳密 → 報告內容顯示已進入登入後頁面；**帳密不出現在 agent log** |
| 5 | **預設安全（D2）** | 任務未主動選擇時 `scan_mode` 為**被動**；選主動類時 FE 顯示攻擊流量警語 |
| 6 | **取消與逾時** | 執行中可取消（`cancel_event` 即時中止），且有 timeout 上限不會無限等待 |
| 7 | **D11 解耦生效** | agent 依派工夾帶的 `detection_tool_code` 取 connector；fallback 路徑有 warning log |
| 8 | **既有工具迴歸** | OpenVAS 與 OpenSCAP 全鏈不受影響（factory 取 connector / 憑證檢查 / 多檔回收 / FE 動態渲染） |

#### A.3 驗收紀律（決策者明確要求）

- **任務發佈後最長等 300 秒才會真的開跑**——這是心跳週期造成的**正常行為，不是故障**。看到「按了沒反應」不要當 bug 追
- **測試一律 headless**
- **主 session 不自己跑測試**——跑的動作派 subagent（用 1M model），本棒只派工 + 輕量抽查
- **不要每個子任務各自跑一次端到端**——一輪至少 10 分鐘起跳，重建 image + 部署三台 + 等心跳才是真成本

### 情境 B：只有一個 session 回報，或回報部分完成 → 等齊再驗收

**不要急著驗收。** 理由很直接：重建 agent image + 部署三台 + 等 300 秒心跳是整個流程**最貴的動作**，做一次要一小時起跳。只驗一半等於這筆成本要付兩次。

處理方式：
1. 確認已回報那一側的 Notion 卡狀態與 commit 內容（`git log` 看實際進來什麼）
2. 可以做的**便宜檢查**（不觸發昂貴前置）：import／語法檢查、對假 fixture 跑解析邏輯的單元測試、SQL 套 DEV 後 `SELECT` 確認 seed 進去
3. 等另一側回報後，再一次跑情境 A 的完整流程

### 情境 C：實作 session 回報遇到阻礙需要決策 → 本棒可直接拍板

本棒是決策角色，遇到下列常見阻礙可直接判斷，**判斷原則見 design.md §2 的 D1–D11 定案理由**：

| 可能阻礙 | 判斷依據 |
|---------|---------|
| **ZAP 2.17.0 的警報去重語意導致 summary 數字與預期不同** | 2.17.0 加入警報去重與 Systemic alert 支援（Issue 9067 / 9097），alert 數量本來就會比舊版少。**統計解析要對齊新語意，不能沿用舊版計數假設**。另外解析器**不要依賴 `generatedString`**（官方已預告移除），改用新的 ISO 8601 `created` 欄位 |
| **CINC Auditor 打包進 agent image 的方式** | D5 定案必須用 **CINC Auditor**（Apache 2.0 自由重建版），**不可用官方 InSpec 6+ 商業 binary**（需 Chef EULA + license key）。這是授權紅線，不可為了方便妥協。打包方式本身可自由選（apt / 直接下載 binary），但授權來源不可換 |
| **WinRM 憑證的存放層級** | 預設走**租戶層** `tenant_detection_tool_configs` 加密鏈（與 SSH 同模型）。**只有在「每台主機不同帳密」的情境**才走 FR-058.0 的任務層敏感參數。不要一開始就往任務層推 |
| **content 要不要打包進 agent image** | D7 明確約束：**content 不可硬編進 connector 或 agent image，必須可從外部載入**。理由是 content 會持續變動，打包進去等於每次更新都要重新發版 + 更新所有客戶站點 agent |

**遇到 D1–D11 沒涵蓋的新決策**：停下來問決策者，不要自己開新方向。

---

## §4 開工順位

1. **跑 §6 pre-flight**（確認 branch / working tree / 三 repo 現況 / 服務健康）
2. **確認兩個實作 session 的回報狀態**——看 Notion 卡（CM-959、CM-962〜965、CM-960、CM-967〜970）狀態是否已改「修正待驗證」，並 `git log` 對照三 repo 實際進來的 commits
3. **依 §3 三情境對應處理**（A：跑驗收／B：等齊／C：拍板）
4. **第一批驗收通過後 → 產第二批（InSpec·CINC → GCB）交接 prompt**——Notion 母案末段明寫「交接 prompt 待第一批驗收完成後才產出，屆時可能已有新的實作發現需要帶進去，先寫容易過期」
5. **更新 `discussion.html` 反映分批出貨安排**——⚠️ **這是明確的待辦項**，決策者說「等派發出去後再用 subagent 產」。現在已派出，可以做了。派 subagent 時記得帶 §2.1 的分段寫檔要求，以及「不要動 mermaid 的 `%%{init:...}%%` 行」

> **收尾類動作（SPEC / SUMMARY / memory / Notion 母案收尾）一律等決策者明確下令**，plan approve 不等於自動執行收尾。

---

## §5 該讀的檔案 / 預期改動範圍

### 5.1 BE（`/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/`）

| 檔案 | 為何要讀 |
|------|---------|
| `docs/features/FR-058-2607-detection-tools-expansion/design.md` | 本案唯一權威設計文件，D1–D11 + 詳細設計 + 拆分 + 驗收全在裡面 |
| `docs/features/FR-058-2607-detection-tools-expansion/discussion.html` | 含流程圖與官方資源盤點；**本棒要更新它** |
| `app/detection_tools/service/detection_orchestration_service.py` | 派工編排 `start_execution()`；Session 1 的改動落點之一 |
| `app/remote_agent/service/agent_enrollment_service.py` | 心跳派工 `_collect_pending_tasks()` / `_resolve_tool_credentials()`；T-X.1 與 T-0.2 都動這裡 |
| `scripts/sql/2026-07-28-fr057-1-openscap-seed.sql` | **加工具的 SQL 範本（最重要）**——含 `setup_guide`、`config_field_schema`、`param_schema`、`schema_migrations` 收尾。驗收時對照實作 session 產出的 seed 檔是否照此寫 |

### 5.2 FE（`~/Projects/Billows/Audit-Manager/compliance-manager-fe/`）

| 檔案 | 為何要讀 |
|------|---------|
| `src/components/detection-tools/DetectionConfigField.vue` | schema 驅動的七種欄位型態 + `condition` 條件顯示；ZAP 登入欄位用現成的 `password` + `condition`，**不需新元件** |
| `src/components/grc/JobExecutionDrawer.vue` | 執行抽屜。⚠️ 注意 `_PRIMARY_PARAM_KEYS` 硬編 `['profile']`、`summaryLabel()` 查 `lang.my_tasks.summary_${key}` 找不到會印原始 key——T-0.4 要驗證 secret key 被剝除後不會顯示異常空白或原始 key |
| `src/views/plugin/ToolPluginManage.vue` | 工具管理頁（`/plugin/tool-plugin-manage`，決策者原始需求指的就是這頁） |

### 5.3 evidence-agent（`~/Projects/Billows/Audit-Manager/evidence-agent/`）

| 檔案 | 為何要讀 |
|------|---------|
| `core/task_executor_connectors/__init__.py` | factory，D11 要改的檔（移除 `_TOOL_ID_TO_CODE` 硬編表） |
| `core/task_executor_connectors/base.py` | `DetectionConnector` ABC、`ScanResult`、`ScanCancelledError`、`cancel_event` / `host_failures` / `content_mismatch_hosts` 注入點 |
| `core/task_executor_connectors/openvas.py` | **ZAP 的實作範本**（API / daemon 型）——連線設定解析、`probe()`、輪詢、雙格式取報告、`cancel_event`、timeout、summary 解析容錯 |
| `core/task_executor_connectors/openscap.py` | InSpec 的實作範本（SSH / 多目標多檔型）——多目標處理與 `host_failures` 用法 |
| `core/task_executor_connectors/zap.py` | **Session 2 新增**，驗收時要看 |
| `core/task_executor.py` | `dispatch_tasks()`，每筆任務一條 daemon thread |

---

## §6 Pre-flight Command（必跑）

```bash
# ① 三 repo branch / working tree / HEAD 對照
for repo in compliance-manager-be compliance-manager-fe evidence-agent; do
  echo "=== $repo ==="
  cd ~/Projects/Billows/Audit-Manager/$repo
  git rev-parse --abbrev-ref HEAD
  git status --short | grep -v "^??" | head -10   # 只看非-untracked 的異動
  git log -3 --format="%h %s"
done
# BE 預期：feature/FR-058；若只有 a9e9bbdb 代表 Session 1 還沒 commit 進來

# ② BE 與 origin 同步狀態
cd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be
git rev-list --left-right --count origin/feature/FR-058...HEAD
# 左＝origin 領先數、右＝本地領先數

# ③ 確認 FR-058 規劃產出物完好
ls -la /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-058-2607-detection-tools-expansion/
wc -l /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-058-2607-detection-tools-expansion/design.md \
      /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-058-2607-detection-tools-expansion/discussion.html
# 預期：design.md 441 行、discussion.html 951 行

# ④ BE 服務是否在跑（port 8000）
lsof -i :8000 | head -3
# 沒有 listener 時：
#   cd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be && nohup python main_app.py > /dev/null 2>&1 &
# 注意：main_socketio.py 是 socket（8002），不是 API 入口

# ⑤ BE log 有無異常（接手先掃一眼）
tail -200 /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/log/app.log \
  | grep -A 20 -i 'Traceback\|ERROR' | head -60

# ⑥ 三環境 detection_tools 現況（看 ZAP 有沒有 seed 進去、三環境 id 是否一致）
for db in "192.168.50.188 guidant_ai_dev" "192.168.50.188 guidant_ai_stg" "192.168.50.189 guidant_ai_poc"; do
  set -- $db
  echo "=== $2 @ $1 ==="
  psql -h $1 -p 25432 -U cmmgr -d $2 \
    -c "SELECT id, code, connection_type, status FROM config.detection_tools ORDER BY id"
done
# 密碼請查 .env 的 DB_SECRET 或部署文件
# 基線（FR-058 開工前）：1 openvas / 2 nessus / 3 sonarqube(coming_soon) / 4 openscap

# ⑦ agent 測試（必須帶 PYTHONPATH=.，repo 缺 conftest.py）
cd ~/Projects/Billows/Audit-Manager/evidence-agent
PYTHONPATH=. poetry run pytest test/ -q

# ⑧ 三台 agent 健康狀態（121 POC / 122 STG / 123 DEV）
for h in 192.168.50.121 192.168.50.122 192.168.50.123; do
  echo "=== $h ==="
  ssh jedi@$h "docker ps --filter name=guidant-ai-agent --format '{{.Names}}\t{{.Image}}\t{{.Status}}'"
done
```

---

## §7 驗收前的前置確認（跑昂貴流程之前必做）

> 本節取代範本的「§7 Verify 前一個 bug 確實 close」。本案沒有前一個 bug，取而代之的是「不要在條件不齊時付出昂貴的驗收成本」。

**下列四項全部 ✅ 才啟動 §3 情境 A 的環境準備。任一項 ❌ 就走情境 B（等齊）。**

| # | 確認項 | 怎麼確認 | ❌ 時怎麼辦 |
|---|--------|---------|-----------|
| 1 | **Session 1（平台側）已回報完成** | Notion CM-959、CM-962、CM-963、CM-964、CM-965 五張卡狀態皆為「修正待驗證」；BE + FE repo 有對應 commits | 走情境 B。可先做便宜檢查（SQL 套 DEV 後 SELECT、單元測試） |
| 2 | **Session 2（agent 側）已回報完成** | Notion CM-960、CM-967、CM-968、CM-969、CM-970 五張卡狀態皆為「修正待驗證」；evidence-agent repo 有對應 commits | 同上 |
| 3 | **migration 檔存在且可套** | `ls /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/*fr058*`；檔頭有 `-- Date:`、收尾有 `INSERT public.schema_migrations` | 回頭要 Session 1 補；沒有 migration 就沒有 ZAP seed，驗收無從跑起 |
| 4 | **三台 agent 目前健康**（升級前的基線） | §6 pre-flight 第 ⑧ 步，三台都 `Up` | 先修好現有 agent 再談升級。**不要在有台 agent 本來就掛的狀態下重建部署**，會分不清是新版問題還是舊問題 |

**額外確認（`git log` 抽查，不要只信 Notion 卡狀態）**：

```bash
# 三 repo 各看最近 15 筆，確認實際進來的東西與 Notion 宣稱一致
for repo in compliance-manager-be compliance-manager-fe evidence-agent; do
  echo "=== $repo ==="
  cd ~/Projects/Billows/Audit-Manager/$repo
  git log -15 --format="%h %ad %s" --date=short
done
```

> 教訓來源見 §2.3：本 session 抽查抓到過 subagent 自報完成但內容有誤的情況。**Notion 卡改了狀態不等於程式碼真的到位。**

---

## §8 行為規範重要提醒（挑本案會踩到的）

- **不切 branch**：任何 `git checkout <branch>` / `git switch <branch>` **一律不執行**，永遠在 `feature/FR-058` 工作。發現 branch 不對**停下問決策者**，不自己 fix。「為了在正確 branch commit 而切 branch」也不允許
- **push 永遠等決策者明示**，不自動 push
- **顯式 git add 檔名，禁用 `-am`**——⚠️ 本案有**兩個實作 session 並行**，誤 add 對方未完成改動的風險特別高
- **收尾必須等決策者下令**：SPEC / SUMMARY / memory / Notion 母案收尾都是。plan approve ≠ 自動執行收尾
- **憑證禁入版控**：密碼、token、API key 絕不寫進任何會 commit 的檔案（含 SQL migration、docs、handoff）。需要連線資訊時只寫帳號 / host / port / db 名，密碼一律寫「請查 `.env` 或部署文件」
- **BE 出錯先看 log，不問決策者**：`tail -200 log/app.log | grep -A 30 -i 'Traceback\|ERROR'`。確認 log 沒線索才回頭問
- **BE 起服務用 `main_app.py`（port 8000）**，`main_socketio.py` 是 socket（8002）不是 API 入口；重啟必先 `kill -9`（orphan pid 會卡住 port）
- **文件產出類工作一律派 subagent**（含 §2.1 的分段寫檔要求），主 session 只做決策、派工、抽查
- **執行類工作發出去**：跑測試、跑部署一律派 subagent，本棒不自己執行
- **subagent 一律用 1M context model**，不帶 `model` 參數
- **SQL migration 鐵則**：檔頭 `-- Date:`、每個語句加日期註解、新建 table 必加 `GRANT ... TO cm_app` + sequence 權限、收尾必 `INSERT public.schema_migrations`；套用一律 `psql --single-transaction -v ON_ERROR_STOP=1 -f <檔>` 用 `cmmgr` 帳號；三環境（DEV / STG / POC）都要套
- **跨 repo 各自分開 commit**，commit message 對應各自 repo 的脈絡
- **不要晶晶體**（中英夾雜的語法），**不要用「速贏」**這個詞

---

## §9 收尾流程（全案完成後，等決策者下令才做）

> ⚠️ **本節列的是「等命令」的動作，不要主動執行。** 第一批驗收通過不等於全案完成。

**分批進行中的階段性動作**（每批驗收通過後）：
1. 回寫該批 Notion 子任務卡狀態
2. 產下一批的交接 prompt
3. commit（顯式 git add，不 push）

**全案完成後**（三批都驗收通過、決策者下令才做）：
1. 更新 `docs/specs/current/` 對應頁面 spec（`tool-plugin-manage.md` / `project-task-edit.md` / `my-tasks.md`）+ 檔頭「變更紀錄」加一行——SOP 走 `writing-feature-specs` skill
2. 寫 SUMMARY 到 `docs/features/FR-058-2607-detection-tools-expansion/handoff/`
3. Notion 母案 CM-957 + 六張子需求卡狀態同步
4. **收尾兩項未開卡的工作**（見 §10）
5. memory feedback 沉澱
6. 若要發版 → 走 `version-bump` skill（release note + `pyproject.toml` bump 成對必做 + FE 版號對齊）

---

## §10 不在本期 scope（明列，避免 scope creep）

| 項目 | 說明 |
|------|------|
| **SonarQube** | **本期不做，日後另議**（決策者裁示）。`detection_tools` 的 `id=3` SonarQube 佔位列**維持 `status='coming_soon'` 不動**，不要順手處理它 |
| **macOS 15（`TWGCB-01-015`）** | **不納入本案**（D7 明確排除）。基準 2026/6 才發布、版本尚新，官方部署資源頁無任何機器可讀檔案，且現有 connector 未曾處理 macOS |
| **GCB content 產製** | 逐條 TWGCB-ID 轉檢查規則（Windows GPO 產生器 / Linux 人工撰寫）**不在本案**。那是持續性人力投入，且是最沒有技術風險的部分。本案只做引擎 + **一份 Windows 最小 Demo profile** |
| **後台 content 更新機制** | 上傳 / 版更 / 套用的管理介面**不在本案**，日後另案。但 **content 外部載入接口本案就要留**（D7，事後補要改動 connector 結構） |
| **T-9.1 清 FE 硬編死碼工具清單** | `WorkflowSetupEditor.vue` 的 `toolsMenu = ["Nessus","Nmap","OWASP ZAP","Wireshark"]` 與 `detection_tools` 完全脫鉤。**未開 Notion 卡**，寫在母案「收尾項目」表格。**全案完成後才做**，不要在第一批就順手清 |
| **T-9.2 補 database-schema.md 三表記載** | `docs/claude/database-schema.md` 完全沒有 config schema 與 detection 三表的記載（grep 零命中）。**未開卡**，同樣**全案完成後才做** |
| **`_pick_agent` 負載平衡** | 現況直接取 `agents[0]`，工具變多後單一 agent 被打滿的機率提高。**本案不處理**，屬既有技術債 |
| **`tenant_config_id` 從未被寫入** | 永遠是 NULL，實際靠 `(tenant_id, detection_tool_id)` UNIQUE 約束回退查詢。**本案不處理**，加工具不會惡化 |

---

## §11 前次 session commits 清單

| Commit | 說明 | push 狀態 |
|--------|------|----------|
| `a9e9bbdb` | docs(fr058): 檢測工具擴充四工具接入——討論稿 + design.md + FR 登記 | **已 push**，`feature/FR-058` 與 origin 同步 |
| `ed5db89e` | docs(fr057): v1.12.0 全案收尾——SUMMARY + spec 守門段更新 + handoff 結案標頭 | 已 push（前一個 arc，非本案） |

**⚠️ 本棒接手時務必先 `git log` 看實際狀況**——兩個實作 session 的 commits 會陸續進來，本表只反映規劃 session 結束當下的快照。

---

## §12 給 fresh session 的超短 prompt

```
接手 FR-058（檢測工具擴充，四工具接入）的協調與驗收工作。

先讀 docs/features/FR-058-2607-detection-tools-expansion/handoff/2026-07-30-fr058-planning-complete-handoff.md
的「🧭 原始需求 / WHY」與 §0 讀序（含 design.md §1-§2 的硬 gate），讀完先回答 §0.4 的冷接自檢五問。

答完再跑 §6 pre-flight，然後依 §3 判斷落在哪個情境（A：兩個實作 session 都回報完成 → 跑第一批端到端驗收；
B：只回報一半 → 等齊，不要付昂貴的重建部署成本；C：回報阻礙 → 你是決策角色可直接拍板）。

規劃已完成並 push，實作已派給兩個外部 session 並行中——你的角色是協調與驗收，不是實作。
不要自己下去寫 connector 或改 BE。收尾類動作一律等我下令。
```

---

> 📌 **寫完自檢**：下個 session 只看這一份，能不能答出冷接自檢五問（懂 WHY）、跑得動 pre-flight、判斷得出情境並開工？——本文所有 SQL / bash / 路徑 / Notion URL 皆可直接複製貼上，不需回頭問決策者。
