# A.1 盤點：grc 專案稽核模組端點 × 三段式能力點

> 卡片 CM-2511（FR-132 T-1.1）。唯讀盤點，不改程式、不改 DB。

## 範圍與數量

- **範圍**：專案（project）、稽核輪次與其下的 AP／AR／POA&M、任務（job，含成員、指派、留言、佐證）、流程執行（`api/flow_engine/` 的任務提交與稽核階段推進）、專案摘要報告、資訊系統（information-system）、`workflow` 能力點。
- **主專案 route 檔 16 支、92 個 method**：`api/flow_control/routes/` 4 支、`api/project/routes/` 4 支、`api/project_summary_report/routes/` 2 支、`api/flow_engine/routes/` 6 支（30 個）。另有 1 個已註解停用的 Resource（`ProjectMenuResource`，`/projects/menu`），不計入。
- **外部套件 route 檔 10 支、33 個 method**：jedi-compliance-audit 2 支（5）、jedi-task-platform task 5 支（12）、jedi-task-platform participant 2 支（10）、jedi-asset 1 支（6）。讀的是 `.venv/lib/python3.11/site-packages/` 內實際執行的版本。
- **合計 26 支檔、125 個 method**。其中流程範本管理 11 個屬 T-1.2 範圍，只列不定名。jedi-flow-engine 套件本身沒有 route。
- **DEV 查詢時間**：2026-10-06 16:18（本機 `localhost:5432/guidant_ai_dev`）。範圍內舊能力點 23 個。

## 先看這幾點

1. **125 個 method 裡，只有 25 個掛了能力點守門。** 主專案 8 個（專案列表／明細／改／刪／批次刪、建專案）、流程範本 11 個（`flow_template.*`），jedi-asset 資訊系統 6 個。其餘 100 個只有 `jwt_required`（多數加授權模組軸 `require_license`），**專案內的權限全靠 service 層的專案成員角色檢查**。
2. **`audit.*` 四個能力點一支 route 都沒守。** 它們只出現在三處：稽核管理頁的選單綁定（`/project/audit-manage`）、出貨角色範本、FE 儀表板的 `hasCap('audit.read')`。整個稽核輪次／AP／AR／POA&M 共 36 個 method 都沒有公司層守門。
3. **`project.approve`／`project.assign`／`project.export` 三個獨立動詞一支 route 都沒守。** 只綁在專案列表頁選單與角色範本，FE `ProjectListView` 只讀 CREATE／UPDATE／DELETE。下面主表把「指派」接到專案成員端點、「匯出」接到任務匯出；approve 沒有任何對應功能（任務提交與審核關卡走 `/flow-engine/task/complete` 與 `.../stage/advance`，都不是它），名字移給 `grc.audit.approve`（審閱標記）。
4. **兩層守門要分開看**：主表「現有守門」欄先寫公司層（能力點），再寫「成員角色：」後的專案層檢查。成員角色守門是「你在這個專案裡是什麼身分」，能力點是「公司准不准你碰這類功能」，有前者不等於有後者。
5. **個人視角端點**（我的任務、我的稽核、儀表板摘要、我被指派的專案下拉）只回「自己參與的」資料，service 層不做角色檢查。新名照主資源 read 填，**定案要掛守門**；上線前要確認出貨角色範本都帶了對應 read。

## 新名總覽（定案）

grc 模組 7 個資源，資訊系統歸 `asset` 模組：

| 新資源 | read | create | update | delete | 獨立動詞 | 取代的舊名 |
|---|---|---|---|---|---|---|
| `grc.project` | ✓ | ✓ | ✓ | ✓ | `assign`（專案成員） | `project.read/create/update/delete/assign` |
| `grc.audit` | ✓ | ✓ | ✓ | ✓ | `import`（AP Word、AR Excel 匯入）、`close`（結束輪次）、`approve`（審閱標記，控制項與 AO 兩層同一個） | `audit.*` 四個；`project.approve`（名字移過來） |
| `grc.job` | ✓ | ✓ | ✓ | ✓ | `assign`（任務指派、啟動任務執行、強制開始）、`import`、`export` | `project.export`（移過來） |
| `grc.job-comment` | ✓ | ✓ | ✓ | ✓ | — | 無 |
| `grc.job-evidence` | ✓ | ✓ | ✓ | ✓ | — | 無 |
| `grc.summary-report` | ✓ | ✓ | ✓ | ✓ | `export` | `project-summary-report.*` |
| `asset.information-system` | ✓ | ✓ | ✓ | ✓ | — | `information-system.*` |

- `grc.audit` 一組涵蓋稽核輪次、評估計畫（AP）、評估結果（AR，含風險）、矯正計畫（POA&M）。這四塊誰能寫，由專案層的成員角色分（AP／AR 給 auditor／manager，POA&M 給 manager），公司層只分「能不能看、能不能改」。
- `workflow.*` 四個刪除，不給新名。
- 「每個資源固定四個」照規則全開；某資源今天沒有 delete 或 create 端點的，仍要建出來。

## 主表

欄位：route 路徑 ｜ HTTP ｜ 程式位置 ｜ 現有守門 ｜ 新三段名 ｜ 現有能力點 ｜ 對應 UI ｜ 可指派範圍層 ｜ 備註。

守門縮寫：**J**＝`jwt_required`；**L(x)**＝`require_license("x")`；**C(x)**＝`require_capability("x")`。專案層守門寫「成員：」＋放行角色（`參與者`＝任何成員）。路徑前綴：主專案 grc 段＝`/api/1.0/grc`，其餘＝`/api/1.0`。

### 專案（grc.project）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/oscal-project/start` | POST | `api/project/routes/project_route.py:28` | J＋L(project)＋C(project.create)；成員：無（建立者此時尚非成員） | `grc.project.create` | `project.create` | `ProjectCreateView` | org_unit | |
| `/grc/projects/list` | POST | `api/flow_control/routes/project_route.py:99` | J＋L(project)＋C(project.read)；成員：可見性過濾（只列參與的） | `grc.project.read` | `project.read` | `ProjectListView`、`DashboardView`、`ProjectSummaryReportManage`（下拉） | org_unit | |
| `/grc/project/<uid>` | GET | `project_route.py:146` | J＋L(project)＋C(project.read)；成員：參與者（`is_admin` 一律傳 False） | `grc.project.read` | `project.read` | `ProjectPlanningView`、`ProjectSettingsView`（`GRC_PROJECT` 前綴，未逐支核） | org_unit | |
| `/grc/project/<uid>` | PUT | `project_route.py:177` | J＋L(project)＋C(project.update)；成員：manager | `grc.project.update` | `project.update` | `ProjectListView`（編輯鈕）、`ProjectSettingsView` | org_unit | |
| `/grc/project/<uid>` | DELETE | `project_route.py:232` | J＋L(project)＋C(project.delete)；成員：owner 或 manager（admin 可刪任意） | `grc.project.delete` | `project.delete` | `ProjectListView` | org_unit | service 內有 `is_admin` 旁路，見「確認要補的守門缺口」 |
| `/grc/projects/batch-delete` | POST | `project_route.py:258` | J＋L(project)＋C(project.delete)；成員：同上，逐筆判 | `grc.project.delete` | `project.delete` | `ProjectListView` | org_unit | |
| `/grc/jobs/my/projects/menu` | GET | `project_route.py:74` | J＋L(project)；成員：無（只回自己被指派的） | `grc.project.read` | 無 | `MyTasksView`（篩選下拉） | tenant | 輔助端點；個人視角 |
| `/grc/dashboard/summary` | GET | 套件 `jedi_task_platform/task/api/routes/dashboard_route.py:28` | J＋L(project)；成員：無（`is_admin=False`，只算自己參與的） | `grc.project.read` | 無 | `DashboardView` | tenant | 個人視角；FE 自己用 `hasCap('project.read')` 決定顯示 |
| `/project-participants/menu` | GET | 套件 `jedi_task_platform/participant/api/routes/project_participant_route.py:20` | J（宿主注入）；成員：參與者 | `grc.project.read` | 無 | `ProjectPlanningView` | org_unit | 輔助端點；無 license 軸 |
| `/project-participants` | POST | `project_participant_route.py:39` | J；成員：參與者 | `grc.project.read` | 無 | `ProjectPlanningView`、`ProjectSettingsView` | org_unit | 列表 |
| `/project-participant` | POST | `project_participant_route.py:55` | J；成員：manager | `grc.project.assign` | 無 | `ProjectSettingsView`（成員設定） | org_unit | 「指派」類獨立動詞，`project.assign` 的落點 |
| `/project-participant` | PUT | `project_participant_route.py:71` | J；成員：manager | `grc.project.assign` | 無 | 同上 | org_unit | 改成員角色 |
| `/project-participant` | DELETE | `project_participant_route.py:84` | J；成員：manager | `grc.project.assign` | 無 | 同上 | org_unit | |
| `/grc/project/<pid>/start-task-execution` | POST | 套件 `task/api/routes/task_execution_route.py:34` | J＋L(project)；成員：manager | `grc.job.assign` | 無 | `ProjectPlanningView`、`ProjectAuditorOverview` | org_unit | 把已指派的任務一次派發並通知被指派人，屬派工 |
| `/grc/project/<pid>/task-execution/progress` | GET | `task_execution_route.py:57` | J＋L(project)；成員：參與者 | `grc.project.read` | 無 | `ProjectPlanningView` | org_unit | 輪詢進度 |

### 稽核：輪次（grc.audit）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/projects/<pid>/audit-rounds` | POST | `api/project/routes/audit_round_route.py:58` | J＋L(audit)；成員：manager | `grc.audit.create` | 無 | `ProjectPlanningView`、`ProjectAuditorOverview` | org_unit | J 組 |
| `/projects/<pid>/audit-rounds/list` | POST | `audit_round_route.py:74` | J＋L(audit)；成員：參與者 | `grc.audit.read` | 無 | 同上、`MyTasksView` | org_unit | J 組 |
| `/audit-round/<rid>` | GET | `audit_round_route.py:86` | J＋L(audit)；成員：參與者 | `grc.audit.read` | 無 | 輪次各頁 | org_unit | J 組 |
| `/audit-round/<rid>/close-round` | POST | `audit_round_route.py:97` | J＋L(audit)；成員：manager | `grc.audit.close` | 無 | `ProjectAuditorOverview` | org_unit | J 組；狀態轉換 |
| `/audit-round/<rid>/launch-reverify` | POST | `audit_round_route.py:110` | J＋L(audit)；成員：auditor/manager | `grc.audit.create` | 無 | `ProjectApListView` | org_unit | J 組；產生一輪新的覆核輪 |
| `/audits/my/list` | POST | 套件 `jedi_compliance_audit/api/routes/auditor_dashboard_route.py:26` | J＋L(audit)；成員：無（只回自己是 auditor 的） | `grc.audit.read` | 無 | `MyAuditsView`（`/project/audit-manage`）、`DashboardView` | tenant | 個人視角；回的是 AP 清單 |

### 稽核：評估計畫 AP（grc.audit）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/grc/project/<pid>/assessment-plans/menu` | GET | `api/flow_control/routes/assessment_plan_route.py:32` | J＋L(project)；成員：參與者 | `grc.audit.read` | 無 | `ProjectAuditorOverview`、`ImportDocxPage` | org_unit | J 組；輔助端點 |
| `/grc/project/<pid>/ap/<ap>/dashboard` | GET | `assessment_plan_route.py:54` | J＋L(project)；成員：參與者 | `grc.audit.read` | 無 | `ProjectAuditorOverview` | org_unit | J 組 |
| `/audit-round/<rid>/ap` | GET | `audit_round_route.py:139` | J＋L(audit)；成員：參與者 | `grc.audit.read` | 無 | `RoundApAuthoringView` | org_unit | |
| `/ap/<ap>/reviewed-controls` | PUT | `audit_round_route.py:152` | J＋L(audit)；成員：auditor/manager | `grc.audit.update` | 無 | `RoundApAuthoringView` | org_unit | |
| `/ap/<ap>/assessment-subjects` | PUT | `audit_round_route.py:167` | J＋L(audit)；成員：auditor/manager | `grc.audit.update` | 無 | `RoundApAuthoringView` | org_unit | |
| `/ap/<ap>/tasks` | PUT | `audit_round_route.py:182` | J＋L(audit)；成員：auditor/manager | `grc.audit.update` | 無 | `RoundApAuthoringView` | org_unit | |
| `/ap/<ap>/parties` | GET | `audit_round_route.py:198` | J＋L(audit)；成員：參與者 | `grc.audit.read` | 無 | `RoundApAuthoringView` | org_unit | |
| `/ap/<ap>/parties` | POST | `audit_round_route.py:208` | J＋L(audit)；成員：auditor/manager | `grc.audit.update` | 無 | `RoundApAuthoringView` | org_unit | |
| `/ap/<ap>/docx-imports/parse` | POST | `api/project/routes/ap_docx_import_route.py:42` | J＋L(audit)；成員：auditor/manager | `grc.audit.import` | 無 | `ImportDocxPage`（`ApDocxImportService`） | org_unit | J 組 |
| `/ap-docx-import/<parse>` | GET | `ap_docx_import_route.py:66` | J＋L(audit)；成員：參與者（預覽較寬） | `grc.audit.import` | 無 | 同上 | org_unit | J 組；匯入流程內的預覽，跟匯入同一個能力點 |
| `/ap-docx-import/<parse>` | DELETE | `ap_docx_import_route.py:82` | J＋L(audit)；成員：auditor/manager | `grc.audit.import` | 無 | 同上 | org_unit | J 組；放棄本次解析 |
| `/ap-docx-import/<parse>/confirm` | POST | `ap_docx_import_route.py:103` | J＋L(audit)；成員：auditor/manager | `grc.audit.import` | 無 | 同上 | org_unit | J 組 |
| `/grc/project/<pid>/ap/<ap>/control/<c>/review` | POST | 套件 `jedi_compliance_audit/api/routes/review_route.py:18` | J＋L(project)；成員：manager/reviewer | `grc.audit.approve` | 無 | `JobExecutionDrawer`、`ProjectAuditorOverview` | org_unit | 審閱標記（控制項層） |
| 同上 | DELETE | `review_route.py:27` | 同上 | `grc.audit.approve` | 無 | 同上 | org_unit | 取消標記（控制項層） |
| `/grc/project/<pid>/ap/<ap>/control/<c>/ao/<ao>/review` | POST | `review_route.py:40` | 同上 | `grc.audit.approve` | 無 | 同上 | org_unit | AO 層 |
| 同上 | DELETE | `review_route.py:49` | 同上 | `grc.audit.approve` | 無 | 同上 | org_unit | AO 層 |

### 稽核：評估結果 AR，含風險（grc.audit）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/audit-round/<rid>/ar/findings` | GET | `audit_round_route.py:223` | J＋L(audit)；成員：參與者 | `grc.audit.read` | 無 | `RoundAuditReviewView` | org_unit | |
| `/audit-round/<rid>/ar/finding/<f>` | PUT | `audit_round_route.py:235` | J＋L(audit)；成員：auditor/manager | `grc.audit.update` | 無 | `RoundAuditReviewView` | org_unit | 判定符合／不符合 |
| `/audit-round/<rid>/ar/observations` | POST | `audit_round_route.py:252` | J＋L(audit)；成員：auditor/manager | `grc.audit.create` | 無 | `RoundAuditReviewView` | org_unit | |
| `/audit-round/<rid>/ar/observation/<o>` | PUT | `audit_round_route.py:270` | 同上 | `grc.audit.update` | 無 | 同上 | org_unit | |
| 同上 | DELETE | `audit_round_route.py:285` | 同上 | `grc.audit.delete` | 無 | 同上 | org_unit | |
| `/audit-round/<rid>/ar/risks` | GET | `audit_round_route.py:299` | J＋L(audit)；成員：參與者 | `grc.audit.read` | 無 | `RoundAuditReviewView`、`RoundPoamView` | org_unit | |
| 同上 | POST | `audit_round_route.py:309` | J＋L(audit)；成員：auditor/manager | `grc.audit.create` | 無 | `RoundAuditReviewView` | org_unit | 風險不另開資源 |
| `/audit-round/<rid>/ar/risk/<r>` | PUT | `audit_round_route.py:327` | 同上 | `grc.audit.update` | 無 | 同上 | org_unit | |
| 同上 | DELETE | `audit_round_route.py:342` | 同上 | `grc.audit.delete` | 無 | 同上 | org_unit | |
| `/audit-round/<rid>/ar/risk/<r>/findings` | PUT | `audit_round_route.py:357` | 同上 | `grc.audit.update` | 無 | 同上 | org_unit | 風險連結發現 |
| `/audit-round/<rid>/ar-imports/parse` | POST | `api/project/routes/ar_import_route.py:42` | J＋L(audit)；成員：auditor/manager | `grc.audit.import` | 無 | `ArImportDialog`（`ArImportService`） | org_unit | J 組 |
| `/ar-import/<parse>` | GET | `ar_import_route.py:66` | 同上 | `grc.audit.import` | 無 | 同上 | org_unit | J 組 |
| `/ar-import/<parse>` | DELETE | `ar_import_route.py:82` | 同上 | `grc.audit.import` | 無 | 同上 | org_unit | J 組 |
| `/ar-import/<parse>/confirm` | POST | `ar_import_route.py:103` | 同上 | `grc.audit.import` | 無 | 同上 | org_unit | J 組 |

### 稽核：矯正計畫 POA&M（grc.audit）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/audit-round/<rid>/poam-items` | GET | `audit_round_route.py:373` | J＋L(audit)；成員：參與者 | `grc.audit.read` | 無 | `RoundPoamView` | org_unit | |
| `/audit-round/<rid>/poam-item/<i>` | GET | `audit_round_route.py:384` | 同上 | `grc.audit.read` | 無 | 同上 | org_unit | |
| `/audit-round/<rid>/ar/risk/<r>/remediations` | POST | `audit_round_route.py:396` | J＋L(audit)；成員：manager | `grc.audit.create` | 無 | 同上 | org_unit | |
| `/audit-round/<rid>/remediation/<m>/milestones` | POST | `audit_round_route.py:412` | 同上 | `grc.audit.create` | 無 | 同上 | org_unit | |
| `/audit-round/<rid>/poam-milestone/<ms>` | PUT | `audit_round_route.py:432` | 同上 | `grc.audit.update` | 無 | 同上 | org_unit | |
| 同上 | DELETE | `audit_round_route.py:444` | 同上 | `grc.audit.delete` | 無 | 同上 | org_unit | |
| `/audit-round/<rid>/remediation/<m>` | DELETE | `audit_round_route.py:458` | 同上 | `grc.audit.delete` | 無 | 同上 | org_unit | |

### 任務（grc.job）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/grc/project/<pid>/ap/<ap>/control-group/<g>/control/<c>/assessment-object/<ao>/jobs/list` | POST | `api/flow_control/routes/job_route.py:52` | J＋L(project)；成員：參與者 | `grc.job.read` | 無 | `ProjectPlanningView` | org_unit | J 組 |
| `/grc/project/<pid>/job/<job>` | GET | `job_route.py:82` | J＋L(project)；成員：參與者 | `grc.job.read` | 無 | `ProjectPlanningView`、`JobExecutionDrawer` | org_unit | J 組 |
| 同上 | PUT | `job_route.py:104` | J＋L(project)；成員：manager | `grc.job.update` | 無 | `AoTaskForm` | org_unit | J 組 |
| 同上 | DELETE | `job_route.py:147` | J＋L(project)；成員：manager | `grc.job.delete` | 無 | `AoTaskSection` | org_unit | J 組 |
| `/grc/project/<pid>/assessment-object/<ao>/jobs` | POST | `job_route.py:183` | J＋L(project)；成員：manager | `grc.job.create` | 無 | `ProjectPlanningView`、`AoTaskForm` | org_unit | J 組 |
| `/grc/jobs/my/list` | POST | `job_route.py:231` | J＋L(project)；成員：無（只回自己的） | `grc.job.read` | 無 | `MyTasksView`、`DashboardView` | tenant | J 組；個人視角 |
| `/grc/project/<pid>/job/<job>/blocked-status` | GET | `api/flow_control/routes/job_force_start_route.py:41` | J＋L(project)；成員：參與者 | `grc.job.read` | 無 | `JobBlockedNotice`、`AoTaskSection` | org_unit | J 組 |
| `/grc/project/<pid>/job/<job>/force-start` | POST | `job_force_start_route.py:66` | J＋L(project)；成員：manager | `grc.job.assign` | 無 | `AoTaskSection`、`AoTaskForm`（`useJobForceStart`） | org_unit | J 組；任務卡在前一關時由經理放行，屬派工 |
| `/grc/project/<pid>/jobs/batch-complete` | POST | 套件 `task/api/routes/job_batch_complete_route.py:26` | J＋L(project)；成員：參與者 | `grc.job.update` | 無 | `MyTasksView` | org_unit | 執行人把自己的任務批次完成 |
| `/grc/project/<pid>/ap/<ap>/jobs/export` | GET | 套件 `task/api/routes/job_import_route.py:34` | J＋L(project)；成員：參與者 | `grc.job.export` | `project.export`（建議移過來） | `ProjectPlanningView` | org_unit | |
| `/grc/project/<pid>/ap/<ap>/jobs/import` | POST | `job_import_route.py:52` | J＋L(project)；成員：參與者（只驗不寫） | `grc.job.import` | 無 | `JobImportDialog` | org_unit | 上傳＋驗證 |
| `/grc/project/<pid>/ap/<ap>/jobs/import/validate` | POST | `job_import_route.py:70` | 同上 | `grc.job.import` | 無 | 同上 | org_unit | 重新驗證 |
| `/grc/project/<pid>/ap/<ap>/jobs/import/confirm` | POST | `job_import_route.py:86` | J＋L(project)；成員：manager | `grc.job.import` | 無 | 同上 | org_unit | 寫入 |
| `/task-assignees` | POST | 套件 `participant/api/routes/task_assignee_route.py:44` | J（宿主注入）；成員：參與者 | `grc.job.read` | 無 | `JobExecutionDrawer` | org_unit | 列表；無 license 軸 |
| `/task-assignees/batch` | POST | `task_assignee_route.py:20` | J；成員：manager | `grc.job.assign` | 無 | `JobExecutionDrawer` | org_unit | |
| `/task-assignee` | POST | `task_assignee_route.py:59` | J；成員：manager | `grc.job.assign` | 無 | 同上 | org_unit | |
| `/task-assignee` | PUT | `task_assignee_route.py:73` | J；成員：manager | `grc.job.assign` | 無 | 同上 | org_unit | |
| `/task-assignee` | DELETE | `task_assignee_route.py:95` | J；成員：manager | `grc.job.assign` | 無 | 同上 | org_unit | |

### 任務留言（grc.job-comment）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/grc/job-executions/<je>/comments` | GET | 套件 `task/api/routes/job_comment_route.py:25` | J＋L(project)；成員：**無** | `grc.job-comment.read` | 無 | `JobExecutionDrawer` | org_unit | service `list_comments` 不驗成員，見「確認要補的守門缺口」 |
| 同上 | POST | `job_comment_route.py:35` | J＋L(project)；成員：參與者 | `grc.job-comment.create` | 無 | 同上 | org_unit | |
| `/grc/job/comment/<c>` | PUT | `job_comment_route.py:58` | J＋L(project)；只有作者本人 | `grc.job-comment.update` | 無 | 同上 | org_unit | |
| 同上 | DELETE | `job_comment_route.py:74` | 同上 | `grc.job-comment.delete` | 無 | 同上 | org_unit | |

### 流程執行（api/flow_engine，歸 grc）

任務提交、審核關卡的通過／退回、稽核階段推進都在這一段，`project.approve` 跟它們無關。流程執行的 19 個 method 全部只有 `jwt_required`，沒有 license 軸。

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/flow-engine/task/complete/<id>` | POST | `api/flow_engine/routes/flow_engine_route.py:38` | J；成員：該任務被指派人或 manager（`assert_job_operator`） | `grc.job.update` | 無 | `JobExecutionDrawer`（`TASK_COMPLETE`） | org_unit | 任務提交；審核關卡的通過／退回也走這支 |
| `/flow-engine/task/revert/<id>` | POST | `flow_engine_route.py:63` | 同上 | `grc.job.update` | 無 | `JobExecutionDrawer`（`TASK_REVERT`） | org_unit | 退回上一關 |
| `/flow-engine/task/queue` | GET | `flow_engine_route.py:88` | J；成員：無（只回自己的待辦） | `grc.job.read` | 無 | `userUtil.js`（`TASK_QUEUE`，待辦數字） | tenant | 個人視角 |
| `/flow-engine/process-definition/<uid>` | GET | `flow_engine_route.py:27` | J；**無任何檢查** | 待 T-1.2 定 | 無 | `WorkflowSetupForm`（`/workflow/workflow-setup`） | tenant | 讀流程圖 XML。呼叫它的頁面是舊入口，見「確認要補的守門缺口」 |
| `/job-evidences` | GET | `api/flow_engine/routes/job_evidence_route.py:27` | J；成員：參與者 | `grc.job-evidence.read` | 無 | `JobExecutionDrawer`、`RoundAuditReviewView` | org_unit | 一個任務執行的佐證列表 |
| `/job-evidences` | POST | `job_evidence_route.py:36` | J；成員：參與者 | `grc.job-evidence.create` | 無 | `JobExecutionDrawer` | org_unit | 上傳佐證（多檔） |
| `/job-evidence` | POST | `job_evidence_route.py:131` | J；成員：參與者 | `grc.job-evidence.create` | 無 | `JobExecutionDrawer` | org_unit | 單筆新增 |
| `/job-evidence/<uid>` | GET | `job_evidence_route.py:64` | J；成員：參與者 | `grc.job-evidence.read` | 無 | `JobExecutionDrawer` | org_unit | |
| 同上 | PUT | `job_evidence_route.py:78` | J；成員：參與者 | `grc.job-evidence.update` | 無 | 同上 | org_unit | |
| 同上 | DELETE | `job_evidence_route.py:97` | J；成員：參與者 | `grc.job-evidence.delete` | 無 | 同上 | org_unit | |
| `/project/<pid>/audit-round/<rid>/stage/info` | GET | `api/flow_engine/routes/stage_advance_route.py:28` | J；成員：參與者（先核輪次歸屬） | `grc.audit.read` | 無 | `FlowPhaseBanner`（`StageService`） | org_unit | |
| `/project/<pid>/audit-round/<rid>/stage/advance` | POST | `stage_advance_route.py:49` | J；成員：該階段指定角色或 manager；`force` 只限 manager | `grc.audit.update` | 無 | `FlowPhaseBanner` | org_unit | 推進稽核階段；審核階段的通過／退回也在這裡 |
| `/project/<pid>/audit-round/<rid>/stage/rollback` | POST | `api/flow_engine/routes/stage_rollback_route.py:29` | J；成員：manager | `grc.audit.update` | 無 | `FlowPhaseBanner` | org_unit | 退回上一個稽核階段 |
| `/project/<pid>/audit-round/<rid>/stage/transitions` | GET | `stage_rollback_route.py:59` | J；成員：參與者 | `grc.audit.read` | 無 | `StageTransitionTimeline` | org_unit | 階段異動紀錄 |
| `/flow-engine/stage-objects` | GET | `api/flow_engine/routes/stage_object_route.py:16` | J；無成員檢查 | 待 T-1.2 定 | 無 | `FlowTemplateService`（範本編輯器下拉） | tenant | 輔助端點，跟流程範本的 read |

佐證開獨立資源 `grc.job-evidence` 的理由跟留言相同：「只能看任務、但要上傳佐證」的執行人，不必拿到改任務本身的權限。

### 流程範本管理（T-1.2 範圍，只列不定名）

`api/flow_engine/routes/flow_template_route.py`，11 個 method 已掛 `flow_template.*`（`:37-147`），綁 `/flow/template-manage` 頁。讀取用 `require_capability("flow_template.read", "project.create", mode="any")`（`:23-25`），因為建專案第一步要選流程範本。改 `project.create` 名字時這一處要跟著改。

| route 路徑 | HTTP | 現有守門 |
|---|---|---|
| `/flow-engine/flow-templates` | GET／POST | read（或 project.create）／create |
| `/flow-engine/flow-templates/<uid>` | GET／PUT／DELETE | read（或 project.create）／update／delete |
| `/flow-engine/flow-templates/<uid>/duplicate` | POST | create |
| `/flow-engine/flow-templates/<uid>/publish` | PUT | update |
| `/flow-engine/flow-templates/<uid>/unpublish` | PUT | update |
| `/flow-engine/flow-templates/validate` | POST | update |

### 專案摘要報告（grc.summary-report）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/project-summary-reports` | POST | `api/project_summary_report/routes/project_summary_report_route.py:83` | J（**無 license 軸**）；成員：只列自己參與專案的報告 | `grc.summary-report.read` | `project-summary-report.read` 只綁選單 | `ProjectSummaryReportManage` | org_unit | J 組；列表 |
| `/project-summary-report` | POST | `project_summary_report_route.py:103` | J；成員：參與者 | `grc.summary-report.create` | 同上 `.create` | `ProjectSummaryReportForm` | org_unit | J 組 |
| `/project-summary-report/<uid>` | GET | `project_summary_report_route.py:120` | J；成員：參與者 | `grc.summary-report.read` | `.read` | 同上 | org_unit | J 組 |
| 同上 | PUT | `project_summary_report_route.py:129` | J；成員：參與者 | `grc.summary-report.update` | `.update` | 同上 | org_unit | J 組 |
| 同上 | DELETE | `project_summary_report_route.py:149` | J；成員：參與者 | `grc.summary-report.delete` | `.delete` | `ProjectSummaryReportManage` | org_unit | J 組 |
| `/project-summary-report/export/<uid>` | GET | `project_summary_report_route.py:160` | J；成員：參與者 | `grc.summary-report.export` | 無 | `ProjectSummaryReportManage` | org_unit | J 組；PDF 匯出 |
| `/project-summary-report/histories` | POST | `project_summary_report_history_route.py:28` | J；成員：參與者（有帶報告 uid 時） | `grc.summary-report.read` | `.read` | `ProjectSummaryReportHistory` | org_unit | J 組 |
| `/project-summary-report/history/<uid>` | GET | `project_summary_report_history_route.py:47` | J；成員：參與者 | `grc.summary-report.read` | `.read` | 同上 | org_unit | J 組 |
| `/project-summary-report/history-revert` | POST | `project_summary_report_history_route.py:61` | J；成員：參與者 | `grc.summary-report.update` | `.update` | 同上 | org_unit | J 組；還原成舊版＝改內容 |

`project-summary-report.*` 四個舊名在 DEV 有綁 `/project-summary-report` 頁選單，但後端沒有任何 route 守它們。

### 資訊系統（asset.information-system）

| route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註 |
|---|---|---|---|---|---|---|---|---|
| `/information-systems/menu` | GET | 套件 `jedi_asset/api/routes/information_system_route.py:44` | 套件 `auth_required`＋`capability_required(read)` | `asset.information-system.read` | `information-system.read` | `ModuleFrameSspInfoSystemsPanel`、`EntityPickerDialog` | org_unit | 輔助端點 |
| `/information-systems/list` | POST | `information_system_route.py:62` | 同上（read） | `asset.information-system.read` | `.read` | `InformationSystemManage`（`/information-system/manage`） | org_unit | |
| `/information-systems` | POST | `information_system_route.py:85` | 同上（create） | `asset.information-system.create` | `.create` | 同上 | org_unit | |
| `/information-system/<uid>` | GET | `information_system_route.py:106` | 同上（read） | `asset.information-system.read` | `.read` | 同上 | org_unit | |
| 同上 | PUT | `information_system_route.py:118` | 同上（update） | `asset.information-system.update` | `.update` | 同上 | org_unit | |
| 同上 | DELETE | `information_system_route.py:139` | 同上（delete） | `asset.information-system.delete` | `.delete` | 同上 | org_unit | |

能力點名不是寫死的：route 讀 `rt.config.information_system_*_capability`，預設值在套件 `jedi_asset/plugin/contract.py:81-84`。改名時改這四個預設值（或宿主注入），route 不用動。

## 現有能力點反查（DEV 23 個）

| 舊名 | 主表有 route 守它？ | 新名 | 其他引用處 |
|---|---|---|---|
| `project.create` | 有：`/oscal-project/start` | `grc.project.create` | 範圍外另有兩處拿它當放行條件：`api/module_frame/routes/module_frame_route.py:26`、`api/flow_engine/routes/flow_template_route.py:24`（`mode="any"`）。角色範本 `core/role_templates.py:37` |
| `project.read` | 有：列表、明細 | `grc.project.read` | FE `DashboardView`（7 處）、`userUtil.js`；角色範本 :13；已停用的 `ProjectMenuResource` 也掛它 |
| `project.update` | 有：PUT 專案 | `grc.project.update` | 角色範本 :25 |
| `project.delete` | 有：DELETE、批次刪 | `grc.project.delete` | 角色範本無（只有管理員角色帶得到） |
| `project.approve` | **無**，全系統找不到對應功能 | `grc.audit.approve`（名字移去給審閱標記） | 只綁 `/project/projects` 選單、角色範本 :39 |
| `project.assign` | **無** | `grc.project.assign`（接專案成員端點） | 只綁選單、角色範本 :38 |
| `project.export` | **無** | `grc.job.export`（接任務匯出） | 只綁選單、角色範本 :40 |
| `audit.create/read/update/delete` | **無** | `grc.audit.*` | 宣告在套件 `jedi_compliance_audit/plugin/contract.py`；綁 `/project/audit-manage`（我的稽核頁）選單；FE `DashboardView` 用 `hasCap('audit.read')`；角色範本 :33-36、:46 |
| `project-summary-report.create/read/update/delete` | **無** | `grc.summary-report.*` | 宣告在 `core/host_capabilities.py:45`；綁 `/project-summary-report` 選單；角色範本 :41、:47 |
| `information-system.create/read/update/delete` | 有：jedi-asset 六支 | `asset.information-system.*` | 角色範本 :15（read） |
| `workflow.create/read/update/delete` | **無** | 刪除 | 宣告在套件 `jedi_flow_engine/plugin/contract.py:29-32`；不綁任何選單；在新租戶排除清單 `core/plugins/identity.py:89-95`；DEV 只有 Administrator 角色掛著這四個 |

### 沒有任何 route 用到的能力點（孤兒）

`project.approve`、`project.assign`、`project.export`、`audit.*`（4）、`project-summary-report.*`（4）、`workflow.*`（4），共 15 個。前 11 個有綁選單、出貨角色範本也有給，**孤兒不等於沒用**：它們目前決定「選單看不看得到」，只是端點沒跟著守。改名後由主表對應的 route 掛上守門。`workflow.*` 才是真正什麼都沒用到。

## 定案結論

1. **資訊系統歸 `asset` 模組**，名字 `asset.information-system.*`。它跟設備同屬資產：程式同一支套件（jedi-asset）、各自有獨立管理頁，專案與 SSP 只是挑選它來用，只需要 read。設備那份盤點要採用同一個模組名 `asset`。能力點名由套件 `jedi_asset/plugin/contract.py:81-84` 的預設值決定，改那四個值即可，route 不用動。
2. **`workflow.*` 刪除。** AO 流程執行走專案成員角色，流程範本走 `flow_template.*`，兩者都沒用到它。要動三處：套件 `jedi_flow_engine/plugin/contract.py` 的 `CAPABILITIES` 清空；`core/plugins/identity.py` 排除清單的 `"workflow"` 拿掉；migration 刪除四個能力點與 Administrator 角色的綁定。
3. **稽核保留一組 `grc.audit.*`，不拆成輪次／AP／AR／POA&M 四組。**
   - 誰能寫哪一塊，專案層成員角色已經分好（AP／AR：auditor、manager；POA&M：manager），公司層再拆一次是同一件事管兩遍，且兩層可能互相矛盾。
   - 拆開後，「專案裡是稽核員、但公司角色少勾一格」會變成按鈕 403 且沒有錯誤訊息，正是本 FR 要避免的失敗形狀。
   - 公司層真正要分的是「能看／能改」，`read` 與 `update` 已足夠。
   - **反悔條件**：客戶要求「矯正計畫交給被稽核單位的人寫、但不能碰稽核結果」時，把 POA&M 拆成 `grc.poam.*`，其他三塊不受影響。
4. **`project.approve` 沒有對應功能。** 任務提交與審核關卡走 `/flow-engine/task/complete`，稽核階段審核走 `.../stage/advance`，問卷審核屬問卷模組，都不是它。名字移給第 5 項。
5. **審閱標記用 `grc.audit.approve`**，控制項層與 AO 層打勾、取消共用一個。用 `approve` 不另創 `review`，是為了守住獨立動詞只有四類的規則；跟 `update` 分開，客戶才能做到「外部顧問可寫判定，最後審閱確認留給內部人員」。
6. **風險不另開資源**，跟評估結果一起，屬 `grc.audit`。
7. **啟動任務執行、強制開始任務都歸 `grc.job.assign`。** 兩者本質是派工，有權指派任務的人就要能派發。
8. **任務留言獨立成 `grc.job-comment`**；佐證比照獨立成 `grc.job-evidence`。
9. **個人視角端點掛 read 守門**（我的任務、我的稽核、儀表板摘要、我被指派的專案下拉、待辦數字）。上線前要確認出貨角色範本都帶了 `grc.project.read`、`grc.job.read`、`grc.audit.read`，否則只有成員身分的使用者會突然 403。

## 確認要補的守門缺口

決策者已裁「要補」。本卡只讀，修正另開卡（由首腦排入 A.2 或獨立一棒）。

1. **任務留言列表不驗專案成員**：`GET /grc/job-executions/<je>/comments` 的 service（`jedi_task_platform/task/app/service/job_comment_service.py:27`）直接查，知道 job_execution uid 的登入者就能讀。要補成員檢查，比照同檔 `add_comment` 的 `assert_project_participant`。
2. **專案摘要報告整組沒有授權模組檢查**：`api/project_summary_report/routes/` 9 個 method 都沒有 `require_license`。成員檢查有（列表以參與專案過濾，單筆走 `_require_participant`）。
3. **刪除專案有管理員旁路**：`app/flow_control/service/project_service.py:547` 起，`is_admin` 可刪任意專案，跟「明細讀取一律不給 admin 旁路」不一致。
4. **流程圖定義端點沒有任何檢查**：`GET /flow-engine/process-definition/<uid>`（`api/flow_engine/routes/flow_engine_route.py:27`），任何登入者給 uid 就能讀流程範本 XML。呼叫它的 `WorkflowSetupForm` 是舊入口，先確認還有沒有在用，沒用就退役，有用就補守門。歸屬由 T-1.2 一併處理。

## 改名時要一起動的地方（給下一棒）

- `core/host_capabilities.py`（`PROJECT`、`PROJECT_SUMMARY_REPORT`）
- `core/role_templates.py`（:13、:15、:25、:33-41、:46-47）
- 套件 contract：`jedi_compliance_audit/plugin/contract.py`（audit.*）、`jedi_asset/plugin/contract.py:81-84`（information-system 預設名）、`jedi_flow_engine/plugin/contract.py:29-32`（workflow 刪除）
- 範圍外放行條件：`api/module_frame/routes/module_frame_route.py:26`、`api/flow_engine/routes/flow_template_route.py:24`
- DEV `route_capabilities`：`/project/projects`、`/project/audit-manage`、`/project-summary-report`、`/information-system/manage` 四頁的綁定
- FE：`DashboardView.vue` 的 `hasCap('project.read')`／`hasCap('audit.read')`、`userUtil.js`；`ProjectListView` 讀的是頁面動作字（CREATE／UPDATE／DELETE），三段式動詞不變就不用改

沿革見 `FR-132-LOG.md` 與 git log。
