# Handoff Prompt — A3 Session D → Session E（test 補齊 + cucumber + 收尾）

> **使用方式**：把下方「交接 prompt 本體」整段貼到新 session 即可接手。
> **前置狀態**：Session D（T3+T4）完成；A2 confirm flows 透過 `_write_all_data` 串接 reconcile；Person/Organization Reconciler 三階段（exact + normalized + fuzzy）全落地；working tree 乾淨（除 pre-existing pyproject.toml + conversation-history zip 異動）。
> **Session E 任務**：T5 unit + integration test 補齊（matrix gap + ParsedParty fixture）+ T6 cucumber regression 5 scenarios + T7 changelog / design §11 reconciliation / final SUMMARY 收口 → 3~4 個 commit。

---

## 交接 prompt 本體（從此貼到新 session）

```
我要進 SSP 匯入匯出 Phase 2 / A3 phase Session E — 收口（T5 test 補齊 + T6 cucumber + T7 changelog / design §11 / SUMMARY）。

前置狀態
========

Branch: feature/ssp-import-export-phase2（BE，37 commits 未 push）
- A0 / A0.1 / A1 / A2 都 shipped；A3 Session A / B / C / D 都 shipped
- Session D 累計 2 commits：
  - 854b238 feat(oscal): A3 T3 wire A2 confirm flows to reconciliation
  - 773e108 feat(oscal): A3 T4 fuzzy match algorithm
- 工具狀態：jedi-oscal 仍 path-dep；pyproject.toml dev-path 改動仍未 commit
- Test 基準：96 passed（A3 累計 57 + 既有 39）
  - 13 normalizers + 7 base + 3 facade + 15 person + 13 org + 6 A2 integration = 57 A3
  - 既有 8 party facade + 29 A2 service + 2 docx pre-existing fail（不擋 ship）

A3 Session E 是 5 session 切分中的最後站，task：
  T5 unit + integration test 補洞（matrix gap + ParsedParty asdict fixture）
     - Person / Org matrix gap edge cases ~2~4 case
     - ParsedParty 4 fixture（default / 顯式 set / asdict / JSONB value）
  T6 cucumber regression（compliance-manager-test repo）
     - 5 scenarios：4 exact/normalized + 1 fuzzy
     - 跑通可接受 — partial ship 允許（A0.1 follow-up #7 GitLab env 未配齊）
  T7 收口：
     - changelog feat type / modules: oscal
     - design-A3 §11 reconciliation 條目（5~7 條）
     - tracker README 更新 A3 row → done
     - final SUMMARY
  預估 ~1.25d（E session 整段）

開工 SOP
========

第一步：必讀文件（順序）

1. docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-A3.md
   ← T5 段（line 1534~） + T6 段（line 1671~） + T7 段（line 1850~）
   ← 含 ~51 vs 實際 ~57 test 數差異說明（§11 補一條）
2. docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A3.md
   ← §8 acceptance criteria（test 數對照）
   ← §11 reconciliation（待補條目）
3. docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-20-a3-session-C-to-D-implementation.md
   ← Session C 已識別的 plan vs reality 偏差（3 條 §11 待補）
4. domain/oscal/service/reconciliation/person_reconciler.py + organization_reconciler.py
   ← Session D 落地的 normalized + fuzzy 完整實作
5. tests/test_a3_reconciliation_person.py + tests/test_a3_reconciliation_organization.py
   ← 既有覆蓋面（檢查哪些 matrix gap 還沒寫）

讀完後給「我看完了，建議下一步 X」的簡短確認。

第二步：執行 T5 + T6 + T7

T5 — Test 補齊（先做，純 BE 主 repo）
  5.1 補 Person / Org matrix gap 2~4 case（plan §5.1 list）
     Person:
       - 全 stage 拿到 active=False → UNMATCHED（exact stage 已 cover，可省）
       - email 「a@」域名空 → UNMATCHED
     Org:
       - name 「公司」單獨 → stripped == "" → UNMATCHED（base 已 cover stripped == normalized）
       - 多層 suffix（「ACME Co. Inc.」）只 strip 第一個 trailing
       - 全空白 name → UNMATCHED
  5.2 新建 tests/test_a3_parsed_party_fields.py（plan §5.2 完整 code 已給）
     - default match_method == UNMATCHED + confidence 0
     - 顯式 set FUZZY_EMAIL_DOMAIN + 0.7
     - asdict() 序列化值（StrEnum instance）
     - JSON dump 後反序列化值（string "fuzzy_email_domain"）
  5.3 跑全套 pytest tests/ -q 確認累計 ~270 BE test 全綠（既有 213 + A3 ~57 新）
  5.4 commit
     test(oscal): A3 T5 unit + integration tests

T6 — Cucumber regression（compliance-manager-test repo）
  6.1 切到 test repo（BE working tree clean 先）
       cd ~/Projects/Billows/Audit-Manager/compliance-manager-test
       git checkout -b feature/a3-ssp-party-match-regression
  6.2 寫 features/regression/module-frame/05-ssp-docx-import-party-match.feature
       5 scenarios（plan §6.2 完整 gherkin 已給）
  6.3 撰寫 step definitions + page object（如需）
  6.4 partial pass 接受（GitLab env 未配齊 → 列 T8 follow-up）
  6.5 在 test repo 內 commit（branch 跟 BE 主 repo 分開）
       test(a3): SSP docx party match regression scenarios

T7 — 收口（回 BE 主 repo）
  7.1 docs/changelog/2026-05-XX-feat-party-matcher-shared.md
       frontmatter type: feat / modules: [oscal] / breaking: false
       commit: 854b238 + 773e108 + 本次 T5 commit
  7.2 design-A3.md §11 reconciliation 補條目（5~7 條最少）：
       1. T1.7 plan vs reality — __init__.py 只 export 2 個（circular import）
       2. T2.1 plan vs reality — 既有 8 test fixture 必須改 inject _make_facade()
       3. import path entities 複數 vs plan code snippet 單數
       4. T3 plan vs reality — reconcile 插在 _write_all_data 而非 _confirm_*_flow
          （DRY + call chain 自動受惠）
       5. test 數 ~57 vs design §8 ~38 差異（含 normalizers helper 13 個）
       6. fuzzy stage candidate cache exception → return [] 靜默路徑
          vs docx parse-time logger.warning swallow 行為差異
       7. A3 期間 A2 fuzzy 自動寫入（A5 改 user 拍板，已列 F2）
  7.3 docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md tracker A3 row → done
  7.4 docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-XX-a3-SUMMARY.md
       含 commits 清單 / 改動範圍 / 行為差異 / 規範文件清單 / 已知 follow-up
       / 部署 handover
  7.5 commit
     docs(ssp-import-export-phase2): A3 收尾 — changelog + design §11 + SUMMARY

第三步：BE smoke + 提醒 user

T5 commit 後不需 BE smoke（純 test 補洞）；
T7 commit 後也不需 BE smoke（純文件）；
**但 Session D 的 T3 改動了 app service 層** — 若 user 還沒重啟 BE 跑過 manual
測 reconcile 串接，必再次提醒：
  lsof -ti:8000 | xargs kill -9

第四步：A3 完工確認 + 換 phase

A3 ship 後：
  - tracker README 更新到 A4 row 為 next
  - 若 user 想直接接 A4（5 個新 reconciler）→ 走 A4 brainstorm SOP

A3 Session A / B / C / D 已產出（參考）
=====================================

- design-A3.md（13 章 / ~670 行）+ implementation-plan-A3.md（~2,045 行）
- handoff prompts × 4：brainstorm-kickoff / A→B (writing-plans) / B→C / C→D / D→E（本 handoff）
- Session B / C SUMMARY + Session D SUMMARY（本 handoff 配對）
- tracker README A3 row 等 Session E 收口才一次更新

Session D 已落地（可直接 import）：
  app/oscal/service/ssp_excel_import_app_service.py
    _write_all_data — 補 self._reconciliation.reconcile(all_parties, tenant_id=...)
                       在 write_parties 前；try/except logger.warning swallow
                       （沿用 docx flow line 258-264 既有 pattern）
  domain/oscal/service/reconciliation/person_reconciler.py
    _try_normalized_match — _strip_plus_alias + 二次 query exact
    _try_fuzzy_match — email domain + nickname/login_name + cache + is_active filter
  domain/oscal/service/reconciliation/organization_reconciler.py
    _try_normalized_match — _normalize_name（全形→半形 / collapse）+ 二次 query
    _try_fuzzy_match — _strip_org_suffix（雙邊 normalize + strip 後比對）+ cache
  tests/test_a3_reconciliation_a2_integration.py — 6 case 全綠
  tests/test_a3_reconciliation_person.py — 累計 15 case 全綠
  tests/test_a3_reconciliation_organization.py — 累計 13 case 全綠

Session D 撞到 / 需注意的事
==========================

1. **Plan T3.2/3.3 vs reality**：plan 寫「插入 _confirm_superset_flow + _confirm_update_flow」，
   實際結構兩 flow 都呼叫 _write_all_data — 插一處（_write_all_data）即可 DRY 受惠。
   test 仍透過 confirm_import / _confirm_*_flow 進入，reconcile.call_count == 1 assertion 成立。
   → Session E T7.2 design §11 reconciliation 補一條（第 4 條）

2. **Plan T3.4 test fixture 簽章 vs 實際 signature**：plan 寫 `decisions=[], overrides={}`
   參數，實際 `_confirm_superset_flow(job, parsed_result, payload, user_context)` —
   test 改用 `confirm_import("u", {}, user_context)` 走完整 path 比較自然，
   而非直接 call private method。

3. **既有 test_ssp_excel_import_app_service.py fixture 沒注入 reconciliation**：
   既有 29 test 不需改動 — `party_reconciliation_service` 是 optional default None，
   `_write_all_data` 內 `if self._reconciliation is not None` 守門保住舊行為。
   新 test 自己 inject mock_reconciliation。
   → Session E T7.2 design §11 補一條（解釋 backward-compat 路徑）

4. **既有 docx test 2 個 pre-existing fail 沿用 Session C 標記**（與 A3 無關）：
   - test_create_mode_get_parse_result_all_controls_added
   - test_get_parse_result_party_added_when_ssp_has_no_parties
   - follow-up F-T1-pre 不擋 ship

5. **.env JSON parse 問題**（Session C 開工撞，Session D 未再撞）：
   - line 3 JWT_SECRET={ ... } 沒 quote
   - Session D 跑 pytest 用 `set -a; source .env 2>/dev/null; set +a` 略過 source 錯誤
   - 標 follow-up F-env

鐵律（沿用 A0.1 + A1 + A2 + Session A + B + C + D 全部）
======================================================

1. 顯式 git add <file>，禁 -am / -A — commit 前 git status --short 確認
2. jedi-oscal 維持 path-dep，不 bump 版本不推 Nexus（Phase 2 整體完工才 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，純函式 helper / matcher 不接 DB
7. 不寫 docstring / 註解除非真有 non-obvious 的 why
   - A3 例外：BaseReconciliationService 4 hook + _get_or_load_candidates exception 行為兩處屬「non-obvious why」
8. Test 用 mock 包 logger 避開 jedi DBLogHandler 撞 SessionLocal=None；@transaction 用 patch_session_scope
9. plan 階段假設不準確時主動修 design.md (§11 reconciliation Session E 一次補)
10. 跨 repo 切換時提醒 user：cucumber 在 compliance-manager-test repo，commit / branch 分開
11. 改 docx parser 既有行為 → cucumber regression 必跑（Session E T6）
12. 重大決策 → docs/analysis/YYYY-MM-DD-<topic>.md
13. 重型 bug / 跨域問題 → docs/issues/pending/YYYY-MM-DD-<title>.md
14. Handoff 文件統一放 docs/features/<feature>/handoff/
15. brainstorm dump 留 Session E 收口才一次 dump
16. 階段性 commit 不用問
17. Subagent dispatch prompt 必加「git add 顯式檔名，禁用 -am」

A3 phase 預估規模（剩餘）
========================

| Session | 範圍 | Task | 預估 |
|---------|------|------|------|
| E（self）| Test 補齊 + cucumber + 收尾 | T5 + T6 + T7 | ~1.25d |

剩餘總計：~1.25d → A3 完工

工具預期
========

- 直接照 plan-A3 內 bite-sized step（TDD：write failing test → run FAIL → implement → run PASS → commit）
- T5 commit 後跑全套 BE pytest（pytest tests/ -q）確認 ~270 累計
- T6 在 test repo（跨 repo 切換）
- T7 純文件 + tracker → 結束 A3
- 不需 dispatch subagent — Session E 工作量在主 session 推得動

謝謝。
```

---

## 收口清單（Session D 端）

本 handoff prompt + Session D SUMMARY commit 後，新 session 接手即可：

1. 讀 5 個必讀文件（plan-A3 / design-A3 / Session C→D handoff / person_reconciler / org_reconciler + 對應 test）
2. 執行 T5 + T6 + T7（plan 內含完整 code snippet）
3. T7 commit 後 A3 完工，可進 A4 phase

## Session D 累計 commits（feature/ssp-import-export-phase2）

```
854b238  feat(oscal): A3 T3 wire A2 confirm flows to reconciliation
773e108  feat(oscal): A3 T4 fuzzy match algorithm
```

外加本 handoff prompt + Session D SUMMARY commit。

## Session D 規範遵守

- [x] 顯式 `git add <file>` 顯式檔名（兩個 commit 都用 explicit list，禁 -A）
- [x] Commit 含 `Co-Authored-By: Claude Opus 4.7 (1M context)` footer
- [x] jedi-oscal 沒動
- [x] `pyproject.toml` dev-path 不 commit
- [x] DDD 規範貫徹（reconciler 純 domain layer，不接 session；reconcile 串接點在 app service layer 內，try/except swallow 沿用 docx pattern）
- [x] T0 verify 4 項全綠（git status / log / branch / write_parties location）
- [x] T3 plan vs reality 偏差（reconcile 插入點 + test signature）標 Session E §11 reconciliation 補
- [x] 既有 docx 2 個 pre-existing fail（與 A3 無關）沿用 Session C follow-up F-T1-pre
- [x] Handoff prompt 放對位置（`docs/features/<feature>/handoff/`）
