Handoff Prompt — A4 Session D → E(T3 + T4 + T5 ship → T6 + T7 + T8 開工)

使用方式:把下方「交接 prompt 本體」整段貼到新 session 即可接手。 前置狀態:A4 Session D BE shipped;working tree 乾淨(除 pre-existing pyproject.toml dev-path + 2 zip)。


§1

交接 prompt 本體(從此貼到新 session)

我要進 SSP 匯入匯出 Phase 2 / A4 Session E — T6 + T7 + T8(WriteStrategy + SSP shell + _write_all_data 8-step pipeline)。

執行策略(user 拍板:順序做 + base class 抽共用 pattern,不用 subagent)
=====================================================================

進度有點落後,但**不要拆 subagent 平行做** — 原因:
1. T6 → T8 → T7 強耦合鏈(T8 _write_all_data Step 2 call T6 _create_ssp_shell;Step 6 call T7 5 WriteStrategy;T7 ControlWriteStrategy 回 ctrl_impl_id_map 給 AoWriteStrategy)— 平行做反覆 mock 對方比順做還慢
2. DI Container 是 single point(5 strategy + 3 domain service + 11 dep 全寫 oscal_containers.py)— subagent 平行改這檔 100% merge conflict
3. T0 偏差 context cost 高(T0.1/T0.2/T0.5/T0.6/T0.7 + ssp_domain.add 簽章)— 每個 subagent prompt 都要貼 design §11.1,寫 prompt 時間 ≈ 自己順做時間
4. TDD 流程在 subagent 內難 control — 容易踩 Session D 那種「app service logger patch」debug 陷阱,subagent 報「做完」但實際 RED

**加速關鍵:T7 抽 base class 共用 80% pattern**

5 個 WriteStrategy 中:
- Control / AO:跨兩個 table(control_implementations + control_impl_objectives),各自獨立寫
- Device / InfoSystem / Leveraged:都寫**同一張表** `ssp_system_implementation_items`,差別只在 `kind` 欄位 + parsed entity 不同

→ 抽 `AbstractSspImplementationItemWriteStrategy` base class:
```python
class AbstractSspImplementationItemWriteStrategy(ABC):
    def write(self, parsed_list, ssp_id, sys_impl_main_id, user_context) -> int:
        """共用 80% loop / session / mapper / count return."""
        # ... 統一 transaction-friendly loop + count

    @property
    @abstractmethod
    def kind(self) -> str: ...

    @abstractmethod
    def _parsed_to_item_payload(self, parsed) -> dict: ...

subclass 各只 ~10 行:

  • DeviceWriteStrategy.kind = "device" + _parsed_to_item_payload(ParsedDevice)
  • InformationSystemWriteStrategy.kind = "information_system" + _parsed_to_item_payload(ParsedInformationSystem)
  • LeveragedWriteStrategy.kind = "leveraged" + _parsed_to_item_payload(ParsedLeveraged) + party_uuid

效益:T7 5 strategy 順做 ~60 min → base class + 5 subclass 順做 ~30-40 min,0 整合風險。

順序: T6 ship → T7 (base + 5 strategy + DI Factory) → T8 (5 dict_to* + 8-step pipeline + DI 11 dep)

調整後預估 ~1.5-2d(原 plan ~2.25d);T6/T7/T8 各自 commit。

前置狀態

Branch: feature/ssp-import-export-phase2(BE,45 commits 未 push)

  • A4 Session A (design-A4.md) shipped:1de5f51
  • A4 Session B (implementation-plan-A4.md) shipped:57bcb2e
  • A4 Session C T0/T1/T2(base stage 0 + 5 reconciler skeleton + orchestrator + DI)shipped:57a9567 + 72ef229
  • A4 Session D T3 reconciler full algorithm shipped:7c2b860
  • A4 Session D T4 A3 retroactive USER_SELECTED stage shipped:8936aab
  • A4 Session D T5 A2 §15.3 superset include_controls 反查 shipped:9d7c102
  • jedi-oscal 仍 path-dep;pyproject.toml dev-path 改動仍未 commit
  • A3 67 + A4 116(T1+T2+T3+T4+T5)= 183 test 全綠(test_a3_.py + test_a4_.py)

Session D 結束 design §11.1.3 已補正 T0.4 偏差最終決議(Option C — entity.name 直接 query); 以下 Session C T0 偏差會直接影響 Session E:

⚠️ T0.1 偏差 — 影響 T6.2 _create_ssp_shell SspEntity 構造

  • design §6.2 假設 SystemSecurityPlanEntity(title=..., profile_uid=..., status='draft')
  • 實際 SystemSecurityPlanEntity 欄位:
    • description: str = ""title
    • profile_id: Optional[int]profile_uid 字串
  • T6 改寫:用 description=metadata.get("target_mf_name") or f"SSP for MF {mf_uid[:8]}"profile_id=mf.oscal_profile_id(MF 載入時取 int)

⚠️ T0.2 偏差 — 影響 T6.2 SystemCharacteristic class name

  • design §6.2 用 SspSystemCharacteristicEntity
  • 實際 class name SystemCharacteristicEntity(無 Ssp 前綴)
  • import path: jedi-oscal domain/entity/ssp/ssp_system_characteristic_entity.py
  • 欄位齊全:name / system_security_plan_id / description / system_identifier / security_sensitivity_level / target_type / scope_description / status / owner_uid ✅

⚠️ T0.5 偏差 — 影響 T7.2 ControlImplementation class name

  • design §6.6 假設 class name SspControlImplementation / SspControlImplementationEntity
  • 實際 ControlImplementationEntity(無 Ssp 前綴)
  • jedi-oscal domain/entity/ssp/ssp_control_implementation_entity.py
  • 欄位齊全 ✅;DI Container 已注入 control_implementation_domain_service ✅

⚠️ T0.6 偏差 — 影響 T7.3 AoWriteStrategy upsert key

  • design §6.6 假設 upsert key (ssp_control_implementation_id, catalog_control_assessment_id)
  • 實際 ControlImplementationObjectiveEntity 欄位:
    • statement_identifier(字串對應 statement_id)
    • catalog_control_assessment_id FK
  • T7 改寫:AoWriteStrategy 用 (control_implementation_id, statement_identifier)
  • 連動:AO Reconciler.matched_catalog_control_assessment_id 在 WriteStrategy 派不上用場 (statement_identifier 字串才是 key);保留 matched_id 給 A5 UI 顯示

⚠️ T0.7 偏差 — ⭐ critical,影響 T6.3 _resolve_existing_ssp_shell

  • design §6.3 假設 self._ssp_domain.get_one_by_profile_uid(mf.oscal_profile_uid)
  • 實際 SystemSecurityPlanDomainService 沒此 method
  • 但 SystemSecurityPlanQueryEntity 有 profile_id 欄位 ✅
  • T6 改用:
    ssp = self._ssp_domain.get_one(SystemSecurityPlanQueryEntity(profile_id=mf.oscal_profile_id))
  • 連動:MF 需提供 oscal_profile_id(int),不是 oscal_profile_uid(uuid 字串)

⚠️ ssp_domain_service.add() 簽章偏差 — 影響 T6.2

  • design §6.2 寫 self._ssp_domain.add(SystemSecurityPlanEntity(...), curr_user=user)
  • 實際 add(_entity: SystemSecurityPlanEntity) 只接 1 個 entity 參數
  • T6 改寫:curr_user 透過 entity 內 created_user=user 帶入;不傳 kwarg

Session E 範圍

T6 — SSP shell + 新 error code(implementation-plan-A4.md §Task 6)

  • 6.1 加 GRC_EXCEL_UPDATE_FLOW_NO_SSP error code(序號注意 grep "GRC_412" 確認下個 free)
  • 6.2 _create_ssp_shell(superset flow):建 SSP + SystemCharacteristic + SystemImplementation main
    • 三個 entity 構造參數受 T0.1 / T0.2 偏差影響(見上)
    • ssp_domain.add() 簽章用 entity.created_user 帶入
  • 6.3 _resolve_existing_ssp_shell(update flow):MF → profile_id → SSP;無 SSP raise 412
    • 用 SystemSecurityPlanQueryEntity(profile_id=) 走 get_one(T0.7 偏差)

Commit:feat(oscal): A4 T6 SSP shell + GRC_EXCEL_UPDATE_FLOW_NO_SSP

T7 — 5 WriteStrategy(implementation-plan-A4.md §Task 7)

新建 domain/oscal/service/write_strategy/ 子目錄。先抽 base class 再寫 subclass

  • 7.1 AbstractSspImplementationItemWriteStrategy base class(新增 — design 階段未列入,Session E 落地)
    • 共用 write(parsed_list, ssp_id, sys_impl_main_id, user_context) -> int 樣板:loop / count / mapper
    • 抽 abstract: kind property + _parsed_to_item_payload(parsed) -> dict method
  • 7.2 ControlWriteStrategy(建/更 ControlImplementation;回 ctrl_impl_id_map)
    • class name 注意 T0.5:ControlImplementationEntity(無 Ssp 前綴)
    • 不繼承 base — 寫 control_implementations 表,跟 ssp_system_implementation_items 是兩張表
  • 7.3 AoWriteStrategy(建/更 ControlImplementationObjective)
    • upsert key 用 (control_implementation_id, statement_identifier) assessment_id(T0.6)
    • 不繼承 base — 寫 control_impl_objectives 表
  • 7.4 DeviceWriteStrategy(繼承 base)→ kind = "device";payload from ParsedDevice
  • 7.5 InformationSystemWriteStrategy(繼承 base)→ kind = "information_system";payload from ParsedInformationSystem
  • 7.6 LeveragedWriteStrategy(繼承 base)→ kind = "leveraged";payload 含 party_uuid from ParsedLeveraged
  • 7.7 DI Container 加 5 WriteStrategy Factory(5 個 Factory 同一檔,一次寫完)

Commit:feat(oscal): A4 T7 5 WriteStrategy

T8 — _write_all_data 8-step pipeline + DI 注入(implementation-plan-A4.md §Task 8)

  • 8.1 5 個 dict_to* helper(device / info_system / leveraged / control / ao;ParsedExcel dict → typed dataclass)
  • 8.2 _write_all_data 重整為 8-step pipeline(design §6.1)
    • Step 1: party reconcile(A3 既有)
    • Step 2: SSP shell(superset 走 _create_ssp_shell,update 走 _resolve_existing_ssp_shell)
    • Step 3: parsed dict → typed dataclass(5 個 dict_to*)
    • Step 4: A4 orchestrator reconcile
    • Step 5: party write(A3 既有)
    • Step 6: 5 WriteStrategy 順序寫入
    • Step 7: parsed_result 回填 matched_* / match_method 給 A5 UI
    • Step 8: 統計回傳
  • 8.3 DI 加 11 個依賴注入到 SspExcelImportAppService(5 reconciler factory + 5 writer factory + orchestrator + ssp_domain / system_characteristic_domain / system_implementation_domain)
  • 8.4 改 _confirm_superset_flow / _confirm_update_flow 串新 _write_all_data 簽章

Commit:feat(oscal): A4 T8 _write_all_data 8-step pipeline + DI

開工 SOP

第一步:必讀文件(順序)

  1. docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-A4.md ← §Task 6 / §Task 7 / §Task 8 段
  2. docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A4.md ← §6.1 8-step pipeline / §6.2 SSP shell / §6.3 _resolve_existing_ssp_shell / §6.6 5 WriteStrategy / §6.7 DI Container / §11.1 T0 偏差(T0.1/T0.2/T0.5/T0.6/T0.7)
  3. jedi-oscal entity 真實 signature(pre-flight verify 不再做,T0 verify 結果已紀錄 §11.1):
    • jedi_oscal/domain/entity/ssp/ssp_entity.py (SystemSecurityPlanEntity)
    • jedi_oscal/domain/entity/ssp/ssp_system_characteristic_entity.py (SystemCharacteristicEntity)
    • jedi_oscal/domain/entity/ssp/ssp_system_implementation_entity.py (SspSystemImplementationEntity)
    • jedi_oscal/domain/entity/ssp/ssp_control_implementation_entity.py (ControlImplementationEntity)
    • jedi_oscal/domain/entity/ssp/ssp_control_impl_objective_entity.py (ControlImplementationObjectiveEntity)
    • jedi_oscal/domain/entity/ssp/system_security_plan_query_entity.py (SystemSecurityPlanQueryEntity)
  4. domain service 簽章:
    • ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/domain/services/ssp/system_security_plan_domain_service.py
    • ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/domain/services/ssp/system_characteristic_domain_service.py
    • ~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/domain/services/ssp/system_implementation_domain_service.py
  5. 主專案 caller skeleton:
    • app/oscal/service/ssp_excel_import_app_service.py(confirm*_flow + _write_all_data + _dict_to_parsed_parties)
  6. di_containers/oscal/oscal_containers.py(已注入 ssp_domain_service + control_implementation_domain_service / control_implementation_objective_domain_service 等;T8 補 system_characteristic_domain / system_implementation_domain)

讀完後給「我看完了,準備 T6 開工」確認。

第二步:T6 開工

  • TDD:先寫 test_a4_ssp_shell.py → RED → 改 SspExcelImportAppService → GREEN
  • _create_ssp_shell 4 test (3 domain.add 各呼叫 + 返回值 + 用 metadata 字段 / profile_id 連動)
  • _resolve_existing_ssp_shell 3 test (existing pass / no SSP raise 412 / no impl_main raise 412)
  • 加 GRC_EXCEL_UPDATE_FLOW_NO_SSP 到 grc_error_code.py(grep "GRC_412" 確認序號)
  • DI 注入 ssp_domain + system_characteristic_domain + system_implementation_domain 三個 service
  • Commit T6:feat(oscal): A4 T6 SSP shell + GRC_EXCEL_UPDATE_FLOW_NO_SSP

第三步:T7 開工(依賴 T6 ship)

  • 新建 domain/oscal/service/write_strategy/ 子目錄
  • 先寫 base class AbstractSspImplementationItemWriteStrategy + base unit test(mock subclass 驗 loop / count / payload 注入)
  • ControlWriteStrategy + AoWriteStrategy 各自 TDD(兩張獨立 table,不繼承 base)
  • Device / InfoSystem / Leveraged 三 subclass 各 TDD(繼承 base,每個只測 kind + payload mapping)
  • DI Container 加 5 Factory + 注入 SspExcelImportAppService
  • Commit T7:feat(oscal): A4 T7 5 WriteStrategy + base class

第四步:T8 開工

  • dict_to* helper 各 TDD
  • _write_all_data 8-step pipeline 串完整
  • DI 注入 11 個 dep(5 reconciler + 5 writer + orchestrator + 3 domain service)
  • Commit T8:feat(oscal): A4 T8 _write_all_data 8-step pipeline + DI

第五步:Session E 收尾

依 plan §「換 Session 收尾規範」E → F:

  • 跑 pytest tests/test_a3_.py tests/test_a4_.py 確認 0 regression
  • 跑 import smoke:poetry run python3 -c "from core.app_factory import create_app; app = create_app()" → DI wiring 不能炸
  • working tree 乾淨(除 pre-existing pyproject.toml dev-path + zip)
  • design §11.1 補 T6-T8 偏差條目(T0.1/T0.2/T0.5/T0.6/T0.7 落地後實際偏差紀錄)
  • 產 handoff prompt → docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-XX-a4-e-to-f.md 含已 ship commits + T9/T10/T11(test 補齊 + cucumber + 收尾)開工指引

鐵律(沿用 A0.1 + A1 + A2 + A3 + A4 全部)

  1. 顯式 git add ,禁 -am / -A — commit 前 git status --short 確認
  2. jedi-oscal 維持 path-dep,不 bump 版本不推 Nexus(feature 整體完工才 bump)
  3. pyproject.toml dev-path 改動不該 commit
  4. BE 重啟必 lsof -ti:8000 | xargs kill -9
  5. 改 BE service code 後必提醒 user 重啟 — BE 沒 hot reload
  6. DDD 嚴格分層 — Route 不碰 DB,App Service @transaction,純函式 WriteStrategy 不接 session
  7. 不寫 docstring / 註解除非真有 non-obvious 的 why
    • T6 例外:_create_ssp_shell docstring 需標示「caller 必須在 @transaction scope 內」(DDD 約束)
    • T7 例外:5 WriteStrategy upsert key 與 reconciler matched_id 關係(T0.6 偏差連動)需 docstring
  8. Test 用 mock 包 logger 避開 jedi DBLogHandler 撞 SessionLocal=None;@transaction 用 patch_session_scope
    • patch path: app.oscal.service.ssp_excel_import_app_service.logger
  9. T0 verify 不符 design 假設 → 主動修 design-A4 §11.1 reconciliation(Session D 已補 T0.4 偏差最終決議)
  10. 跨 repo 切換時提醒 user:cucumber 在 compliance-manager-test repo,commit / branch 分開
  11. 重大決策 → docs/analysis/YYYY-MM-DD-.md
  12. 重型 bug / 跨域問題 → docs/issues/pending/YYYY-MM-DD-<title>.md
  13. Handoff 文件統一放 docs/features//handoff/(CLAUDE.md 2026-05-20 規則)
  14. 階段性 commit 不用問
  15. Subagent dispatch prompt 必加「git add 顯式檔名,禁用 -am」
  16. 套件異動需 user 拍板(jedi-* 加欄位 / 改 service 簽章前必先提)

Session E 預估規模(user 拍板 base class 加速版本)

  • T6: 0.5d(SSP shell + error code + ~7 test + DI 注入 3 service)
  • T7: 0.5-0.75d(base class + 5 strategy + ~25 test + DI 5 Factory;base class 抽 80% 共用 loop,subclass 只 ~10 行)
  • T8: 0.5-0.75d(5 dict_to* + 8-step pipeline + DI 11 dep + ~10 integration test)

總計:~1.5-2d / 3 commit(原 plan ~2.25d,base class pattern 加速 ~0.25-0.75d);T7 / T8 串聯依賴(T7 ship 後 T8 才能跑完整 pipeline)。

不要 subagent 平行 — 強耦合鏈 + DI single point + TDD 流程難 control 三點不適合(詳見頂部「執行策略」段)。

不在 Session E 範圍

  • Test 補齊 + 邊界 case + Cucumber regression(T9/T10 — Session F)
  • design §11 reconciliation 收口 + handoff F → 收尾 SUMMARY(T11 — Session F)

工具預期

  • 直接照 plan A4 樣板:每 method TDD
  • T6 / T7 / T8 各自 commit(plan 建議分 3 commit)
  • 不需 dispatch subagent — Session E 工作量在主 session 推得動

謝謝。


---

## 收口清單(A4 D → E 端)

本 handoff prompt commit 後,新 session 接手即可:

1. 讀 6 個必讀文件(plan-A4 §Task 6-8 / design-A4 §6/§11 / jedi-oscal entity / domain service / app service / DI container)
2. T6 SSP shell + GRC_EXCEL_UPDATE_FLOW_NO_SSP(受 T0.1/T0.2/T0.7 + ssp_domain.add() 簽章偏差影響)
3. T7 5 WriteStrategy(受 T0.5/T0.6 偏差影響)
4. T8 _write_all_data 8-step pipeline + DI 11 個依賴注入
5. Session E → F 收尾(產下個 handoff,準備 T9/T10/T11 test 補齊 + cucumber + 收尾)

## A4 完成度 reference(Session E 直接擴)

Session E 直接擴的 A3 + A4 Session A/B/C/D 產物:

design-A4.md(§11.1 含 T0.4 偏差最終決議;plan / spec review 都 approved) implementation-plan-A4.md(3466 行 / 11 task / 4 session)

domain/oscal/service/reconciliation/ # ✅ Session C/D 完整 ├── base.py # stage 0 dispatch ✅ ├── match_method.py # USER_SELECTED ✅ ├── _normalizers.py # 4 helper ✅ ├── person_reconciler.py # A4 T4 加 stage 0 ✅ ├── organization_reconciler.py # A4 T4 加 stage 0 ✅ ├── device_reconciler.py # A4 T3 完整演算法 ✅ ├── information_system_reconciler.py # A4 T3 完整 + system_owner ✅ ├── leveraged_reconciler.py # A4 T3 完整 + FUZZY_NAME_PREFIX ✅ ├── catalog_control_reconciler.py # A4 T3 二階段 ✅ ├── assessment_objective_reconciler.py # A4 T3 + T0.4 偏差後 entity.name query ✅ └── ssp_entity_orchestrator.py # facade ✅

domain/oscal/parser/ssp_intermediate.py # 5 dataclass + Bundle + Context + ParsedParty label ✅

di_containers/oscal/oscal_containers.py # 6 reconciler Factory + orchestrator + catalog_control_reconciler 注入 ssp_excel_import_app_service ✅ di_containers/containers.py # device_container 注入 oscal_container ✅

app/oscal/service/ssp_excel_import_app_service.py

  • _dict_to_parsed_parties 加 3 個 label 傳遞 ✅ (T4)
  • _confirm_superset_flow include_controls 反查整併 ✅ (T5)
  • _create_ssp_shell / _resolve_existing_ssp_shell # T6 要加(Session E)
  • _write_all_data 8-step pipeline # T8 要重寫(Session E)

tests/ ├── test_a4_reconciliation_helpers.py # T1 ✅ ├── test_a4_parsed_entity_fixtures.py # T1 ✅ ├── test_a4_reconciliation_device.py # T3 完整 14 test ✅ ├── test_a4_reconciliation_information_system.py # T3 完整 16 test ✅ ├── test_a4_reconciliation_leveraged.py # T3 完整 16 test ✅ ├── test_a4_reconciliation_catalog_control.py # T3 完整 11 test ✅ ├── test_a4_reconciliation_assessment_objective.py # T3 完整 13 test ✅ ├── test_a4_reconciliation_orchestrator.py # T2 ✅ ├── test_a4_a3_retroactive.py # T4 12 test ✅ └── test_a4_a2_integration.py # T5 5 test ✅


## A4 Session D 規範遵守

- [x] 顯式 `git add <file>` 顯式檔名(T3 11 檔 + T4 4 檔 + T5 3 檔,禁 -A)
- [x] Commit 含 `Co-Authored-By: Claude Opus 4.7 (1M context)` footer
- [x] jedi-oscal 沒動(path-dep 維持)
- [x] `pyproject.toml` dev-path 不 commit
- [x] DDD 規範貫徹(reconciler 純 domain,無 session / @transaction;app service @transaction)
- [x] T0.4 偏差最終決議補進 design §11.1.3(Option C — entity.name 直接 query)
- [x] handoff 文件統一放 `docs/features/<feature>/handoff/`
- [x] Session D 預估 ~1.25d 實際在 1 session 內完成
- [x] 183 a3+a4 test 全綠(0 regression)