A3 Phase Final SUMMARY — 共用 matcher 抽取 + fuzzy 三態 + A2 串接

Phase:A3(Track A:Excel 匯入第三階段) 狀態:BE shipped ✅(Session A→E 共 5 session 全完成) Ship 日期:2026-05-20 Task arc 跨度:2026-05-20 同日 5 session(A 設計 / B 計畫 / C 架構 / D fuzzy / E 收口) 依賴本 phase:A4(5 個新 reconciler)建在本 phase BaseReconciliationService


1. Commit 清單

compliance-manager-be(branch feature/ssp-import-export-phase2

Commit 內容 Session
b011f2f feat(oscal): A3 T1 base + reconcilers skeleton C
82f0dc8 feat(oscal): A3 T2 facade + DI wiring C
854b238 feat(oscal): A3 T3 wire A2 confirm flows to reconciliation D
773e108 feat(oscal): A3 T4 fuzzy match algorithm D
761fd7c test(oscal): A3 T5 unit + integration tests E
(本 commit) docs(ssp-import-export-phase2): A3 收尾 — changelog + design §11 + SUMMARY E

外加 4 個 Session 收尾 docs commit(A→B / B→C / C→D / D→E handoff prompt + Session SUMMARY)。

compliance-manager-test(branch feature/a3-ssp-party-match-regression

Commit 內容
27e52fe test(compliance-manager-test): A3 T6 cucumber party match regression

jedi-* 套件

不動(design §10 鎖定範圍)。jedi-oscal 維持 path-dep(feature 完成才一次 bump + Nexus)。


2. 改動範圍(檔案清單)

新建檔案(compliance-manager-be)

Domain layer(6 個 .py)

domain/oscal/service/reconciliation/
├── __init__.py                  # export BaseReconciliationService + MatchMethod
├── base.py                      # BaseReconciliationService[TParsed, TEntity] (97 行)
├── match_method.py              # MatchMethod StrEnum (EXACT/NORMALIZED/FUZZY_EMAIL_DOMAIN/FUZZY_NAME_PREFIX/UNMATCHED)
├── _normalizers.py              # _strip_plus_alias + _normalize_name + _strip_org_suffix (41 行)
├── person_reconciler.py         # PersonReconciler 三階段 (85 行)
└── organization_reconciler.py   # OrganizationReconciler 三階段 (84 行)

Test layer(7 個 test 檔,67 個 A3 新 test)

tests/
├── test_a3_reconciliation_normalizers.py    # 13 case
├── test_a3_reconciliation_base.py           # 7 case
├── test_a3_reconciliation_facade.py         # 3 case
├── test_a3_reconciliation_person.py         # 16 case
├── test_a3_reconciliation_organization.py   # 16 case
├── test_a3_reconciliation_a2_integration.py # 6 case
└── test_a3_parsed_party_fields.py           # 4 case (T5.2 新)

Changelog + tracker

docs/changelog/2026-05-20-feat-party-matcher-shared.md
docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-20-a3-SUMMARY.md (本文件)

改動既有檔案

檔案 改動
domain/oscal/service/party_reconciliation_service.py 改門面(facade),dispatch by party_type 到兩個 reconciler;公開 method reconcile(parties, tenant_id) 簽章不變
domain/oscal/parser/ssp_intermediate.py ParsedPartymatch_method(default MatchMethod.UNMATCHED)+ match_confidence(default 0.0)optional 欄位
app/oscal/service/ssp_excel_import_app_service.py _write_all_dataself._reconciliation.reconcile(all_parties, tenant_id=...) 呼叫(在 write_parties 前;try/except logger.warning swallow)
di_containers/oscal/oscal_containers.py 新增 person_reconciler / organization_reconciler Factory;門面改注入兩 reconciler
docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md A3 row → BE shipped ✅ + commit chain
docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A3.md §11 reconciliation 填 9 條偏差條目 + 已對齊條目

compliance-manager-test 新建

features/regression/module-frame/05-ssp-docx-import-party-match.feature
steps/module-frame/ssp-docx-import-party-match.steps.js

3. 行為差異(A3 前 vs A3 後)

Caller A3 前 A3 後
Docx flow parse-time(SspDocxImportAppService line 258-264) exact match only exact + normalized + fuzzy 三階段 fallback
Docx flow confirm-time(line 489-490) 同上 同上
A2 superset flow(_confirm_superset_flow_write_all_data 不呼叫 reconcile 呼叫 reconcile,fuzzy 自動寫入
A2 update flow(_confirm_update_flow_write_all_data 不呼叫 reconcile 呼叫 reconcile,fuzzy 自動寫入
ParsedParty dataclass match_method / match_confidence 欄位 加 2 個 optional 欄位(default UNMATCHED / 0.0),既有 caller 不破壞
PartyReconciliationService 公開介面 直接接 jedi-auth domain service 改門面,內部 dispatch 兩 reconciler,公開 method 簽章 100% 不變

Fuzzy 演算法

  • Person:normalized +alias 移除 → fuzzy 拉全 tenant cache → email domain + nickname/login_name 對齊
  • Organization:normalized 全形→半形 / collapse → fuzzy 拉全 tenant cache → 雙邊 strip suffix 後比對

Confidence + UI 互動

  • EXACT / NORMALIZED confidence=1.0 → A3 期間都自動寫入
  • FUZZY_* confidence=0.7 → A3 期間也自動寫入;A5 phase 預覽 UI 補 user 拍板(follow-up F2)

4. 規範文件齊全度

項目 路徑 狀態
Changelog docs/changelog/2026-05-20-feat-party-matcher-shared.md ✅ 完成(commit chain + behaviour diff + follow-up F1~F7+T8)
Tracker docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md ✅ A3 row → BE shipped
Design §11 reconciliation docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A3.md ✅ 9 條偏差條目 + 已對齊條目(分 11.1~11.5 5 子段)
Implementation plan docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-A3.md ✅ Session B 已 ship(~2,045 行)
Design.md docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A3.md ✅ Session A 已 ship(13 章 / ~670 行 + §11 補完)
對話歷史 dump docs/conversation-history/2026-05-20/ssp-import-export-phase2-A3/ ⚠ 留待 user export 後 dump(不二次加工 / 原樣寫入)
Issue 修補關係 對應 docs/issues/pending/2026-05-19-person-role-cross-domain-inconsistency.md 修補 A(A1 已 ship 修補)— A3 不整併修補 B(role normalize map),列 follow-up F1

5. 已知 follow-up

# 項目 處理時機
F1 Issue 修補 B(docx parser role normalize map)— role 是 enum 規範問題、不是 matcher 問題 A3 後獨立 commit / 或 issue resolution 階段
F2 A5 phase 補 A2/docx fuzzy 的 user 拍板路徑(A3 期間 fuzzy 自動寫入) A5 phase
F3 A4 加 5 個新 reconciler(device / info_system / leveraged / catalog control / AO)建在 BaseReconciliationService 上 A4 phase(直接擴 base)
F4 jedi-auth Query Entity 加 _in_email_domain / _in_name_prefix 補 fuzzy DB-side filter Prod 大 tenant feedback 後評估
F5 OSCAL party ↔︎ project_participant 雙寫設計(issue 修補 D) 未來 OSCAL 匯出規劃時
F6 Dev DB oscal_responsible_parties.role_id 髒資料 audit / 清理(issue 修補 C) A3 ship 後執行 audit SQL
F7 docx parser 拿到 fuzzy 標記後預覽 UI 互動(A5-similar UI) A5 phase
T8 Cucumber partial ship:GitLab env 配齊後跑 5 scenarios + 補 fixture user/org seeding helper + docx fixture ASIA-CMMC-SSP-DRAFT-party-match.docx env 配齊後
F-T1-pre 既有 docx 2 個 pre-existing fail(test_create_mode_get_parse_result_all_controls_added / test_get_parse_result_party_added_when_ssp_has_no_parties 與 A3 無關,獨立排查
F-env .env JWT_SECRET={...} 沒 quote 導致 source .env syntax error 工具修補

6. 部署 Handover

6.1 套件狀態

  • jedi-oscal:維持 path-dep(pyproject.toml dev-path 改動不 commit;feature 完成才一次 bump version + 推 Nexus)
  • jedi-auth / jedi-common:A3 不動 — 維持 Nexus pin
  • 主專案 pyproject.toml:A3 期間 dev-path 改動 working tree 仍未 commit(正常 — Phase 2 整體完工才一起還原 + commit)

6.2 BE 重啟需求

T3 commit 854b238 改動了 app service 層app/oscal/service/ssp_excel_import_app_service.py)— BE 沒 hot reload,下次 manual smoke 測 A2 reconcile 串接前必先重啟

lsof -ti:8000 | xargs kill -9
nohup poetry run python main_app.py > /dev/null 2>&1 &

6.3 DB / Migration

  • A3 無 schema 異動(純 in-memory 行為改動)
  • 既有 oscal_parties / oscal_responsible_parties schema 不動
  • oscal_responsible_parties.role_id 髒資料 audit / 清理列 F6 follow-up(與 A3 無關)

6.4 後續 phase 開工建議

A3 完工後:

  • A4 phase(5 個新 reconciler)直接擴 BaseReconciliationService[TParsed, TEntity];template 在 domain/oscal/service/reconciliation/{person,organization}_reconciler.py 樣板上 copy 即可
  • A5 phase(預覽 UI + Confirm)補 fuzzy 拍板路徑,drift fuzzy 自動寫入 → 預覽攔截

6.5 Cucumber T6 partial ship 注意

  • compliance-manager-test branch feature/a3-ssp-party-match-regression 尚未 merge 回 main
  • Env 配齊後:
    1. fixtures/cmmc/imports/ssp-docx/ASIA-CMMC-SSP-DRAFT-party-match.docx(含預期 5 scenarios party 內容)
    2. seeding helper(admin API or test data hook 種 user/org_unit)
    3. npm run cucumber -- features/regression/module-frame/05-ssp-docx-import-party-match.feature
    4. 全綠後 merge

7. A3 task arc 規範遵守清單


8. A3 phase 收口確認

A3 phase 完整 ship — 5 session(A→E)on schedule,partial ship cucumber 允許

進入 A4 phase 條件已就緒:

  • BaseReconciliationService 抽象基類 + 兩個 reference reconciler(person / organization)+ A2/docx 兩條 caller 串接 + 67 個 A3 新 test 全綠 + cucumber regression baseline 撰寫完成(env 配齊後跑通即 ship)

下一個 phase:A4 — devices / info_systems / leveraged / 控制項 / AO 鉤稽(5 個新 reconciler 建在 BaseReconciliationService 上)。