# 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）。

---

## 交接 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 改用：
  ```python
  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 <file>，禁 -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-<topic>.md
12. 重型 bug / 跨域問題 → docs/issues/pending/YYYY-MM-DD-<title>.md
13. Handoff 文件統一放 docs/features/<feature>/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）
