# 換 session Handoff — AP-mode 最終清理（FR-038）

| 項目 | 內容 |
|------|------|
| 緣由 | FR-038 v1→v2 OSCAL redesign 收尾：把舊「AP-mode」的 FE dual-mode code path 整套退役 |
| Branch | BE + FE 皆 `feature/oscal-refactor`（**勿切 branch**） |
| 範圍 | 主要 FE（compliance-manager-fe），含少量 BE（compliance-manager-be） |
| 接手前必讀 | 本文件全文 + `docs/review/2026-06-18-fr038-api-inventory-dead-and-broken.md` §1B/§2 |
| 預估時間 | 主要 FE 重構 ~半天（**需開 FE dev/build 邊改邊驗**）；BE cleanup ~1h；task-setup ~30m |
| 狀態 | 安全核心已做（v1 AR/POA&M 頁刪檔）；**糾纏部分待本棒** |

---

## 🧭 原始需求 / WHY（先讀，gate）

**v2 把產品模型從「專案 → AP（評估計畫）」改成「專案 → 稽核輪次（round）」**：每個專案可多輪稽核，每輪有自己 round-scoped 的 SSP 快照 / AP / AR / POA&M。FR-038 之前是 v1「專案→AP」模型，頁面都是 **AP-scoped**（URL 帶 `/ap/:apUid`）。

v2 切換時 FE **兩套路由並存**：round-scoped（`*-round`，URL 帶 `/round/:roundUid`）+ 舊 AP-scoped（`*-ap` 或 base），靠各元件的 `isRoundRoute = !!route.params.roundUid` 三元切換。

**現在**：所有 live 專案都是 round-based（新建專案 project-start 一定建 round_no=1 的 initial 輪）；舊 AP-mode 專案已全部 soft-deleted（DEV 實查：199 archived + 8 pending 皆 `deleted_at` 已設，只剩 1 個 live 專案 round-based）。User 明確要求：**舊 AP-mode 都不要了，把 ap-mode code path 全退役**。

**目標**：FE 導航與元件純 round；移除 `-ap`/base 的 ap-mode 分支、`-ap` 路由、已退役 v1 頁；BE 移除 v1 dark-stub 路由/repo。**避免使用者誤入 ap-mode 看到空/壞頁**。

**本棒在大圖的位置**：FR-038 2B 收尾的「死碼清理」。功能性 v2 重建已完成（dashboard/覆核/任務/匯入匯出 un-500 等，見 §11 commits）；本棒是**拔掉並存的 v1 ap-mode 殘留**。

### 🔒 冷接自檢（答不出回去讀 WHY，別碰 code）
1. v2 的核心模型是什麼？AP-mode 跟 round-mode 差在哪？
2. 為什麼 base 路由（`project-auditor-overview` / `project-planning`）**不能直接刪**？
3. 使用者現在點「回專案總覽」（從設定頁）會發生什麼？（提示：base → `fetchApList` → redirect `-ap`）
4. 為什麼這段「不能盲改」、必須開 FE build 驗證？

---

## §A 現況：本 session 已完成（已 commit）

**功能性 v2 重建 + v1 退役都已做完並 commit**（見 §11）。與本棒直接相關的：
- **v1 AR/POA&M 頁已刪檔**（FE commit `50c1061`）：`AuditReviewView.vue` / `PoamView.vue` / `AuditVerdictPanel.vue` / `PoamDetailDrawer.vue` + router 的 `project-audit-review` / `project-poam` 條目移除。稽核/POA&M 全走 `RoundAuditReviewView` / `RoundPoamView`。
- **§1B BE 路由已註解停用**（commit `a23f614e`）：grc 的 v1 AR（`/ar/controls/list`、`/ar/control/<uid>/verdict` 等 6 條）+ POA&M（`/poams/list`、`/poam/<uid>`）在 `api/grc/__init__.py` 已註解（標 `FR-038 DEAD-V1 §1B`）。
- **§2 DEAD 路由已註解停用**（commit `a23f614e`）：oscal scoped import / grc control-group·control·AO list·detail / project job-execution-device / flow-engine main-process 等 / participant menu —— 全用 `# [FR-038 DEAD-V1｜...]` 標記（`grep -rn "FR-038 DEAD-V1" .` 可一次撈出）。

> ⚠️ 這些 §1B/§2 目前是**註解**（停用但保留），本棒要把它們**真的刪掉**。

---

## §B 本棒要做的（3 部分，建議順序 2 → 3 → 1）

### Part 1（主要、糾纏）— FE ap-mode 導航/元件移除 ⚠️ 需開 FE build 驗證

**核心難點**：base 路由（`project-auditor-overview` / `project-planning`）**是 live 的**（被多個元件 push），且「進 base 會被 `ProjectAuditorOverview.fetchApList` redirect 到 `-ap`」是 ap-mode 入口還通的原因。所以**不能只刪路由** —— 要把「進 base → 導 round」改通，並重接所有還指向 base/`-ap` 的元件。

**可達性實況（grep 出來的，下個 session 直接照這改）**：

| 路由 name | 還被誰 push（非 router 定義/非 _archived） | 處置 |
|-----------|------------------------------------------|------|
| `project-auditor-overview`（base） | ProjectSettingsView:59,86；TaskSetupView:615；ProjectPlanningView:1521 | **保留**，但要讓進 base 改導 round |
| `project-auditor-overview-ap` | PoamView(已刪)；ProjectAuditorOverview:95,108（ap-branch 內部） | 收掉 ap-branch 後即無人引用 → **可移路由** |
| `project-planning`（base） | SspDocxImportPage:811,847 | **保留**或改導 round（看 SspDocxImportPage 能否拿到 round） |
| `project-planning-ap` | ProjectAuditorOverview 三元(1298/1346/1487)；ImportExcelPreviewPage:84 | 重接後 **可移** |
| `project-task-setup` / `-ap` | **0 導航** | → Part 3 |
| `project-ap-list` | ProjectCreateView:262；ProjectListView:291 | **live，保留**（這是輪次列表入口，非 ap-mode） |

**具體改動**（`compliance-manager-fe`）：
1. **`src/views/project/ProjectAuditorOverview.vue`**：
   - `fetchApList`（~line 67-102）：目前 `if (isRoundRoute) {撈 audit-rounds/list; return} else {撈 assessment-plans/menu; router.replace(-ap)}`。把 else 分支改成：**resolve 該專案最新一輪 round → `router.replace({name:'project-auditor-overview-round', params:{id, roundUid}})`**（讓 base 進來自動轉 round）。可用既有 `/projects/<pid>/audit-rounds/list` 拿最新輪 uid。
   - `onApChange`（~104-110）：移除 else 的 `-ap` 分支，一律 round。
   - 3 個 planning 三元（搜 `project-planning-ap`，~1298/1346/1487）：移除 `: {name: ...-ap/...base...}` fallback，一律 `project-planning-round`（roundUid = selectedApUid，round 模式下即 roundUid）。
2. **重接其他元件的 base/`-ap` 導航 → round**（每個都要先能拿到 round uid；拿不到的，保留 base 但靠上面 fetchApList 自動轉 round）：
   - `ProjectSettingsView.vue:59,86`、`TaskSetupView.vue:615`：push base auditor-overview → 維持 base（fetchApList 會自動轉 round）**或**直接帶 roundUid。
   - `ProjectPlanningView.vue:1521`：三元 `-ap`/base → round。
   - `SspDocxImportPage.vue:811,847`：push base planning → 維持 base 或帶 round。
   - `ImportExcelPreviewPage.vue:84`：`project-planning-ap` → round（需 resolve round）。
3. **`src/config/router/index.js`**：確認上面都改完、grep 無人引用後，移除 `project-auditor-overview-ap` / `project-planning-ap` 條目（base 保留）。`node --check` 驗語法。
4. **i18n**：`src/config/locales/i18n/{zh-tw,en}/menu.json` 移除 `project-auditor-overview-ap` / `project-planning-ap` label（task-setup-ap 看 Part 3）。
5. **驗證**：`npm run build`（或 dev server）跑過 + 手動點：專案設定→回總覽、規劃頁、docx/excel 匯入後導航，都落 round 頁、不再進 ap-mode。

> ⚠️ **盲改風險高**：這些是 live 共用元件 + 核心導航，且部分元件手上只有 projectId/apUid 沒 roundUid（要先 resolve 當前 round）。**務必開 FE build 邊改邊驗**，不要靜態改完就 commit。

### Part 2（較安全，建議先做）— BE 刪除已停用的 v1 路由/repo

目前是「註解停用」，本棒真刪。**用 py_compile + blueprint register probe 驗（不需 FE build）**。
1. `api/grc/__init__.py`：刪除 §1B（Ar*/POA&M）+ §2（control-group/control/AO、projects/menu 等）的**已註解 add_resource 區塊** + 對應的 route class import。
2. 刪 orphan route classes：`api/grc/routes/audit_route.py` 的 `ArControlListResource`/`ArControlDetailResource`/`ArVerdictResource`/`ArFindingCreateResource`/`ArFindingDetailResource`/`ArRoundListResource`；`api/grc/routes/poam_route.py` 的 v1 PoamList/PoamDetail（**先確認 FE 已無呼叫** —— §1B 已退役、round 走 `/audit-round/*`）。
3. 刪 v1 dark-stub repo / service（**先 grep 確認無其他 consumer**；v2 round 用的是 `assessment_result_app_service`/`poam_app_service`，不同物）：
   - `infra/grc/repository/grc_audit_repo_impl.py`（`GrcAuditRepoImpl`，全 dark stub）+ DI `audit_service` provider（`di_containers/grc/grc_containers.py`）。
   - v1 poam repo / `oscal_audit_query.py`（注意：`oscal_audit_query` 被 `oscal_stage_preconditions` 用，**確認後再決定**，可能要保留）。
4. 其餘 §2 DEAD（di_containers 內已註解的 provider、孤兒 service 檔如 `oscal_project_service.py` / `project_system_info_service.py` / `oscal_audit_service.py`）→ 連同 `test/` 內直接建構它們的單元測試一起評估刪除。
5. 驗：`grep -rn "FR-038 DEAD-V1" .` 確認都清掉；`.venv/bin/python -m py_compile <檔>` + blueprint register probe（見 §F）。

### Part 3 — task-setup（0 導航）移除
- `project-task-setup` / `project-task-setup-ap` 路由 **0 code 導航** → 可移 router 條目 + 刪 `src/views/project/TaskSetupView.vue` + menu.json label。
- ⚠️ **先確認 DB `public.ui_routes` 沒掛 task-setup**（側邊選單可能 DB 驅動）：
  ```bash
  PGPASSWORD='<查 .env DB_SECRET>' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -tA \
    -c "SELECT route_name, path, enable FROM public.ui_routes WHERE route_name ILIKE '%task-setup%' OR path ILIKE '%task-setup%';"
  ```
  若有 enable=1 的列 → 先停用/移除該 ui_route 再刪 FE。
- 注意：BE 的 `grc_task_setup_repo_impl.get_task_setup_tree` 這 session 已重建成 v2（commit `4880a0a0`）。TaskSetupView 移除後它變孤兒，可一併清或留著（無害）。

---

## §C 必先 verify「已做的」沒壞（開工前跑）

本棒會疊在 B6 + 覆核讀回上，先確認它們 runtime OK（這兩個我只做了 compile/import/SQL 驗，**沒跑 runtime**）：
- **B6（docx AO key）**：用 Word 匯入一個**有 AO 層現況**的 SSP（挑有 AO 的控制項）→ 匯出 DOCX/Excel 或看 SoA → AO 層敘述要出得來（不再空白）。注意舊資料已被 migration 轉過，**測新匯入**才看得到。
- **覆核讀回**：專案總覽勾一個控制項 + 一個 AO 覆核 → **重新整理頁面** → 覆核 chip 應還在；且只有本人能取消自己的覆核。
- 若這兩個有問題，先修它們再做本棒（root cause 線索見 commit `2ed83f49` 的 diff）。

---

## §D 該讀的檔（含為何讀）
- `docs/review/2026-06-18-fr038-api-inventory-dead-and-broken.md` §1B/§2 — 哪些路由為何停用、FE 哪些頁取代。
- `src/views/project/ProjectAuditorOverview.vue` — ap/round dual-mode 主嫌（fetchApList / onApChange / 三元）。
- `src/views/project/ProjectPlanningView.vue` — 另一個 dual-mode 共用元件（規劃/任務指派頁，round 主路徑）。
- `src/config/router/index.js` — round/ap/base 路由定義 + breadcrumb parent 鏈。
- `api/grc/__init__.py` — BE 已註解的 §1B/§2 路由區塊（Part 2 清理對象）。

---

## §E 行為規範（適用本棒）
- **勿切 branch**：BE/FE 都留在 `feature/oscal-refactor`，branch 不對停下問 user。
- **push 永遠等 user 明示**，不自動 push。
- **跨 repo 先讀目標 CLAUDE.md**：動 FE 前讀 `compliance-manager-fe/CLAUDE.md`（646 行，PrimeVue quirks / i18n / 元件慣例）。
- **各 repo 分開 commit**，顯式 `git add` 檔名、**禁用 `-am`**。
- **改 BE service 層後提醒 user 重啟 BE**（無 hot reload）；**服務 user 自己起，別代啟**。
- **改完要驗**：FE 開 build/dev 驗導航；BE py_compile + blueprint probe。**不盲改 commit**。
- **收尾等 user 下令**：fix 完給 status + 手測 checklist，別自動寫 changelog/SUMMARY/Notion。
- **不晶晶體**：動詞連接詞用中文。

---

## §F Pre-flight Commands（開工前跑）
```bash
# BE
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git branch --show-current          # 應為 feature/oscal-refactor
git status --short                 # 確認乾淨（除 pyproject.toml/README.md/FR-039/proposal.html 等非本 arc）
# FE
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
git branch --show-current; git status --short

# BE blueprint register probe（改完 Part 2 驗，需 env stub 繞過 gitlab/github import）
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
export GITLAB_API_VERSION=4 GITLAB_URL=https://x.invalid GITLAB_PRIVATE_TOKEN=d GITHUB_PRIVATE_TOKEN=d ENV=DEVELOP_PREMISE
.venv/bin/python -c "
import os
[os.environ.setdefault(k.strip(), v.strip().strip(chr(34)).strip(chr(39))) for k,v in (l.split('=',1) for l in open('.env') if '=' in l and not l.startswith('#'))]
for k,v in {'GITLAB_API_VERSION':'4','GITLAB_PRIVATE_TOKEN':'d','GITHUB_PRIVATE_TOKEN':'d','GITLAB_URL':'https://x.invalid'}.items(): os.environ[k]=v
import api.grc, api.project; assert api.grc.create_module() and api.project.create_module()
print('blueprint register OK')"
```

---

## §G 不在本棒 scope（勿順手做）
- **勿重做 B6 / 覆核讀回 / §1A v2 重建**（已 commit，見 §11）。
- **勿動 round 頁本身**（RoundAuditReviewView / RoundPoamView / ProjectApListView / 規劃頁的 round 主邏輯）—— 只拔 ap-mode 分支。
- **其餘 review 未做項另案**（不在本棒）：R4 框架 Excel/YAML 匯入（`add_module_frame` stub）、M6 ai_dashboard `OscalFramework(Version)Service` resolver、M7 `frameworks.main_version` 雙真相、L1 dead write-strategy、L2 重複 error code、L4 stage precondition、ssp-import-template/control-defaults 填值完整 v2。清單見 `docs/review/2026-06-18-fr038-api-inventory-dead-and-broken.md`。
- **勿清 DB 資料**（已 soft-deleted 的舊專案）—— 屬破壞性操作，要 user 另外明確指示。

---

## §H 本 session commits（origin 皆**未 push**）
**BE（`feature/oscal-refactor`）**：
- `f6163e02` 專案列表狀態改顯示最新一輪 + 稽核計畫改輪次總數
- `a23f614e` 退役死 v1 路由/DI + un-500 資源庫匯入樣板（§1B/§2 註解停用）
- `4880a0a0` 重建 GRC dark-stub 為 v2（dashboard/覆核寫入/menus/AO jobs/task-setup/batch-notify）+ My Audits 漏排已刪專案修正
- `f7a9f752` 上線前審查報告 + API 死碼/壞掉盤點（docs）
- `2ed83f49` docx AO key 統一 _obj.N（B6）+ 覆核狀態讀回控制樹（閉環）

**FE（`feature/oscal-refactor`）**：
- `c62eb4c` 專案列表狀態改最新一輪 + 移除完成率欄
- `2182ef0` 稽核/POA&M 導覽全改走 round + 我的稽核移除證據就緒
- `50c1061` 刪除已退役 v1 稽核/POA&M 頁（4 檔 + router）

---

## §I 給 fresh session 的超短 prompt（複製貼到下個 session 開頭）

```
接手 FR-038「AP-mode 最終清理」。請先讀
docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-18-ap-mode-removal-handoff.md
全文，特別是「🧭 原始需求/WHY」+ 冷接自檢 4 問 —— 答得出再開工。

任務：把 v2 並存的舊 AP-mode code path 退役（現行專案全 round-based、舊 AP 專案已刪）。
建議順序 Part 2（BE 刪已停用 v1 路由/repo，py_compile+blueprint probe 驗）→ Part 3
（task-setup，先查 ui_routes）→ Part 1（FE ap-mode 導航/元件移除，**務必開 FE dev/build 邊改邊驗**，
base 路由保留但改導 round、重接 ~5 個元件、移 -ap 路由）。

先跑 handoff §C 驗「B6 + 覆核讀回」runtime OK，再疊上去。規範見 §E（勿切 branch / 不自動 push /
跨 repo 先讀 FE CLAUDE.md / 改完要驗不盲改）。§G 列了勿順手做的項目。
```
