Handoff Prompt — A3 Brainstorm Kickoff(共用 matcher 抽取:parties / org-units)

使用方式:把下方「交接 prompt 本體」整段貼到新 session 即可接手。 前置狀態:A2 BE 完整 shipped(9 commits + 205 test 全綠);working tree 乾淨;A3 是重型 phase 第一次開工,要走 superpowers:brainstorming SOP。


§1

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

我要進 SSP 匯入匯出 Phase 2 / A3 phase brainstorm。

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

Branch: feature/ssp-import-export-phase2(BE,30+ commits 未 push)
- A0 / A0.1 / A1 / A2 都 BE shipped;A3 是 Phase 2 第 4 個 task arc
- 工具狀態:jedi-oscal 仍 path-dep;pyproject.toml dev-path 改動仍未 commit
- Test: 205 passed (A1 既有 106 + A2 新增 99)

A3 是重型 phase(requirement-understanding §6.1)→ 走完整 brainstorm SOP:
brainstorm 收斂 → design-A3.md → implementation-plan-A3.md → 開工

開工 SOP
========

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

1. docs/features/FR-011.2-2605-ssp-import-export-phase2/handoff/2026-05-19-a1-SUMMARY-t6-final.md
   ← A1 整段收口(207 行)— A2/A3 都建在 A1 樣板基礎上
2. docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A2.md (v1.x, 含 §15.1-15.3)
   ← A2 phase design + reconciliation
   ← 特別看 §12 follow-up 第 2 條(A3 範圍 parties / org-units matcher 抽出)
   ← 特別看 §15.3 superset flow include_controls 待 A4 — 跟 A3 無直接關係但要記住
3. docs/features/FR-011.2-2605-ssp-import-export-phase2/requirement-understanding.md
   ← §6.2 Track A 階段表 A3 row(line 434)
   ← §6.4 A3 ship 條件(line 480)
   ← 風險表 line 541、547、568(A3 抽不乾淨會踩 A4 follow-up)
4. docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md
   ← Phase 2 整體 tracker — A2 row 標 BE shipped;A3 row pending
5. docs/issues/pending/2026-05-19-person-role-cross-domain-inconsistency.md
   ← issue 修補 B / C 跟 A3 範圍有重疊(docx parser role normalize + DB 髒資料 audit)
6. domain/oscal/service/party_reconciliation_service.py(97 行)
   ← 抽出的對象 — 目前 docx 用、A2 confirm 也接但未明用

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

第二步:Brainstorm 收斂(superpowers:brainstorming)

A3 重點 question 清單(brainstorm 時務必確認):

1. 共用 matcher 的擺放層級
   - 選項 A:domain/oscal/service/(既有位置)— 純 domain logic,無 app 層依賴
   - 選項 B:app/oscal/service/matching/(新建)— app service 層,可依賴多個 domain service
   - 選項 C:抽到 jedi-oscal 套件(跨專案共用,但動 jedi 套件需 user 拍板)
   現有 PartyReconciliationService 在 domain 層 + 注入 user_domain_service + org_unit_domain_service。
   抽共用如何安排?

2. matched / unmatched / fuzzy 三狀態的 contract
   - 既有實作:email exact match (person) / name exact match (organization) — 二態(matched / unmatched)
   - A3 是否引入 fuzzy 狀態?fuzzy 場景:name normalize (大小寫 / 全形半形) / Levenshtein 距離 / email domain match
   - fuzzy match 結果 user 要明確確認還是自動接受?
   - 對 issue 修補 B(docx role normalize 字串 → ParticipantRole)是否一併納入 fuzzy matcher?

3. 既有 docx 路徑切換時的 regression 策略
   - 切換前後測試:docx parser path 行為「100% 不變」是 ship 條件(requirement §6.4)
   - 跨 repo cucumber regression 必跑 — 但 user 端 GitLab env 未配齊(A0.1 follow-up #7)
   - 過渡期是否雙路徑並存(feature flag),確認 regression 後再砍舊路徑?

4. fuzzy matching 演算法精細度
   - Email:exact only?還是 normalize (lowercase / trim / 移除 +alias)?
   - Person name:normalize?模糊?(中文姓名常見「王小明」vs「王 小明」空格不同)
   - Org name:normalize?「股份有限公司」vs「股份有限公司股份」尾綴差異?
   - 第一版接受多少模糊度?太鬆 → false positive 害 user 確認;太緊 → unmatched 太多

5. 跟 issue 修補 B 整併還是拆開
   - 修補 B:docx parser 加 role normalize map(OSCAL 字串 → ParticipantRole)
   - A3 範圍 matcher 抽出 — 跟 role normalize **不在同層**,但同樣是「字串歧義 → 系統實體」對映問題
   - 整併進 A3:matcher 統一處理 user / org / role 三類映射
   - 拆開:A3 只動 user / org,role 另開 issue resolution commit
   建議在 brainstorm 階段拍板。

6. A4 對 A3 的「擴展契約」
   - A4 加 devices / info_systems / leveraged / controls / AOs 5 種新 matcher
   - A3 抽出的共用 base 要長什麼樣才能讓 A4 直接擴?
   - Generics / Strategy pattern / 各 matcher 自己一個 class?

第三步:Pre-flight Verification(design 寫之前必做)

在 brainstorm 收斂後 / design.md 寫之前,verify 下列假設:

- [ ] PartyReconciliationService 唯一 caller 是 docx import flow(grep `party_reconciliation_service` 確認)
- [ ] A2 SspExcelImportAppService.__init__ 已 inject reconciliation 但 `_confirm_*` flow 沒實際使用(confirm 直接走 ModuleFrameWriteStrategy.write_parties,沒先 reconcile)— A3 是否同時補上 A2 的 reconcile 串接
- [ ] jedi-auth UserQueryEntity / OrgUnitQueryEntity 既有欄位(reconciliation 既有靠 RLS 自動 tenant 過濾,這部分要不要動)
- [ ] cucumber 既有 docx import scenarios 有沒有 cover matched / unmatched 標記(regression test 基準)
- [ ] cucumber compliance-manager-test repo 結構(A3 ship 要新測試覆蓋)

每項用具體 grep / read 命令在 brainstorm log 內列。

第四步:產出 design-A3.md + implementation-plan-A3.md

brainstorm 拍板後:
- design-A3.md 寫到 docs/features/FR-011.2-2605-ssp-import-export-phase2/design-A3.md
- implementation-plan-A3.md 寫到 docs/features/FR-011.2-2605-ssp-import-export-phase2/implementation-plan-A3.md
- 路徑 override(CLAUDE.md 規範):brainstorming spec / writing-plans plan 都放這資料夾,不要走 docs/superpowers/specs/

問我 brainstorm 各題答案後才開工,不要直接動 code。

鐵律(沿用 A0.1 + A1 + A2 全部)
=================================

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
8. Test 用 mock 包 logger 避開 jedi DBLogHandler 撞 SessionLocal=None;@transaction 用 patch_session_scope 接 contextlib.contextmanager
9. T0/Pre-flight verify 結果不符 design 假設時主動修 design.md (§15 reconciliation)
10. 跨 repo 切換時提醒 user:cucumber 在 compliance-manager-test repo,commit / branch 分開
11. 改 docx parser 既有行為 → cucumber regression 必跑 — 是 ship 必要條件
12. 重大決策 → docs/analysis/YYYY-MM-DD-<topic>.md(trade-off 取捨理由)
13. 重型 bug / 跨域問題 → docs/issues/pending/YYYY-MM-DD-<title>.md(不擠在 changelog)
14. Handoff 文件統一放 docs/features/<feature>/handoff/(CLAUDE.md 2026-05-20 新規)

A1 + A2 既有 contracts(A3 必看)
================================

PartyReconciliationService 現有 contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

位置: domain/oscal/service/party_reconciliation_service.py (97 行)
DI: di_containers/oscal/oscal_containers.py:482 (Factory)
唯一 method:
  reconcile(parties: List[ParsedParty], tenant_id: Optional[int]) -> List[ParsedParty]
  - 對 person:email lowercased+trim → UserQueryEntity 找 → matched_user_id
  - 對 organization:name trimmed → OrgUnitQueryEntity 找 → matched_org_unit_id
  - in-place mutate + return same list
  - tenant 隔離靠 RLS(不在 service 層加 filter)

Caller:
  - docx import flow: SspDocxImportAppService.upload_and_parse line 258-264
    (parse 階段就跑 reconcile,結果存 parsed_result.parties)
  - A2 Excel import flow: 已 inject 但 _write_all_data 沒呼叫 reconcile —
    parties 直接走 ModuleFrameWriteStrategy.write_parties 寫入(無 matched 標記)
    這是 A3 要補的串接點之一

ParsedParty 結構
~~~~~~~~~~~~~~~

domain/oscal/parser/ssp_intermediate.py line 50:
  @dataclass
  class ParsedParty:
      name: str
      party_type: Literal["person", "organization"]
      role: Optional[str]
      email_address: Optional[str]
      ...
      matched_user_id: Optional[int]      # ← reconciliation 填
      matched_org_unit_id: Optional[int]  # ← reconciliation 填
      target_party_uid: Optional[str]     # diff service 用

A3 抽共用後仍保留此 dataclass(contract 對外不動)— 變的是內部演算法
跟可能新增的 fuzzy 狀態欄位(如 match_confidence / match_method)。

OSCAL 表寫入點(A3 結果最終落地處)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ModuleFrameWriteStrategy.write_parties (domain/oscal/strategy/module_frame_write_strategy.py:265)
  - 接 List[ParsedParty]
  - 寫 oscal_parties (按 matched_user_id / matched_org_unit_id 填欄位)
  - 寫 oscal_responsible_parties (link to module_frame context)

SspWriteStrategy 同上 pattern but context_type='ssp'

issue 修補 A / B / C 對齊
~~~~~~~~~~~~~~~~~~~~~~

修補 A(A2 已做):Excel parser 03 sheet role enum validation
修補 B(A3 可整併):既有 docx parser 加 role normalize map
修補 C(A3 後):dev DB oscal_responsible_parties.role_id 髒資料 audit + 清理
修補 D(後遠期):OSCAL party ↔ project_participant 雙寫設計(不在 A3 範圍)

已知限制 / risk
================

- BE 完整 boot 撞 jedi-issue GitLab env 問題(A0.1 follow-up #7,非 A3 引入)
- cucumber regression 需 user 端配齊 GitLab env 後才能完整跑
- 改 docx parser path 行為若有偏差 → 影響 SSP docx 匯入 existing customer flow
- pre-existing test fail: test_write_parties_refreshes_existing_party_in_place(非 A3 引入)

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

- 用既有 superpowers:brainstorming skill 走完整 6 question 收斂
- 用既有 superpowers:writing-plans skill 寫 implementation-plan-A3
- Subagent 平行查(Explore agent)— PartyReconciliationService caller 盤點 / cucumber existing matched 標記 scenarios / jedi-auth UserQueryEntity 既有支援

謝謝。

§2

收口清單

本 handoff prompt commit 後,新 session 接手即可:

  1. 讀 6 個必讀文件
  2. Brainstorm 6 個 question 拍板
  3. Pre-flight verify 5 項假設
  4. 產 design-A3.md + implementation-plan-A3.md
  5. 等 user GO 才開 T0
§3

A3 phase 預估規模

  • Brainstorm: 0.5d(含 6 question 收斂 + verify)
  • design-A3.md + plan-A3.md 產出: 0.5d
  • T0 verify: 0.25d
  • T1 抽 PartyReconciliationService 共用層: 0.5d
  • T2 docx path 切換 + regression: 0.5d
  • T3 A2 path 串接 matcher: 0.25d
  • T4 fuzzy matching 演算法(若 brainstorm 拍板要做): 0.5d
  • T5 unit test + cucumber regression: 0.5d
  • T6 changelog + tracker: 0.25d

總計:~3.25 working day(不含 cucumber 跑 BE smoke 需 user 配齊 env)

§4

跨 repo 影響

Repo 預期工作
BE(主) matcher 抽共用層 + docx path 切換 + A2 path 串接 + unit test
FE A3 phase 不動 FE(matched 標記已有 schema 接,預覽 UI 在 A5 補)
jedi-* 不動(reconciliation 維持在主專案)— 除非 brainstorm 選項 C 才動 jedi-oscal
test cucumber regression:docx existing matched scenarios + 新加 fuzzy match scenarios
changelog docs/changelog/YYYY-MM-DD-feat-party-matcher-shared.md + 可能 tweak-docx-role-normalize.md
§5

既有 A1/A2 commit 鏈速查

詳見 docs/features/FR-011.2-2605-ssp-import-export-phase2/README.md tracker。

A3 起手前不需要 push BE branch(30+ commits 累積中,等 Phase 2 主線完工或 user 拍板才 push)。