# FR-048 Phase 2b-2 交接 prompt（2b 剩餘 + 跨 phase 縫隙收口）

> 下面整段是給執行 session 的 prompt。前置：Phase 2b 第一波已收（`80514dff` 等 4 commits）。
> 本波收完才輪 Phase 2c（收編）。

---

【接手主題】FR-048 Phase 2b-2 — 完成 2b 交棒單剩餘項（flow_engine 守門復活、participant canonical、4 組端點）＋ 2 個跨 phase 縫隙（MF 敏感 GET、D14 補 JWT 批）

必讀：
1. `docs/features/FR-048-2607-unified-auth-guard/endpoint-authz-matrix.md` §7.2（2b 交棒單）＋ §1 D14 定調 ＋ §3 各模組矩陣
2. `docs/analysis/2026-07-07-unified-auth-guard-design.md` §2 軸模型
3. `common/authz/` 現況（project.py / workflow.py / decorators.py）

【branch】fix/v1.8.0-bugs，禁止切 branch。不動 jedi-* 套件。守門一律 `common.authz` canonical。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【工作清單（建議照序）】

### 1. `common/authz/project.py` 增 `assert_project_participant`（前置 canonical）

「任一參與者放行」版（軸③，與 `assert_project_manager` 同族）：participant 為 None → `ForbiddenError(GRC_NOT_PROJECT_PARTICIPANT)`。查法用 project 層 participant 精確查詢或 role_service（跟 assert_project_manager 同查法即可，manager/auditor/reviewer/viewer 任一角色皆過）。登進 `__init__.py` 決策表。

### 2. 掛 4 組「參與者級」端點（矩陣 §7.2）

| 端點 | 守門 | 備註 |
|------|------|------|
| grc `/jobs/export`（GET，Excel） | 參與者 | project 維度從 request 取，resolve 後 assert_project_participant |
| grc `job-execution comments POST` | 參與者 | 留言＝協作行為；PUT/DELETE 已有 owner 守門不動 |
| project_summary_report create/update/delete/export/history-revert | 參與者 | spec 明載無角色細分＝驗「專案歸屬」；update/delete/export 依 uid，需 domain service 補 report→project 反查（先 grep 有沒有既有反查，禁重複造輪） |
| subtask_status_history POST/PUT/DELETE | 參與者 | 同上，subtask→project 反查 |

### 3. evidence_classification 四支 → **統一軸③ manager（2026-07-07 user 定調）**

- `run/state`、`archive`：由 run/folder resolve 專案 → `assert_project_manager`（與已守的 trigger-classify 同軸同級）。
- `ground-truth import`（`POST /classification-ground-truth`）：**API 無專案維度（per-tenant）**，user 定調統一 manager → 實作為「**該租戶內至少任一專案的 manager**」判定（查 participant 表 role=manager 存在即過；RLS 已鎖租戶範圍）。加 docstring 說明此語意；不改 API 契約。此端點無 FE 頁面（維運 runbook 用），收緊零影響。

### 4. flow_engine 守門復活（壓軸，量大）

`common/authz/workflow.py` `assert_workflow_project_participant` 開頭 `return` 拔掉，反查鏈改 v2：
- **v2 鏈**：workflow_execution → `task_assignees.project_id`（memory 定調：別比對 workflow_execution_uid；v1 的 AP-task mapping 已 dark 不能用）
- 判定政策不變：任一參與者放行、fail-closed（查無 mapping 一律拒）、admin 放行、無 context（背景任務）不守
- 受守端點：task/complete、task/revert、job-evidence PUT/DELETE、**加掛裸奔的** job-evidences POST/job-evidence POST、process/comments PUT、task/link PUT
- grc `jobs/batch-complete`：每 job 走 flow_engine complete_job 隨之受守；其自身入口再補 project 反查（job_uids 可跨專案→逐 job 驗）
- **改完必補測試**（fail-closed、參與者過、非參與者 403、admin 過）；這支曾因 v1 鏈斷把正當使用者全擋（FR-038 教訓），復活前先用 DEV DB 實資料驗反查鏈走得通

### 5. MF 敏感 GET 掛軸②（P3 縫，D1 定調涵蓋）

`api/module_frame/` 下含實際資料的 GET 補 `@require_super_admin_route`（順序：jwt 外、authz 內）：template export/download（`module_frame_template_import_route.py:66/94`）、control-defaults GET list/detail（`module_frame_control_default_route.py:50/75`）、excel-template / ssp-import-template / ssp-export 類（見矩陣 3.2）。menu/list/detail 一般讀不掛。掛完登記過渡名單（併入 `module_frame.manage`）。

### 6. D14 補 JWT 批（無人認領件收口）

| 端點 | 處置 |
|------|------|
| `/module-frame/download/template`、`/download/yaml-template`（`module_frame_import_route.py:27/38`） | 補 `@jwt_required()`（靜態範例檔，補認證即可） |
| `/survey/import-template` | 同上 |
| `/issue/get_members` | 補 `@jwt_required()` |
| `/task-survey/import-template/<task_uid>` | **先查 FE 呼叫方式**：axios（interceptor 帶 bearer）→ 補 JWT；`<a href>`/window.open → 不能盲加，改列 Phase 4 signed-token（矩陣註記）。有 ffa01a78 前科，不確定就查 FE source |

### 7. 收尾

- 矩陣逐列銷帳（§7.2 各列 → 已修待驗＋commit hash；§7.3 新開 2b-2 落地紀錄）
- **回歸驗證**：baseline worktree 集合 diff（在本波起點 commit 開 worktree 跑 `python -m pytest test/ -q --continue-on-collection-errors`，comm 比對 FAILED/ERROR 清單，新增失敗必須 0）
- commit 按主題分批（顯式 add、禁 -am、不 push）；一句話 status＋手測 checklist，收尾動作等 user 下令

【FE 影響】本波多為 service 層守門（403 對正常流程不可見）；flow_engine 復活影響任務完成/證據增刪——手測 checklist 必含「被指派 auditor 完成任務、增刪證據照常」。
