| 項目 | 內容 |
|---|---|
| 緣由 | 接續 FR-038 稽核生命週期測試。控制層輪的 start auditing → 判定 → 風險 → finalize → POA&M → 結案 已實測通過;剩最後一段「覆核(reverify)閉環」未測 |
| 下一棒 | 用 round 26(覆核輪)端到端測覆核:稽核計畫 → 開始稽核 → 重新判定(這次判符合)→ finalize → 確認母輪 round 24 連動 closed |
| BE branch | feature/oscal-refactor(勿切 branch) |
| FE branch | 同名 feature/oscal-refactor(~/Projects/Billows/Audit-Manager/compliance-manager-fe/) |
| 🔴 狀態 | 三 repo 大量未 commit(working tree);本 session 改了一堆 BE/FE,BE 必須重啟才生效;未收尾(changelog/SUMMARY/Notion 都沒做,等 user 令) |
| 測試專案 | 專案 277「新專案測試新專案測試」(uid 94046c32-81d3-4bb3-860d-ccb368f74c26) |
| 接手前必讀 | 本文件全讀 + 前一份 handoff 2026-06-17-final-stages-audit-poam-handoff.md(稽核+POA&M 背景) |
大圖:FR-038 把 OSCAL v1→v2。本弧把生命週期最後階段(audit / poam / 覆核)遷到 v2 round-scoped,並依 OSCAL 模型重做 UI。
這一棒要驗的價值:稽核生命週期是 開立 → 進行中 → 已矯正 → 已驗證/結案。「已矯正 ≠ 已驗證」——改善負責人說修好了,要由稽核覆核確認改善有效才能真正關閉。系統用 pending_reverify + 覆核輪(close-out round) 實作這個「驗證」步驟(對齊 NIST RMF / ISO 內稽的矯正後追蹤驗證)。
覆核閉環機制(OSCAL + 本專案):
pending_reverify(不直接 closed)closed 並連動把母輪也設成 closed → 閉環完成本棒任務 = 驗證上面第 2~4 步真的跑得通(前 1 步已測過)。
2026-06-17-final-stages-audit-poam-handoff.md(稽核+POA&M 設計決策:判定=控制層、風險鉤 finding、manager override 4 處)冷接自檢(答不出回去讀):
pending_reverify 不是 closed?→ 因為「已矯正 ≠ 已驗證」,要覆核確認改善有效才能關(OSCAL/RMF 驗證步驟)。parent_round_id 指母輪;覆核輪 closed 會連動關母輪。narrowed_control_ids_for:母輪 not_met 的控制集(覆核只查那幾條)。_obj)。專案 277 現有兩輪:
| round id | uid | 輪次 | 類型 | 狀態 | parent | workflow 綁定 |
|---|---|---|---|---|---|---|
| 24 | 57bdf192-5db7-4875-9b3c-e7143547def8 |
第1輪 | initial | pending_reverify |
— | ✅ 已綁 |
| 26 | 6664a2d2-e941-491e-95fb-8dd480e11ae3 |
第2輪 | close-out | audit_planning |
24 | ✅ 已綁(workflow_execution_uid 有值) |
launch_reverify 漏綁的 bug 後重新發起的)→ 進它的稽核計畫頁不會再報「無法載入階段資訊」。pending_reverify。驗證 SQL(密碼查 .env DB_SECRET,host 192.168.50.188:25432 db guidant_ai_dev user cmmgr):
SELECT id,uid,round_no,round_type,status,parent_round_id,workflow_execution_uid
FROM compliance.project_audit_rounds WHERE project_id=277 ORDER BY round_no;_obj.N)反覆踩坑——3 筆 poam_item、補不到風險、卡死。控制層輪才是 1 控制 1 finding 1 poam_item(target_id=控制碼)。round 24/26 都是控制層、乾淨。判別法:finding.target_id 無 _obj 後綴。<!-- <Column>...{{ }}... </Column> --> 隱藏欄位,害整個 DataTable 渲染壞掉(跑出 stray >、整列不渲染)。要隱藏欄位直接刪 Column(要恢復看 git 歷史),別用 HTML 註解包。_bind_round_main_workflow,比照 create_round)。launch_reverify 檢查 auditor(manager override),但 FE 按鈕只在 isProjectManager 顯示(ProjectApListView.vue line ~283)。所以 auditor(blspan) 看不到按鈕、要用 manager(blsadmin) 發起。語意上覆核是稽核員動作 → FE 應放寬給 auditor,但本 session 沒改(user 沒要求)。start_at/end_at 全專案沒 code 在寫(end_at 完全沒接),所以那欄結構性空白,本 session 已從 ProjectApListView 刪該 Column。若要顯示需另接(start_at=開始稽核時寫 / end_at=finalize 時寫),列 follow-up。這是測試任務,不是修 bug。 目標:驗證覆核閉環跑得通。
IA.L1-3.5.1)IA.L1-3.5.1 無 _obj)IA.L1-3.5.1 為符合(模擬改善已生效)→ 存判定closed,並連動母輪 24 → closedclosed、母輪 24 closed-- 覆核輪 findings(控制層?符合?)
SELECT target_id, target_status_state FROM oscal.assessment_findings
WHERE ar_result_id = (SELECT ar_result_id FROM compliance.project_audit_rounds WHERE id=26);
-- 兩輪最終狀態(finalize 後應都 closed)
SELECT id,round_no,status FROM compliance.project_audit_rounds WHERE project_id=277 ORDER BY round_no;追 app/grc/service/audit_round_app_service.py 兩條路徑:
finalize_audit(line ~390):not_met_count == 0 → 把覆核輪設 STATUS_CLOSED。但這條沒有 parent 連動邏輯 —— 它只關覆核輪自己。close_round(line ~425 close-out 分支):e.status in (remediation, closed) 才不擋 → 設覆核輪 closed → 若 parent.status == pending_reverify → 把母輪設 closed(連動只在這裡)。所以推論(待實測):覆核輪全判符合 → finalize 把覆核輪設 closed,但母輪不會自動 closed(連動在 close_round,不在 finalize)。要關母輪,得在覆核輪**再按一次「完成本輪/結案」**觸發 close_round(它允許 status==closed 通過 → 跑 parent 連動)。
疑慮:FE 對「已 closed 的覆核輪」可能不顯示結案按鈕 → 那 close_round 永遠不會被呼叫 → 母輪卡在 pending_reverify 關不掉。
下一棒實測重點:覆核輪全符合 finalize 後,(a) 母輪有沒有自動 closed?(b) 若沒有,FE 有沒有地方能對覆核輪觸發 close_round?(c) 若兩者皆無 → 這是真 bug,要讓 finalize(覆核輪 not_met=0 時)直接呼叫 parent 連動,或 FE 補觸發點。先用 §3 SQL 看 finalize 後兩輪狀態再決定。
~/Projects/Billows/Audit-Manager/compliance-manager-be/)本 session 在前一弧基礎上新增/修改:
app/grc/service/poam_app_service.py — 里程碑負責人 user_uid→user_id resolve;get_item 重做(回 deficiency + observations + 全部 risks 各帶 remediations/milestones);list_items 加 control_id/risk_count/max severity;_item_control_id/_item_findings/_item_risks/_item_observations/_max_severity helpersapp/grc/service/assessment_result_app_service.py — get_findings 每控制附 observations;_observation_dto;新增 delete_observation/update_observation;judge_finding 觀察改累加(原本覆蓋);list_risks/_linked_for_risk 來源 control_id 用 _ao_to_control_for_ap 映射(去 _obj)app/grc/service/audit_round_app_service.py — launch_reverify 補綁 workflow(核心修正);launch_reverify 加防重複發起 guardapp/grc/service/assessment_plan_app_service.py — get_ap_for_round 回 round_status(FE ap-authoring 唯讀判定用,原本誤用 ap.status 永遠 draft → 全唯讀)infra/grc/repository/auditor_dashboard_query.py — 我的稽核 SQL :status IS NULL → CAST(:status AS text) IS NULL(修 AmbiguousParameter 崩潰)api/project/serializers/audit_round.py — AddMilestone/UpdateMilestone 加 assignee_user_uid;新增 UpdateObservationRequestapi/project/routes/audit_round_route.py — milestone 路由 wire assignee_user_uid;新增 AuditRoundObservationRoute(PUT/DELETE observation)api/project/__init__.py — 註冊 AuditRoundObservationRoute(/audit-round/<r>/ar/observation/<obs_uid>)common/code/grc_error_code.py — 加 GRC_REVERIFY_ALREADY_LAUNCHED(GRC_412041)⚠️
pyproject.toml/docs/features/README.md非本弧改動,勿一起 commit。
~/Projects/Billows/Audit-Manager/compliance-manager-fe/)src/views/project/RoundPoamView.vue — POA&M 頁依 OSCAL 重做(缺失脈絡→觀察證據→風險(弱點/影響)→各風險矯正措施→里程碑);里程碑負責人指派下拉;里程碑改「明確儲存按鈕」(原本 select 即存);里程碑進度標籤(🚩里程碑 x/y / 尚無里程碑)src/views/project/RoundAuditReviewView.vue — 觀察記錄區重做(清單+編輯+刪除,收合式新增);判定區加標題/分隔線src/views/project/ProjectApListView.vue — 發起覆核/啟動稽核按鈕改大顆有字;reverifyLaunched 已發起就隱藏發起鈕;刪除 col_period(稽核時間)Columnsrc/views/project/ProjectAuditorOverview.vue — 移除標題列的專案狀態 badge(未開始,沒在更新)src/config/locales/i18n/{zh-tw,en}/round-audit-poam.json(??新檔)— 觀察/風險/里程碑/POA&M 文案src/config/locales/i18n/{zh-tw,en}/ap-authoring.json(??新檔)src/config/locales/i18n/{zh-tw,en}/project-ap-list.json — round_type_close-out 翻譯、reverify_already_launched、發起覆核按鈕RoundApAuthoringView.vue(??)、AuditTreeNav.vue、MyAuditsView.vue、locales/index.js、router/index.js(前一弧)cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
git branch --show-current # 應 feature/oscal-refactor
git status --short | grep -vE '^\?\? ' # 對照 §5
set -a; source .env; set +a; export GITLAB_API_VERSION=4 GITLAB_URL=x GITLAB_PRIVATE_TOKEN=x GITHUB_PRIVATE_TOKEN=x
.venv/bin/python -c "import api.project, app.grc.service.poam_app_service, app.grc.service.audit_round_app_service; print('OK')"cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe && npm run build:DEV-- 應看到 round 24(pending_reverify) + round 26(close-out, audit_planning),兩者 workflow_execution_uid 皆有值
SELECT id,round_no,round_type,status,parent_round_id,workflow_execution_uid
FROM compliance.project_audit_rounds WHERE project_id=277 ORDER BY round_no;若 round 26 不存在或 workflow_execution_uid 為 NULL → launch_reverify 沒生效(BE 沒重啟到帶修正的版本),停下查。
-amlog/app.log(先 grep ERROR/Traceback,不問 user){"severity":...})ao_findings/ao_id、judge_finding docstring「單 AO 判定」、控制層輪不需要的 _derive_ao_map)2026-06-17-ar-assessment-subjects.sql無 —— 本 session 所有 BE/FE 改動都在 working tree、未 commit、未 push。前一弧的改動也還在 working tree(從未 commit)。下一棒測試通過後等 user 下令才一起整理 commit。
讀 docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-17-reverify-testing-handoff.md,
先答冷接自檢(為何 pending_reverify / 覆核輪與母輪關係 / 覆核控制範圍來源 / 判定=控制層)再開工。
本弧稽核+POA&M 已實測通過,剩「覆核(reverify)閉環」未測。
下一棒:請 user 重啟 BE(本 session 大量 BE 改動在 working tree)→ 用專案 277 的 round 26(覆核輪)
端到端測覆核:稽核計畫→開始稽核→判定符合→finalize→確認母輪 round 24 連動 closed(§3 有推測待 verify:
覆核輪走 finalize 直接 closed 還是要 close_round 才連動關母輪)。每步查 DB(§3/§7 SQL)。
不切 branch、push 等 user、收尾等 user 令。