FR-141 · 討論稿 · 2026-10-15

稽核計畫重做

計畫概要與評估目標兩個頁籤、一份兩頁 Excel 匯出即匯入範本、亞航格式報告稿匯出

現在的稽核計畫頁以「行程」為單位、查核方法只能填到控制項,顧問實際工作是逐條評估目標(AO)決定怎麼查、查誰、誰來查;而且計畫沒辦法匯出成報告稿。本案把這一頁整個重做:「計畫概要」頁籤放亞航報告稿的六段表頭,「評估目標」頁籤用左樹右表單逐條填查核方法與對象;一份兩頁的 Excel 匯出就是匯入範本,匯出三選一(報告稿、Excel、打包)。資料照 OSCAL 評估計畫的標準欄位存。D1~D4 已由決策者裁定,請先開 雛形 點一遍,再回來看規格。

D1~D4 已裁(2026-10-15) 雛形第二輪完成:左樹右表單 動兩支套件:jedi-oscal-v2、jedi-compliance-audit 與 FR-140 卡 3 的 AP 複製對齊

雛形審過、拍板後寫 design.md,本稿不再改寫。

狀態:討論稿(D1~D4 已裁,雛形第二輪完成待審;P1~P3 待裁) | 日期:2026-10-15 | 母卡:CM-2799 | 雛形卡:CM-2800 | 雛形:prototype/index.html

路徑慣例:BE 路徑相對 compliance-manager-be/;pkg/ 代表 ~/Projects/Jedicogy/module/jedi-python-package/<套件>/<套件底線名>/;FE 路徑相對 compliance-manager-fe/。

名詞:AP=稽核計畫(assessment plan);AO=評估目標(assessment objective),每條控制項底下的檢查項目,全系統用 part_id 當鍵(格式 <控制項代碼>_obj.N);計畫概要=一份 AP 的表頭,原稱「場次」(依據、目的、稽核單位、日期起迄、範圍、方法論、稽核人員);activity=OSCAL AP 裡「一個查核動作」的物件,本案一條 AO 一個;稽核次=FR-140 新增的概念,一輪內每做一次稽核(內部或外部)一筆,每筆自己一份 AP;受評對象=被查的人或系統元件、資產;查表頁=Excel 裡隱藏的工作表,放下拉選項。

§1

變更紀錄

日期 變更 對應
2026-10-15 初稿:需求、兩份範例檔結構、已裁 D1~D4、大量 AO 策略、現況盤點、OSCAL 落點、Excel 契約、匯出三選一、三棒拆卡草案 母卡 CM-2799
2026-10-16 決策者看第二輪雛形後:D1 改判為一格多值(換行或分號)+填寫說明頁+可選清單頁;頁籤「場次」改名「計畫概要」;稽核單位改單行只填公司名稱;方法組不限數量 D1
2026-10-15 決策者看第一版雛形後:AO 頁籤改左樹右表單、匯入預覽併入同畫面、稽核次麵包屑;回答「內外稽核不會混,各自一份 AP」 雛形第二輪

§2

分工概述(30 秒版)

整案一句話:稽核計畫頁換成「計畫概要」+「評估目標」兩個頁籤,顧問逐條 AO 填怎麼查、查誰、誰查;一份兩頁 Excel 匯出即匯入範本;一鍵匯出亞航格式報告稿。

棒 做什麼 產出 完成怎麼判定(決策者親手做什麼、看到什麼)
1 雛形 純前端假資料刻兩個頁籤、匯入預覽、報告稿預覽,加一份範例 Excel prototype/ 四頁+sample/*.xlsx 開雛形索引頁點一遍:切頁籤、左樹點控制項、右側加方法組、套用到所有 AO、匯入預覽對應未知方法;用 Mac Excel 開範例檔點每個下拉
2 計畫概要+報告稿 套件加「條款與說明」表;計畫概要頁籤;Excel 讀寫骨架與第一頁;docx/pdf 匯出;FR-043 docx 匯入改填新欄位 新表 migration、計畫概要頁籤、匯出按鈕三選一(先通報告稿與 Excel 第一頁) DEV 開一輪的稽核計畫,填六段文字與日期起迄存檔,重整還在;按匯出選報告稿 docx,用 Word 開看到 1~6 段與自己填的內容;選 Excel 開第一頁看到同樣內容
3 AO+Excel 第二頁 套件 activity 寫 AO、受評對象、負責人員;評估目標頁籤(左樹右表單、控制項預設套用、篩選);Excel 第二頁讀寫與查表頁;匯入預覽未知方法對應;覆核輪與 FR-140 第 2 次內稽的 AP 複製改 AO 粒度;AR 讀 AP 改逐 AO 評估目標頁籤、Excel 兩頁完整來回、clone 改寫 DEV 在評估目標頁籤填三條 AO 存檔,匯出 Excel 第二頁看到那三列有值、其他列預帶控制項預設;改兩格匯回,預覽頁標出那兩格、確認後畫面更新;啟動覆核輪,新 AP 只剩不合格的 AO

依賴:1 → 審雛形 → design.md 與開 2、3 的卡 → 2、3 可平行(碰的檔不同)。第 3 棒的 AP 複製要接在 FR-140 卡 3(CM-2788)之後,同一支 audit_round_app_service.py 周邊不同時派。AR 側改逐 AO 另開 FR。

%%{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[棒 1 雛形<br/>CM-2800] --> R{決策者審}
    R --> D[design.md<br/>開棒 2、3 的卡]
    D --> B2[棒 2 計畫概要+報告稿<br/>terms 表、docx、Excel 第一頁]
    D --> B3[棒 3 AO+Excel 第二頁<br/>activity 改 AO 粒度、clone]
    F3[FR-140 卡 3 CM-2788<br/>開新稽核次、AP 複製] -.先做.-> B3
    B3 -.另開 FR.-> AR[AR 逐 AO 判定與匯出]
圖 1 — 三棒依賴與 FR-140 接點

§3

需求

決策者原話(2026-10-15)

  • 稽核計畫要多一塊「文字表單」,內容是報告稿的 1~6 大點,時間用時間範圍;存了要能匯出類似範例稿的 docx 或 pdf;看有沒有合適的 OSCAL 欄位放。
  • 現在的稽核計畫很難填寫,不能 by AO 只能 by 控制項,要改成 by AO 填寫、要提供 Excel 匯出匯入,格式參考亞航附件;要加每一項的查核方法與指引、受評對象、稽核人員,都可能多筆,目前人員跟元件放在一起。
  • 場次就是 docx 那份的資料;詳細內容在 Excel 處理,顧問 by AO 填他要怎麼檢查;後續 AR 也會改。
  • 畫面不要用舊的,舊的真的很難用;場次與 AO 分兩個 Tab;先出雛形。
  • 一份 Excel 兩個 sheet:一頁是 docx 表單、一頁是 AO 規劃,這樣不用分兩種匯入;匯出要可以選文件稿或 Excel 完整資料,打包在一起也可以。
  • 看第一版雛形後:AO 部分參考 SSP 那種有導航的模式,預覽也找更好的方法;內外稽核會不會混、有沒有辦法分開。

兩份範例檔長什麼樣

報告稿 docx(亞航-CMMC L1內部稽核報告20260612(稿).docx)

只有段落、沒有表格,七段:1 依據、2 目的(三點條列)、3 稽核單位、4 稽核日期(一天+時段)、5 稽核範圍(指向 SSP 文件編號)、6 稽核方法(三段方法論文字)、7 稽核結果(三點發現+附件指向 Excel)。第 7 段是稽核結果,屬 AR 那一側,本案在報告稿留佔位。

附件 xlsx(…附件-稽核紀錄.xlsx)

一張工作表,表頭 4 列(專案名稱、受驗型態、受稽核人數、稽核日期),正文 59 列=59 條 AO、15 條控制項。欄:序號、索引號(控制項代碼)、實務要求、執行說明、Objectives 控制目標(AO)、Status(實際內容是「方法:指引」多行)、Evidence 佐證(多行檔名)、稽核結果(全 MET)。同一控制項的實務要求只寫第一列。「方法」前綴七種:文件檢查、文件檢視、訪談、現場觀察、電腦設定比對、門禁系統設定比對、測試。

新畫面長什麼樣

一頁兩個頁籤,頁首是稽核次麵包屑「專案 › 第 1 輪 › 第 N 次 內部/外部稽核 › 稽核計畫」。

頁籤 內容 填寫方式
計畫概要 依據、目的、稽核單位、稽核日期起迄、稽核範圍、稽核方法論六段+這場的稽核人員名單;上方唯讀標示「第 N 次 內部/外部」(從稽核次讀) 多行文字框;稽核單位是單行(只填公司名稱,報告稿印成「由○○協助辦理。」);日期時間起迄;人員多選(可當場新增)
評估目標 左側控制族群→控制項樹(完成狀態點、搜尋、篩選「全部/尚未填/已覆寫稽核人員」);右側一次一條控制項:實務要求與執行說明(唯讀)、「控制項預設」區塊(方法組+受評人員+受評元件/資產+稽核人員,「套用到底下所有 AO」)、每條 AO 一張小卡(方法組可加減、受評人員、受評元件/資產、稽核人員預帶計畫概要名單可覆寫);上一條/下一條 下拉、多選、文字框;「總覽」子檢視是唯讀大表格

匯入預覽與正式頁同一個畫面、進入「預覽模式」:左樹節點標「有變更/有問題」,右側被覆寫的欄位高亮、原值灰字劃掉,未知方法就地出對應下拉,頂部橫幅計數與「確認匯入」。


§4

已裁決策

D1 Excel 多值表示=一格多值,靠填寫說明與預覽檢查兜底(2026-10-16 改判)

  • 查核方法與指引:一格,一行一組,寫成「方法:指引」,和亞航原表一樣。只認換行分組,不認逗號或分號,因為指引內文常有逗號、分號。
  • 受評人員、受評元件/資產、稽核人員:各一格,多個值用換行或分號(全形「;」或半形「;」)隔開。稽核人員留空=沿用計畫概要。
  • 不限組數與人數,畫面上方法組也可一直加。
  • 檔案附「填寫說明」頁(含格內換行快捷鍵)與看得到的「可選清單」頁(人員、元件、方法的正確寫法,可複製)。
  • 匯入時名字或方法詞對不到清單,預覽頁在那一格就地讓使用者對應(同 D4)。

為什麼:決策者看雛形後判斷顧問不會用往右延伸的固定格數。業界匯入範本(Salesforce 多選欄位、Jira、ServiceNow)多數也是一格多值加分隔符,配「填寫說明+可選清單+匯入檢查」三件套;固定格數表太寬、第 N+1 個不知道放哪。

被排除:固定格數每格一般下拉(2026-10-15 原裁,表寬約 22 欄,決策者看過後否決);xlsm 多選巨集(Mac/LibreOffice 不相容、資安擋);逗號當方法分隔(與指引內文衝突)。

D2 稽核人員=計畫概要預設、AO 可覆寫

計畫概要頁籤填整場名單,AO 每列預帶、可個別改,改過的列標「已覆寫」可還原;Excel 每列印有效值。

為什麼:只在計畫概要層看不出「這條誰查」;只在 AO 層每列都要重填、與報告稿「稽核單位」段脫節。

D3 受評對象拆兩欄

「受評人員」一欄、「受評系統元件/資產」一欄。元件與資產在 OSCAL 都是 subject、只是 type 不同,合一欄選。

為什麼:現況一個分組多選把人員、元件、資產混在同一欄,決策者點名不好用;拆三欄則 Excel 與畫面各多一欄,沒有對應的業務需要。

D4 匯入遇到選單沒有的方法詞=預覽頁提示、使用者對應

例「文件檢視」「門禁系統設定比對」目前後台選單沒有。匯入預覽標出,使用者逐個對應到既有選項或略過;選單本身由管理員在後台 system_menus 維護。

為什麼:直接擋下顧問會卡住;自動新增選項會讓打錯字也變一個選項、選單長髒。

大量 AO 的填寫策略(決策者 2026-10-15 同意)

CMMC L2 約 110 條控制項、320 條 AO。控制項層先填一組方法/人員再「套用到底下所有 AO」只改例外;左樹預設收合、可篩「尚未填」;Excel 匯出時預帶值已填好、使用者只改少數格。「填好」的定義:至少一個方法,且受評人員或元件/資產至少一個。

內外稽核不會混:每一次稽核各自一份 AP

FR-140 之後每一筆稽核次(第 1 次內稽、第 2 次內稽、外稽)自己一份完整的 AP,各有計畫概要表頭與 AO 表。要分開的是進入點,不是 AP 內部:總覽頁選第幾次進來(FR-140 卡 10)、稽核計畫頁首麵包屑帶「第 N 次 內部/外部」、可切換同一輪其他次唯讀看。第 2 次內稽從第 1 次複製來的 AO 標「承襲自第 1 次」,改過就消。AP 內容本身不需要任何「內部/外部」欄位。

AR 另案

評估結果(AR)之後另開 FR 改成逐 AO 讀取與匯出,Excel 的「佐證資料」「稽核結果」兩欄到那時接上。本案 AP 匯出不印那兩欄、匯入忽略。本案第 3 棒只把 AR 「從 AP 反推方法」的讀取改成逐 AO(不改 AR 畫面)。

待裁(雛形審完一併裁)

P1 第 2 次內稽複製 AP 縮範圍的單位(FR-140 卡 3 接點)

FR-140 卡 3 定「複製上一次內稽的 AP、縮到不合格的控制項」。改成 AO 粒度後有兩種縮法:

  • 甲:只留不合格的 AO——第 2 次只查上次沒過的那幾條目標,最省;但同一控制項其他 AO 在第 2 次的 AP 看不到。
  • 乙:不合格 AO 所屬控制項整條保留——與 FR-140 現有文字一致(縮到控制項),顧問看得到整條控制項的脈絡。

我的建議:乙。AR 判定逐 AO,但稽核現場是整條控制項一起看;且 FR-140 卡 3、覆核輪 launch_reverify 既有邏輯都是控制項粒度,乙改動最小。甲留給未來「AO 層勾選」需求。

P2 報告稿附件表要不要多印「受評對象」「稽核人員」兩欄

亞航原表沒有這兩欄(它們在原表是混在 Status 文字裡或根本沒寫)。雛形第一版多印了。建議:印,顧問交報告時這兩欄有用;但做成匯出時可勾選。

P3 方法選單對齊

雛形用六個方法(訪談、文件檢查、現場觀察、電腦設定比對、測試、門禁系統設定比對);DEV 的 system_menus AUDIT_METHOD 群組現有 INTERVIEW/DOCUMENT_REVIEW/EXAMINE/COMPUTER_CONFIG_COMPARE/TEST/ACCESS_CONTROL_CONFIG_COMPARE(pkg/jedi-compliance-audit/app/service/import_adapter/method_keywords.py:11),有「檢視」沒有「現場觀察」。建議:第 2 棒出一支 phase=seed migration 補「現場觀察」,不在程式寫死。


§5

現況盤點:接入點 × 現況 × 本案動作

座標以 2026-10-15 為準,runner 開工先開檔核一次。

① 資料模型(套件 jedi-oscal-v2,schema oscal,pkg/jedi-oscal-v2/infra/model/ap/)

表 現況 本案動作
assessment_plans(oscal_ap.py:26) uuid、metadata_id(1:1 oscal.metadata)、status、import_ssp_id、local_definitions_remarks 不改
ap_reviewed_controls(:22,與 AP 1:1) control_selections 必填、control_objective_selections 選填——從未寫入,只在 clone 時原樣搬(主專案 assessment_plan_app_service.py:401) 第 3 棒寫入 AO 的 part_id
ap_assessment_activities(:23) uuid、title、description、steps JSONB(每筆 {uuid,title,description,props[method]})、related_controls(只寫 control-selections,到控制項層)、responsible_roles 第 3 棒:一條 AO 一個 activity;related_controls 加 control-objective-selections;responsible_roles 帶 party
ap_tasks(:24) parent_id、type、title、description、timing、associated_activities、subjects、responsible_roles JSONB 第 2 棒:一份 AP 一個 task 當計畫概要容器,timing.within-date-range 放日期起迄;第 3 棒:associated_activities[].subjects 放每條 AO 的受評對象
ap_task_subjects、ap_task_participants(:25) FR-040 的行程層受評對象與參與人員連結表 第 3 棒後退役(新模型不再用行程層對象);migration 不刪表,舊資料留
ap_assessment_subjects(:25) AP 層受評對象 保留,存檔時由 AO 列聯集回填(沿用 FR-040 做法)
ap_local_objectives(:23) control_id、parts JSONB,主專案與套件都沒有呼叫端 不用(AO 以 part_id 引用 catalog,不複製)
oscal.parties/oscal.roles 掛在 AP metadata 下;稽核人員已是 party、角色放 props responsible-role=assessor(assessment_plan_app_service.py:70-71) 第 2 棒:稽核單位存 organization 型 party
沒有表:terms-and-conditions、assessment-assets、objectives-and-methods、AP 的 back-matter FR-037 差距審查時略過 第 2 棒新增 ap_terms_and_conditions;其餘不做

套件服務:pkg/jedi-oscal-v2/app/service/ap/assessment_plan_service.py:43 的 set_reviewed_controls(:113)、set_assessment_subjects(:153)、set_tasks(:184)一律整批先刪後寫,task uuid 每次重產;app/service/io/oscal_io_service.py:263 export_oscal 不支援 assessment-plan。

② 主專案 AP 服務與端點

接入點 現況 本案動作
端點(api/project/routes/audit_round_route.py:144-205) GET /audit-round/<round_uid>/ap、PUT /ap/<ap_uid>/reviewed-controls、PUT …/assessment-subjects、PUT …/tasks、`GET POST …/parties`;FE 實際只呼叫 tasks 與 parties
App service app/flow_control/service/assessment_plan_app_service.py:74(730 行) _METHOD_PROP(:68)、_methods_to_props(:115)、指引放 activity.steps[](:130-165)、related_controls 只到控制項(:169-178)、_ap_detail(:247)、clone_ap_for_round(:330,覆核輪 clone 並收斂範圍)、set_tasks(:583)、resolve_ap_and_check_auditor(:190,限 auditor/manager,過了規劃階段唯讀) 檔已近上限,新邏輯一律新檔:ap_terms_app_service.py(第 2 棒)、ap_objective_app_service.py、ap_workbook_app_service.py、ap_clone_service.py(第 3 棒),原檔一行呼叫。權限守門沿用 resolve_ap_and_check_auditor
AR 反推 AP(pkg/jedi-compliance-audit/app/service/assessment_result_app_service.py:303) 把 AP activities 反推成 {control_id: {methods, guidance, subjects}} 第 3 棒改成 {ao_part_id: …},控制項層由 AO 聚合;AR 畫面不動
覆核輪 clone(pkg/jedi-compliance-audit/app/service/audit_round_app_service.py:596 launch_reverify) clone AP 縮到 not_met 控制項;FR-140 卡 3 會抽成共用函式供「第 2 次內稽」重用 第 3 棒接在 FR-140 卡 3 之後改成 AO 粒度(P1 裁法)
FR-043 docx 匯入(pkg/jedi-compliance-audit/app/service/ap_docx_import_app_service.py:288-336) adapter 已解析出 basis/objective/audit_org/methodology/scope/audit_date(ap_report_parser/base.py:13-31),但全部塞進每筆 task 的 description 第 2 棒改填 terms 表與 organization party;adapter 不擴充
AO key(pkg/jedi-compliance-audit/app/service/ao_derivation.py) part_id <control_id>_obj.N,控制項無 AO 時退回 control_id;wecm、AR、SSP Excel 隱藏欄、程序書池全用同一把 key 第 3 棒沿用,不另造

③ 可沿用的匯出匯入能力

能力 位置 本案用法
SSP docx/pdf 匯出 app/oscal/service/export/ssp_export_app_service.py:27、ssp_docx_generator.py:47(docxtpl 範本+python-docx 追加)、ssp_libreoffice_converter.py:57(LIBREOFFICE_CMD,逾時 60 秒)、範本 app/oscal/templates/ssp/ssp_cmmc_template.docx 由 scripts/generate_ssp_docx_template.py 產 第 2 棒照同套路加 ap_report_template.docx 與 ap_docx_generator.py,pdf 走同一支 converter
Excel 範本產生器 app/module_frame/excel_template/generator.py:132 ExcelTemplateGenerator,同目錄 data_validation_builder.py、lookup_builder.py、cell_protection.py、styles.py 第 2、3 棒:查表頁、list 型資料驗證、鎖唯讀欄
隱藏 A 欄放 AO part_id app/oscal/service/ssp_control_impl_import_service.py 檔頭與 export_excel(:105)、confirm_import(:375) 第 3 棒第二頁同慣例
公式注入防護 jedi_common/utils/export_safety.py set_text_cell、neutralize_formula 所有寫入儲存格
FE 匯入 dialog src/components/grc/JobImportDialog.vue(426 行,AP 範圍的 job 匯入,驗證→逐列→確認) 第 3 棒上傳步驟參考;預覽改在正式頁的預覽模式
FE 匯出 src/composables/useSspDocumentExport.js(docx/pdf/odt 選格式+downloadBlob) 第 2 棒擴成三選一

④ FE 現況

接入點 現況 本案動作
填寫頁 src/views/project/RoundApAuthoringView.vue(995 行,已超標) 以行程為單位:TaskRow(:34-44)、方法 MultiSelect(:719)+每方法指引 Textarea(:727)、控制項 MultiSelect 只攤到控制項(:142-145)、受評對象分組 MultiSelect 混人員/元件/資產(:146-160、:743)、參與人員 MultiSelect(:754);單一存檔 PUT /ap/:apUid/tasks(:566) 整頁換掉:新開 RoundApPlanView.vue(殼)+ap-plan/SessionTab.vue、ObjectiveTab.vue(左樹右表單)、ObjectiveControlForm.vue、ApImportPreviewBanner.vue、composable useApPlan.js、useApObjectiveTree.js。舊頁留到新頁上線後再刪
路由 src/config/router/index.js:1118 project-ap-authoring-round 指向舊頁 改指新頁
API 常數 src/config/api/api.js:212-221、:252-253 AUDIT_ROUND、AP_AUTHORING、AP_DOCX_IMPORT 加 terms、objectives、export、xlsx-imports
總覽頁 ProjectAuditorOverview.vue:1150(2128 行) 按鈕導到 ap-authoring FR-140 卡 10 加稽核次切換;本案頁首麵包屑讀同一組資料
i18n src/config/locales/i18n/zh-tw/ap-authoring.json 94 行 新開 ap-plan.json

🔴 三個靜默落差

  1. control_objective_selections 欄位存在但從未寫入——系統其他部分(規劃任務卡、AR 判定)早已逐 AO,只有 AP 停在控制項層,AR 反推時再用控制項聚合。本案是把 AP 拉齊,不是另起一套。
  2. FR-043 匯入把六段文字塞進每筆行程的說明,使用者看到的是重複一遍的長文;沒有欄位可放,所以沒人發現。
  3. set_tasks 先刪後寫、task uuid 每次重產:任何下游拿 task uuid 當鍵都會斷。第 2 棒改成「一份 AP 一個 task」時要查 wecm 與 AR 有沒有引用 task uuid(探脈時未見,runner 開工 grep 一次)。

§6

資料模型草案

OSCAL 落點

畫面欄位 OSCAL 路徑 套件表/欄 本案動作
依據、目的、稽核範圍、稽核方法論 assessment-plan.terms-and-conditions.parts[](name=basis/objective/scope/methodology,prose) 新表 oscal.ap_terms_and_conditions 第 2 棒
稽核單位 metadata.parties[type=organization]+responsible-parties[role=assessor] oscal.parties(既有) 第 2 棒
稽核人員(計畫概要) metadata.parties[type=person],props responsible-role=assessor oscal.parties(既有做法) 不改
稽核日期起迄 tasks[0].timing.within-date-range{start,end} ap_tasks.timing JSONB 第 2 棒:一份 AP 固定一個 task
第 N 次 內部/外部 稽核次表(FR-140);OSCAL 匯出時 props audit_nature、nature_seq(FR-140 卡 10) compliance.audit_round_passes 讀,不寫
每條 AO 的查核方法與指引 activity.steps[](title=指引,props method) ap_assessment_activities.steps(既有結構) 第 3 棒:一條 AO 一個 activity
AO 本身 activity.related-controls.control-objective-selections[].include-objectives[].objective-id=part_id ap_assessment_activities.related_controls 第 3 棒
每條 AO 的受評人員、元件/資產 tasks[0].associated-activities[activity-uuid].subjects[](type=party/component/inventory-item) ap_tasks.associated_activities JSONB 第 3 棒
每條 AO 的稽核人員 activity.responsible-roles[role=assessor].party-uuids ap_assessment_activities.responsible_roles 第 3 棒;空=沿用計畫概要(D2)
控制項層預設 不進 OSCAL;只是畫面與 Excel 的填寫輔助,存檔時展開到每條 AO — 第 3 棒 FE 邏輯

新表 oscal.ap_terms_and_conditions

欄 型別 說明
id bigserial PK
assessment_plan_id FK → assessment_plans.id 與 AP 1:N(一段一列)
part_name varchar basis/objective/scope/methodology;值集合進 system_menus group ap_terms_part 供畫面標籤與順序
title text 可空
prose text 內容
sort_order int
props、links jsonb 共用欄
審計欄 同其他 oscal 表

migration 進套件 jedi-oscal-v2/migrations/,主專案不另出。GRANT 給 cm_app。

Excel 契約(填寫說明、計畫概要、評估目標、可選清單四頁)

「計畫概要」頁:直向「欄位/內容」兩欄,列:依據、目的、稽核單位(只填公司名稱)、稽核日期起、稽核日期迄、稽核範圍、稽核方法論、稽核人員(換行或分號隔開,匯入比對人名)。隱藏列放 AP uid 與匯出時間戳當版本比對用。

「評估目標」頁:

欄 內容 匯入
A(隱藏) AO part_id 比對鍵;找不到→預覽標「找不到 AO」略過
B 序號、C 控制項代碼、D 實務要求、E 執行說明、F 評估目標 灰底唯讀 忽略
G 查核方法與指引 一行一組「方法:指引」 依換行切組、依第一個全形或半形冒號切方法與指引;沒有冒號的行當指引、方法空白;方法詞不在選單→D4 就地對應
H 受評人員 換行或分號隔開 名字比對 SSP parties;對不到→預覽就地對應
I 受評元件/資產 同上 比對 SSP components+inventory-items
J 稽核人員 同上;空白=沿用概要 與概要名單相同=不算覆寫

一條 AO 一列,表頭凍結,同控制項的 D/E 只在第一列寫。

「填寫說明」頁:每一欄怎麼填、格內換行快捷鍵(Mac:Option+Enter;Windows:Alt+Enter)、匯入流程。

「可選清單」頁(看得到):查核方法、受評人員、受評元件/資產、稽核人員四欄,匯出時由 SSP 與 system_menus 帶入,讓使用者複製正確寫法。

匯出三選一

選項 內容 實作
報告稿 docx/pdf 1~6 段照亞航排版(標題帶「第 N 次 內部/外部」)、第 7 段「稽核結果」佔位「(評估結果完成後帶入)」、附件 AO 表(方法與指引合回「方法:指引」一行一組;P2 決定是否印受評對象與稽核人員) docxtpl 範本+LibreOffice 轉 pdf
Excel 完整資料 上面兩頁檔,匯出即可改完匯回;空 AP 匯出就是空白範本 ExcelTemplateGenerator
打包 zip docx+xlsx 兩者串起來

§7

端到端流程

%%{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 U as 顧問
    participant FE as 稽核計畫頁
    participant BE as AP 服務
    participant PK as jedi-oscal-v2
    U->>FE: 計畫概要頁籤填六段+日期起迄+稽核人員
    FE->>BE: PUT /ap/{uid}/terms
    BE->>PK: 寫 ap_terms_and_conditions、organization party、tasks[0].timing
    U->>FE: 評估目標頁籤:控制項預設→套用→改例外
    FE->>BE: PUT /ap/{uid}/objectives(展開後逐 AO)
    BE->>PK: 每 AO 一個 activity(steps、objective-selections、responsible-roles)+ tasks[0].associated-activities.subjects
    U->>FE: 匯出 → 報告稿 pdf
    FE->>BE: GET /ap/{uid}/export?format=pdf
    BE->>BE: 讀同一份內部模型 → docxtpl → LibreOffice
    BE-->>U: 下載
圖 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'}}}%%
sequenceDiagram
    participant U as 顧問
    participant FE as 稽核計畫頁(預覽模式)
    participant BE as AP 工作簿服務
    U->>FE: 上傳兩頁 Excel
    FE->>BE: POST /ap/{uid}/xlsx-imports/parse
    BE->>BE: 串流讀、列數上限、第一頁→計畫概要 diff、第二頁→依 part_id 比對、方法詞比對選單
    BE-->>FE: parse_uid+diff(變更/找不到 AO/未知方法)
    FE->>U: 左樹標徽章、右側高亮、未知方法就地對應
    U->>FE: 處理完待辦 → 確認
    FE->>BE: POST /ap/{uid}/xlsx-imports/{parse_uid}/confirm(帶對應表)
    BE->>BE: 寫 terms+objectives(同存檔路徑)
    BE-->>FE: 新狀態
圖 3 — Excel 匯入與預覽

§8

拆卡草案

第 1 棒已建卡(CM-2800)。第 2、3 棒在 design.md 定案後一次建完。排序以「決策者何時看得到畫面」為準。

棒 卡 做什麼 碰哪裡 套件 依賴 驗收
1 CM-2800 雛形兩輪 prototype/ — — 決策者點過
2 2a terms 表+organization party+單一 task 日期起迄;PUT …/terms;FR-043 匯入改填新欄位 套件 model/migration/service;主專案 ap_terms_app_service.py、route jedi-oscal-v2、jedi-compliance-audit design DEV 存六段重整還在;匯入亞航 docx 六段進新欄位、行程說明不再重複
2 2b 計畫概要頁籤(新頁殼+SessionTab)+稽核次麵包屑讀 FR-140 卡 10 的 API(卡 10 未完成時先讀輪次) FE 新檔、路由改指 — 2a 畫面填存;麵包屑顯示第 N 次
2 2c 報告稿 docx/pdf 匯出+Excel 第一頁匯出+zip;FE 匯出三選一 ap_docx_generator.py、範本、ap_workbook_app_service.py(第一頁)、route、FE composable — 2a Word 開 docx 看 1~6 段;Excel 第一頁內容相同
3 3a activity 改 AO 粒度:PUT …/objectives、_ap_detail 回逐 AO;AR 反推改逐 AO;assessment_subjects 聯集回填 套件 service;主專案 ap_objective_app_service.py;jedi-compliance-audit AR 讀取 jedi-oscal-v2、jedi-compliance-audit 2a API 存三條 AO 再讀回一致;AR 頁方法顯示不變
3 3b 評估目標頁籤:左樹右表單、控制項預設套用、篩選、總覽子檢視、承襲標籤 FE ObjectiveTab.vue 等新檔 — 3a 照雛形第二輪操作一遍
3 3c Excel 第二頁匯出(查表頁、資料驗證、鎖格、隱藏 A 欄)+匯入 parse/confirm+預覽模式 FE ap_workbook_app_service.py 第二頁、ap_xlsx_import_service.py、FE 預覽模式 — 3a、3b 匯出改兩格匯回、預覽標出、確認後畫面更新;未知方法對應
3 3d AP 複製改 AO 粒度(覆核輪+FR-140 第 2 次內稽,P1 裁法) ap_clone_service.py(抽自 clone_ap_for_round 與 FR-140 卡 3 的共用函式) jedi-compliance-audit 3a、FR-140 卡 3 CM-2788 完成 啟動覆核輪新 AP 只剩不合格項且 AO 列的方法/人員承襲
3 3e 方法選單 seed(P3)+ap_terms_part 選單 seed+i18n+舊頁下線 scripts/sql/*-seed-*.sql、FE i18n、路由 — 3b 後台看到新選項;舊路由不可達

§9

不做清單

像但不做 為什麼
AR 畫面改逐 AO、「佐證資料」「稽核結果」欄 另開 FR;本案只改 AR 從 AP 讀方法那一段
AP 的 OSCAL JSON 匯出 套件 export_oscal 不支援 AP;列為第 3 棒可選,不進驗收
docx 匯入擴充(其他格式 adapter、AO 表匯入) 新主路徑是 Excel;FR-043 只改落點
一份 AP 多個行程 決策者定「計畫概要就是 docx 那份」,一份 AP 一個;多次稽核由 FR-140 的多稽核次承接
assessment-assets、objectives-and-methods、AP back-matter 本案用不到
刪 ap_task_subjects/ap_task_participants 表 舊資料留;下線舊頁後另開清理卡

§10

坑與陷阱總表

# 坑 影響 對策
1 set_* 先刪後寫、task uuid 重產 下游拿 task uuid 當鍵會斷 2a 改單一 task 前 grep wecm、AR、job 有無引用 task uuid
2 四槽位讀取者十餘處(FR-140 盤點) FR-140 改完前,AP 掛在輪次上;改完後掛在稽核次 本案所有讀 AP 的新碼一律經 FR-140 卡 1 的 pass domain service 取「目前稽核次」,不直接讀輪次
3 覆核輪 clone 靠「同名」重對應 party(assessment_plan_app_service.py:348) 同名稽核人員會對錯 3d 改以 party uuid 對應,同名時才退回名字
4 ProjectAuditorOverview.vue 2128 行、RoundApAuthoringView.vue 995 行皆超標 不能往裡加 新頁全新檔;總覽頁只改一行導向
5 Excel 下拉的查表頁選項是匯出當下快照 匯出後 SSP 加了人員,舊檔選不到 匯入時人名比對不到→預覽標出讓使用者對應(同 D4 機制)
6 DEV 方法選單與雛形不一致(P3) 匯入亞航檔「現場觀察」會變未知 seed migration 補選項
7 控制項無 AO 時 key 退回 control_id Excel 第二頁該控制項只有一列、part_id=control_id 匯出匯入都要處理這個退路(沿用 ao_derivation.py)
8 同一支 audit_round_app_service.py 周邊,FR-140 卡 3/4/11 與本案 3d 都要改 平行派會互撞 3d 排在 CM-2788 之後,母卡標序列組
FR-141 · 討論稿 · 2026-10-15 · 輸入:決策者 2026-10-15 需求與 D1~D4 裁示、亞航 CMMC L1 報告稿與附件 · 現況依據:程式碼行號(2026-10-15)· 沿革見 git log