給下個 session 的 prompt:「讀
docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-15-Q1-prep-jobs-START-HERE-handoff.md,先過 §0 讀序硬 gate 懂 Q1 要解決什麼(requirement §4.3b + specq1-prep-workflow-job-design.md),能答冷接自檢 4 問再碰 code。跑 §6 pre-flight + §7 verify,接手 Q1 實作 5 增量。」 本檔自包含。狀態為 2026-06-15 verified(現場跑真 CMMC L1 匯入 + B5 矩陣 + pytest)。
| 項目 | 值 |
|---|---|
| 進度 | Wave 2B:B1✅ B2✅ B3a✅ B4.1✅ B4.2✅ B5(.1~.6)✅ OSCAL匯出✅ B5 AO收斂fix✅。Q1 設計定稿 + 地基驗證完成(spec committed)。下一棒 = Q1 實作(5 增量) |
| 主專案 branch / HEAD | feature/oscal-refactor / d8b8d47f(本 session 10 commits) |
| 主專案 working tree | 乾淨,只有 M pyproject.toml(jedi-oscal-v2 dev path-dep,照規範勿 commit) |
| ⚠️ **v2 套件未 commit dev 改動(B1 留下,本 session 沒動套件) | repo ~/Projects/Jedicogy/module/jedi-python-package(branch feature/oscal-refactor):M catalog_service.py、M framework_service.py、?? tests/catalog/test_catalog_service_import.py。dev path-dep,BE 重啟即生效;發版等整 feature 完成 + user 明示。別搞丟。** |
| 跑得起來嗎 | create_app() BOOT OK;python -m pytest test/ = 56 failed + 50 errors(持平 2A baseline、零新回歸)+ 9 skipped |
| push | 未 push,等 user 明示 |
| dev DB 新增真資料 | 本 session 匯入真 CMMC L1:framework id 169(code CMMC_L1_VFY_*)/ catalog 729(17 控制 / 59 assessment-objective / 273 method)。Q1 smoke 直接用 catalog 729 當資源庫底材。 |
一句話:FR-038 把舊「AP 兼當稽核輪次」重寫成正式 OSCAL 模型。Q1 = 受評公司在「稽核前」的準備工作(My Jobs)——準備 SSP / 收證據(PM 教材:合規工作八成在準備期、證據在 SSP 階段就備好)。這塊在 2A 翻地基時被卡住(見下),是 Wave 2B 唯一還沒做的 BE 功能。
Q1 目標模型(requirement §4.3b 定案):
Q1 為何之前被卡(2A 留下的洞):舊「control→workflow template」設定(profile_assessment_workflow_mapping)依賴 v1 entity,v2 切換後 dark;新資源庫 module_frames.template_uid 全 NULL;vw_user_job_queue 的 control_* 留 NULL 佔位等 2B 接。本 session 把架構決策 + 資料地基都敲定/驗證完了,下一棒純實作。
冷接自檢 4 問(答不出回 §0 讀序):① Q1 的 job 綁在哪個資料實體上、為什麼是 AO 不是控制層?② 「AO」在新模型 = 哪張表的哪種 row、part_id 長怎樣?③ 為什麼設定用「單一預設 template / 資源庫」(A1)而非 per-AO 設定?④ 為什麼要「重建」
workflow_execution_control_mapping而不是「加欄」?
q1-prep-workflow-job-design.md(Q1 spec,全讀 — 決策 D-Q1-1~7、資料模型、流程、view、scope)requirement-analysis.md §4.3b(Q1 定案)+ §6「任務執行」列 + design.md §4.4(主專案↔︎套件邊界:workflow/job 綁 SSP 控制項留主專案)app/grc/service/assessment_result_app_service.py 的 _derive_ao_map / _project_catalog_controls(Q1 要用同一套 AO 來源,建議抽共用 helper)app/flow_engine/service/workflow_execution_service.py(不直接用、走 direct insert,見 §3)6886ccec)AP 三件事接 v2 set_reviewed_controls / set_assessment_subjects / set_tasks + 重生草稿;auditor 守門(ap_uid → round → project 解析)。tasks 的 methods 落 props、timing 原樣 JSONB。
987948cd fb8ea7a8 49d94704 c138de79 f9e61129 cac9b844)poam_id(已套 dev)round_uid(非 contract 扁平 /ar-finding/{uid},避免改套件 — v2 query entity 無 uuid 欄)f28f3d9c)GET /oscal/export/<doc_type>/<uid>(catalog/ssp/assessment-results/poam)回裸 OSCAL JSON 不包 envelope。權限暫 jwt-only(per-project authz 為 follow-up)。
a971e81b)— 本 session 後段真資料驗出的真 bugB5.1 用套件 list_aos,但 list_aos 回 AO_PART_NAMES(assessment-objective + assessment-method + assessment-objects + objective)全集 → 矩陣把「查核方法」也誤算一筆判定。實測真 L1:59 objective vs 273 method → 矩陣會建 332 筆而非 59(超量 5.6x)。修成 get_by_control + filter name=='assessment-objective'(不動套件)。Q1 必須用同一個 AO 定義。
d8b8d47f)見 §3。
assessment-objective part(user 拍板)。list_aos 不能用(含 method/objects);用 CatalogControlPartRepoImpl.get_by_control(cc.id) + filter name=='assessment-objective'。B5 與 Q1 共用此定義(建議把 B5 的 _derive_ao_map 抽成共用 helper 給 Q1 用,避免兩邊 AO 兜不起來)。catalog_control_parts 是 clone 來的、專案專屬,可掛 job。已驗 clone 有保留 AO parts(catalog 729 建專案後矩陣正確展 59 AO)。AC.L1-3.1.1_obj.2)→ 跨控制不撞名,可安全當 ao_part_id 識別。compliance.workflow_execution_control_mapping DB 沒這張表(只有 ORM model),且 model 的 version_id FK 指向已被 drop 的 oscal.oscal_framework_versions(v2 改名 framework_versions)。→ 要「建」這張表(shape 改 workflow_execution_id + control_id + ao_part_id、丟掉壞 version FK),不是「加欄」。對齊 model/entity/mapper/repo(infra/flow_engine/models/workflow_execution_control_mapping.py 等)。WorkflowExecutionDomainService.create_workflow_execution + JobExecutionDomainService.create_job_execution direct insert。python -m pytest test/;boot 要補 dummy GITLAB/GITHUB env(見 §6)。| # | 決策 | 值 |
|---|---|---|
| D-Q1-1 | 綁定粒度 | per-AO(每 assessment-objective part 一個準備 job) |
| D-Q1-2 | AO 定義 | 只算 assessment-objective(與 B5 共用) |
| D-Q1-3 | 設定粒度 | A1:單一預設 template / 資源庫(用 module_frames.template_uid) |
| D-Q1-4 | template | seed 一個最簡「收證據」BPMN(單一 user task) |
| D-Q1-5 | 生成時機 | 同步(project_start @transaction 內,direct insert) |
| D-Q1-6 | 執行綁定 | 建 workflow_execution_control_mapping(workflow_execution_id + control_id + ao_part_id;丟壞 version FK) |
| D-Q1-7 | 指派 | task_assignees(建立時不指派,PM 之後;FE Wave 3) |
framework_app_service.import_framework_version → import_catalog_from_pdf,framework_code="CMMC_2",L1/L2 共用同一 parser)→ catalog 729:17 控制 / 59 assessment-objective / 273 method / 17 statement。part_id AC.L1-3.1.1_obj.2。compliance.workflow_templates(欄:uid/name/type/version/provider/xml=BPMN/enable)。最簡 BPMN = 單一 <bpmn:userTask id="collect_evidence" name="收證據">(start→userTask→end)。template_id(NN)、name、status(WorkflowStatus)、type(WorkflowType);main_workflow_execution_id nullable。建:WorkflowExecutionDomainService.create_workflow_execution(entity)。workflow_execution_id(NN)、main_workflow_execution_id(NN)、template_id(NN)、template_job_id(NN=BPMN task id)、status(JobStatus)、type(JobType);name/description 可放 AO label。建:JobExecutionDomainService.create_job_execution(entity)(direct insert,不 parse BPMN)。di_containers/flow_engine/workflow_excution_containers.py 暴露 workflow_execution_domain_service / job_execution_domain_service / workflow_execution_control_mapping_domain_service / workflow_template_repo。scripts/sql/view/vw_user_job_queue.sql — control_* 全 NULL 佔位;rebind = job(b) → workflow_execution_control_mapping(by b.workflow_execution_id) → control_id + ao_part_id → 專案 catalog catalog_controls / catalog_control_parts。每增量照 canonical:改 code → BOOT OK → 真資料 smoke(用 catalog 729)→ pytest 不多紅 → 顯式 git add commit。
compliance.workflow_execution_control_mapping(workflow_execution_id bigint, control_id varchar, ao_part_id varchar,PK 視需要;GRANT cm_app;INSERT schema_migrations)。對齊 model/entity/mapper/repo(去 version FK、加 ao_part_id)。workflow_templates(固定 uid + 最簡 BPMN,task id collect_evidence)+ UPDATE compliance.module_frames SET template_uid=<該 uid> WHERE "group"='resource-library' AND template_uid IS NULL。_derive_ao_map / _project_catalog_controls 抽成共用(如 app/grc/service/_ao_derivation.py 或 util),B5 + Q1 共用「assessment-objective only」來源。project_start_app_service.start_project 在 _init_ssp_control_implementations 後,解析資源庫 template_uid(無→log skip 不擋成立)→ 對每個 in-scope 控制的每個 assessment-objective part:direct insert workflow_execution + job_execution + workflow_execution_control_mapping(ao_part_id, control_id)。注入 flow-engine domain services + workflow_template_repo + part repo。| 檔案 | 為何 |
|---|---|
app/project/service/project_start_app_service.py |
Q1.3 加 per-AO job 生成(_init_ssp_control_implementations line ~159 / start_project line ~203) |
app/grc/service/assessment_result_app_service.py |
_derive_ao_map 抽共用 helper(Q1.2) |
infra/flow_engine/models/workflow_execution_control_mapping.py + entity/mapper/repo(domain/flow_engine/... infra/flow_engine/...) |
Q1.1 重建表 shape(去 version FK、加 ao_part_id) |
di_containers/flow_engine/workflow_excution_containers.py / di_containers/project/project_containers.py |
wire flow-engine domain services 進 project_start |
scripts/sql/view/vw_user_job_queue.sql |
Q1.4 rebind control_* |
~/Projects/Jedicogy/.../jedi-flow-engine/.../models/{workflow_execution,job_execution}.py |
必填欄位參考(direct insert) |
cd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be
git branch --show-current # feature/oscal-refactor
git status --short # 僅 ' M pyproject.toml'
git log --oneline -3 # d8b8d47f / a971e81b / f28f3d9c
( cd ~/Projects/Jedicogy/module/jedi-python-package && git status -sb | head -6 ) # 套件 dev 改動還在
poetry run python -c "import jedi_oscal_v2; print('v2 OK')"
set -a; source .env; set +a 2>/dev/null
export GITLAB_API_VERSION=4 GITLAB_URL=http://localhost GITLAB_PRIVATE_TOKEN=dummy GITHUB_PRIVATE_TOKEN=dummy
poetry run python -c "import eventlet; eventlet.monkey_patch(all=False, socket=True); import sys; sys.setrecursionlimit(5000); from core.app_factory import create_app; create_app(); print('BOOT OK')"
poetry run python -m pytest test/ -q -p no:cacheprovider --continue-on-collection-errors 2>&1 | tail -1 # 56 failed,...,50 errors(持平)
.env在 shellsource會在 JSON 行報 parse error;跑 python script 改用from dotenv import load_dotenv; load_dotenv()。
cd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be
PW=$(poetry run python -c "from dotenv import load_dotenv; load_dotenv(); import os,json; print(json.loads(os.environ['DB_SECRET'])['rds_master_password'])" 2>/dev/null)
# 真 L1 catalog 729 的 AO 結構(Q1 smoke 底材)
PGPASSWORD="$PW" psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -tA -c "
SELECT p.name, count(*) FROM oscal.catalog_control_parts p
JOIN oscal.catalog_controls cc ON p.catalog_control_id=cc.id
WHERE cc.catalog_id=729 GROUP BY p.name ORDER BY 2 DESC;" # assessment-method 273 / assessment-objective 59 / statement 17
# 綁定表不存在(Q1.1 要建)→ 應回空 / NULL
PGPASSWORD="$PW" psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -tA -c "SELECT to_regclass('compliance.workflow_execution_control_mapping');" # 空 = 表不存在,要 CREATE
PGPASSWORD="$PW" psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -tA -c "SELECT to_regclass('oscal.framework_versions'), to_regclass('oscal.oscal_framework_versions');" # 前者存在 / 後者 NULL(壞 FK 別接)B4.2/B5 端到端 smoke(可選複跑):見本 session B5 各 commit message 的 verify 段;canonical 流程 = 建合成 catalog→資源庫→專案→輪次→launch-audit→start-auditing→judge→risk→finalize→poam→close→reverify。
feature/oscal-refactor;branch 不對停下問 user)。git add 檔名、禁 -am;各 repo 分開);push / 收尾 / 套件發版等 user 明示。pyproject.toml path-dep 勿 commit;v2 套件 dev 改動別搞丟、別發版。feedback_cross_schema_fk_must_qualify)。--single-transaction -v ON_ERROR_STOP=1,新表 GRANT cm_app + sequence、收尾 INSERT schema_migrations;本期 migration 只套 dev(stg/poc 待辦)。盤點 commits → changelog(type=feat)→ analysis(Q1 per-AO 綁定 + 重建綁定表 + 同步建 取捨)→ SUMMARY → 回頭更新本 handoff ✓ + spec §狀態 + design §4.4 + api-contract(My Jobs/§3.2 job_count)→ memory feedback(≥1 條:如「list_aos 含 method 會超量、AO 只算 assessment-objective」「綁定表 DB 不存在要先驗 to_regclass」)→ Notion。push 等 user。
本 session 自身的收尾(B4.2/B5/匯出/AO fix 的 changelog / SUMMARY / analysis / memory / Notion)也都還沒做 —— user 還沒下收尾命令,全留著。下一棒若 user 要收 Wave 2B 全弧,一起補。
pyproject.toml 改回 pin(整 feature 完成 + user 明示)。feature/oscal-refactor,未 push)d8b8d47f docs(FR-038): Q1 準備期 per-AO My Jobs 設計定稿(spec)
a971e81b fix(FR-038): Wave 2B B5 — AO 矩陣只取 assessment-objective(修 method 超量 5.6x)
f28f3d9c feat(FR-038): Wave 2B B5 — OSCAL 匯出端點(catalog/ssp/assessment-results/poam)
cac9b844 feat(FR-038): Wave 2B B5.6 — close-round + launch-reverify(覆核連動,完整生命週期收口)
f9e61129 feat(FR-038): Wave 2B B5.5 — POA&M item 列表/詳情 + 整改計畫 + 里程碑(三層 D-4b)
c138de79 feat(FR-038): Wave 2B B5.4 — AR finalize(定版)+ 有 not_met 自動生 POA&M
49d94704 feat(FR-038): Wave 2B B5.3 — 系統風險總結(多對多 finding 關聯)
fb8ea7a8 feat(FR-038): Wave 2B B5.2 — AO 逐項判定 + observation(證據引用不複製)
987948cd feat(FR-038): Wave 2B B5.1 — start-auditing 建 engagement AR + ar_result + AO 全量矩陣
6886ccec feat(FR-038): Wave 2B B4.2 — AP 編輯端點接 v2 set_*
2026-06-15-fr038-audit-rounds-poam-id.sql(B5.4)。前置(前 session):2026-06-15-fr038-project-extensions-living-ssp.sql / 2026-06-15-fr038-audit-rounds-uid.sql。讀 docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-15-Q1-prep-jobs-START-HERE-handoff.md。
先過「🧭 開工前必懂」+ §0 讀序硬 gate(q1-prep-workflow-job-design.md 全讀 + requirement §4.3b + design §4.4),
能答冷接自檢 4 問(job 綁哪/為何 AO 層 / AO=哪張表 / 為何 A1 / 為何重建綁定表)才往下。
跑 §6 pre-flight(BOOT OK + pytest 56f/50e 持平)+ §7 verify(catalog 729 AO 結構 + 綁定表不存在)。
接手 Q1 實作 5 增量:Q1.1 seed template+建綁定表 → Q1.2 AO 推導共用 helper → Q1.3 project_start
per-AO job direct insert → Q1.4 vw_user_job_queue rebind → Q1.5 真資料 smoke(catalog 729/59 AO)+commit。
每增量 smoke + 顯式 git add commit。push/收尾/套件發版等 user 明示;不切 branch;pyproject.toml dev path-dep 勿 commit。
看本檔 + §0 讀序 + 跑 §6/§7 → 能確認現況(B4.2/B5/匯出/AO-fix shipped + commits + 套件 dev 改動 + 真 L1 catalog 729)、懂 Q1 WHY(準備期 per-AO My Jobs、為何 AO 層、為何卡)、知道 5 增量怎麼接、知道地基發現(綁定表 DB 不存在要建、壞 version FK 別接)、知道規範界線。不需 user 額外解釋即可開工。