# FR-058 協調者中繼交接（第三棒）— SonarQube 三棒全數 commit、待端到端驗收（2026-07-31 晚）

> **這是中繼交接不是收尾**。四款工具（ZAP / CINC Auditor / Nmap / GCB）已全數上線實測通過、
> 收尾文件已完成；**FR-058.5 SonarQube 的 T-5.1~5.3 已全部 commit，但端到端驗收尚未完成**
> （目前只有 probe 通過，`detection_executions` 內 sonarqube 執行紀錄仍為 0 筆）。
>
> **⚠️ 三 repo 的 push 狀態已與前一棒不同——origin 上已有 FR-058 commits**（見 §1.1），
> 前一棒「全部未 push」的敘述已過期。
>
> 前兩棒交接文件：
> - `2026-07-31-fr058-coordinator-handoff-2.md`（第二棒）—— §1 狀態盤點已過期，其 §2 教訓 / §3 裁示仍有效
> - `2026-07-31-fr058-coordinator-midarc-handoff.md`（第一棒）—— **只讀** §2 教訓 / §3 決策脈絡 / §7 裁示

| 項目 | 內容 |
|------|------|
| 本棒角色 | **協調、決策、派工、抽查。不寫 code、不跑測試、不寫文件——全部派 subagent（不帶 model 參數）** |
| Branch | 三 repo 皆 `feature/FR-058`（**不切 branch**） |
| 接手第一件事 | 跑 §6 pre-flight 重查現況（**SonarQube 那條線可能仍在跑，數字會變**） |
| 下一個動作 | §4.1——確認 SonarQube 線是否收工，未收工則不碰 agent repo；已收工則驗收 T-5.2/5.3 |

---

## 🧭 原始需求 / WHY（不讀會誤解範圍）

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

**工具清單的演變**（不讀會搞不清楚為何最終清單跟原話對不上）：

| 階段 | 發生什麼 |
|------|---------|
| 起點 | 四項：OpenSCAP for Windows / Nmap / GCB / SonarQube |
| 中途 | 追加 **ZAP**（第五個） |
| 裁示① | **SonarQube 本期不做**（D10 成 tombstone） |
| 調研 | 「OpenSCAP for Windows」**證實不可行**（官方已放棄 Windows 支援），改由 **InSpec / CINC Auditor** 承擔 |
| 裁示② | ZAP 優先上線，其餘分批 |
| 裁示③（2026-07-31 晚） | **SonarQube 重新納入**，立為 **FR-058.5**（D13–D16 定案） |

**本案要解決兩件事**：① 接入工具（現已八款）；② 補平台結構性缺口（任務層敏感參數——原本
只有租戶層憑證有加密，任務層是明文且 FE 執行紀錄會顯示）。

---

## §0 接手讀序

1. **本文「🧭 原始需求 / WHY」+ §1 現況 + §4 下一步 + §5 待辦**（必讀）
2. **第二棒 `2026-07-31-fr058-coordinator-handoff-2.md` 的 §2（教訓）**——⚠️ 其 §1 狀態盤點已過期
3. **第一棒 `2026-07-31-fr058-coordinator-midarc-handoff.md` 的 §2 / §3 / §7**——同樣只讀這三節
4. `design.md` **§2 決策表 D1–D16**（D13–D16 是 SonarQube 決策）
5. **全案 SUMMARY**：`2026-07-31-fr058-arc-SUMMARY.md`
6. 需細節才開：`handoff/2026-07-31-fr058-batch4-gcb-dispatch.md`、`handoff/2026-07-31-fr058-closing-dispatch.md`

### 冷接自檢（答不出來回去讀，不要碰任何東西）

1. **CINC Auditor 與 OpenSCAP 的架構方向剛好相反，差在哪？**
   提示：引擎裝在哪一端、誰主動連出。這個差異決定了 content 要送到哪裡。
2. **SonarQube connector 跟其他七款在行為上有一個根本差異，是什麼？為什麼？**
   提示：`run()` 裡少了一個所有其他 connector 都有的東西。
3. **為什麼 `fr058-1~13` 套進了三環境，但 `14~17` 只在 DEV？**
   提示：中間發生了一件事，導致立了一條鐵律。

---

## §1 當前狀態（2026-07-31 20:47 實查）

### 1.1 🔴 三 repo —— push 狀態已變，前一棒敘述過期

| Repo | HEAD | 相對 main | **相對 origin/feature/FR-058** | working tree |
|------|------|----------|------------------------------|-------------|
| **BE** | `dbd57824` 第二棒交接文件 | 43 | **未推 1** | 有 `.claude/skills/big-feature-workflow/SKILL.md` modified + 一批**與本案無關**的 untracked docs（別的 arc 留的，**不要清理不要 commit**） |
| **FE** | `fef7e94` T-5.3 FE 微調 | 13 | **未推 1** | 乾淨 |
| **agent** | `2d92ac4` T-5.2 SonarQube connector | 25 | **未推 1** | 乾淨 |

**⚠️ 與前一棒最大的差異**：前一棒寫「三 repo 全部未 push」。**現在 origin 上已經有 FR-058
的 commits**，三 repo 各只差 1 個未推。決策者已確認 **push 是他自己做的，不需追究**。

**裁示不變**：**push 永遠等決策者明示**，不要因為「origin 已經有了」就自己補推剩下那 1 個。

### 1.2 三環境現況

| 環境 | fr058 migration | sonarqube 狀態 | agent image | 說明 |
|------|----------------|---------------|-------------|------|
| **DEV** | **17** | `available` | **`0.2.20-fr0585-verify`**（⚠️ 見下） | 開發環境，領先是正常的 |
| STG | 13 | `coming_soon` | `0.2.18` | 刻意落後 |
| POC | 13 | `coming_soon` | `0.2.18` | 刻意落後 |

**DEV 領先的四支 migration**（`14`–`17`）：TWGCB profile 選項 / 工具描述 v2 / GCB 下拉清理 /
SonarQube seed。**皆刻意只套 DEV**（見 §3 鐵律）。

> **⚠️ DEV agent 目前跑的是驗證用臨時 tag `0.2.20-fr0585-verify`，不是正式 `0.2.20`。**
> 這是 SonarQube 那條線為了驗 T-5.2 自建的。`/opt/evidence-agent/deploy/.env` 的
> `AGENT_IMAGE` 已指向該臨時 tag。**SonarQube 驗收完成後必須決定**：是要 bump 成正式版號
> （如 `0.2.21`）重建部署，還是回退到 `0.2.20`。**不要就這樣留著臨時 tag 進入上版流程。**

### 1.3 八款工具現況

| id | code | 名稱 | status | 驗收 |
|----|------|------|--------|------|
| 1 | openvas | OpenVAS | available | 既有 |
| 2 | nessus | Nessus | coming_soon | 佔位 |
| 3 | **sonarqube** | SonarQube | **available（僅 DEV）** | 🔄 **probe 通過，端到端未驗**（見 §1.5） |
| 4 | openscap | OpenSCAP | available | 既有（FR-057） |
| 5 | zap | ZAP | available | ✅ 被動/主動通過；**登入後對 SPA ❌**（會打掛共用 ZAP，見 D12） |
| 6 | inspec | CINC Auditor | available | ✅ 雙 transport 實跑：151 pass 131/fail 60、160 pass 300/fail 472 |
| 7 | nmap | Nmap | available | ✅ 實跑通過，HTML 證據 |
| 8 | gcb | 政府組態基準（GCB） | available | ✅ TWGCB 708 項實跑，檔名修正已驗（見 §1.4） |

### 1.4 本棒（第三棒）完成的事

**① §4.1 agent `0.2.20` 部署矛盾已釐清 + GCB 檔名實測通過**

前一棒留的矛盾（commit 訊息寫「待部署」但實查已是 `0.2.20`）**已確認：commit 訊息寫在部署
之前，實際已部署**，不是 tag 覆蓋。實測證據（BE log 20:09:35 的 result payload）：

```
政府組態基準（GCB）檢測報告_192.168.50.160_20260731.html
summary: pass 397 / fail 122 / notapplicable 225 / error 0
```

檔名不再是 `CINC檢測報告_`，統計數量級與 708 項基準相符。**§4.1 結案。**

**② 敏感參數遮蔽查核完成——FE 無洩漏，但發現一個獨立的 at-rest 曝險**（詳見 §5 第 1 項）

**③ GCB 報告標題調查——已裁示作罷**

決策者要求把報告最上方的 `Cinc Auditor Report` 拿掉。已確認：那行是 **html2 reporter 寫死的**，
與 profile 無關；profile 的 `inspec.yml` title 有正確生效（顯示在它下一行）。原本「改 title
就好」的假設不成立，要動得改 reporter 那層。**決策者裁示：不能改就算了，不查了。**
（副帶發現：報告左上角的 `Display controls that are: Passed/Failed/Skipped` 篩選 UI 與 profile
標題左緣重疊，同屬 reporter 內建，一併作罷。）

**④ SonarQube 免費報表方案調研**（決策者提問「除了付費還有其他模式轉報表嗎」）

結論：**沒有值得換的方案，現行自組 HTML 路線正確。**

| 方案 | 型態 | 格式 | 對公司 26.7.0 的風險 |
|------|------|------|---------------------|
| CNES Report | Java CLI 或 server 外掛 | DOCX/XLSX/MD/CSV | ⚠️ 已知 CE 10.4 產報表噴 500，open issue 稱 standalone 不支援 25.x |
| RedCoffee | Python CLI 打 Web API | PDF | 🟢 跑在 server 外，不受外掛相容性連累 |
| sonar-pdf-report | 舊 post-job 外掛 | PDF | ❌ 已廢棄 |
| bitegarden | 商用外掛 | PDF/ODT/XLSX | 💰 付費（決策者排除） |

**關鍵認知**：SonarQube 本身**沒有任何原生報表匯出**，這是多年公開痛點。所有免費方案的做法
**都跟我們的 connector 一樣**——打 Web API 拉數據自組報表，差別只在誰組。裝在 server 上的
外掛全都脆弱（Community Build 改滾動發版後外掛跟不上）。公司自有 server 實查為 **26.7.0.124771**，
很新，會篩掉一半選項。若將來要豐富報表內容，可參考 CNES Report 的 DOCX 版面編排，實作路徑不用改。

### 1.5 🔄 FR-058.5 SonarQube —— 三棒全 commit，端到端未驗

| 棒 | 內容 | commit | 狀態 |
|----|------|--------|------|
| T-5.1 | BE seed（**UPDATE 既有佔位列**不是 INSERT）+ param_schema v1 | `7b9d831e` | ✅ 已套 DEV |
| T-5.2 | agent connector `sonarqube.py`（probe + 拉快照 + 自組 HTML） | `2d92ac4`（agent repo） | ✅ 已 commit |
| T-5.3 | FE 微調（`project_key` 進主要參數 + summary 中文標籤） | `fef7e94`（FE repo） | ✅ 已 commit |

**⚠️ 但端到端驗收尚未完成**，實查證據：

- `compliance.detection_executions` 內 **sonarqube 執行紀錄 0 筆**——從未實際派工跑過
- agent log 只看得到 **probe（測試連線）成功**：
  ```
  SonarQube probe 成功 base_url=http://192.168.50.171:9000 version=26.7.0.124771
  ```
- 另有一筆 probe 失敗，是**預期中的正確行為**（token 型別檢查生效）：
  ```
  SonarQube Token 無效。請確認填入的是 User Token（squ_ 開頭）——
  sqp_ / sqa_ 開頭的分析用 Token 只能推送掃描結果，無法讀取資料。
  ```
- `config.tenant_detection_tool_configs` 已有 **1 筆** sonarqube 租戶設定

**T-5.2 的設計要點**（從 commit message 摘，供驗收對照）：
- **平台第一支 pull-snapshot 型 connector**——`run()` **沒有輪詢迴圈**（其他七款都有），
  因為 SonarQube Server **沒有任何觸發掃描的 API**，只能讀 CI 已推上去的既有結果
- `probe()`：`api/system/status` + 帶 Bearer 的 `api/authentication/validate`（**顯式檢查
  `valid` 欄位**——該端點驗的是「當前身分是否有效」，匿名也算一種身分）
- `run()` 序列：`project_analyses`（回空則明確 raise 引導先跑 sonar-scanner）→ `qualitygates`
  → `measures` → `issues+facets` → `hotspots`，逐呼叫間檢查 `cancel_event`，單呼叫 timeout 60 秒
- issue severity **`impacts[]` 優先**（MQR），缺席 fallback `type+severity`，相容 9.9 LTS〜2026.x
- **自包含繁中 HTML**（無外部資源），summary 取自 `measures` 不自行加總清單

**connector 檔頭記錄的三項實測修正**（都是打 API 才會踩到的坑，驗收時別回頭改掉）：
1. `api/issues/search` **不帶篩選會連 RESOLVED/ACCEPTED 一起回**，但 summary 來自 `measures`
   只算未處理項 → 實測 `guidant-ai-backend` 不帶篩選 total=845、measures 全是 0，兩者放同一份
   報告就是「摘要說 0 項、明細列 845 條」的自我矛盾。故一律帶 `resolved=false` 統一口徑
2. **10,000 筆硬上限**：`ps=500` 第 21 頁起直接報錯 → 統計數字取自 facets/measures（全量、
   一次拿到），明細只取前 200 條並標示截斷事實
3. **branch 行為與原設計假設不符**：原假設「Community 填 branch 一律回錯」，實測帶
   `branch=main` 正常回 200，只有**不存在的分支**才 404 → 改為不預判 edition，照使用者填的送出

### 1.6 Notion

母案 **CM-957**（修正待驗證）。相關卡：CM-986~991（平台能力與 InSpec 線）、CM-992（GCB content
產製後續案）、CM-993（收尾追蹤）、**CM-994~997（FR-058.5 SonarQube）**、CM-984（ZAP SPA 後續案）、
CM-985（報告呈現優化，全案驗收後才做）。

> Notion free plan **不支援 SQL 查資料源**，用 `notion-search` + `notion-fetch`。

---

## §2 本棒的教訓

### 2.1 多 session 併發是本 arc 的常態，pre-flight 必須重查而非信任交接文件

本棒接手時交接文件寫「T-5.2 未開始」，但實查 agent repo working tree 有**未 commit 的 768 行
connector + 602 行測試**，mtime 顯示 4 分鐘前還在寫。到本棒寫這份文件時，T-5.2 **和** T-5.3
都已 commit 完成。

**紀律**：
- 交接文件的狀態盤點**寫下當刻就開始過期**，pre-flight 一律實查 git log / DB / 容器，不要憑文件
- 發現另一條線在同一 repo 作業 → **完全不碰該 repo**（不 commit、不跑測試、不改檔），
  唯讀查看可以
- 派 subagent 前先確認有誰在跑，dispatch 必寫「顯式 `git add` 檔名、禁 `-am`」

### 2.2 log 裡的 ERROR 不一定是缺陷——先確認那筆資料是怎麼來的

pre-flight 在 BE log 撈到 `on_scan_succeeded 找不到對應 detection_executions`，時間軸看起來像
「派工當下還在、回報時就消失」的資料遺失。深入查證後：**那是另一條線手動 INSERT 的驗證用
task，繞過了 `start_execution`，所以從來沒有 `detection_executions` 列**（不是被刪）。

判別方法：`compliance.agent_tasks.created_user IS NULL` 只可能來自手工 INSERT（API 路徑必填
`curr_user`）。同一晚共 4 筆（id 49/50/51/57）都留下相同錯誤簽名。

**教訓**：查異常先問「這筆資料是走哪條路徑產生的」，再問「程式有沒有 bug」。順序反了會花大
成本追一個不存在的缺陷。（決策者最終裁示不追，判定為 SonarQube 那條線做一半停掉的產物。）

### 2.3 「已修」的宣稱要問清楚範圍

第二棒 SUMMARY 寫「任務層敏感參數已修」，本棒實查 `agent_tasks.params->'_credentials'` 卻是
**明文 WinRM 密碼 + 完整 SSH private key**，一度誤判為迴歸。查證後是**兩件不同的事**：

| | FR-058 修的 | 我看到的 |
|---|---|---|
| 來源 | `job_execution_detection_tools.tool_params` 內 `secret:true` 欄位 | 租戶層憑證表解密後的派工快照 |
| at-rest | **加密** envelope | **明文** |
| 目前實例 | 只有 ZAP `login_password` | `winrm_password` / `ssh_private_key` |
| 誰做的 | FR-058 本輪 | FR-056 既有 |

「已修」的宣稱是準確的，只是範圍不含 `_credentials`。**收尾時 SUMMARY 措辭要補範圍界定**，
否則下一棒會跟本棒一樣誤判（subagent 也自述進場時同樣誤判）。

### 2.4 承襲前兩棒的教訓（仍然有效，不要重蹈）

- **錯誤分類要問「這個錯誤最早能在哪裡被發現？現在是在那裡被發現的嗎？」**——本 arc 兩次把
  真因（image 缺 `git`、Ruby 3.4 訊息截斷）誤報成「檢測引擎連線失敗」，兩次都把查修方向帶去
  憑證與網路
- **協調者的假設要標明是假設、容許被實測推翻**——Ruby 3.4 那個 bug，協調者給 subagent 的
  三個初始假設全錯
- **subagent 自報完成不等於正確**，驗收要獨立抽查（開檔 / 查 DB / fetch Notion）

---

## §3 已定裁示（不要重新討論）

| # | 裁示 |
|---|------|
| 1 | **push 永遠等決策者明示**——origin 上已有的 commits 是決策者自己推的，不追究；剩下未推的 1 個/repo 不要自己補推 |
| 2 | **開發只動 DEV**，STG/POC 需**當次明確指示**（含 DB 與部署）——見 §2.2 of 第二棒文件 |
| 3 | **commit 標籤不一致不回頭修**——rebase 改寫歷史風險大於整齊 |
| 4 | **GCB content 產製 + 後台 content 管理機制**列後續案 CM-992，不在 FR-058 |
| 5 | **`config.detection_tools` 沒有 i18n 機制**——先不動（平台級缺口，FR-056 建表時就有） |
| 6 | **CM-985（報告呈現優化）全案驗收後才做** |
| 7 | **SonarQube 為 FR-058.5**（D13–D16） |
| 8 | **GCB 報告內的 `Cinc Auditor Report` 標題不處理**（2026-07-31，本棒新增）——那是 html2 reporter 寫死的，要改得動 reporter 層，決策者裁示作罷 |
| 9 | **SonarQube 不換報表方案**（2026-07-31，本棒新增）——免費方案都是同樣的「打 API 自組」路線，無值得換的 |

---

## §4 下一步

### 4.1 🔴 立即：確認 SonarQube 那條線是否收工

**本棒交接當下，SonarQube 線疑似仍在進行**（20:20 commit T-5.2、20:22 commit T-5.3、20:41
容器換成驗證用 image）。接手第一件事是確認它收工了沒：

```bash
# ① agent repo 有沒有新的未 commit 檔案（有 → 那條線還在跑，不要碰該 repo）
git -C ~/Projects/Billows/Audit-Manager/evidence-agent status --short
git -C ~/Projects/Billows/Audit-Manager/evidence-agent log --oneline -3

# ② DEV 容器跑的是不是還是驗證用臨時 tag
ssh jedi@192.168.50.123 "docker ps --filter name=guidant-ai-agent --format '{{.Image}}|{{.Status}}'"
# 目前：guidant-ai-agent:0.2.20-fr0585-verify

# ③ SonarQube 有沒有實際跑過（0 筆 = 端到端還沒驗）
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c \
  "SELECT e.uid, e.status, e.started_at FROM compliance.detection_executions e
   JOIN config.detection_tools d ON d.id=e.detection_tool_id WHERE d.code='sonarqube'
   ORDER BY e.id DESC LIMIT 5"
```

**若那條線還在跑** → 不碰 agent repo，等它收工同步 Notion 後再更新（決策者已指示：
「sonarqube 有另一個主線在跑，他們那邊結束會同步回 notion，你再更新就好」）。

**若已收工** → 進 §4.2 驗收。

### 4.2 SonarQube 端到端驗收（那條線收工後才做）

派 subagent（不帶 model 參數）實際派一次 SonarQube 任務，驗收重點：

1. **證據 HTML 產出**且自包含（無外部資源）、繁中、可在證據池預覽
2. **summary 數字與 SonarQube UI 對得起來**——注意 connector 刻意讓明細與摘要**同口徑**
   （皆為未處理項，`resolved=false`），若看到「摘要 0 項、明細 845 條」就是回歸
3. **`project_key` 有出現在執行紀錄的主要參數區**（T-5.3 的目的），不是落在摺疊區
4. **summary 標籤顯示中文**（T-5.3 補的五個 i18n key）
5. 驗收環境：`http://192.168.50.171:9000`，專案 `guidant-ai-backend` / `guidant-ai-fe`，
   **token 查本機 `~/.zshrc`，絕不寫進任何檔案**

### 4.3 ⚠️ 驗收後必做：處理 DEV 上的臨時 image tag

DEV 目前跑 `0.2.20-fr0585-verify`（驗證用臨時 tag），`.env` 的 `AGENT_IMAGE` 已指向它。
**驗收通過後要決定**：bump 成正式版號（如 `0.2.21`）重建部署，或回退 `0.2.20`。
**不要讓臨時 tag 進入上版流程。**

### 4.4 待決策者裁示

| # | 事項 | 建議 |
|---|------|------|
| 1 | **`agent_tasks.params._credentials` at-rest 明文**（DEV 現有 45 筆含明文 SSH 私鑰/WinRM 密碼） | 選項①：改存加密 envelope、心跳組裝時才解（**複用現成 `common/util/detection_secret_params.py`，不用新造輪子**，且消除「同一張表兩種標準」）。會動派工路徑，四款工具都要回歸 → 現在做還是列後續案？ |
| 2 | **121/122 主機的 `AGENT_VERSION` 舊 pin** | STG/POC，依鐵律等決策者當次指示。屬顯示用版本字串不影響功能，可等上版一併處理 |
| 3 | **CM-985 是否納入 SonarQube** | CM-985 要檢討「summary 語意跨工具不一致」，SonarQube 會再加一種形狀。建議寫進 CM-985，否則做完又要再調。已提出未回覆 |

---

## §5 待辦與風險

| # | 項目 | 嚴重度 | 說明 |
|---|------|--------|------|
| 1 | **`agent_tasks.params._credentials` 永久明文** | **中** | 租戶憑證表本身加密（`credentials_encrypted`），但每次派工都解密後明文快照進 `agent_tasks` 且**從不清除**。DEV 53 筆帶憑證、其中 45 筆含明文 WinRM 密碼或完整 SSH private key。等於加密儲存的防護被派工快照繞過——拿到 DB 讀權限即可取得全部私鑰。寫入點 `app/detection_tools/service/detection_orchestration_service.py:117`。**非本輪迴歸**（FR-056 既有）。**對外 API 無洩漏**（見下） |
| 2 | ~~FE 是否洩漏敏感參數~~ | ✅ 已查證無虞 | 執行紀錄經兩層剝除（`detection_orchestration_service.py:399-400`：底線開頭 key 整個拿掉 + 加密 envelope 拿掉）。api_logs 實測近 7 天：`winrm_password` 40 次、`ssh_private_key` 45 次、`login_password` 5 次，**全部為 `***`，未遮蔽 0 次**；`BEGIN ... PRIVATE KEY` 明文 0 筆 |
| 3 | **孤兒 execution 會讓 job 永久卡死** | 低 | 若出現「execution 存在但 agent_task 不見」，`start_execution` 見到 running 就 409，而 `_cancel_running_execution` 反查不到 task 直接拋 `CANCEL_FAILED`，**連 force 都繞不過**。兩表間無 FK（全 soft-ref），手動刪 `agent_tasks` 不會連帶清。最小修法：cancel 在 task 查無時仍標 cancelled 後放行 + warning log |
| 4 | **稽核 log 遮罩是欄位名 regex** | 低（前瞻） | `common/util/common_util.py:202-221` 的 `mark_password()` 比對 `password\|passphrase\|secret\|token\|credential\|authorization\|api_key\|private_key`。未來若新工具的 secret 欄位名不含這些字（如 SNMP `community_string`）會**靜默漏遮**。目前八款工具無此類欄位 |
| 5 | **DEV agent 跑臨時 tag** | 中 | `0.2.20-fr0585-verify`，見 §4.3 |
| 6 | **STG/POC 落後 DEV 四支 migration + 兩版 agent** | 低（刻意） | 開發期正常。上版時一併處理，且 **sonarqube 在 STG/POC 仍是 `coming_soon`** |
| 7 | **GCB profile 的角色分流用不到** | 低 | profile 支援 `--controls-tag role=baseline`（只跑 326 項），但 `InspecConnector._scan_one_host` 沒傳該參數，派工一律跑全部 708 項。要支援需同時改 connector 與 param_schema |
| 8 | **CM-974 第 5 條假設（SIGTERM 取消路徑）未驗** | 低 | 可用取消按鈕順便驗 |
| 9 | **ZAP 對 SPA 的登入後掃描會打掛共用 ZAP** | 已知 | D12，後續案 CM-984。`setup_guide` 已標示「請勿對 SPA 使用登入後掃描」 |
| 10 | **`detection_tools` 無 i18n** | 中（已裁示不動） | `name`/`description`/`setup_guide`/param_schema label 全中文寫死 |
| 11 | **`_pick_agent` 無負載平衡 / `tenant_config_id` 從未寫入 / 證據關聯靠 description 字串前綴** | 低 | 既有技術債，本案不處理 |
| 12 | **DEV 殘留 3 筆手工 agent_tasks**（id 49/50/51，`created_user IS NULL`） | 極低 | 無執行紀錄、不影響 UI（執行歷史以 `detection_executions` 為準）。清不清都行 |

---

## §6 Pre-flight（必跑）

```bash
# ① 三 repo 狀態（含 origin 落差——本 arc 已有部分 push）
for d in ~/Projects/Billows/Audit-Manager/compliance-manager-be \
         ~/Projects/Billows/Audit-Manager/compliance-manager-fe \
         ~/Projects/Billows/Audit-Manager/evidence-agent; do
  echo "=== $(basename $d) ==="; git -C "$d" branch --show-current; git -C "$d" log --oneline -1
  echo "ahead-main=$(git -C "$d" rev-list --count main..HEAD) unpushed=$(git -C "$d" rev-list --count origin/feature/FR-058..HEAD 2>/dev/null)"
  git -C "$d" status --short | head -5
done

# ② 三環境 DB（密碼查 .env 的 DB_SECRET，勿寫進任何檔案）
export PGPASSWORD=$(python3 -c "import json;from dotenv import dotenv_values;print(json.loads(dotenv_values('$HOME/Projects/Billows/Audit-Manager/compliance-manager-be/.env')['DB_SECRET'])['rds_master_password'])")
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
  h=${db%%|*}; n=${db##*|}; echo "=== $n ==="
  psql -h $h -p 25432 -U cmmgr -d $n -tAc \
    "SELECT id||' '||code||' '||status FROM config.detection_tools ORDER BY id" | tr '\n' ' '; echo ""
  psql -h $h -p 25432 -U cmmgr -d $n -tAc \
    "SELECT count(*) FROM public.schema_migrations WHERE filename LIKE '%fr058%'"
done

# ③ SonarQube 端到端是否已驗（0 筆＝未驗）
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -c \
  "SELECT e.uid, e.status, e.started_at FROM compliance.detection_executions e
   JOIN config.detection_tools d ON d.id=e.detection_tool_id WHERE d.code='sonarqube'
   ORDER BY e.id DESC LIMIT 5"

# ④ 三台 agent（⚠️ 123 目前是臨時 tag 0.2.20-fr0585-verify）
for h in 192.168.50.121 192.168.50.122 192.168.50.123; do
  echo -n "$h: "; ssh -o ConnectTimeout=6 jedi@$h \
    "docker ps --filter name=guidant-ai-agent --format '{{.Image}}|{{.Status}}'"
done

# ⑤ BE 服務（port 8000；main_socketio.py 是 socket 8002 不是 API 入口，重啟必先 kill -9）
lsof -i :8000 | head -3
tail -200 ~/Projects/Billows/Audit-Manager/compliance-manager-be/log/app.log | grep -i 'Traceback\|ERROR' | tail -10

# ⑥ agent 測試（必帶 PYTHONPATH=.，repo 缺 conftest.py）
# ⚠️ 若 agent repo working tree 有未 commit 檔案 → 另一條線在跑，不要跑測試
cd ~/Projects/Billows/Audit-Manager/evidence-agent && PYTHONPATH=. poetry run pytest test/ -q | tail -3
# 基線：435 passed, 1 skipped（0.2.20 當下，T-5.2 加入後會更多）
```

---

## §7 環境座標

| 項目 | 值 |
|------|-----|
| DEV agent | `192.168.50.123`，容器 `deploy-guidant-ai-agent-1`，deploy 目錄 **`/opt/evidence-agent/deploy`**（⚠️ 不是 `~/deploy`），ssh `jedi` |
| STG / POC agent | `192.168.50.122` / `192.168.50.121` |
| 測試目標 | `192.168.50.151`（Linux/SSH，`audit-scan` 已設 NOPASSWD sudo）、`192.168.50.160`（Windows Server 2025/WinRM） |
| SonarQube | `http://192.168.50.171:9000`（實查版本 **26.7.0.124771**；token 查 `~/.zshrc`，**絕不入檔**） |
| DB | DEV/STG `192.168.50.188`、POC `192.168.50.189`，port **25432**，帳號 `cmmgr` |
| content 投放 | 主機 `/opt/evidence-agent/deploy/content/` → 容器 `/data/content/`（唯讀掛載） |
| profile 版控 | BE repo `content/detection-profiles/`（**不放 agent repo**——Dockerfile 最後一行 `COPY . .` 會讓它進 image，違反 D7） |

---

## §8 行為規範

- **不切 branch**——永遠在 `feature/FR-058`；branch 不對停下問決策者
- **push 永遠等明示**——即使 origin 上已有部分 commits
- **顯式 `git add` 檔名，禁 `-am`**——BE working tree 有一批無關的 untracked docs
- **收尾等命令**——plan approve ≠ 自動收尾
- **憑證禁入版控**
- **BE 出錯先看 log**：`tail -200 log/app.log | grep -A 30 -i 'Traceback\|ERROR'`
- **改 service/serializer 後 BE 必重啟**（`kill -9` 舊 pid，`use_reloader=False` 不會自動重載）
- **subagent 一律不帶 `model` 參數**（繼承 1M context）；文件類派工必寫「**分段寫檔**」
- **測試一律 headless**
- **任務發佈後最長等 300 秒才開跑**（心跳週期，正常行為不是故障）
- **agent image 重建必帶 `--platform linux/amd64`**，build 完 `docker inspect` 確認
- **`docker compose up` 必指定 service 名**（`guidant-ai-agent`），否則連帶 recreate `agent-db`/`nginx`
- 已知現象：本機 `docker image inspect <tag>` 對新 tag 可能報 No such image，用 image ID 可以
  （Docker Desktop containerd 多平台索引行為，非損壞）
- **另一條線在同一 repo 作業時，完全不碰該 repo**（唯讀查看可以）

---

## §9 收尾流程（全案驗收通過、決策者下令才做）

1. SPEC 三頁補 SonarQube（`writing-feature-specs` skill）
2. 使用手冊補 SonarQube 段
3. `design.md` §11 對應段標 closed
4. **全案 SUMMARY 更新**——⚠️ 「任務層敏感參數已修」必須**補範圍界定**（見 §2.3），
   否則下一棒會誤判 `_credentials` 明文為迴歸
5. Notion CM-994~997 標 Done + 母案 CM-957 更新
6. memory：本 arc 教訓濃縮（候選：多 session 併發 pre-flight 紀律、log ERROR 先問資料來源路徑）
7. commit 收尾文件（顯式 `git add`）

---

## §10 不在本期 scope

- GCB content 產製 + 後台 content 管理機制（CM-992）
- ZAP 對 SPA 的登入後掃描（CM-984）
- 報告呈現優化（CM-985，全案驗收後）
- `detection_tools` i18n（已裁示不動）
- GCB 報告內的 `Cinc Auditor Report` 標題（裁示 8，作罷）
- SonarQube 換報表方案（裁示 9，作罷）

---

## §11 給 fresh session 的短 prompt

```
接手 FR-058（檢測工具擴充）的協調與決策工作。

交接文件：
docs/features/FR-058-2607-detection-tools-expansion/handoff/2026-07-31-fr058-coordinator-handoff-3.md

先讀該文的「🧭 原始需求 / WHY」+ §0 讀序，讀完回答 §0 的冷接自檢三問，再跑 §6 pre-flight。
⚠️ 前兩棒交接文件的 §1 狀態盤點都已過期，只讀它們的教訓 / 決策脈絡 / 裁示三節。

現況：四款工具（ZAP / CINC Auditor / Nmap / GCB）全數上線實測通過。FR-058.5 SonarQube
的 T-5.1~5.3 已全部 commit，但端到端驗收未做（detection_executions 內 sonarqube 0 筆，
只有 probe 通過）。⚠️ 三 repo 的 origin 上已有 FR-058 commits（決策者自己推的），
各只差 1 個未推——但 push 仍永遠等我明示。

你的下一個動作是 §4.1——確認 SonarQube 那條線是否收工（它在本文寫成當下疑似仍在跑）。
還在跑就完全不碰 agent repo，等它同步 Notion 後再更新；已收工則進 §4.2 端到端驗收。
另注意 §4.3：DEV agent 目前跑的是驗證用臨時 tag 0.2.20-fr0585-verify，不是正式版號。

你的角色是協調、決策、派工、抽查。不寫 code、不跑測試、不寫文件——全部派 subagent
（不帶 model 參數）。收尾類動作等我下令。
🔴 開發只動 DEV，STG/POC 要我當次明確指示才能碰（含 DB 與部署）。
```
