---
title: "改善計畫（POA&M）逐評估目標重做：對原任務設到期日不追蹤、Excel 往右延伸、OSCAL 落點修正 — 討論稿 (FR-143)"
brand: "Guidant AI · **FR-143** 改善計畫重做"
eyebrow: "FR-143 · 討論稿 · 2026-10-17"
h1: "改善計畫逐評估目標重做"
subtitle: "左樹只列不符合、上半稽核缺失、下半對原任務設執行人員／到期日／改善措施、送出即退回不等改完、Excel 往右延伸一列一任務、POA&M 匯出合規"
lede: "現在的缺失改善頁被使用者說「國中生設計、看不懂怎麼用」：一張表列缺失、點開抽屜建風險再建矯正再建里程碑，三層巢狀，而且要等每個里程碑都打完成流程才走得下去。本案把頁面換成與稽核計畫、稽核結果同一套骨架：左樹只列判不符合的評估目標，中間一條一條看——上半段是「稽核缺失」文件表單（稽核發現、風險、稽核建議、稽核人員、查核方法、採用佐證），下半段列出這條評估目標底下的原任務，PM 對每個任務設執行人員（可多人）、到期日、改善措施，不新增任務。只排計畫、不追蹤完成，送出就推進：內稽每個任務退回第一站整條重跑、外稽進待覆核。Excel 與稽核計畫、稽核結果同一份往右延伸，一列一個任務。資料照 OSCAL POA&M 標準落點，順手修掉四個匯出不合規。請先開 [雛形](prototype/index.html) 點一遍再看規格。"
chips: [
  {text: "D1～D12 全部定案（2026-10-17）", kind: ok},
  {text: "雛形三畫面＋Excel 已審", kind: accent},
  {text: "動兩支套件：jedi-oscal-v2、jedi-compliance-audit", kind: crit},
  {text: "站在 FR-142（已合回 feature/FR-139）之上", kind: plain}
]
footer: "FR-143 · 討論稿 · 2026-10-17 · 輸入：決策者 2026-10-16 需求、OSCAL v1.2.2 POA&M schema、FR-140／141／142 設計 · 現況依據：程式碼（2026-10-16）· 沿革見 git log"
---

> D1～D12 已全部拍板，下一步寫 design.md；本稿定案後不再改寫。

**狀態**：定案（D1～D12 已裁，雛形與 Excel 已審）　｜　**日期**：2026-10-17　｜　**雛形**：[prototype/index.html](prototype/index.html)　｜　**範例 Excel**：[prototype/sample/FR-143-改善計畫範例.xlsx](prototype/sample/FR-143-改善計畫範例.xlsx)

路徑慣例：BE 路徑相對 `compliance-manager-be/`；`pkg/` 代表 `~/Projects/Jedicogy/module/jedi-python-package/<套件>/<套件底線名>/`；FE 路徑相對 `compliance-manager-fe/`。三個 repo 都在 `feature/FR-139`（FR-141、FR-142 已併入）。

名詞：**評估目標**＝AO，控制項底下的檢查項目，全系統用 part_id 當鍵；**POA&M**＝改善計畫（plan of action and milestones），OSCAL 評估層第三個文件；**poam-item**＝一條缺失的封面，引用 finding 不複製；**risk**＝風險；**response**＝風險的處置計畫（表 `assessment_remediations`，程式稱 remediation），`lifecycle` 三值：recommendation 稽核建議／planned 已排計畫／completed 已完成；**task**＝response 底下一筆工作（表 `poam_milestones`），`type` 兩值：action 要做的事／milestone 查核點；**稽核次**＝FR-140 的概念，一輪內每次稽核一筆，各自一組 AP／AR／POA&M。

## 變更紀錄 {#changelog nav="變更紀錄"}

| 日期 | 變更 | 對應 |
|---|---|---|
| 2026-10-17 | 初稿：需求白話、現況盤點（頁面與流程）、OSCAL 核對四個落差、D1～D8、資料落點、Excel 契約、流程、拆卡草案、雛形第一輪 | — |
| 2026-10-17 | 決策者提問「要不要像我的稽核一樣有側邊入口」→ 新增 D9 與雛形畫面 0（改善計畫清單）、卡 6 | — |
| 2026-10-17 | 決策者：清單不要卡片式，資料多會撐不住，改表格（後端分頁、排序、篩選） | 畫面 0 重做 |
| 2026-10-17 | 決策者：改善計畫不新增任務，只對原任務卡設執行人員（可多人，預設原指派人）、到期日（＝里程碑）、改善措施；稽核缺失區改文件表單 | 雛形畫面 1 重做 |
| 2026-10-17 | 決策者裁：每個任務退回第一站整條重跑，到期日逐任務設定；Excel 走一列一任務＋合併儲存格、計畫欄與結果欄原樣保留（往右延伸原則）、計畫欄群組收合；執行人員多人用分號；拿掉「納入改善」 → D10、Excel 契約改寫 | — |
| 2026-10-17 | 逐項裁決收尾：D7 不做報告只留 Excel 與 OSCAL JSON；D8 舊資料不管；D11 拿掉暫行緩解措施；D12 到期日寫進任務每一站不加欄；D1～D6、D9 改定案文字，D3 重寫、D4 併入 D10。全部定案 | — |
| 2026-10-17 | 棒 2、5 驗收發現規格把三站寫成三塊；決策者裁一塊對一條流程、執行人員只改第一站（design.md v1.1） | — |

---

## 分工概述（30 秒版） {#overview nav="概述"}

整案一句話：**缺失改善頁換成左樹只列不符合評估目標、中間上半看「稽核缺失」下半對原任務逐一設執行人員、到期日、改善措施，送出就退回任務重跑不等改完；側邊選單多一個「改善計畫」入口；Excel 與稽核計畫、稽核結果同一份往右延伸、一列一任務；POA&M 資料照 OSCAL 標準落點並修匯出不合規。**

| 棒 | 做什麼 | 產出 | 完成怎麼判定（決策者親手做什麼、看到什麼） |
|---|---|---|---|
| **0 雛形** | 純前端假資料：入口清單、改善計畫頁、匯入預覽、範例 Excel | `prototype/`＋`sample/*.xlsx` | 已審定版（2026-10-17） |
| **1 套件 POA&M 修正** | jedi-oscal-v2：task 匯出補期限與負責人、related-findings 剝私有鍵、POA&M 檔內嵌被引用的 finding／risk／observation、task 狀態欄退場；jedi-compliance-audit：POA&M 粒度確認逐評估目標、送出守門改「每個任務三欄齊」、推進流程不再等全部完成 | 套件 service、匯出合規 | DEV 一個有不符合的稽核次送出改善計畫後，匯出 OSCAL POA&M JSON 用官方 schema 驗證通過，檔內看得到每個 task 的到期日與執行人員 |
| **2 BE 改善計畫端點** | 讀（逐不符合評估目標：稽核缺失＋底下的任務與原指派人）、整批存、送出（守門→鎖定→寫回任務指派人與各站到期日→退卡或進待覆核）、匯出 | 新 app service 檔、route、schema | Swagger 存一條有三個任務的評估目標讀回一致；有任務沒填齊時送出回 412 並列出是哪個；送出後再存回 409；送出後該任務三站 `due_at` 都等於計畫上的到期日、指派人等於執行人員 |
| **3 FE 頁面重做** | 新頁殼、舊 `RoundPoamView.vue` 退役 | FE 新檔、路由 | 對照雛形逐頁操作一遍，每個動作對應後端真資料 |
| **4 Excel 匯入匯出** | 一列一任務 workbook 讀寫（計畫欄與結果欄合併、群組收合）、執行人員比對、匯入預覽就地處理 | `poam_workbook_*.py`、FE 預覽模式 | 匯出改兩列匯回，預覽標出、確認後畫面更新；寫一個不是成員的名字預覽要求處理；同一任務兩列到期日不同時標待確認 |
| **6 入口清單（D9）** | 側邊選單「改善計畫」＋我是 PM 的待規劃輪次表格 | 讀模型、端點、FE 清單頁、路由 seed | 用 PM 帳號登入，側邊選單點「改善計畫」看到待規劃輪次表格，換頁與排序由後端回、點列進該輪頁；用只當 auditor 的帳號登入不見此清單或為空 |
| **5 報告與 FR-140 對齊** | 改善計畫報告 Word／PDF（稽核紀錄＋計畫表）；退卡期限改讀 task 期限、覆核輪新卡期限同；到期提醒不動 | `poam_report_export_service.py`、`not_met_job_redo_service.py` | Word 開檔看到每條不符合的軌跡與任務表；退回的任務卡抽屜期限＝計畫上的日期 |

依賴：1 先做 → 2、4、5 可平行 → 3、6 等 2。全部接在 `feature/FR-139`。

```{.mermaid cap="圖 1 — 棒次依賴"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
    P[棒 0 雛形] --> R{決策者審 D1～D12}
    R --> D[design.md 開卡]
    D --> B1[棒 1 套件 POA&M 修正]
    B1 --> B2[棒 2 BE 端點]
    B1 --> B4[棒 4 Excel]
    B1 --> B5[棒 5 報告＋FR-140 對齊]
    B2 --> B3[棒 3 FE 新頁]
    F142[FR-142 已合回] -.骨架、Excel 四頁、軌跡資料.-> B3
    F142 -.-> B4
```

---

## 需求 {#need nav="需求"}

### 決策者原話（2026-10-16）

> 再來我們一樣要來重新設計 POA&M 的 UI，現在這版直接被 user 說是國中生的設計，完全看不懂要怎麼用。
> 比照稽核、稽核計畫的 UI 設計，有控制項／AO 導航，中央讓 user 去填寫 POA&M 的計劃，也要看到為什麼不合格，前面應該都有軌跡。
> 一樣要提供 Excel 匯入匯出，就用稽核的那一份往後延伸就好，前面的就不給改，後面要讓 user 把任務設定好；要注意有可能有多個任務，不過應該都是一樣，要在一定期間內把那個 AO 做完改正。
> 要把稽核結果有問題的控制項，讓 PM 去設定要怎麼改善以及時間，只需要提供計畫就好，不需要等他改完——因為現在好像要等他改完，我們的流程就變成是交給下一輪。
> 內稽的話就是去重新設定當輪的任務，給出時間以及怎麼修正，提交後要發通知給他們重做。外部稽核的話就是會影響下一輪的起始控制項跟任務內容，流程會一樣，只是只需要注意剩的不合格的項目。
> 一樣都要符合 OSCAL 的規範，不要硬塞東西。
> 流程跟建議欄位我們應該都沒問題，不過你還是確認一下好了。

### 白話複述（決策者 2026-10-16 確認，2026-10-17 逐項裁決後更新）

1. 頁面換成與稽核計畫、稽核結果同一套：左樹只列這次判不符合的評估目標；中間一次一條，上半「稽核缺失」文件表單（稽核發現、風險等級、風險說明、稽核建議、稽核人員、查核方法、採用佐證），下半列出這條評估目標底下的原任務。
2. 不新增任務：PM 對每個任務設執行人員（可多人，預設原指派人）、到期日（就是里程碑）、改善措施（可帶入稽核建議）。有缺失的評估目標底下的任務都要退回，沒有納不納入。
3. 只填計畫不追蹤：沒有「完成打勾」，改沒改由下一次稽核判定。
4. 送出＝流程往下走：守門看「每個任務三欄齊」，不再等任務全部完成；送出後鎖定。
5. 內稽送出後：每個任務退回第一站整條重跑（證據上傳重做、主管與顧問再檢核一次），指派人改成執行人員、每一站到期日都是計畫上的日期、改善措施寫成系統留言、發通知。
6. 外稽送出後：母輪進待覆核，覆核輪只建不符合項的任務、從第一站開始、到期日與指派人照計畫。
7. 側邊選單多一個「改善計畫」，表格列出我是 PM 的輪次，預設看待規劃的。
8. Excel 與稽核計畫、稽核結果同一份往右延伸：計畫欄、結果欄灰底唯讀並合併，一列一個任務，填執行人員（分號分隔）、到期日、改善措施。
9. 匯出只有 Excel 與 OSCAL JSON；舊資料不管；資料照 OSCAL POA&M 標準落點，不自創欄位。

### 新畫面長什麼樣

雛形 [`prototype/index.html`](prototype/index.html) 三個畫面：

- **畫面 0 入口清單**（[`my-poams.html`](prototype/my-poams.html)）：側邊選單「改善計畫」，表格一列一輪，欄位見 D9；狀態頁籤帶筆數、預設「待規劃」；點列進該輪。
- **畫面 1 改善計畫頁**（[`poam-authoring.html`](prototype/poam-authoring.html)）：
  - **左樹**：控制族群 → 控制項 → 評估目標，**只列判不符合的**；燈號是「規劃好沒」（綠已規劃、橘部分規劃、紅未規劃）。篩選：全部／未規劃。
  - **點控制項＝控制項總覽**：控制項結論（固定不符合）、「已規劃 k／n 項」、相關任務與目前執行人員；下方逐不符合評估目標列風險等級、稽核發現、計畫狀態、到期日，點列進入。
  - **點評估目標＝評估目標頁**，由上往下：①**稽核缺失**文件表單（左標籤右內容：稽核發現、風險等級、風險說明、稽核建議、稽核人員、查核方法、採用佐證、其他佐證）；②**改善計畫**：這條評估目標底下的每個任務一塊（任務名、原指派人、狀態），每塊填執行人員＊（chip 多選，預設原指派人）、到期日＊、改善措施＊（「帶入稽核建議」一鍵抄入）。同一任務也對到別條評估目標時顯示提示，執行人員與到期日共用。
  - **頁首**：匯入 Excel、匯出（Excel／OSCAL JSON）、儲存、**送出改善計畫**；統計列：不符合控制項、不符合評估目標、已規劃、未規劃、退回任務、最晚到期。
  - **送出**：守門（每個任務三欄齊，缺的列出「任務名 未填：X」）→ 確認表列出每個任務、執行人員（註明相較原指派人增減）、到期日 → 送出後整份鎖定。頂部灰條可切「已送出」與「外部稽核」看差異。
- **畫面 2 匯入預覽**（[`import-preview.html`](prototype/import-preview.html)）：同一畫面的預覽模式，黃框＝會更新、紅框＝待確認（執行人員不是專案成員、到期日格式不對），就地改好才能確認。

---

## 現況盤點 {#inventory nav="盤點"}

### 頁面與資料怎麼長的

| 元件 | 現況 | 本案動作 |
|---|---|---|
| FE `src/views/project/RoundPoamView.vue`（890 行，超標） | 一張 DataTable 列缺失（控制項、嚴重度、狀態、期限），點列滑出抽屜三頁籤：矯正（風險→矯正→里程碑三層巢狀、里程碑可打完成）、脈絡、證據 | 整頁退役，新開 `RoundPoamView.vue` 殼＋`src/components/grc/poam/` 子元件，沿用 FR-142 的 `ArObjectiveTree.vue` 結構 |
| `pkg/jedi-compliance-audit/app/service/poam_app_service.py`（493 行） | `list_items`／`get_item` 逐 item 攤 finding→risk→remediation→milestone；寫入 `add_remediation`／`add_milestone`／`update_milestone`（含 status=done）／`delete_*`；`all_items_closed` 當結案守門 | 讀寫改成「逐評估目標整批」；`update_milestone` 的 status 路徑退場；`all_items_closed` 換成 `all_items_planned`；超標只准新開檔 |
| `pkg/jedi-oscal-v2/app/service/poam/poam_service.py:54 generate_from_findings` | 稽核結案時每條 `not_met` finding 建一筆 poam-item，`related_findings=[{finding-uuid, finding-id}]` | FR-142 後 finding 已是評估目標層，粒度自然對；`finding-id` 留 DB、匯出剝掉 |
| `pkg/jedi-compliance-audit/app/service/audit_round_pass_transitions.py:99 _close_on_pass` | `all_items_closed` 不成立就 412 `GRC_POAM_NOT_ALL_CLOSED`——**這就是「要等改完」的卡點** | 改成 `all_items_planned`（每 item 至少一筆 planned response 且每 task 有期限與負責人） |
| BE `app/flow_control/service/not_met_job_redo_service.py`（126 行） | 內部缺失改善站推進時退卡：`earliest_milestone_dates` 取每控制項最早 `target_date` 當 `due_at`、寫重做紀錄、發通知 | 讀取改到評估目標層（卡對 `ao_part_id`），其餘不動 |
| `pkg/jedi-compliance-audit/.../audit_round_app_service.py:543 launch_reverify` | 覆核輪新卡 `due_at`＝母輪最後稽核次 POA&M 里程碑日期 | 同上，改讀 task 期限，其餘不動 |
| 表 `oscal.poams`／`poam_items`／`assessment_risks`／`assessment_remediations`／`poam_milestones` | 三層結構齊全；`poam_milestones` 有 `task_type`、`assignee_user_id`、`assignee_org_unit_id`、`target_date`、`status`、`completed_at` | 不加表；`status`／`completed_at` 停用（不刪） |
| `pkg/jedi-oscal-v2/app/service/io/oscal_io_service.py:1584 _milestone_to_task_json`、`:1600 _export_poam` | 見下節四個落差 | 棒 1 修 |

### 流程現況（FR-140 已做好的，本案只改觸發點）

```{.mermaid cap="圖 2 — 一輪內的改善計畫在哪裡；本案改的是兩個「推進條件」"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
    ar[內部稽核結案<br/>FR-142] -- 有不符合 --> ipoam[內部改善計畫<br/>本案頁面]
    ipoam -- "送出（守門：計畫齊全）<br/>舊：全部里程碑完成" --> redo[退卡重做<br/>原卡、原人、期限＝計畫]
    redo --> plan2[第 2 次內稽計畫<br/>只剩不符合項]
    ear[外部稽核結案] -- 有不符合 --> epoam[外部改善計畫<br/>本案頁面]
    epoam -- "送出（同守門）" --> pend[本輪待覆核]
    pend -. 發起覆核 .-> rev[覆核輪新卡<br/>期限＝計畫]
```

兩處推進條件現在都呼叫 `all_items_closed`，改成「計畫齊全」後其餘機制（退卡、通知、縮範圍、覆核輪 clone）照舊。

### OSCAL 核對：對的與要修的

對照官方 `oscal_poam_schema.json`（v1.2.2）核過，決策者 2026-10-16 已點頭：

**對的**：
- `response.lifecycle` 三值 recommendation／planned／completed，稽核建議與 PM 計畫分別落 recommendation／planned，完全對應。
- risk → response → task 三層巢狀是官方正規結構，表 `assessment_risks` → `assessment_remediations` → `poam_milestones` 對得上。
- poam-item 只當封面、`related-findings` 引用 finding 不複製，符合官方設計。
- 流程（退卡、覆核）不歸 OSCAL 管。

**要修的四處**（棒 1）：

| # | 落差 | 官方落點 | 怎麼修 |
|---|---|---|---|
| 1 | task 匯出沒帶期限與負責人（`_milestone_to_task_json` 只寫 uuid／type／title／description／props／links） | `task.timing.within-date-range.end`（或 `on-date.date`）；`task.responsible-roles[].party-uuids` | 匯出補兩欄；負責人要對到 SSP `metadata.parties` 的 party-uuid（既有 `_enrich_party_user_org_names` 的反向查法） |
| 2 | `related-findings` 塞私有鍵 `finding-id`，官方 `additionalProperties:false`，驗證直接失敗 | 只允許 `finding-uuid`、`remarks` | 匯出剝掉 `finding-id`；DB 繼續存（查詢用） |
| 3 | POA&M 檔內 `findings[]`／`risks[]`／`observations[]` 是空的（它們掛在 AR 那邊），poam-item 的引用懸空 | 官方：related-findings 指向「本文件 findings 清單」 | 匯出時把被引用的 finding、risk（含 remediations）、observation 一併寫進 POA&M 檔（同 uuid；組裝不複製 DB） |
| 4 | task 有自創 `status`／`completed_at` 欄，官方 task 沒有 | 要留只能放 `props` | 本案「不追蹤完成」，欄位停用不匯出；DB 不刪 |

順手對齊兩點：task `type` 用 `action`（PM 排的事）／`milestone`（查核點），不再全部預設 milestone；計畫送出時 `risk.status` 從 `open` 改 `remediating`、`risk.deadline` 填該項最晚期限。

---

## 決策定案 D1～D12 {#decisions nav="定案"}

全部由決策者 2026-10-17 拍板。每條寫定案、理由、被排除的做法。

::: {.callout .decided}
**D1 粒度＝逐不符合評估目標；控制項只是分組**

左樹到評估目標，每條不符合評估目標各自規劃；控制項總覽只是彙總。理由：稽核判定已是逐評估目標（FR-142），POA&M 跟著走；OSCAL poam-item 本來就對 finding。被排除：控制項層一筆（第 2 次內稽縮範圍是評估目標層，對不上）。
:::

::: {.callout .decided}
**D2 只排計畫不追蹤完成**

畫面沒有打勾、沒有進度條；改好沒有＝下一次稽核判定。理由：決策者原話「只需要提供計畫就好，不需要等他改完」。`poam_milestones.status`／`completed_at` 停用不刪。被排除：保留打勾但不當守門（兩套真相）。
:::

::: {.callout .decided}
**D3 不新增任務；對評估目標底下的原任務逐一設執行人員、到期日、改善措施；守門＝每個任務三欄齊；送出後鎖定**

一條評估目標底下有幾條流程就列幾塊（一條流程多站時仍是一塊，站唯讀列在塊內），有缺失就都要退回重做，沒有「納不納入」。每塊：執行人員（可多人，預設原指派人，可加減）、到期日（即里程碑）、改善措施（可一鍵帶入稽核建議）。有一個任務沒填齊就擋送出並列出哪一個缺什麼；送出後整份唯讀，要改只能下一次稽核重排。理由：決策者原話「不能新增任務，只需要對原本的任務做建議以及設定 milestone 跟執行人員」「里程碑就是對原本的任務卡設到期時間」。被排除：PM 自建改善任務清單（第一版雛形，退回）；一個任務多個里程碑（第二版雛形，決策者裁太複雜且退卡後看不到真實到期日）；任務可勾「不納入改善」（有缺失就都要退）；送出後仍可改到期日（與退回的任務漂移）。
:::

::: {.callout .decided}
**D4 到期日逐任務設定，寫回該任務；一個任務對到多條評估目標時共用一組執行人員與到期日**

畫面上同一個任務在第二條評估目標出現時顯示「此任務同時對應 X，執行人員與到期日一併套用」，一處改兩處變。被排除：一條評估目標一個到期日套到全部任務（決策者裁逐任務）；取最早或最晚（已無多里程碑，不需要）。
:::

::: {.callout .decided}
**D5 執行人員＝任務的指派人，可多人，預設原指派人，PM 可加減；改了就是改任務的指派人**

畫面 chip 多選；送出確認表註明相較原指派人新增／移除了誰。理由：系統任務本來就是 `task_assignees` 多人；改善計畫改的人就是退回後要做的人，不另存一份。被排除：計畫上另記負責人、任務指派人不動（第一版，兩套人）。
:::

::: {.callout .decided}
**D6 Excel＝與稽核計畫、稽核結果同一份往右延伸；一列一個任務；同評估目標的灰底欄合併儲存格**

欄位契約見「Excel 契約」節。理由：決策者原則「每階段往右加欄，最後是完整軌跡」（FR-142 D9 已定）；一格換行 user 一定填錯、往右長一組 user 不會用，只剩一列一任務；合併儲存格讓重複資訊不干擾閱讀。被排除：一格多值換行（A）；每任務往右長一組欄位（B）；砍掉計畫欄只留重點（違反往右延伸原則）；執行人員拆三欄。
:::

::: {.callout .decided}
**D7 匯出兩個：改善計畫工作表（Excel）、OSCAL POA&M（JSON）；不做 Word／PDF 報告**

理由：OSCAL POA&M 頂層沒有文件正文欄位（AP 有 `terms-and-conditions`，POA&M 只有 metadata／remarks），硬放等於自創欄位；業界 POA&M 交付物就是表格（FedRAMP 官方範本是 Excel），我們的 Excel 帶完整軌跡就是那份交付物。被排除：改善計畫報告 docx／pdf（沒有正文可寫）；概要頁籤（標準沒位置）；併進稽核報告（已結案鎖定）。日後客戶要 Word 再從同一份資料產，另開 FR。
:::

::: {.callout .decided}
**D8 舊資料不管**

既有輪次的改善計畫不遷移、不處理，舊頁資料維持現狀。新頁只服務本案上線後結案的稽核次。被排除：寫遷移把控制項層拆到評估目標（要猜）；新頁唯讀顯示舊資料（多做一套讀法）。
:::

::: {.callout .decided}
**D9 側邊選單加「改善計畫」入口，表格列出我是專案經理的待規劃輪次**

與「我的稽核」並列，名稱就叫「改善計畫」不加「我的」。表格一列一輪：專案、輪次與內外稽、狀態（待規劃／已送出／覆核中／已結束）、已規劃／不符合、退回任務數、最晚到期（30 天內橘、過期紅）、結案日、送出日。狀態頁籤帶筆數、預設「待規劃」；可篩稽核性質與標準、搜專案名；分頁、每頁筆數、排序走後端（同「待辦任務」表的 lazy 分頁）。登入者判準只看「是該專案 manager」。理由：排計畫的人是 PM，入口藏在專案三層內是「不知道怎麼用」的原因之一。被排除：卡片式清單（資料多撐不住）；進度條（不好讀）；併進「我的稽核」當分頁。
:::

::: {.callout .decided}
**D10 退卡＝每個任務退回第一站整條重跑**

名詞：一個任務本身是一條流程，可能多站（證據上傳 → 主管檢核 → 顧問檢核）。現行內稽退卡（`not_met_job_redo_service._redo_one`）只把**最新那一站**改回進行中，三站都完成時重開的是顧問檢核，補證據的人改不到；DEV 現有 6 筆退卡紀錄全是單站流程，所以沒被測出來。定案：每個任務的第一站改回進行中、通知該任務全部指派人；後續站改回待辦，前一站完成才依序往下。改善措施照舊寫成任務上的系統留言（`build_redo_comment`）。實作：參考 `workflow_execution_service.revert_job`（目標站重開、後續站改待辦），但它帶操作者與輪次凍結守門，不適用系統退卡，另包一支只做「整條回到第一站」。外稽覆核輪產新任務同理從第一站開始。被排除：只重開最新站（現況）；讓 PM 選退到哪一站。
:::

::: {.callout .decided}
**D11 不設「暫行緩解措施」欄**

OSCAL `risk.mitigating-factors` 匯出留空。理由：改好之前先做的臨時防護（例如先停用登入）本來就該寫進該任務的改善措施，不需要另一格；PM 看不懂這欄是什麼。被排除：評估目標層選填一格（第三版雛形）。
:::

::: {.callout .decided}
**D12 到期日寫進該任務的每一站，不加欄位、不動 jedi-flow-engine**

查證：期限欄 `job_executions.due_at` 已存在（FR-140），但只在每一站上，流程主表沒有；`complete_job` 推出下一站不帶期限；DEV 所有有期限的流程都是單站。定案：退卡時把 PM 設的到期日寫進該任務全部站的 `due_at`（`due_at_manual=false`），「我的任務」清單與抽屜讀各站期限，畫面不用改。被排除：流程主表加欄（要動套件）；流程變數（字串鍵值，查詢排序不便）；只壓第一站（後面站沒期限可無限拖）。
:::

---

## 資料落點 {#model nav="資料落點"}

不加表。畫面欄位與 OSCAL／DB 對照：

| 畫面 | OSCAL POA&M | DB |
|---|---|---|
| 一條不符合評估目標 | `poam-items[]` 一筆：`title`／`description` 抄 finding、`related-findings[{finding-uuid}]`、`related-risks[{risk-uuid}]` | `oscal.poam_items`（既有 `generate_from_findings`） |
| 稽核缺失（唯讀） | 從 AR 讀：finding、observation（relevant-evidence）、risk（characterizations／statement）、remediation lifecycle=recommendation；匯出時內嵌進 POA&M 檔 | 不重存 |
| 改善計畫（整組） | `risk.remediations[]` 一筆 `lifecycle`＝planned，`title`＝「改善計畫」 | `oscal.assessment_remediations` |
| 一個任務的設定 | `response.tasks[]` 一筆：`type`＝action、`title`＝任務名、`description`＝改善措施、`timing.on-date.date`＝到期日、`responsible-roles[{role-id:'remediation-owner', party-uuids:[…]}]`＝執行人員（多人多個 uuid）、`props[name=job-uid]`＝對應的任務流程 uid | `oscal.poam_milestones`：`task_type`＝action、`title`、`description`、`target_date`、`assignee_user_id`（多人：第一人進欄位，全部進 `props`；或改掛關聯表，棒 1 定） |
| 送出 | `risk.status`＝remediating、`risk.deadline`＝該項最晚到期日；`poams.status`＝submitted；同時寫回任務：指派人、每一站 `due_at`、系統留言 | `assessment_risks.status`／`deadline`、`oscal.poams.status`、`compliance.task_assignees`、`compliance.job_executions.due_at` |
| 鎖定 | 無欄位；以 `poams.status` 判 | — |

**官方必填核對**：poam-item（title／description）、response（uuid／lifecycle／title／description）、task（uuid／type／title）、risk（uuid／title／description／statement／status）——每項畫面都有來源。執行人員要對到 party-uuid：專案參與者在 SSP `metadata.parties` 有對應 party（既有 `matched_user_id` 機制），匯出時反查；查不到的只寫 `props`（name=assignee）不寫 responsible-roles，不造假 uuid。`mitigating-factors` 留空（D11）。

```{.mermaid cap="圖 3 — 一條不符合評估目標存成哪些 OSCAL 物件"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
    I[poam-item<br/>封面] -- related-findings --> F[finding<br/>FR-142 判定]
    F -- related-risks --> R[risk<br/>等級／說明／status=remediating]
    R --> REC[response<br/>lifecycle=recommendation<br/>稽核建議，唯讀]
    R --> PL[response<br/>lifecycle=planned<br/>PM 改善計畫]
    PL --> T1[task action 任務 1<br/>到期日／執行人員／改善措施]
    PL --> T2[task action 任務 2]
```

---

## Excel 契約 {#excel nav="Excel"}

與 FR-141、FR-142 同一份檔案往右延伸（決策者原則：每階段往右加欄，最後是完整軌跡）。「改善計畫」頁只列判不符合的評估目標，**一列一個任務**；同一條評估目標有幾個任務就佔幾列：

| 欄 | 內容 | 匯入 |
|---|---|---|
| A（隱藏） | part_id | 比對鍵 |
| B～J（灰，合併） | FR-141 計畫欄：序號、控制項、實務要求、執行說明、評估目標、查核方法、受評人員、受評元件、稽核人員；D～J 群組預設收合 | 不讀 |
| K～Q（灰，合併） | FR-142 結果欄：稽核結果、佐證檔案、其他佐證、稽核發現、風險等級、風險說明、稽核建議 | 不讀 |
| R（隱藏） | task_id | 比對鍵 |
| S～T（淺藍） | 任務、原指派人 | 不讀 |
| U（黃） | 執行人員：一格，多人以 `;` 分隔；預設原指派人；必須是專案成員 | 讀 |
| V（黃） | 到期日：`YYYY-MM-DD`，日期驗證；必填 | 讀 |
| W（黃） | 改善措施；必填 | 讀 |

同一條評估目標的 A～Q 合併儲存格、垂直置中，評估目標之間粗上框；凍結窗格在 S 欄（往右捲仍看得到評估目標與任務名）。沒有「一格多值換行」，也沒有「往右長一組」：執行人員多人是唯一的格內多值，用分號。

「可選清單」頁只留「執行人員（專案成員）」。匯入預覽：異動列標黃、原值劃掉；執行人員對不到成員、日期格式不對 → 標紅「待確認」就地處理；同一任務出現在多條評估目標時執行人員與到期日須一致，不一致也標待確認；全部處理完才能確認。範例：[`prototype/sample/FR-143-改善計畫範例.xlsx`](prototype/sample/FR-143-改善計畫範例.xlsx)。

---

## 端到端流程 {#flow nav="流程"}

```{.mermaid cap="圖 4 — 內稽：排計畫 → 送出 → 退卡"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
    participant PM
    participant FE as 改善計畫頁
    participant BE as BE／套件
    participant Job as 任務卡
    PM->>FE: 開頁
    FE->>BE: GET /poam/objectives
    BE-->>FE: 逐不符合評估目標：稽核缺失＋底下的任務
    PM->>FE: 逐任務設執行人員、到期日、改善措施，儲存
    FE->>BE: PUT /poam/objective/<part_id>
    PM->>FE: 送出改善計畫
    FE->>BE: POST /poam/submit
    BE->>BE: 守門：有任務沒填齊 → 412 列原因
    BE->>BE: risk.status=remediating、poams.status=submitted、鎖定
    BE->>BE: 推進「內部缺失改善」站（close_round return_to_planning）
    BE->>Job: 每個任務退回第一站整條重跑、到期日寫回該任務、通知指派人
    BE-->>FE: 已送出
```

外稽同前半段，送出後 `close_round` 走「母輪→pending_reverify」；`launch_reverify` 新任務從第一站開始、`due_at` 讀 task 到期日、指派人讀執行人員。

---

## 拆卡草案 {#cards nav="拆卡"}

| 卡 | 做什麼 | 碰哪裡 | 依賴 |
|---|---|---|---|
| 1 | 套件：匯出補 timing／responsible-roles、剝 finding-id、POA&M 內嵌引用物件、task 狀態停用；`all_items_closed`→`all_items_planned`；`earliest_milestone_dates` 改評估目標層 | `pkg/jedi-oscal-v2/app/service/io/`（1,720 行超標，新開 `poam_export_parts.py`）、`pkg/jedi-compliance-audit/app/service/poam_app_service.py`、`audit_round_pass_transitions.py` | — |
| 2 | BE：`poam_objective_app_service.py`（讀逐評估目標含底下任務、整批存、送出守門＋鎖定＋寫回任務指派人與各站到期日＋推進）、route、error code | `app/flow_control/service/`、`api/project/routes/`、`common/code/` | 1 |
| 3 | FE：新頁殼＋`src/components/grc/poam/`；舊頁退役 | FE | 2 |
| 4 | Excel：一列一任務 workbook 讀寫（合併儲存格、群組收合）、執行人員比對、預覽 | `poam_workbook_*.py`、FE 預覽 | 1 |
| 5 | 退卡改為每個任務退回第一站整條重跑、到期日寫進每一站（D10／D12）；覆核輪新任務同 | `not_met_job_redo_service.py`、`launch_reverify`、新 `job_restart_service.py` | 1 |
| 6 | 側邊選單「改善計畫」入口（D9）：讀模型＋端點、FE 清單頁、`ui_routes` 與權限 seed migration | `app/readmodel/`（照 `auditor_dashboard_service.py`）、FE 新頁、`scripts/sql/` | 2 |

---

## 不做清單 {#out nav="不做"}

- 任務完成追蹤、進度條、到期後自動結案（D2）。
- 任務逐筆開新卡（FR-140 D1 已排除）。
- 舊輪 POA&M 遷移或唯讀顯示（D8）。
- 改善計畫報告 Word／PDF、概要頁籤（D7）。
- 暫行緩解措施欄（D11）。
- 一個任務多個里程碑、PM 自建改善任務（D3）。
- `poam_milestones.status`／`completed_at` 刪欄（停用不刪）。

---

## 坑與陷阱 {#pitfalls nav="坑"}

| # | 坑 | 後果 | 對策 |
|---|---|---|---|
| 1 | `_close_on_pass` 守門改成「計畫齊全」後，舊流程範本（只有 audit→poam→End）的 close_round 也走新守門 | 舊範本輪次若沒排任務會被擋 | 守門訊息列出哪幾條未排；舊輪已 closed 的不受影響 |
| 2 | 負責人對 party-uuid 查不到 | 匯出 responsible-roles 造假 uuid 不合規 | 查不到只寫 props 不寫 responsible-roles |
| 3 | `related-findings` 的 `finding-id` 被多處讀（`_item_findings` 等） | 剝錯地方查詢壞 | 只在匯出 JSON 時剝，DB 不動 |
| 4 | 一張任務卡對多條不符合評估目標、各自期限不同 | 卡期限取哪個 | D4：取最早；確認表先列 |
| 5 | 同一任務在 Excel 多列到期日或執行人員不一致 | 不知道信哪列 | 預覽標紅待確認，不猜 |
| 6 | `RoundPoamView.vue` 890 行、`poam_app_service.py` 493 行、`oscal_io_service.py` 1,720 行 | 超標不准往裡加 | 全部新開檔，舊檔只改導向或退役 |
| 7 | 內嵌進 POA&M 檔的 finding／risk 與 AR 檔同 uuid | 讀者以為兩份資料 | 官方允許（POA&M 可自帶 findings），匯出 `remarks` 標「引自稽核結果 <uuid>」 |
