# FR-034 — jedi-flow-engine vs BE WorkflowExecutionService 收斂 feasibility

| 項目 | 內容 |
|------|------|
| 日期 | 2026-06-08 |
| 性質 | Feasibility 評估（非 bug、無 live bug）|
| 結論 | **採方案 c：文件化現狀，不做合併**。目前無第二個稽核產品，合併收益收不到、成本高 |
| 來源 | 套件重刻漂移收斂案例2；詳細 method 對照見 `docs/analysis/2026-06-07-flow-engine-package-vs-be-divergence.md` |

---

## 1. 問題

BE `app/flow_engine/service/workflow_execution_service.py`（1615 行）把 `WorkflowExecutionService` 重寫成產品專屬超集，與 jedi-flow-engine 套件版（598 行）各自演化。要不要把兩份收斂成一份？

## 2. 現況差異（2026-06-08 重讀）

**結構：BE 是嚴格超集** —— 13 個同名 method + 17 個 BE 獨有 + 套件獨有 0。

- **9 個薄 CRUD getter**：兩邊精神一致（BE 多 locale/user/RLS）。
- **3 個熱區**（`complete_job` / `revert_job` / `start_workflow_execution`）：核心 BPMN 邏輯相同，但 BE 把產品副作用「織」進控制流。以 `complete_job` 為例（套件 112 行 → BE 171 行），同一骨架插了 9 個產品副作用點：權限守門(C16)、GrcErrorCode、AP-task 通知、專案狀態、I7 CANCEL-aware 判定、AP-task 狀態、job_execution_uid 注入(巢狀子流程)、survey 關聯、todo 通知。`revert_job` 更甚（86 → 221 行）。
- **17 個 BE 獨有 method**：`assert_project_participant` / `complete_main_workflow_job` / `notify_*` / `process_survey_property` / `update_assessment_plan_task_job_link` / `build_task_combinations` 等，全依賴 BE 專屬 domain（AP / Project / Participant / Notification / Survey / Drive），無法進通用套件。

## 3. 關鍵 reframe —— 「重用」不是 yes/no，要看重用哪一層

現況其實已經分兩層：

| 層 | 在哪 | 重用性 |
|---|---|---|
| 通用 BPMN 引擎 | jedi-flow-engine 套件（598 行）| **已可重用**——不同領域的產品今天就能拿去用 |
| GRC/稽核編排（17 method + 9 織入副作用）| BE 主專案 | 綁主專案；只有「另一個稽核產品」會想共用 |

**「很多是這個產品的核心、寫在主專案」本身是對的**（DDD：產品專屬編排該在產品端，不是技術債）。

未來若出現第二個產品：
- **不同領域** → 用套件版通用引擎即可，**不用做任何事**。
- **另一個稽核/合規產品** → 正確做法是**抽第二個套件層** `jedi-audit-workflow`（疊在 flow-engine 之上、裝稽核編排，產品專屬部分 adapter 注入，mirror FR-032 `jedi_information_system` 抽法）——**不是把東西塞進通用的 jedi-flow-engine**（會污染通用層）。

兩條路（hook 化 / 抽 audit 層）**都得先把副作用從控制流裡 un-weave**，這才是真成本，且只有在真有第二個稽核產品時才划算。

## 4. 考慮過的選項

| 方案 | 內容 | 評估 |
|------|------|------|
| **a 對齊現況** | 套件版 5 項修正已是 BE 行為的可攜等價物 | 已完成，無動作 |
| **b 完整合併** | BE 拆薄層 + 套件補 lifecycle hook + 巢狀 sub-process | **不採**：去重收益僅 ~250~300 行共用骨架；17 個 BE method + 9 織入副作用合併後仍留 BE；要動稽核引擎核心 + 全回歸；無 live bug、無第二 consumer |
| **c 文件化現狀** ✅ | 承認刻意雙實作，文件化邊界 | **採用**：低風險；本文件即現狀紀錄 |
| ~~（未來）抽 audit 層~~ | 若有第二個稽核產品才做 | defer，trigger 見 §6 |

## 5. 架構合規檢查（2026-06-08）

對 BE `WorkflowExecutionService` 查 DDD 分層 / transaction / error handling：

| 檢查項 | 結果 |
|--------|------|
| app 層碰 infra session / ORM（`get_session` / `session.query` / `self.session`）| ✅ 無 |
| 直接 import infra ORM model / method 內 new RepoImpl | ✅ 無（走注入的 domain service）|
| 裸 `raise ValueError/Exception`（應用 ErrorCode）| ✅ 無（用 `GrcErrorCode`）|
| public method `@transaction` | ✅ 齊全（2 個例外為 false positive：`build_task_combinations` 純運算不需、`assert_project_participant` 僅在 @transaction method 內被呼叫的 re-entrant guard）|
| 權限檢查在 service 層 | ✅ `assert_project_participant`（C16）|

**結論：BE 版完全合規，且比套件版乾淨**（套件版在 method 內 `WorkflowExecutionDomainService(WorkflowExecutionRepoImpl())` 直接實例化 repo；BE 版走 DI 注入的 domain service）。唯一小建議：`assert_project_participant` 命名無底線但實為內部 guard，可加底線或 docstring 標「caller 須在 @transaction scope」——cosmetic，不影響運作。

## 6. 未來反悔條件（trigger）

- 出現**第二個稽核/合規產品**要共用 AP/生命週期/通知編排 → 啟動「抽 `jedi-audit-workflow` 套件層」（非合併進 flow-engine），屆時才做副作用 un-weave。
- BE 版與套件版**同源 bug 反覆各修各的**造成 drift 痛點 → 重評。
- 以上皆無 → 維持方案 c，本 arc 結案。

### 6.1 純通用能力下沉套件（refine 候選，與「合併」無關，不急）

承上架構討論：**把 GRC 編排整進套件永遠是錯的**（依賴倒置 + 污染 bounded context）。但有一類例外 —— BE 自己刻了、但**本質上屬於通用 BPMN 引擎**的能力，理論上該**下沉到 jedi-flow-engine**（讓引擎原生支援），而非留在 BE fork。候選：

| 候選能力 | 目前在 BE | 下沉條件（**動手前必先 verify**）|
|----------|-----------|------------------------------------|
| 巢狀 sub-process 遞迴實體化 | `_instantiate_sub_workflow`、`_create_leaf_jobs` | 確認遞迴邏輯**不含 GRC 假設**（純 BPMN call-activity 展開）才可下沉 |
| `job_execution_uid` 注入 XML | `_patch_template_xml_job_uids`、`_inject_job_execution_uid` | 確認是通用「實體化時把 execution uid 寫回模板 XML」機制、非 GRC 專屬 |

注意事項：
- 這是「把純通用能力從 BE 搬**進**套件」，跟「把 GRC 編排搬進套件」相反方向——前者對、後者錯。
- **未驗證**：以上兩項是否真的 GRC-free 尚未逐行確認；列為候選，動手前先讀 source 確認無 AP/Project/Survey 耦合。
- 收益小（少 BE fork ~150 行）、非急；與方案 c 結案不衝突，純粹標記未來若動 flow-engine 時可順手評估。

## 7. 結案

本 arc 以方案 c 結案，不開實作。FR-034 狀態標「feasibility done — 方案 c」。
