換 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 頁刪檔);糾纏部分待本棒

§1

🧭 原始需求 / 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 驗證?

§2

§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 目前是註解(停用但保留),本棒要把它們真的刪掉


§3

§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,86TaskSetupView.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:84project-planning-ap → round(需 resolve round)。
  3. src/config/router/index.js:確認上面都改完、grep 無人引用後,移除 project-auditor-overview-ap / project-planning-ap 條目(base 保留)。node --check 驗語法。
  4. i18nsrc/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.pyArControlListResource/ArControlDetailResource/ArVerdictResource/ArFindingCreateResource/ArFindingDetailResource/ArRoundListResourceapi/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.pyGrcAuditRepoImpl,全 dark stub)+ DI audit_service provider(di_containers/grc/grc_containers.py)。
    • v1 poam repo / oscal_audit_query.py(注意:oscal_audit_queryoscal_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 驅動):
    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 移除後它變孤兒,可一併清或留著(無害)。

§4

§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)。

§5

§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 清理對象)。

§6

§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。
  • 不晶晶體:動詞連接詞用中文。

§7

§F Pre-flight Commands(開工前跑)

# 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')"

§8

§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 另外明確指示。

§9

§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)

§10

§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 列了勿順手做的項目。