Handoff Prompt — A3 Session C → Session D(implementation T3+T4)

使用方式:把下方「交接 prompt 本體」整段貼到新 session 即可接手。 前置狀態:Session C(T0+T1+T2)完成;BaseReconciliationService + reconcilers skeleton + 門面 + DI wiring 都 shipped;working tree 乾淨(除 pre-existing pyproject.toml + 既有 conversation-history zip 異動)。 Session D 任務:T3 A2 _confirm_*_flowreconcile() 串接 + T4 fuzzy 演算法完整落地(PersonReconciler + OrganizationReconciler 三階段降級)→ 2 個 commit。


§1

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

我要進 SSP 匯入匯出 Phase 2 / A3 phase Session D — implementation T3+T4。

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

Branch: feature/ssp-import-export-phase2(BE,35 commits 未 push)
- A0 / A0.1 / A1 / A2 都 shipped;A3 Session A / B / C 都 shipped
- Session C 累計 2 commits:
  - b011f2f feat(oscal): A3 T1 base + reconcilers skeleton
  - 82f0dc8 feat(oscal): A3 T2 facade + DI wiring
- 工具狀態:jedi-oscal 仍 path-dep;pyproject.toml dev-path 改動仍未 commit
- Test 基準:42 passed(既有 8 party + 15 normalizers + 7 base + 4 person + 5 org + 3 facade)

A3 Session D 是 5 session 切分中的第 4 站,task:
  T3 A2 _confirm_superset_flow + _confirm_update_flow 補 reconcile() 呼叫
  T4 fuzzy 演算法完整落地:
     - PersonReconciler _try_normalized_match + _try_fuzzy_match
     - OrganizationReconciler _try_normalized_match + _try_fuzzy_match
     - 累計 ~13 個新 test(normalized + fuzzy + corner case)
  預估 ~0.75d(D session 整段)

開工 SOP
========

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

1. docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-A3.md
   ← T3 + T4 段(line 1085~ T3.1-T3.5、line 1247~ T4.1-T4.3)
   ← TDD step-by-step + 完整 code snippet + acceptance
2. docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A3.md
   ← §5.1 PersonReconciler 三階段 / §5.2 OrganizationReconciler 三階段 / §5.4 cache 行為
   ← §6.1-6.3 A2 caller 切換策略 + fuzzy 自動寫入妥協
3. app/oscal/service/ssp_excel_import_app_service.py
   ← T3 改動對象 — _confirm_superset_flow (line ~251) + _confirm_update_flow (line ~325)
   ← Plan T0.2 已 verify body 內無 reconcile() 呼叫,T3 要補
4. domain/oscal/service/reconciliation/person_reconciler.py + organization_reconciler.py
   ← Session C 落地的 skeleton;T4 把 normalized + fuzzy 兩 stub method 填滿
5. tests/test_a3_reconciliation_person.py + tests/test_a3_reconciliation_organization.py
   ← Session C 已寫 exact stage test;T4 補 normalized + fuzzy stage test

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

第二步:執行 T3 + T4

T3 — A2 串接 reconcile(先做,較小)
  3.1 verify 既有 helper 位置 + write_parties 簽章(接 ParsedParty list 還是 dict list)
  3.2 修 _confirm_superset_flow:write_parties 前加 self._reconciliation.reconcile(parties_combined, tenant_id=...)
  3.3 修 _confirm_update_flow:同 pattern
  3.4 寫 4 個 integration test (plan T3.4 已有完整 fixture skeleton + 4 個 case)
  3.5 commit
     feat(oscal): A3 T3 wire A2 confirm flows to reconciliation

T4 — Fuzzy 演算法(較大)
  4.1 PersonReconciler 補 normalized + fuzzy stage
     - normalized: _strip_plus_alias (alice+work@x.com → alice@x.com) → re-query exact
     - fuzzy: 拉全 tenant user list (cache) → email domain + nickname/login_name 對齊
     - 補 ~8 test case
  4.2 OrganizationReconciler 補 normalized + fuzzy stage
     - normalized: _normalize_name (全形→半形、collapse) → re-query exact
     - fuzzy: 拉全 tenant org_unit list (cache) → 剔除尾綴後 exact 對齊
     - 補 ~5 test case
  4.3 commit
     feat(oscal): A3 T4 fuzzy match algorithm

第三步:BE smoke + 提醒 user

T3 + T4 commit 後不需 BE smoke(純 domain layer 改動,T2 已驗 DI wire OK);
但 T3 改動 app service 層 — **必明確提醒 user:BE 沒 hot reload,下次 manual
測 reconcile 串接前必重啟**:
  lsof -ti:8000 | xargs kill -9

**.env JSON parse 問題**(Session C 開工時撞):
  - line 3 JWT_SECRET={ ... } 沒 quote → set -a; source .env 撞 syntax error
  - Workaround:partial smoke 用 OscalContainer 直接 import 驗 DI wire
  - 若 user 想 manual 測 E2E:手動 export 每個變數,或 git stash JWT_SECRET 改用 quoted form
  - 標 follow-up F-env:.env quote JSON 規格化

第四步:跨 session 接續

T4 commit 後產 handoff prompt 給 Session E(test 補齊 + cucumber + 收尾):
  docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-XX-a3-session-D-to-E-implementation.md
然後給 user Session D SUMMARY,等 user GO 換 Session E。

A3 Session A / B / C 已產出(參考)
==================================

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

Session C 已落地(可直接 import):
  domain/oscal/service/reconciliation/
    __init__.py — 只 export BaseReconciliationService + MatchMethod
                  (circular import:PersonReconciler / OrganizationReconciler
                   不可 re-export,caller 直接 import 各自 module)
    base.py — BaseReconciliationService[TParsed, TEntity] 抽象基類 + 5 hook
              + _get_or_load_candidates exception 行為(return [] 不寫 cache)
    match_method.py — MatchMethod StrEnum 5 values
    _normalizers.py — _strip_plus_alias / _normalize_name / _strip_org_suffix
    person_reconciler.py — exact stage 完整;normalized + fuzzy 留 stub return None
    organization_reconciler.py — exact stage 完整;normalized + fuzzy 留 stub return None
  domain/oscal/parser/ssp_intermediate.py — ParsedParty 加 match_method + match_confidence
  domain/oscal/service/party_reconciliation_service.py — 改門面 dispatch
  di_containers/oscal/oscal_containers.py — 加 person_reconciler / organization_reconciler Factory
  tests/test_party_reconciliation_service.py — 既有 8 case fixture 改 inject _make_facade()
                                                (assertion 0 行改動)

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

1. **Plan T1.7 vs reality**:plan 要求 __init__.py export 4 個,實作只 export 2 個
   (PersonReconciler / OrganizationReconciler 會 trigger 對 ssp_intermediate.py
   的 circular import)— Session E T7.3 design §11 reconciliation 補一條
2. **Plan T2.1 vs reality**:plan 寫「既有 8 test 0 行改動全綠」不成立 — facade
   __init__ signature 變更必須同步改 fixture(assertion 仍 0 行改動)— Session E
   §11 補一條
3. **Plan import path vs reality**:plan code snippet 寫 `jedi_auth.domain.entity`,
   既有 code + 實作對齊用 `jedi_auth.domain.entities`(複數)— 不重要 minor,
   仍 §11 補一條
4. **既有 docx test 2 個 pre-existing fail**:
   - `test_create_mode_get_parse_result_all_controls_added`
   - `test_get_parse_result_party_added_when_ssp_has_no_parties`
   - 兩者皆 `default_action == 'use_docx'` 拿到 None;stash A3 改動後仍 fail
   - 確認與 A3 無關,標 follow-up F-T1-pre

鐵律(沿用 A0.1 + A1 + A2 + Session A + B + C 全部)
====================================================

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 | 預估 |
|---------|------|------|------|
| D(self)| Implementation T3+T4 | A2 串接 + fuzzy 演算法 | ~0.75d |
| E | Test + cucumber + 收尾 | T5 + T6 + T7 | ~1.25d |

剩餘總計:~2d

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

- 直接照 plan-A3 內 bite-sized step(TDD:write failing test → run FAIL → implement → run PASS → commit)
- T3 + T4 結束 commit 後不需 BE smoke,但提醒 user 重啟
- 不需 dispatch subagent — Session D 工作量在主 session 推得動

謝謝。

§2

收口清單(Session C 端)

本 handoff prompt + Session C SUMMARY commit 後,新 session 接手即可:

  1. 讀 5 個必讀文件(plan-A3 / design-A3 / ssp_excel_import_app_service.py / person_reconciler.py / organization_reconciler.py + 對應 test 檔)
  2. 執行 T3 + T4(plan 內含完整 code snippet)
  3. T4 結束後提醒 user 重啟(BE 沒 hot reload)
  4. 產 Session D → E handoff + Session D SUMMARY
§3

Session C 累計 commits(feature/ssp-import-export-phase2)

b011f2f  feat(oscal): A3 T1 base + reconcilers skeleton
82f0dc8  feat(oscal): A3 T2 facade + DI wiring

外加本 handoff prompt + Session C SUMMARY commit。

§4

Session C 規範遵守