# A3 Session C — SUMMARY（implementation T0+T1+T2 收口）

> **Session 範圍**：A3 phase Session C — T0 implementer pre-flight verify + T1 BaseReconciliationService / reconcilers skeleton / ParsedParty 加欄位 + T2 PartyReconciliationService 改門面 + DI wiring
> **預估 vs 實際**：預估 ~1d / 實際 ~0.5d（plan 內 bite-sized step 落地順暢 + 兩個 plan 假設不符 inline 修正）
> **狀態**：✅ T1 + T2 shipped；42 A3 + reconciliation test passed；廣域 regression 40 pre-existing fail 與 A3 無關

---

## 1. Session C Commits（feature/ssp-import-export-phase2）

| commit | 訊息 |
|--------|------|
| `b011f2f` | `feat(oscal): A3 T1 base + reconcilers skeleton` |
| `82f0dc8` | `feat(oscal): A3 T2 facade + DI wiring` |
| （待 commit）| handoff `2026-05-20-a3-session-C-to-D-implementation.md` + 本 SUMMARY |

**working tree 狀態**：除 pre-existing pyproject.toml dev-path 改動 + 既有 conversation-history zip 異動 + 一個 untracked `docs/features/ssp-import-export-phase2.zip`（user 自行匯出），本 session 改動全 commit。

## 2. 改動範圍

### 新建檔案

| 檔案 | 行數 |
|------|------|
| `domain/oscal/service/reconciliation/__init__.py` | 17 |
| `domain/oscal/service/reconciliation/base.py` | 95 |
| `domain/oscal/service/reconciliation/match_method.py` | 10 |
| `domain/oscal/service/reconciliation/_normalizers.py` | 39 |
| `domain/oscal/service/reconciliation/person_reconciler.py` | 41 |
| `domain/oscal/service/reconciliation/organization_reconciler.py` | 43 |
| `tests/test_a3_reconciliation_normalizers.py` | 62 |
| `tests/test_a3_reconciliation_base.py` | 96 |
| `tests/test_a3_reconciliation_person.py` | 54 |
| `tests/test_a3_reconciliation_organization.py` | 62 |
| `tests/test_a3_reconciliation_facade.py` | 55 |

### 改動既有檔案

| 檔案 | 動作 |
|------|------|
| `domain/oscal/parser/ssp_intermediate.py` | ParsedParty 加 `match_method` + `match_confidence` optional 欄位（含 docstring 解釋 EXACT/NORMALIZED 都 confidence=1.0 但 method 區分為 A5 UI 提示）|
| `domain/oscal/service/party_reconciliation_service.py` | 97 行 → 30 行門面 dispatch by `party_type` |
| `di_containers/oscal/oscal_containers.py` | 加 import + 加 `person_reconciler` / `organization_reconciler` Factory + 改 `party_reconciliation_service` 注入兩 reconciler |
| `tests/test_party_reconciliation_service.py` | fixture `_make_facade()` 改 inject `PersonReconciler` / `OrganizationReconciler`，**assertion 0 行改動** |

## 3. 行為差異

### A3 前後對照

| Caller | A3 前 | A3 後 |
|--------|-------|-------|
| `PartyReconciliationService.reconcile(parties, tenant_id)` 公開 method | exact match only | **公開 API 簽章不變**；底層改 facade dispatch（A3 T2）；normalized + fuzzy stage 仍是 stub return None（**T4 落地後三階段降級才完整啟用**）|
| `ParsedParty` dataclass | 11 欄位 | 13 欄位（加 `match_method` default `UNMATCHED` + `match_confidence` default `0.0`）— 既有 caller / fixture 不破壞 |
| DI container | 1 個 `party_reconciliation_service` Factory | + 2 個新 Factory (`person_reconciler` / `organization_reconciler`)；門面改注入兩 reconciler instance |

### Session C 期間 Reconciliation 實際行為

T1 + T2 落地後，行為仍等同 A3 前的 exact match — 因為 `PersonReconciler` / `OrganizationReconciler` 的 `_try_normalized_match` 和 `_try_fuzzy_match` 都還是 `return None` stub。**Fuzzy 真正啟用要等 Session D T4**。但既有 docx / Excel caller 已經透過門面接到新架構，A3 後續 T4 / T5 改動對 caller 完全透明。

## 4. 規範文件齊全度

| 文件類型 | 路徑 | 狀態 |
|---------|------|------|
| Implementation commits | `b011f2f` + `82f0dc8` | ✅ shipped |
| Handoff prompt（Session C→D）| `docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-20-a3-session-C-to-D-implementation.md` | ✅ written |
| Session SUMMARY | `docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-20-a3-session-C-SUMMARY.md` | ✅ written（本檔）|
| Changelog | — | ⏳ Session E T7 一次寫 `2026-05-XX-feat-party-matcher-shared.md`（A3 整段 feat）|
| Analysis | — | ❌ 不適用（無重大決策 trade-off — design 階段已落地）|
| Issue | — | ❌ 不適用 |
| Tracker README 更新 | `docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md` | ⏳ A3 row 等 Session E 收口時一次更新 |
| Conversation history dump | — | ⏳ 留 Session E 收口時 task arc 全 dump |

## 5. Plan vs Reality 偏差（留 Session E T7.3 design §11 reconciliation 補）

| # | Plan 假設 | Reality | 處理 |
|---|----------|---------|------|
| 1 | T1.7 `__init__.py` export 4 個（base / enum / 兩 reconciler）| 只能 export 2 個（base + enum）；reconciler 會 trigger `ssp_intermediate → match_method` circular import | `__init__.py` 加 docstring 解釋；caller 從 `person_reconciler` / `organization_reconciler` module 直接 import |
| 2 | T2.1 「既有 8 party test 0 行測試 code 改動全綠」| facade `__init__` signature 從 `(user_svc, org_svc)` 改 `(person_reconciler, organization_reconciler)` → 既有 8 test 直接構建會 fail | 加 `_make_facade(user_svc, org_svc)` helper 包 inject — assertion 行 0 行改動，只改構造方式 |
| 3 | T1.5 / T1.6 code snippet 寫 `jedi_auth.domain.entity.user_query_entity` | 既有 `party_reconciliation_service.py:69 / 88` 用 `jedi_auth.domain.entities.user_query_entity`（**entities 複數**） | 實作對齊既有 import path |
| 4 | T2.2 Step 1b verify `^from.*auth` import 應存在 | OscalContainer 用 `auth_container = providers.DependenciesContainer()`（line 111）sub-container reference pattern，**不是普通 import** | wiring pattern 正確；plan grep 條件需放寬到 `DependenciesContainer` |
| 5 | T2.2 Step 3 BE full boot smoke | `.env` line 3 `JWT_SECRET={...}` 沒 quote → `set -a; source .env` 撞 bash parser syntax error | 改用 OscalContainer module import + provider 宣告檢查（**partial smoke pass**）；plan T2.2 acceptance「BE 起動成功（或 partial smoke pass — wire 無 error）」涵蓋此 fallback ✅ |

5 條偏差留 Session E T7.3 一次補進 design-A3.md §11。

## 6. 已知 Follow-up

| # | 項目 | 狀態 |
|---|------|------|
| F1 ~ F7 | 沿用 plan 預列 7 條 follow-up（docx role normalize / A5 fuzzy 拍板 / A4 5 reconciler / jedi-auth fuzzy filter / OSCAL party 雙寫 / DB 髒資料 audit / docx fuzzy UI）| 沿用 |
| T8 | Cucumber regression 5 scenarios 跑（user 端 GitLab env 未配齊）| Session E 才有 |
| **F-env**（新）| `.env` line 3 `JWT_SECRET={...}` 沒 quote → BE 用 `set -a; source .env` 撞 bash parser；Workaround：手動 export 變數 / 用 python-dotenv | 開發環境問題，跟 A3 無關但 surfaced 出來；標 follow-up 給後續 dev 處理 |
| **F-T1-pre**（新）| 2 個 pre-existing 既有 docx test fail（與 A3 無關，stash verify 過）：`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 | 開 issue 給 docx default_action 邏輯 owner |
| **F-regression-env**（新）| 廣域 regression 40 fail（A3 commit HEAD 上 verify 跟 A3 無關）— 多數是 `_no_jwt` / `_hidden_secret` / `PART_*` / `PROJ_*` 等 conftest fixture / env 相關 pre-existing 問題 | 環境問題，A3 ship 不擋；列 follow-up 給後續 test infra 處理 |

## 7. 部署 Handover

- **無部署需求** — Session C 純 BE domain layer + DI wiring 改動，無 DB migration / 無 jedi-* 套件 bump
- 改了 BE service code（T1 ParsedParty + T2 facade + DI）— **下次 manual 測 reconcile 行為前必重啟 BE**：
  ```bash
  lsof -ti:8000 | xargs kill -9
  ```
- jedi-oscal 維持 path-dep，dev path 設定不變
- pyproject.toml dev-path 改動仍未 commit（按鐵律不該 commit）
- **`.env` 環境 issue**：使用者後續若要 manual smoke E2E，要先處理 `JWT_SECRET` JSON quote 問題（標 follow-up F-env）

## 8. 下一步（Session D 接手）

按 handoff prompt `2026-05-20-a3-session-C-to-D-implementation.md` 執行：

1. 讀 5 個必讀文件（plan-A3 T3+T4 段 / design-A3 §5+§6 / ssp_excel_import_app_service.py / person_reconciler.py / organization_reconciler.py + 對應 test 檔）
2. T3 — A2 `_confirm_*_flow` 補 reconcile() 串接（~0.25d，4 個 integration test）
3. T4 — Fuzzy 演算法完整落地（~0.5d，PersonReconciler normalized + fuzzy 8 case + OrganizationReconciler normalized + fuzzy 5 case）
4. T3 + T4 結束提醒 user 重啟 BE
5. 產 Session D → E handoff + Session D SUMMARY

**預估**：Session D 整段 ~0.75d

---

## A3 phase 整體進度

| Session | 範圍 | 狀態 | Commits |
|---------|------|------|---------|
| A | brainstorm + design 落地 | ✅ shipped | 8e61def, 6e0f103 |
| B | writing-plans | ✅ shipped | 479bc28 |
| **C** | **Implementation T0+T1+T2** | **✅ shipped（本 session）** | **b011f2f, 82f0dc8** |
| D | Implementation T3+T4 | ⏳ pending | — |
| E | Test + cucumber + 收尾 | ⏳ pending | — |

**A3 phase 累計**：5 commits（design ×2 + plan ×1 + implementation ×2）+ 4 handoff + 2 SUMMARY；剩餘 ~2d 進入後續 implementation + 收尾。

---

**Session C 收口完成。**
