本波 2A 已執行完畢:create_app() BOOT OK on v2 + python -m pytest test/ 106 紅 ≤ baseline 117(zero new regression)+ My Jobs view 回 131 列。
docs/changelog/2026-06-14-tweak-fr038-wave2a-oscal-v2-foundation-flip.md + 2B handoff docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-14-2B-START-HERE-handoff.md。給下個 session 的 prompt:「讀
docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-14-2A-corrected-gut-and-disable-handoff.md,跑完 §6 pre-flight 確認現況,接手執行修正版 2A。」 本檔自包含 —— 看完 + 跑完 §6,就能正確開工,不需 user 再解釋。所有狀態為 2026-06-14 verified(現場跑指令,非假設)。 本檔取代2026-06-14-WAVE2-START-HERE-handoff.md對 2A 的描述(該檔把 2.0a 框成「~40 檔 import-rename」,經驗證是錯的,見 §2)。
| 項目 | 值 |
|---|---|
| 緣由 | FR-038 OSCAL 重設計 Wave 2A = 主專案 BE 從舊 jedi_oscal 翻到 jedi_oscal_v2 的地基翻轉(不是業務實作) |
| branch(兩 repo) | feature/oscal-refactor(已 push;main origin 5129f57f、套件 origin 3096f1f) |
| 本棒目標(綠燈) | create_app() 能 boot 在 v2 上 + python -m pytest test/ 不比 baseline 多紅 + My Jobs view 出資料 |
| 範圍 | 只 BE,只到 boot+綠。B1~B5 業務流程在 2A 乾淨 disable,留 2B/2C |
| 接手前必讀 | 本檔 → §0 讀序 |
2A 只是地基翻轉,但你必須先懂它服務的那個大目標 —— 否則會像「只改 import」一樣做錯方向。 本節是自包含的 WHY;細節去讀
requirement-analysis.md(§0~§4)+design.md(§3)。
一句話:把現在「只套 OSCAL 概念、底層大量自定義欄位、拿 AP 兼當稽核輪次」的舊實作,打掉重練成「正式 OSCAL v1.2.2 物件模型落地、三層 clone/snapshot 邊界清楚、稽核輪次獨立成 first-class」的專業 OSCAL GRC 系統。
為什麼要重練(舊實作 6 大結構性問題,requirement §1):
run_no 疊加 → 輪次語意藏在 AR 子表、二次稽核/覆核難建模(user 明指「這設計不對」)。part 表達)。目標新模型(你切 2A 是在為這個鋪地基):
compliance.project_audit_rounds(7 態狀態機),取代「AP 當輪次」。engagement 模型:initial/surveillance 起新 AP+AR;close-out 覆核沿用母輪 AP+AR、append ar_results(自帶 narrowed reviewed-controls);parent_round_id 串血緣。jedi_oscal_v2 只管 OSCAL 物件 CRUD/resolve/snapshot/clone/匯入匯出;輪次狀態機、權限(manager/auditor)、workflow/job 綁定(Q1:綁 SSP 控制項而非 AP task)、project_audit_rounds 留主專案。vw_user_job_queue 的原因(舊 view 綁 AP task,概念已換)。這套東西怎麼分波:Wave 1(套件,已完成 194 綠) → Wave 2 = B1~B5 主專案 BE 把上述業務模型落地 → Wave 3(FE)。2A = Wave 2 的地基翻轉:讓 BE 從舊套件翻到 v2 能 boot,B1~B5 業務先 disable,2B/2C 才真正照上面的新模型實作。**所以 2A 不寫任何業務邏輯,但你切的時候要知道每個被 disable 的東西,2B 會照哪個需求(api-contract §1~§8 對應)重建。**
冷接自檢:讀完本節 + requirement §0~§4 + design §3,你應能回答:① 為何打掉重練(舊 6 問題)② 新模型三層邊界與輪次 engagement 長怎樣 ③ 2A 在這張大圖的位置(地基,非業務)④ My Jobs 為何要重寫。答不出來 → 回去讀,別碰 code。
2A 不是做 framework/資源庫/專案成立/SSP/AP/AR/POA&M 的業務邏輯(那是 B1~B5 = 2B/2C)。2A 只做三件事:
vw_user_job_queue)重寫對新「SSP 控制項」綁定(Q1)—— 這是 2A 唯一必須真的能動的 OSCAL 相鄰流程。為什麼 2A 必須一次切完才有綠點:見 §2 陷阱 1(新舊套件無法共存)。沒有 booting 中間態,要嘛全舊、要嘛全新,無法分批 commit。本棒請用滿格 context budget 一口氣切到
create_app()boot。
過去 fresh agent 接手「一知半解」的根因 = 只讀機械步驟、沒讀原始需求。本檔特意把需求理解放在最前面當 gate。
requirement-analysis.md §0~§4 全讀(§0 一句話總結、§1 舊 6 問題、§2 三層+四邊界、§3.3 輪次 7 態狀態機、§4.x 新舊業務流程對照、§4.3b Q1 job 綁定)design.md §3(engagement 模型:initial/surveillance/close-out 與 AP/AR/ar_results 對應)§4.2(v2 套件對外 service 簽章契約)§4.4(套件 vs 主專案邊界)
api-contract.md §10(My Jobs 綁定點後端改)+ §1~§8(B1~B5 端點 — 2A 全 disable,2B 照此重建,所以 2A disable 時要對齊這裡的端點清單)wave2-migration-plan.md(Wave 2 phase 拆解;2.0a 的「import-rename」框架已被本檔取代)feature/oscal-refactor,兩 repo 與 origin 同步;working tree 僅 M pyproject.toml(dev path-dep,刻意保留勿 commit)。project_oscal_v1_v2_cannot_coexist.md(+ MEMORY.md 一行索引);OscalContainer 等程式碼未動(曾試加 v2 block 觸發 collision 後已還原乾淨)。jedi_oscal_v2 套件:~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal-v2/,194 tests 全綠,已 push。對外 14 app service + 3 domain service,全部自建 repo(repo or RepoImpl()),可零參數 wire。jedi_oscal 0.0.22 仍裝著、import 仍能過;主專案 109 個 source 檔仍 import 舊 jedi_oscal、0 檔用 v2。oscal schema 48 表(base 42 + delta 6)、compliance.project_audit_rounds 在(7 態 CHECK + 4 FK)。新 schema + 舊 code 不一致 → OSCAL runtime 現在是壞的(預期中間態,不是 bug)。python -m pytest test/ --continue-on-collection-errors)518 passed, 67 failed, 50 errors + 9 個 collection-abort 檔。app.ai_dashboard.service.compute_engine / metadata_builder / prompts、domain.module_frame.entity.module_frame_control_default_entity);50 errors 多為 AI dashboard / flow template / google drive fixture。python -m pytest test/ 不得比此 baseline 多紅。只有 3 個 test 檔 import 舊 jedi_oscal(boot 的測試障礙很小):
test_api_catalog_control_assessment_route.pytest_module_frame_template_import_service.pytest_oscal_project_service_template_copy_integration.pyjedi_oscal 與 jedi_oscal_v2 的 ORM model 撞在 jedi_common 同一個 declarative Base 的 MetaData 上,兩邊都註冊 oscal.catalogs 等同名表。一旦同 process 同時 import 到兩邊 model → sqlalchemy.exc.InvalidRequestError: Table 'oscal.catalogs' is already defined。後果:不能「新舊並列 additive 共存遷移」;big-bang 全切是被強制的;無 booting 中間態(半切必倒),所以一次切完才有可 commit 的綠點。extend_existing=True 不可用(會 merge 兩套不同欄位的表 → 汙染)。記憶 project_oscal_v1_v2_cannot_coexist。wire() 急切 import 全部 route + 解析全部 Provide[] — config/di_modules.py:get_di_wire_modules() 自動掃 api/**/routes/*_route.py 全 wire。所以任一 OSCAL route 的 top-level import 碰到已移除符號、或 Provide[Containers.oscal_container.<已移除 provider>],都會在 create_app() boot 時炸。disable route 要從「不被 wire / 不 import 舊符號」下手(見 §5.4)。另 create_app() line ~158 register_stage_hooks_to_registry 會存取 project_container.oscal_audit_service,該鏈也是 boot 必經。.env 沒 GITLAB_*/GITHUB_PRIVATE_TOKEN,jedi_issue 在 import 時建 gitlab/github client 會炸(gitlab.vNone.objects / Auth.Token assert)。驗 boot 必須補 dummy env(見 §6)。這不是 OSCAL 問題,不要去修 jedi_issue。eventlet.monkey_patch + sys.setrecursionlimit(5000) 會誤觸 NonCopyableArgumentError(system_config DI deepcopy)假象。§6 指令已含前置。python -m pytest test/ — 純 pytest 缺專案根在 sys.path → 全 No module named 'app'。testpaths 在 pyproject 指 tests/(另一個目錄),要測的是 test/(單數,60 檔)。目標終點:create_app() 在 v2 上 boot + pytest 不多紅 + My Jobs 出資料。B1~B5 業務面在 2A 全 disable。
切的順序(一次做完,因無 booting 中間態):
A. OscalContainer 重寫
- 移除全部 from jedi_oscal.* import + 舊 provider
- wire v2 primitives(§5.2 map,零參數 Factory/Singleton)
- 外部引用的舊 provider 名(§5.1):能對應 v2 的改 v2;不能的 → 連同其 consumer 一起 disable
B. 6 個互注 container(associations / project_participant / module_frame /
workflow_execution / project / task_assignee / grc)
- 移除/repoint 對 oscal_container 舊 provider 的引用(§5.1 第一份清單)
- 引用已移除 provider 的 consumer service(B1~B5 業務)→ disable 該 provider + 其 route
C. OSCAL routes(api/oscal/routes/* + api/grc、api/module_frame、api/project 內 OSCAL 相關)
- B1~B5 業務 route:disable(見 §5.4 三種 disable 手法)
- 確保 wire() 掃到的每個 route 不 import 已移除符號、不 Provide 已移除 provider
D. My Jobs:重寫 vw_user_job_queue + ORM mirror + repo(§5.5)
E. 3 個 import jedi_oscal 的 test 檔(§1):disable / skip / 改 v2
F. boot loop(§6)→ create_app OK;pytest(§1)→ 不多紅
G. 維護 §5.4 disable 清單(2B 照此逐一 re-enable 重建)
判斷「接真 v2」vs「disable」的準則:
其他 container 從 oscal_container 拉的 provider(移除這些 provider 前必先 repoint/移除引用端):
assessment_plan_service(9) assessment_plan_task_domain_service(6)
assessment_plan_group_domain_service(4) assessment_plan_control_domain_service(4)
system_characteristic_domain_service(3) ssp_domain_service(3)
control_implementation_objective_domain_service(3) control_implementation_domain_service(3)
component_domain_service(3) assessment_result_service(3)
ssp_service(2) ssp_document_pool_query(2) oscal_responsible_party_domain_service(2)
oscal_party_domain_service(2) leveraged_authorization_domain_service(2)
inventory_item_domain_service(2) assessment_result_finding_domain_service(2)
assessment_result_data_domain_service(2) assessment_result_control_domain_service(2)
assessment_plan_domain_service(2)
system_implementation_main_domain_service(1) ssp_versioning_service(1) ssp_project_resolver(1)
profile_service(1) oscal_framework_version_service(1) oscal_framework_service(1)
metadata_domain_service(1) catalog_service(1) catalog_group_domain_service(1)
catalog_control_domain_service(1) assessment_task_control_domain_service(1)
(重跑:grep -rhoE "oscal_container\.[a-z_]+" di_containers/ --include="*.py" | sort | uniq -c | sort -rn)
route 層 Provide[Containers.oscal_container.<x>] 引用的 provider(disable route 前要處理):
ssp_control_implementation_service oscal_framework_service ssp_document_pool_service
ssp_excel_import_app_service oscal_framework_version_service framework_version_edit_service
framework_parse_job_service assessment_plan_task_service ssp_resources_app_service
ssp_permission_checker ssp_party_app_service ssp_leveraged_app_service
ssp_inventory_items_app_service ssp_docx_import_app_service ssp_control_impl_import_service
ssp_components_app_service module_frame_ssp_resources_service module_frame_party_service
module_frame_leveraged_service module_frame_inventory_service module_frame_components_service
ssp_system_characteristic_app_service ssp_export_app_service oscal_import_service
module_frame_system_characteristic_service assessment_result_service profile_service
module_frame_template_ssp_app_service catalog_control_assessment_service
(重跑:grep -rhoE "oscal_container\.[a-z_]+" api/ --include="*.py" | sort | uniq -c | sort -rn)
註:上面大多是主專案 app service(在
app/oscal、app/module_frame),不是 jedi_oscal —— 它們內部 import 舊 jedi_oscal domain service,是 B1~B5 業務碼,2A disable。
| v2 import path | class |
|---|---|
jedi_oscal_v2.app.service.framework.framework_service |
FrameworkService |
jedi_oscal_v2.app.service.catalog.catalog_service |
CatalogService |
jedi_oscal_v2.app.service.profile.profile_service |
ProfileService |
jedi_oscal_v2.app.service.ssp.ssp_service |
SspService |
jedi_oscal_v2.app.service.ssp.ssp_clone_service |
SspCloneService |
jedi_oscal_v2.app.service.ap.assessment_plan_service |
AssessmentPlanService |
jedi_oscal_v2.app.service.ar.assessment_result_service |
AssessmentResultService |
jedi_oscal_v2.app.service.ar.assessment_risk_service |
AssessmentRiskService |
jedi_oscal_v2.app.service.poam.poam_service |
PoamService |
jedi_oscal_v2.app.service.poam.remediation_service |
RemediationService |
jedi_oscal_v2.app.service.snapshot.oscal_snapshot_service |
OscalSnapshotService |
jedi_oscal_v2.app.service.snapshot.metadata_clone_service |
MetadataCloneService |
jedi_oscal_v2.app.service.snapshot.oscal_clone_service |
OscalCloneService |
jedi_oscal_v2.app.service.io.oscal_io_service |
OscalIoService |
jedi_oscal_v2.domain.service.ap.ap_draft_service |
ApDraftService |
jedi_oscal_v2.domain.service.profile.profile_resolution_service |
ProfileResolutionService |
jedi_oscal_v2.domain.service.ar.ar_finding_matrix_service |
ArFindingMatrixService |
jedi_oscal_v2.ports.oscal_parser_factory |
get_oscal_parser_adapter(code) |
全部可 providers.Factory(V2XxxService) 零參數(service __init__ 全 repo=None 自建)。repo 若 2B 要單獨注入再加(path jedi_oscal_v2.infra.repository.<domain>.<name>_repo_impl)。
核心 method 契約(design.md §4.2,簽章 pre-flight 複驗):
add/get/list/update_framework、add_framework_version、list_versions、publish_versionget/list_catalog、get_control_tree、list_aos(catalog_control_id)(AO 已搬到 catalog_control_parts)add_profile、resolve_profile(邊界①)clone_resource_library(邊界②)、snapshot_ssp(邊界③)、deep_clone_sspadd_empty_ssp、get/update_ssp + 子物件 CRUDcreate_ap(import_ssp_id)、generate_draft(ssp_id)、set_reviewed_controls、set_assessment_subjects、set_tasksadd_ar(import_ap_id)、add_result、init_finding_matrix、upsert_finding、list_findings(AO 全量矩陣)add_risk、link_findings、list_risksgenerate_from_findings、upsert_remediation、upsert_milestone、list_*export_oscal(doc_type, uid)v2 收掉了舊套件的 domain-service 層(舊 ~30 個 → v2 只剩 3 個 domain service),且結構改:
domain.services.*(control_implementation / system_characteristic / party / leveraged / component / inventory / catalog_control_assessment / ap_task / ar_data …)→ v2 無對應,consumer 改吃 repo 或 disable。catalog_control_assessments → v2 catalog_control_parts。assessment_plan_tasks → v2 ap_tasks(結構不同)。assessment_result_datas → 沒了(改 ar_results + finding 矩陣)。OscalSystemSecurityPlan→OscalSsp、OscalAssessmentPlan→OscalAp、AssessmentPlanEntity→ApEntity …)。三種 disable 手法(擇省力):(a)
config/di_modules.pyEXCLUDE_MODULES加該 route module(最乾淨,route 不被 wire);(b) route 檔 body 清空只留 blueprint 殼 + 移除舊 import;(c) container provider 直接刪/註解 + 移除引用端。規則:每 disable 一個 route/provider/service,在此表記一行(模組路徑 / 手法 / 對應 2B phase B?)讓 2B 精準復原。
手法代號:(a)=EXCLUDE_MODULES 不被 wire + create_module 停註冊 blueprint;(b)=route 檔層;(c)=container provider 刪/改 Object(None);(d)=leaf service/repo/model stub 成 import-clean。
config/di_modules.py EXCLUDE_MODULES 加 30 條(23 api.oscal.* + 7 api.module_frame.*,清單見該檔)。api/oscal/__init__.py create_module() 整檔簡化成空 blueprint(所有 oscal route)。2B 逐一重建 route 後恢復。api/module_frame/__init__.py create_module() 移除 7 條 import + add_resource:party / ssp_resources / system_characteristic / leveraged / components / inventory / mf_ssp_export。di_containers/oscal/oscal_containers.py 整檔換成 18 個 v2 primitive。舊 ~80 provider 全移除,2B 依 design §4.2 契約把業務 app service 重新接 v2。associations_containers.py:移除 AP-task repo/domain-service/service(5 provider)+ workflow-exec-mapping service 的 assessment_plan_task_service 注入。workflow_excution_containers.py:移除 oscal_control_mapping repo/domain-service + workflow_execution_service 的 assessment_plan_service / assessment_plan_task_domain_service 注入。module_frame_containers.py:catalog/profile/ssp/framework/AP/control-impl 全系列 provider 改 Object(None)。grc_containers.py:register_stage_hooks_to_registry 改 try/except(dark handler 跳過)。stage handler/precondition provider 仍 reference 已移除的 oscal_container.X(lazy,resolve 時失敗被 skip)→ 2B re-wire 後恢復註冊。| 區塊 | 檔案 | 處置 |
|---|---|---|
| GRC infra repo | infra/grc/repository/ 17 檔(grc_job/dashboard/control/control_group/audit/project/review/task_setup/assessment_object repo + auditor_dashboard/job_batch_complete/job_export/job_import_lookup/oscal_audit/ssp_document_pool query + assessment_plan_extension/poam repo) |
import-clean,query 方法回空 |
| GRC service | app/grc/service/:oscal_stage_handlers / oscal_stage_preconditions / poam_service / project_current_ssp_service / project_service / assessment_object_service |
stub(precondition 回 ok、其餘空回傳) |
| Project service | app/project/service/:oscal_project_service / oscal_audit_service / project_system_info_service |
stub |
| OSCAL service | app/oscal/service/ssp_versioning_service + domain/oscal/service/ssp_project_resolver |
stub / resolve 拋 NotFound |
| Module frame | app/module_frame/:dto + service(control_default / control_objective_default / import / reference_document / template_import / ssp_import_template / module_frame_service)+ infra/module_frame/repository/module_frame_repo_impl |
stub |
| Associations | app/associations/dto/ 3 dto + app/associations/service/assessment_plan_task_workflow_execution_mapping_service(AP-status method no-op)+ infra/associations/mapper/ 5 mapper + infra/associations/repository/profile_assessment_workflow_repo_impl |
stub |
| ORM model relationship | infra/associations/model/:assessment_plan_task_workflow_execution_mapping / assessment_plan_task_workflow_mapping / project_assessment_plan_mapping / project_system_characteristic / profile_assessment_workflow_mapping(後者 string-based) |
移除對舊 OSCAL model 的 relationship,保留 FK 欄位 |
| Flow engine | infra/flow_engine/models/workflow_execution_control_mapping(移 relationship)+ app/flow_engine/service/workflow_execution_control_mapping_service(回空)+ app/flow_engine/service/workflow_execution_service(AP 觸點 no-op、live 流程保留) |
stub / no-op |
| Participant(live 保留可動) | app/participant/service/:control_group / project_control / project_group / project_participant(AP/SSP 觸點 stub)+ task_assignee(batch_add stub,My Jobs 改走 view) |
AP-coupled 方法 stub,其餘照舊 |
scripts/sql/view/vw_user_job_queue.sql:去掉 4 張舊 AP 表 join,control_/group_/ap_task_* 填 NULL。2B 把 control_* 接到 oscal.ssp_control_implementations(Q1)。test_ssp_versioning_service / test_oscal_stage_preconditions / test_oscal_project_party_clone_dedup / test_module_frame_template_import_service / test_api_catalog_control_assessment_route / test_oscal_project_service_template_copy_integration / test_oscal_project_service_flow_template / test_fr032_excel_system_assets / test_assessment_plan_extension_repo_impl。
vw_user_job_queue 重寫(S4,2A 必須能動)scripts/sql/view/vw_user_job_queue.sql(view)+ infra/participant/model/vw_user_job_queue.py(ORM mirror)+ infra/participant/repository/task_assignee_repo_impl.py(consumer,line ~163 query(VwUserJobQueue))。LEFT JOIN oscal.assessment_plan_task_workflow_execution_mapping c
LEFT JOIN oscal.assessment_plan_controls f ON a.control_id = f.id
LEFT JOIN oscal.assessment_plan_groups e ON f.group_id = e.id
LEFT JOIN oscal.assessment_plan_tasks g ON c.assessment_plan_task_id = g.idcompliance.job_executions + workflow_execution + task_assignees + 專案 SSP 控制項(oscal.ssp_control_implementations / catalog control)。ORM mirror 欄位(ap_task_* / group_* / control_* / project_* / user_* / job_)盡量維持讓 repo + FE 不動;ap_task_ 可暫填 NULL 或改 map 到 SSP 控制項語意。--single-transaction -v ON_ERROR_STOP=1,view 重建不需 GRANT(繼承),但若新建表記得 GRANT cm_app + INSERT public.schema_migrations。SELECT count(*) FROM public.vw_user_job_queue WHERE user_id=<測試用戶> 出資料 + My Jobs API smoke。cd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be
# 1. branch + 兩 repo + working tree
git branch --show-current # feature/oscal-refactor
git status --short # 期望僅 ' M pyproject.toml'
( cd ~/Projects/Jedicogy/module/jedi-python-package && git status -sb | head -1 )
# 2. 套件可 import + 測試綠
poetry run python -c "import jedi_oscal_v2; print('v2 OK')"
poetry run pytest ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal-v2/tests -q | tail -1 # 194 passed
# 3. 遷移 surface
grep -rl "from jedi_oscal\b\|import jedi_oscal\b" --include="*.py" api/ app/ domain/ infra/ di_containers/ config/ common/ core/ | wc -l # 109
grep -rl "jedi_oscal_v2" --include="*.py" api/ app/ domain/ infra/ di_containers/ config/ common/ core/ | wc -l # 0(開工前)
# 4. 【關鍵】BE boot 驗證(補 dummy env + 真入口前置,否則撞 jedi_issue / deepcopy 假象)
set -a; source .env; set +a
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')"
# 開工前跑這條應印 'BOOT OK'(舊 code 現在能 boot);2A 切完也要這條 'BOOT OK' 才算綠
# 5. pytest baseline(綠燈基準,2A 後不得多紅)
poetry run python -m pytest test/ -q -p no:cacheprovider --continue-on-collection-errors 2>&1 | tail -2
# baseline: 518 passed, 67 failed, 50 errors(+9 collection-abort 檔,皆既有非 OSCAL 技術債)
# 6. dev DB(密碼查 .env DB_SECRET.rds_master_password,勿落檔)
# PGPASSWORD=... psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev -tA -c "SELECT count(*) FROM information_schema.tables WHERE table_schema='oscal'" # 48feature/oscal-refactor;branch 不對停下問 user)。git add 檔名、禁 -am;各 repo 分開);但 2A 無 booting 中間態,commit 點只有「boot 在 v2 上」之後。pyproject.toml path-dep 勿 commit(dev-only)。feedback_cross_schema_fk_must_qualify)。--single-transaction -v ON_ERROR_STOP=1,新表 GRANT cm_app,收尾 INSERT schema_migrations。pyproject.toml 改回 pin。project_oscal_v1_v2_cannot_coexist.md + MEMORY.md 一行索引(程式碼零變更;OscalContainer 試加 v2 後已還原)。working tree 僅 M pyproject.toml。feature/oscal-refactor,origin head 5129f57f):dad80aef(契約) 0af2d862(Wave1 收尾) 5129f57f(Wave2 計畫+API契約) + 後續 docs handoff commits(b200ad52 系列)。3096f1f):57 commits(Phase0→A5c2)。讀 docs/features/FR-038-2606-oscal-redesign/handoff/2026-06-14-2A-corrected-gut-and-disable-handoff.md。
先照 §0 讀序「先懂需求」硬 gate:讀 handoff「🧭 開工前必懂」節 + requirement-analysis §0~§4
+ design §3/§4.2/§4.4,能回答冷接自檢 4 問(為何重練/新模型/2A 位置/My Jobs 為何重寫)才往下。
然後跑 §6 pre-flight(含 BOOT OK + pytest baseline),接手執行修正版 2A:
gut OscalContainer 到 v2 primitives + 乾淨 disable B1~B5 業務面 + 重寫 My Jobs view,
一次切到 create_app() BOOT OK + pytest 不多紅 + My Jobs 出資料 就停。
B1~B5 業務邏輯不做(disable 留 2B)。push / 收尾 / Notion 等我明示。
下個 session 只看本檔 + §0 讀序 + 跑 §6 → 能確認現況(含 BE 現在 boot 得起來、collision 約束)、知道 2A 是 gut-and-disable 非 stub-everything、知道 v2 wiring map、知道 boot 必經面與 disable 手法、知道 My Jobs 重寫方向、知道綠燈定義與規範界線。不需 user 額外解釋即可正確開工。