Session 範圍:SSP 匯入匯出 Phase 2 / A3 phase / Session D(第 4 站 / 5) 產出:T3 A2 串接 reconcile + T4 fuzzy 演算法完整落地 → 2 commits + handoff 下站:Session E(T5 test 補齊 + T6 cucumber + T7 收尾)—
2026-05-20-a3-session-D-to-E-implementation.md
| 改動 | 位置 | 細節 |
|---|---|---|
_write_all_data 內補 self._reconciliation.reconcile(...) |
app/oscal/service/ssp_excel_import_app_service.py:373-379 |
在 write_parties 前;if self._reconciliation is not None 守門;try/except logger.warning swallow(沿用 docx flow line 258-264 既有 pattern) |
| tenant_id 來源 | user_context.tenant_id |
與 docx flow line 261 / 490 一致 |
| 新增 6 個 integration test | tests/test_a3_reconciliation_a2_integration.py |
superset / update / mixed list / tenant_id / no-parties skip / reconcile-exception-swallowed |
plan vs reality 偏差(待 Session E §11 補):
_confirm_*_flow」;實際 _write_all_data 為兩 flow 共用 helper,插一處 DRY 受惠decisions=[], overrides={} 簽章;實際走 confirm_import 完整 entry 比較自然test_ssp_excel_import_app_service.py fixture 不需改 — party_reconciliation_service 預設 None backward-compat| Reconciler | Stage | 演算法 |
|---|---|---|
| PersonReconciler | normalized | _strip_plus_alias (alice+work@acme.com → alice@acme.com) → 二次 get_users(email=normalized) exact 查詢 |
| PersonReconciler | fuzzy | client-side filter:拉全 tenant user list(cache)→ email domain 對齊 + nickname 或 login_name 對齊(透過 _normalize_name)→ confidence 0.7 |
| OrganizationReconciler | normalized | _normalize_name(全形空格→半形 / collapse 連續空白)→ 二次 query exact |
| OrganizationReconciler | fuzzy | client-side filter:拉全 tenant org_unit list → 雙邊 _strip_org_suffix 後比對 → confidence 0.7 |
新增 ~13 case:
A3 Session D pytest 結果:
tests/test_a3_reconciliation_base.py 7 passed
tests/test_a3_reconciliation_facade.py 3 passed
tests/test_a3_reconciliation_normalizers.py 13 passed
tests/test_a3_reconciliation_person.py 15 passed
tests/test_a3_reconciliation_organization.py 13 passed
tests/test_a3_reconciliation_a2_integration.py 6 passed ← T3 新增
tests/test_party_reconciliation_service.py 8 passed
tests/test_ssp_excel_import_app_service.py 29 passed
─────────────────────────────────────────────────────────
累計 96 passed
A3 累計新 test:57(13 normalizers + 7 base + 3 facade + 15 person + 13 org + 6 A2 integration); 本 session 新增:19(6 A2 integration + 8 person normalized/fuzzy + 5 org normalized/fuzzy)。
773e108 feat(oscal): A3 T4 fuzzy match algorithm
854b238 feat(oscal): A3 T3 wire A2 confirm flows to reconciliation
外加本 SUMMARY + handoff prompt commit 一次(docs-only)。
| Caller | A3 前 | A3 後 |
|---|---|---|
| docx flow(parse-time + confirm-time) | email exact / name exact 二態 | 三階段降級(exact / normalized / fuzzy_email_domain / fuzzy_name_prefix / unmatched) |
A2 confirm flow(_write_all_data) |
不呼叫 reconcile(A2 phase 跳過串接) | 寫入 oscal_parties 前先 reconcile,行為與 docx 一致 |
| ParsedParty | 無 match_method / match_confidence 欄位 |
帶 match_method(StrEnum)+ match_confidence(float 0.0~1.0) |
| A2 fuzzy 行為 | N/A | 自動寫入(行為等同 matched)— A5 改 user 拍板(F2 follow-up) |
| 文件 | 狀態 |
|---|---|
docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A3.md |
未動(§11 reconciliation 待 Session E 補 7 條) |
docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-A3.md |
未動(plan 早已 ship;Session E 不再 retroactive 改) |
docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md tracker |
未動(待 Session E 統一 mark A3 done) |
docs/changelog/ |
未建立(A3 完工 Session E T7.1 寫) |
docs/issues/ |
未動(無新 issue) |
docs/analysis/ |
未動(A3 重大決策已落在 design-A3) |
| ID | 內容 | 處理時機 |
|---|---|---|
| F1 | docx parser role normalize map | A3 後獨立 commit / issue resolution |
| F2 | A2 fuzzy user 拍板路徑 | A5 phase 預覽 UI |
| F3 | A4 加 5 個新 reconciler | A4 phase |
| F4 | jedi-auth Query Entity 加 _in_email_domain DB-side filter |
Prod 大 tenant feedback 後評估 |
| F5 | OSCAL party ↔︎ project_participant 雙寫 | 未來 OSCAL 匯出規劃 |
| F6 | Dev DB oscal_responsible_parties.role_id 髒資料 audit |
A3 ship 後 |
| F7 | docx parser fuzzy 預覽 UI 互動 | A5 phase |
| F-env | .env JSON quote 規格化 |
獨立 issue |
| F-T1-pre | 2 個 pre-existing docx test fail(與 A3 無關) | 獨立 issue |
| T8 | cucumber GitLab env 配齊 | Session E partial pass 時列 |
| 項目 | 說明 |
|---|---|
| BE 重啟必做 | T3 改動 app/oscal/service/ssp_excel_import_app_service.py — BE 沒 hot reload,user 必 lsof -ti:8000 | xargs kill -9 |
| jedi-oscal version | 不動 — path-dep dev,A3 完工不 bump(Phase 2 整體 ship 才 bump) |
| migration | 無(ParsedParty 是 dataclass,不涉 schema) |
| rollback path | revert 854b238 + 773e108 兩 commit — 不留遺跡,因 reconcile 結果只標在 ParsedParty(in-memory)+ oscal_parties.matched_user_id/matched_org_unit_id(既有欄位,rollback 後依然合法 null) |
| smoke 提醒 | T4 commit 純 domain layer 改動,BE process 不重啟下啟動 OscalContainer 仍 wire 對;但用 manual e2e 走 confirm_import path 必先重啟 BE |
Session E 是 A3 收尾站,預估 ~1.25d。Task:T5 test 補洞 + T6 cucumber + T7 收口(changelog / design §11 reconciliation / tracker / final SUMMARY)。
A3 完工後:
feature/ssp-import-export-phase2 累計 ~39 commits 未 push(user 點頭才 push)