# FR-058 協調者中繼交接（第二棒）— 收尾完成、SonarQube 開新線（2026-07-31）

> **這是中繼交接不是收尾**。FR-058 的四工具已全數上線並實測通過、收尾文件已完成；
> 但 **SonarQube（FR-058.5）剛開新線**、**agent `0.2.20` 待部署**、**三 repo 全未 push**。
> 前一棒交接文件：`2026-07-31-fr058-coordinator-midarc-handoff.md`（**其 §1 狀態盤點已全面過期，以本文為準**；
> 其 §2 教訓、§3 決策脈絡、§7 裁示仍然有效且重要）。

| 項目 | 內容 |
|------|------|
| 本棒角色 | **協調、決策、派工、抽查。不寫 code、不跑測試、不寫文件——全部派 subagent。** |
| Branch | 三 repo 皆 `feature/FR-058`，**全部未 push** |
| 接手第一件事 | 跑 §6 pre-flight 重查現況（多條線並行，數字會變） |
| 下一個動作 | §4.1——部署 agent `0.2.20` 到 DEV 並驗證檔名 |

---

## 🧭 原始需求 / 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. **前一棒交接文件的 §2（教訓）/ §3（決策脈絡）/ §7（裁示）**——`2026-07-31-fr058-coordinator-midarc-handoff.md`。⚠️ **其 §1 狀態盤點已全面過期**（agent 版本、migration 數、卡片狀態全變了），只讀那三節
3. `design.md` **§2 決策表 D1–D16**（D13–D16 是本輪新增的 SonarQube 決策）
4. **全案 SUMMARY**：`2026-07-31-fr058-arc-SUMMARY.md`（收尾 session 產出，已 commit `289978f3` + `867a7ccf`）
5. 需細節才開：`handoff/2026-07-31-fr058-batch4-gcb-dispatch.md`（GCB 三棒）、`handoff/2026-07-31-fr058-closing-dispatch.md`（收尾派工單）

### 冷接自檢（答不出來回去讀）

1. **CINC Auditor 與 OpenSCAP 的架構方向剛好相反，差在哪？** 提示：引擎裝在哪一端、誰主動連出。這個差異決定了 content 要送到哪裡。
2. **今天最難查的 bug 是什麼？root cause 在哪一層？** 提示：三個初始假設全錯，真因跟 Windows、WinRM、憑證都無關。
3. **為什麼 `fr058-1~13` 套進了三環境，但 `14~17` 只在 DEV？** 提示：中間發生了一件事，導致立了一條鐵律。

---

## §1 當前狀態（2026-07-31 晚實查）

### 1.1 三 repo（全部未 push）

| Repo | HEAD | 相對 main 領先 | working tree |
|------|------|--------------|-------------|
| **BE** | `7b9d831e` T-5.1 SonarQube seed | **42** | 有 `.claude/skills/big-feature-workflow/SKILL.md` modified + 一批**與本案無關**的 untracked docs（別的 arc 留的，不要清理不要 commit） |
| **FE** | `5aea161` T-9.1 移除硬編工具清單 | **12** | 乾淨 |
| **agent** | `71d4e6c` bump `0.2.20` | **24** | 乾淨 |

### 1.2 三環境現況

| 環境 | fr058 migration | sonarqube | agent | 說明 |
|------|----------------|-----------|-------|------|
| **DEV** | **17** | `available` | **`0.2.20`**（⚠️ 見下）| 開發環境，領先是正常的 |
| 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 鐵律）。

> ⚠️ **`0.2.20` 的 image 可能尚未實際部署**——commit 訊息寫「待部署 DEV 123」。**接手第一件事就是確認並部署**（§4.1）。

### 1.3 八款工具現況

| id | code | 名稱 | status | 驗收 |
|----|------|------|--------|------|
| 1 | openvas | OpenVAS | available | 既有 |
| 2 | nessus | Nessus | coming_soon | 佔位 |
| 3 | **sonarqube** | SonarQube | **available（僅 DEV）** | 🔄 **FR-058.5 進行中**，T-5.1 已完成 |
| 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 項實跑：**pass 394 / fail 128 / n/a 225 / error 0** |

### 1.4 本輪（第二棒）完成的事

**四工具全部實機驗證通過**，另完成六項平台能力：任務層敏感參數、ID 解耦（D11）、互斥憑證組別（CM-986）、憑證部分更新改 merge（CM-987）、取消執行中任務（CM-988）、`requires_credentials`/`requires_target_host` 宣告欄位。

**UI 改善**：憑證分組左右並排、部署前說明 Dialog 加寬+逐段複製、卡片同列等高、工具描述兩版改寫、profile 改下拉選擇。

**收尾已完成**（收尾 session 產出）：SPEC 三頁、使用手冊、`database-schema.md`、discussion.html、T-9.1 死碼清理、全案 SUMMARY。

**GCB content 換裝**：Demo profile（2 項）→ **TWGCB-01-011 Windows Server 2022 v1.1（708 項）**，決策者提供，下拉只留這一筆。

### 1.5 Notion

母案 **CM-957**（修正待驗證）。本輪新增：CM-986~991（六項平台能力與 InSpec 線）、CM-992（GCB content 產製後續案）、CM-993（收尾追蹤）、**CM-994~997（FR-058.5 SonarQube）**。

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

---

## §2 本棒的關鍵教訓

### 2.1 最難的 bug：Ruby 3.4 錯誤訊息截斷（中文 profile 全滅）

TWGCB profile 對 160 全部失敗，訊息是 `invalid byte sequence in UTF-8`，被分類成「檢測引擎連線 160:5985 失敗」。

**我給 subagent 的三個假設全錯**（Windows 端編碼 / profile 中文轉碼 / 我們的 subprocess 解碼）。真因：

1. 報告產生時 `MethodSource.expression_at` 要嵌入每個 control 的原始碼片段
2. 它找 control 結尾的方式是**累積行數反覆 `eval`**——**SyntaxError 是正常運作機制**
3. **Ruby 3.4 會附上原始碼摘錄並按位元組截斷**——切在多位元組字元中間（實測 `\xE8`），讓 `ex.message` 自己變成非法 UTF-8
4. 後續正則比對該訊息 → `ArgumentError`

**觸發條件＝長中文行 + 後接未結束區塊**。純英文 profile 一個都不中。

**教訓**：① 協調者的假設要標明是假設、容許被實測推翻（本案三個全錯）；② subagent 解密 DEV 憑證跑真實端到端才找到真因——**只做模擬會一直在錯方向打轉**。

### 2.2 環境異動鐵律（本棒最重要的產出）

`fr058-1~13` 被同步套進三環境，其中四支是 seed 新工具（`status='available'`），使 **POC 上四個工具顯示可用但 agent 停在 `0.2.11` 沒有對應 connector**。所幸無人使用（租戶設定 0 筆、派工 0 筆）。

**根因是協調者派工單寫錯**，不是 subagent 亂做——把 CLAUDE.md「schema 對齊」（release 前檢查）誤套成「開發中每支 migration 都推三環境」。

**已立鐵律**（CLAUDE.md「環境異動鐵律」段 + `.claude/skills/sql-migration/SKILL.md` 開頭 + memory `feedback_dev_only_never_touch_stg_poc`）：**開發階段只動 DEV，STG/POC 需決策者當次明確指示**。同樣適用於部署。

### 2.3 錯誤分類今天修了兩次，都是同一個形狀

| 次數 | 真因 | 被誤報成 |
|------|------|---------|
| ① | agent image 沒裝 `git`（CINC 對裸 GitHub URL 會 shell out 呼叫 git） | 「檢測引擎連線 151:22 失敗」 |
| ② | Ruby 3.4 訊息截斷 | 「檢測引擎連線 160:5985 失敗」 |

**兩次都把查修方向帶去憑證與網路。** 審核任何實作時問一句：**「這個錯誤最早能在哪裡被發現？現在是在那裡被發現的嗎？」**

### 2.4 多 session 併發的代價

一度有三個 session 同時寫同一個 repo。發生過：commit 撞 HEAD 移動、subagent 看到別人 staged 的檔案、**收尾 session 拿到的環境狀態在它工作期間被另一條線改變**（它查到 agent 是 `0.2.11`，實際已更新到 `0.2.18`）。

**紀律**：subagent 一律**顯式 `git add` 檔名、禁 `-am`**、commit 前 `git status` 確認沒捲進別人的檔案。派工前先確認有誰在跑。

---

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

| # | 裁示 |
|---|------|
| 1 | **push 暫緩到全案驗收通過**——三 repo 全未 push，不要因「commit 累積很多」自己推 |
| 2 | **開發只動 DEV**，STG/POC 需當次明確指示（§2.2） |
| 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**（2026-07-31 晚，D13–D16） |

---

## §4 下一步

### 4.1 🔴 立即：部署 agent `0.2.20` 到 DEV

`71d4e6c` bump `0.2.20` 的 commit 訊息寫「**待部署 DEV 123**」，但 §1.2 實查 DEV 顯示已是 `0.2.20`——**兩者矛盾，接手要先確認哪個是真的**：

```bash
ssh jedi@192.168.50.123 "docker ps --filter name=guidant-ai-agent --format '{{.Image}}\t{{.Status}}'"
# 若 Status 顯示剛啟動 → 已部署；若 Up 很久 → 那是舊 image 被 tag 覆蓋，要重新確認
ssh jedi@192.168.50.123 "docker exec deploy-guidant-ai-agent-1 grep -c '政府組態基準' /app/core/task_executor_connectors/inspec.py"
# 應 >= 1（0.2.20 的檔名修正）
```

**驗收**：派一次 GCB 任務，證據檔名應是 `政府組態基準（GCB）檢測報告_<host>_<date>.html`（不再是 `CINC檢測報告_`），且統計數字仍是 pass 394 / fail 128 / n/a 225 / error 0 的量級。

### 4.2 FR-058.5 SonarQube（進行中）

| 棒 | 內容 | 狀態 |
|----|------|------|
| T-5.1 | BE seed（**UPDATE 既有佔位列**，不是 INSERT）+ param_schema v1 | ✅ `7b9d831e`，已套 DEV |
| T-5.2 | agent connector `sonarqube.py`（probe + run 拉快照 + 自組 HTML） | ⬜ 未開始 |
| T-5.3 | FE 微調 + DEV 端到端驗收 | ⬜ 未開始 |

**關鍵認知**：SonarQube Server **沒有觸發掃描的 API**——本 connector 是平台第一個 **pull-snapshot 型**（拉最近一次分析結果，不觸發新掃描）。驗收環境是公司自有 `http://192.168.50.171:9000`，專案 `guidant-ai-backend` / `guidant-ai-fe`，**token 查本機 `~/.zshrc`，絕不寫進任何檔案**。

### 4.3 待決策者裁示的兩件事

1. **報告內的標題**——決策者問「GCB 報告裡的 Title 能改嗎」。已查：HTML 由 `html2` reporter 依 profile 的 `inspec.yml` 的 `title` 產生，該檔已寫「TWGCB-01-011 Microsoft Windows Server 2022 政府組態基準 (GCB)」。**待決策者回報實際看到的字**，才知道是改 title 還是 reporter 不吃該欄位。
2. **CM-985 是否納入 SonarQube**——CM-985 要檢討「summary 語意跨工具不一致」，而 SonarQube 會再加一種形狀。建議把「需納入 SonarQube」寫進 CM-985，否則做完又要再調。**已向決策者提出，尚未回覆。**

---

## §5 待辦與風險

| # | 項目 | 嚴重度 | 說明 |
|---|------|--------|------|
| 1 | **三 repo 全未 push**（BE 42 / FE 12 / agent 24） | 中 | 本機硬碟是唯一副本。決策者已裁示暫緩，知情並接受 |
| 2 | **STG/POC 落後 DEV 四支 migration + 兩版 agent** | 低（刻意） | 開發期正常狀態。上版時要一併處理，且 **sonarqube 在 STG/POC 仍是 `coming_soon`** |
| 3 | **GCB profile 的角色分流用不到** | 低 | profile 支援 `--controls-tag role=baseline`（只跑 326 項），但 `InspecConnector._scan_one_host` 沒傳該參數，派工一律跑全部 708 項。要支援需同時改 connector 與 param_schema |
| 4 | **CM-974 第 5 條假設（SIGTERM 取消路徑）未驗** | 低 | 本輪走正常完成路徑未觸發取消。可用新做的取消按鈕順便驗 |
| 5 | **ZAP 對 SPA 的登入後掃描會打掛共用 ZAP** | 已知 | D12，列後續案 CM-984。`setup_guide` 已標示「請勿對 SPA 使用登入後掃描」 |
| 6 | **`detection_tools` 無 i18n** | 中（已裁示不動） | `name`/`description`/`setup_guide`/param_schema label 全中文寫死 |
| 7 | **`_pick_agent` 無負載平衡 / `tenant_config_id` 從未寫入 / 證據關聯靠 description 字串前綴** | 低 | 既有技術債，本案不處理 |

---

## §6 Pre-flight（必跑）

```bash
# ① 三 repo 狀態
for d in ~/Projects/Billows/Audit-Manager/{compliance-manager-be,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; git -C "$d" status --short | head -5
done

# ② 三環境 DB（密碼查 .env 的 DB_SECRET）
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 -c \
    "SELECT id, code, status FROM config.detection_tools ORDER BY id;
     SELECT count(*) FROM public.schema_migrations WHERE filename LIKE '%fr058%'"
done

# ③ 三台 agent（預期 123=0.2.20、121/122=0.2.18）
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}}\t{{.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）
cd ~/Projects/Billows/Audit-Manager/evidence-agent && PYTHONPATH=. poetry run pytest test/ -q | tail -3
# 基線：425 passed, 1 skipped（0.2.19 當下）
```

---

## §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`（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 永遠等明示**
- **顯式 `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 多平台索引行為，非損壞）

---

## §9 給 fresh session 的短 prompt

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

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

先讀該文的「🧭 原始需求 / WHY」+ §0 讀序，讀完回答 §0 的冷接自檢三問，再跑 §6 pre-flight。
⚠️ 前一棒交接文件（2026-07-31-fr058-coordinator-midarc-handoff.md）的 §1 狀態盤點已全面過期，
只讀它的 §2 教訓 / §3 決策脈絡 / §7 裁示。

現況：四款工具（ZAP / CINC Auditor / Nmap / GCB）全數上線並實測通過，收尾文件已完成。
GCB 已換裝成 TWGCB-01-011 Windows Server 2022 v1.1（708 項，實跑 pass 394 / fail 128 /
n/a 225 / error 0）。SonarQube 剛重新納入為 FR-058.5，T-5.1 已完成、T-5.2/5.3 未開始。

你的下一個動作是 §4.1——確認並部署 agent 0.2.20 到 DEV（commit 說「待部署」但實查顯示
已是 0.2.20，兩者矛盾要先釐清），驗證 GCB 證據檔名已改為「政府組態基準（GCB）檢測報告_」。

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