FR-155 · Project Role → RBAC — 設計文件 · 2026-10-21
角色管理分「系統角色」「專案角色」兩個 tab;參與者指角色不指字串;判定查能力點
專案參與者今天是四個寫死的英文字(manager/reviewer/auditor/viewer),散在三個 repo 約一百多處與流程圖 XML 裡。本案把專案角色做成跟系統角色一樣「可建、可勾」:角色管理頁分兩個 tab,專案角色有自己的一組能力點(proj.*);專案參與者仍每個專案指派,但指的是角色;守門、通知、流程推進、待辦清單全部改成「他在這個專案的角色展開後有沒有這個能力點」。D1–D10 已定案;一版做完,流程編輯器併入第一版。
狀態:實作完成、待進版(D1–D10 已拍板,D5/D7 依實作改寫)|日期:2026-10-22|討論稿(含圖與現況細節):
discussion.html|雛形:prototype/index.html|母案:CM-2977(子卡 CM-2978~CM-3014)|收口審查:docs/review/2026-10-22-fr155-review.md前作:FR-132 存取管理(capabilities.assignable_scopes預留project)、FR-048 統一授權守門(common/authz/)、FR-069 jedi-iam
| 日期 | 變更 | 對應 |
|---|---|---|
| 2026-10-21 | 初版設計定案:D1–D10 全數照討論稿建議拍板;雛形三頁審過;流程編輯器(原第二版)併入第一版,與畫面同批 | 本檔 |
| 2026-10-21 | Notion 母卡 CM-2977 + 11 子卡 CM-2978~2988 建卡完成,工作坊四欄已填;等決策者在工作坊派第 1 批 | 母案 CM-2977 |
| 2026-10-22 | 依實作與決策者裁示改成現況:D5 指派只在專案層;D7 my_capabilities 是專案層精確版;§6.1 能力點 13 個(去 proj.stage.advance、proj.job.execute,加 proj.round.manage);§6.4 設計器主責只收三個;§6.5 補 GET /project-roles?project_uid=、錯誤碼實際編號與 GRC_400185 |
CM-3014 |
| 缺口 | 白話 | 證據 |
|---|---|---|
| 專案角色寫死 | 四個英文字散在 BE 28 支檔、6 支 jedi 套件、FE 約 30 支;enum 有兩份相同複本 | FR-132 inventory/02-project-roles-and-flow.md §1–2;本案 grep |
| 管理員不能自訂專案角色 | 客戶要「資深稽核員」(稽核+審閱)做不到;角色的能力綁在程式裡 | common/enum/participant_enum.py、ParticipantPicker.vue:102-107 |
| 流程圖綁角色名 | BPMN UserTask main_role 存四值;進行中實例約 4,400 份 XML 快照 |
stage_advance_service.py:672-679;inventory §3、§5 |
| 通知與待辦各自比字串 | 8 支 SQL 讀模型 pp.role IN (...)、5 處通知收件人各寫一遍 |
infra/readmodel/audit/*.py、stage_entered_recipients.py |
| 兩套角色沒有關係 | 系統角色是資料驅動(FR-132),專案角色不是 | roles/role_capability_patterns vs 五張參與者表 role varchar |
proj.audit.judge、proj.review.approve、proj.report.read。my_capabilities,前端只顯示那三個能力點對應的按鈕。assert_project_capability("proj.review.approve", ...) 由下往上找她的參與者指派、展開角色、比對通過。proj.review.approve 的參與者」,她會收到;「稽核待辦」清單列有 proj.audit.judge 的人,她也在。proj.audit.judge,她可以推進;負責人有 proj.stage.advance-any 也可以。一句話:先收斂與定清單(B.1),再改表與升級(B.2),改判定與通知與 API(B.3),最後畫面與流程編輯器(B.4)。
| 批 | 做什麼(白話) | 產出 | 完成怎麼判定(決策者可親手檢查) | 依賴 |
|---|---|---|---|---|
| B.1 收斂與清單 | 四個字只留一份;守衛擋字面值;專案能力點清單與四個範例角色對照定稿 | enum 單一來源+守衛測試;inventory/b1-project-capabilities.md |
守衛測試綠,故意加一行 'manager' 會紅;對照表每個專案內動作都有一列 |
— |
| B.2 資料地基 | roles 加種類與預設負責人旗標;capabilities 加 proj.*;五張參與者表加 project_role_id;升級腳本每租戶建四範例角色並回填;新裝 seed |
migration、seed、排練紀錄 | 真實舊庫升級後,任一專案參與者頁每個人角色名與升級前一樣;舊帳號能按的按鈕一樣 | B.1 |
| B.3 判定、通知、API | 守門四支改收能力點;get_user_role 回角色並能展開;8 支 SQL+5 處通知改同一查法;流程推進讀取換算;專案角色 CRUD/GET /project-roles/專案詳情回 my_capabilities/FE 常數檔 |
判定層、讀模型、API | 建「只能看」的自訂角色指派給某人:他進得了專案、按審核 403、不收審閱通知;勾回審閱立刻能按、會收信 | B.2 |
| B.4 畫面與流程編輯器 | 角色頁兩個 tab+專案角色詳情;參與者下拉讀表;專案內按鈕改讀 my_capabilities;流程編輯器「主要角色」改選能力點 |
FE 改動+BE 存檔驗證 | 對照雛形三頁;新增「資深稽核員」指派後該人只看得到三個動作;流程編輯器下拉列的是能力點 | B.3 |
B.1→B.2→B.3→B.4 照順序;B.4 內三張可依卡號串行。
| # | 題目 | 定案 | 理由 | 被排除方案 |
|---|---|---|---|---|
| D1 | 專案能力點命名 | 新模組前綴 proj.<資源>.<動詞>,assignable_scopes=['project']、module='proj';13 個(§6.1) |
與 grc.*(租戶層「能不能碰這類東西」)一眼分得開;有 grc.audit.approve 的系統角色不會被誤判成每個專案都能審核 |
沿用 grc.* 靠範圍欄分——畫面與 grep 都會混 |
| D2 | 出貨範例專案角色 | 每租戶四個範例(負責人/審核者/稽核員/瀏覽者),可改名、改能力點、可刪;其中一個 is_default_owner=true(建專案建立者自動指派、不可刪、每租戶恰一個);使用中專案數 >0 不可刪 |
與 FR-132 D6 同精神:保證至少一個角色能管專案,其餘是起點 | 四個全鎖——客戶要自訂只能複製 |
| D3 | 參與者表怎麼改 | 五張表加 project_role_id FK → roles.id(nullable 一版);舊 role 欄並存一版後退役;寫入路徑雙寫(新欄為準、舊欄回填對應範例角色名) |
回滾不必動資料,與 FR-132 D2 同手法 | 直接改 role 欄語意——回滾要動資料 |
| D4 | 系統管理員進專案 | proj.* 只從「這個專案的參與者指派」展開,不從 user_roles 展開;系統 *.*.* 不包含 proj.*;不是參與者就看不到專案(與今天相同) |
管理員不會每封通知都收、稽核員清單不會列他;程式上兩個入口各查各的 | 包含——每種通知都收 |
| D5 | 群組/控制項層回退 | 指派只在專案層。子層參與者表(群組、控制項、控制群組、流程)保留 project_role_id 欄,但沒有寫入路徑;回退版守門(控制項→群組→專案找第一筆)仍在,實際一律落到專案層那一列 |
子層的寫入端點早已退役、DEV 0 筆、前端控制項樹也不送;為它補畫面與回填沒有使用者 | 補子層指派畫面——沒有需求;拿掉子層欄——回滾要動表 |
| D6 | 稽核範圍(內稽/外稽) | audit_scope 留在參與者列;判定時只對「有 proj.audit.judge」的參與者生效;前端只對有該能力點的角色顯示欄位 |
不綁角色名、綁能力點 | 搬到角色——同一人兩專案不同範圍要兩個角色 |
| D7 | 專案內按鈕前端判定 | 專案詳情與專案列表回 my_capabilities,是專案層精確版(只看專案層那一列,與後端精確版守門同判法;D5 下子層沒有指派,兩版結果相同);FE 新增 v-can-project="PROJ.X" 指令;權限名由 BE 產常數檔(比照 gen_permission_matrix.py) |
回退規則 BE 算一次,FE 不重算 | FE 自己展開角色 |
| D8 | 流程圖 main_role |
改存能力點名(例 proj.audit.judge,逗號多值照舊);_resolve_main_roles 讀到舊四值即換算;flow_templates.bpmn_xml 與 stage_objects.default_main_roles 用 migration 改成能力點名;workflow_templates.xml(實例快照)不碰;manager 全域覆寫改成「有 proj.stage.advance-any」;設計器存範本的 main_role 只收 proj.review.approve、proj.audit.judge、proj.stage.advance-any 三個(§6.4) |
客戶會改角色名、加角色,角色名不能當程式依據;4,400 份快照不改最安全 | 存角色 uid(刪角色流程壞);批次改寫 XML |
| D9 | jedi 套件改法 | 參與者表、enum、serializer 的 owner 是 jedi_task_platform,改在套件;jedi_compliance_audit/jedi_survey/jedi_evidence_classification/jedi_detection/jedi_flow_engine 只改呼叫端參數(角色 tuple → 能力點名);開發走 poetry path dependency,發版等決策者明示 | 不造第二套真相 | 主專案 monkeypatch |
| D10 | 分版 | 一版做完(原「流程編輯器放第二版」改判:客戶第一版就能自建角色,編輯器若還只能選四個舊字,自建角色在流程裡沒用) | 決策者 2026-10-21 改判 | 兩版 |
明確不做:專案角色的部門範圍與起訖日(參與者指派本來就是專案範圍、無時效);拒絕規則;專案角色匯出 JSON;專案角色的萬用字元(只有十幾個能力點,逐個勾比較清楚)。
| 元件 | 現況 | 本案動作 | 批 |
|---|---|---|---|
common/enum/participant_enum.py+jedi_task_platform/participant/common/participant_enum.py |
兩份相同 StrEnum | 只留套件那份;主專案改 re-export;守衛測試 test/test_project_role_literal_guard.py 掃 api/ app/ infra/ core/ 的 `'manager' |
'reviewer' |
common/authz/project.py:28-137 四支守門 |
allowed_roles tuple 比對 |
新增 assert_project_capability(cap, role_service, project_id, group_id, control_id)(回退版)與 assert_project_capability_exact(cap, participant_ds, project_id, user_id)(精確版);舊四支留 shim 一版,內部把 allowed_roles 對到能力點(manager→proj.participant.manage、auditor→proj.audit.judge、reviewer→proj.review.approve) |
B.3 |
jedi_task_platform participant_role_service.py:34-85 get_user_role |
回角色字串 | 新增 get_user_project_role_id(同回退規則,回 project_role_id)與 get_user_project_capabilities(展開,走 jedi-iam pattern 展開服務+Redis 快取鍵 authz:proj:{tenant}:{catalog_version}:{role_id});get_user_role 保留一版回舊欄 |
B.3 |
8 支 infra/readmodel/ SQL(audit_plan_list_query.py:47,53,196,310、closing_report_list_query.py:27,32、auditor_dashboard_query.py:60,157、poam_dashboard_query.py:61、job_due_reminder_query.py:55、job_batch_complete_query.py:239、detection_job_notify_query.py:156、support_end_query.py:52) |
字面值 | 先查「租戶內有能力點 X 的專案角色 id 集合」(新 helper project_role_ids_with_capability(tenant_id, cap),可快取),再 project_role_id = ANY(:ids);查詢形狀不變 |
B.3 |
stage_entered_recipients.py、job_batch_complete_query.py:232、detection_job_notify_query.py:150、auditor_dashboard_query.py:58 |
各自比角色 | 統一改為「有能力點 X 的參與者」 | B.3 |
project_start_app_service.py:135-146 |
建立者寫 'manager' |
寫該租戶 is_default_owner 角色 |
B.3 |
api/project/serializers/project.py:27-29、excel_parser/validators.py:9、jedi_task_platform serializer ×2、jedi_compliance_audit serializers/project.py:77 |
OneOf 四值 |
收 project_role_uid,驗存在於該租戶 kind='project' 且啟用;Excel 匯入收角色名、對不到報錯列出 |
B.3 |
stage_advance_service.py:148-156,344-355,672-679 |
main_role 四值 vs user_role |
_resolve_main_roles 回能力點名(舊四值換算表);判定改 cap in user_caps or "proj.stage.advance-any" in user_caps;force 改要求 proj.stage.advance-any |
B.3 |
stage_objects.default_main_roles、flow_templates.bpmn_xml |
角色名 | migration 改能力點名(XML 用 regexp_replace 只動 name="main_role" 的 value) |
B.2 |
jedi-iam api/routing.py roles CRUD |
無 kind | list 加 kind 篩選;create 收 kind;kind='project' 的角色 pattern 只准 proj.*、必含 proj.project.read;is_default_owner 切換時同租戶其他角色清掉;刪除前查使用中專案數 |
B.3 |
專案詳情 API(api/project/) |
回 participants | 加 my_capabilities: [str];GET /project-roles(租戶內啟用的專案角色,含能力點清單) |
B.3 |
scripts/deliverables/gen_permission_matrix.py |
產 CAP 常數 |
加產 PROJ 常數(FE src/config/projectCapabilities.js) |
B.3 |
FE views/access/RoleList.vue、RoleDetail.vue、PermissionResourceMatrix.vue |
單一列表 | 兩個 tab(?kind=);專案角色詳情矩陣只列 proj.*、拿掉可見選單預覽、加預設負責人開關 |
B.4 |
FE ParticipantPicker.vue:102-115、ProjectParticipantsPanel.vue、ProjectCreateView.vue |
四值寫死 | 下拉讀 GET /project-roles;audit_scope 欄只對含 proj.audit.judge 的角色顯示 |
B.4 |
FE 專案頁約 30 處 p.role === 'manager'(ProjectPlanningView.vue 9、ProjectAuditorOverview.vue 6、MyTasksView.vue 2、FlowPhaseBanner.vue 1、ApPlanOverviewTab.vue 2…) |
字面值 | v-can-project="PROJ.X"+my_capabilities |
B.4 |
FE FlowTemplateEditorView.vue:129-134,376-389,594、FlowTemplatePreviewDialog.vue |
四值寫死 | 下拉讀 proj.* 能力點;存檔驗證改能力點名 |
B.4 |
5 支套件呼叫端(review_service.py、task_survey_guard.py、evidence_batch_service.py:1476、detection_orchestration_service.py ×8、core/plugins/compliance_audit.py:73-93) |
allowed_roles=("manager",) |
改能力點名參數,透過 adapter 轉發新函式 | B.3 |
| 能力點 | 白話 | 負責人 | 審核者 | 稽核員 | 瀏覽者 |
|---|---|---|---|---|---|
proj.project.read |
看專案(所有專案角色必含) | ✓ | ✓ | ✓ | ✓ |
proj.project.update |
改專案設定、刪專案 | ✓ | |||
proj.participant.manage |
增刪參與者、指派任務 | ✓ | |||
proj.job.manage |
增刪改任務、批次完成不限指派 | ✓ | |||
proj.review.approve |
標記已審閱、退回 | ✓ | ✓ | ||
proj.audit.judge |
稽核判定、AP 編輯、AO 結果 | ✓ | ✓ | ||
proj.stage.advance-any |
推進任何階段、強制推進 | ✓ | |||
proj.round.manage |
建立輪次、啟動稽核、結束輪次、各段退回 | ✓ | |||
proj.poam.manage |
改善計畫建立與結案 | ✓ | |||
proj.report.read |
看結案報告、匯出 | ✓ | ✓ | ||
proj.report.manage |
建立與發布結案報告 | ✓ | |||
proj.evidence.manage |
證據分類批次操作 | ✓ | |||
proj.ssp.update |
SSP 各區塊寫入 | ✓ |
共 13 個。四個範例角色的勾選以 resources/roles/project-roles.json 為準(與 seed migration 由 test/test_role_templates.py 鎖成一致)。「執行任務、上傳佐證、填答」不設能力點:這類動作走「被指派人 ∪ proj.job.manage」(任務被指派人本身就有操作權,不看角色)。「推進自己負責的階段」也不設:可不可以推進由站的 main_role 決定(§6.4)。每個接入點對到哪一個能力點見 inventory/b1-project-capabilities.md。
舊角色→能力點換算表(守門 shim、main_role 讀取換算、get_user_role 相容用同一張):manager→proj.participant.manage(守門語意「是 manager」)/proj.stage.advance-any(流程語意);reviewer→proj.review.approve;auditor→proj.audit.judge;viewer→proj.project.read。
| 表 | 動作 |
|---|---|
public.roles |
kind varchar(10) NOT NULL DEFAULT 'system'(CHECK in system/project);is_default_owner boolean NOT NULL DEFAULT false;部分唯一索引 (tenant_id) WHERE kind='project' AND is_default_owner AND is_delete=0 |
public.capabilities |
proj.* 13 列:module='proj'、assignable_scopes=['project']、is_platform=false、license_module 空(不受授權照管)、display_name='capability.proj.<資源>.<動詞>' |
compliance.project_participants、project_group_participants、project_control_participants、control_group_participants、process_participants |
加 project_role_id integer NULL REFERENCES public.roles(id) ON DELETE RESTRICT+索引;role 欄保留一版 |
compliance.stage_objects.default_main_roles |
值改能力點名 |
compliance.flow_templates.bpmn_xml |
main_role 值改能力點名(設計器範本);workflow_templates.xml 不碰 |
升級腳本(T-2.2):對每個 tenants 列建四個 kind='project' 角色(名稱走 i18n key project_role.owner 等,DB 存中文名「負責人/審核者/稽核員/瀏覽者」)+ role_capability_patterns 照 6.1 表;負責人 is_default_owner=true;五張表 UPDATE ... SET project_role_id = (該租戶對應角色) WHERE role = '<舊值>';結尾斷言五張表 project_role_id IS NULL 為 0 列,否則 rollback。新裝:scripts/init/ seed 與 tenant_provisioning_service.py 同樣建四個;回寫時明講「出貨基線待重產」。
get_user_project_capabilities(project_id, user_id, group_id=None, control_id=None)+assert_project_capability(由下往上找第一筆指派);精確版 get_participant_capabilities+assert_project_capability_exact(只看專案層那一列)。兩版都是 project_role_id → jedi-iam CapabilityExpansionService.expand_project_roles → Redis 快取(鍵 authz:proj:{tenant}:{catalog_version}:{role_id},角色寫入時 invalidate_all 一併清)。非參與者回 None/空集合,守門轉 404;是成員但缺能力點 403。docker/production/docker-compose.yml 的 guidant-api/socketio/worker 三個帳號都要開 ~authz:proj:*(與 ~authz:exp:* 同權限)。漏開時 Redis 回 NOPERM,adapter 只把連線錯誤當「快取不可用、改查 DB」,NOPERM 會直接變 500。assert_project_manager/assert_project_role_fallback/assert_project_role 保留一版,內部用換算表把舊角色字對到能力點(manager→proj.participant.manage、reviewer→proj.review.approve、auditor→proj.audit.judge、viewer→proj.project.read)。新程式一律用能力點版。proj.participant.manage。user_roles 展開 proj.*(D4):CapabilityGuard 的展開服務遇到 assignable_scopes=['project'] 的能力點一律不納入租戶層集合;守衛測試驗 *.*.* 展開結果不含 proj.*。common/authz/project.py;決策表(common/authz/__init__.py)軸③改寫為能力點版。audit_scope(D6):resolve_stage_recipients 與 _get_user_role 的稽核性質比對只對 proj.audit.judge in caps 的參與者做。_resolve_main_roles → _resolve_main_caps:讀 main_role,每個值若在舊四值換算表就換,否則視為能力點名;回 list[str]。caps & main_caps 非空,或 proj.stage.advance-any in caps。force 要 proj.stage.advance-any。stage_entered_notify_service:收件人=有 main_caps 任一的參與者(套 D6)+有 proj.stage.advance-any 的參與者。main_role 空值時退回站預設 stage_objects.default_main_roles,與推進判定共用 stage_entered_recipients.resolve_main_caps。proj.stage.advance-any 的人整個不算該站負責人(預設負責人什麼都有,算進去就永遠不提醒)。main_role 只能是 proj.review.approve、proj.audit.judge、proj.stage.advance-any(app/flow_engine/service/main_role_check.py MAIN_ROLE_ALLOWED),否則 400 GRC_400185 並列出允許值。其他能力點不准選:推進判定只看「持有任一主責」,選了 proj.ssp.update 這類會讓守門失真。lang.capability.*;載入舊範本時整張換算;舊範本存了三個以外的值,臨時併入選項並標「不在建議清單」。| 端點 | 說明 |
|---|---|
POST /roles(jedi-iam 列表,filters.kind) |
預設 system;kind=project 列專案角色,每列回 kind/is_default_owner/in_use_project_count |
POST /role、PUT /role/<uid>(既有,收 kind) |
kind='project' 時只准 proj.* 完整名、自動補 proj.project.read;is_default_owner=true 時同交易先清同租戶其他角色的旗標;專案角色不過授權照守門(proj.* 不是售賣模組) |
GET /project-roles |
登入者租戶內啟用的專案角色(uid/name/capabilities/is_default_owner),建專案的參與者下拉用;任何登入者可讀 |
GET /project-roles?project_uid=<uid> |
專案所在租戶的專案角色(總部帳號改子租戶專案時用);限專案成員,非成員 404 |
GET /grc/project/<uid>(既有) |
加 my_capabilities;participants 每筆補 project_role_uid/project_role_name |
| 專案列表(既有) | 每列加 my_capabilities(整頁一次展開) |
PUT /grc/project/<uid>(既有) |
改設定要 proj.project.update;送 participants 另要 proj.participant.manage,且每個 project_role_uid 須屬專案所在租戶(否則 404 GRC_404057) |
GET /users/me/capabilities(既有) |
不含 proj.* |
錯誤碼(common/code/flow_control_error_code.py,FE i18n 已同步):
| 碼 | 常數 | 情境 |
|---|---|---|
GRC_403071 |
GRC_PROJECT_CAPABILITY_REQUIRED |
是專案成員但角色缺該能力點(呼叫端沒給專屬碼時用;原本有專屬 403 碼的守門保留原碼) |
GRC_404057 |
GRC_PROJECT_ROLE_NOT_FOUND |
專案角色不存在、停用,或不屬專案所在租戶 |
GRC_412078 |
GRC_DEFAULT_OWNER_REQUIRED |
租戶沒有預設負責人(建專案時);預設負責人不可停用 |
GRC_400183 |
GRC_PROJECT_ROLE_AUDIT_SCOPE_NOT_ALLOWED |
給沒有 proj.audit.judge 的角色設內稽/外稽範圍 |
GRC_409050 |
GRC_PROJECT_ROLE_IN_USE |
刪除仍有專案在用的專案角色(優先於 412) |
GRC_400184 |
GRC_PROJECT_ROLE_INVALID_PATTERN |
專案角色勾了非 proj.* 或萬用字元 |
GRC_400185 |
GRC_INVALID_MAIN_ROLE |
流程圖 main_role 不在允許清單 |
照雛形三頁:prototype/roles.html(兩個 tab)、prototype/role-detail.html(專案角色詳情)、prototype/participants.html(參與者)。v-can-project 指令實作比照 v-can(src/directives/),資料源是當前專案 store 的 my_capabilities;專案 store 載入專案詳情時一併存。
下表是原始拆分;驗收後加開第 5 批(CM-3010 合回與套件 bump、CM-3011 BE 裁示收尾、CM-3012 FE 裁示收尾)與第 6 批(CM-3013 出貨基線重產、CM-3014 收口審查與文件)。
依賴鏈:B.1 → B.2 → B.3 → B.4;同批依卡號串行。jedi 套件開發走 poetry path dependency(pyproject.toml:118-124 的註解行),發版等決策者明示;migration 只套 DEV。
| 編號 | 卡號 | 批 | 做什麼(白話) | 改哪些檔 | 驗收條件 | 建議 model/effort |
|---|---|---|---|---|---|---|
| T-1.1 | CM-2978 | 1 | 四個字只留一份:主專案 enum 改 re-export jedi_task_platform;守衛測試擋字面值 | common/enum/participant_enum.py、test/test_project_role_literal_guard.py、jedi_task_platform enum |
守衛綠;突變(加一行 'manager')會紅 |
sonnet/medium |
| T-1.2 | CM-2979 | 1 | 專案能力點清單定稿:逐一對照 70 處守門、8 支 SQL、5 處通知、6 個流程站、FE 30 處按鈕各對哪個 proj.*;缺的補進清單 |
inventory/b1-project-capabilities.md |
每個接入點都有一列且有能力點;四個範例角色勾選表定稿 | opus/high |
| T-2.1 | CM-2980 | 2 | 資料表:roles 加欄、capabilities 加 proj.*、五張參與者表加 FK;jedi-iam/jedi_task_platform ORM 同步 |
scripts/sql/2026-10-xx-fr155-*.sql ×1、jedi-iam infra/models/role.py、jedi_task_platform 五個 model |
DEV 套用後 \d 看得到欄;capabilities 多 14 列 |
opus/medium |
| T-2.2 | CM-2981 | 2 | 升級腳本與新裝 seed:每租戶建四範例角色、回填 project_role_id、stage_objects/flow_templates 改能力點名;拿真實舊庫排練;tenant_provisioning_service 建四個 |
migration ×2、scripts/init/ seed、tenant_provisioning_service.py、inventory/b2-upgrade-rehearsal.md |
排練後五張表 project_role_id IS NULL 0 列;任一專案參與者頁角色名不變;回報「出貨基線待重產」 |
opus/high |
| T-3.1 | CM-2982 | 3 | 守門改能力點:新兩支守門函式+舊四支 shim;get_user_project_capabilities 展開+快取;D4 守衛;6 套件呼叫端改參數;serializer 收角色 uid;建立者寫預設負責人 |
common/authz/project.py、__init__.py 決策表、jedi_task_platform participant_role_service.py、5 套件呼叫端、core/plugins/compliance_audit.py、project_start_app_service.py、serializer ×4、error code |
「只能看」自訂角色按審核 403;非成員 404;*.*.* 展開不含 proj.*(守衛) |
opus[1m]/high |
| T-3.2 | CM-2983 | 3 | 通知與 SQL 讀模型:project_role_ids_with_capability helper;8 支 SQL 改 project_role_id = ANY;5 處收件人改能力點 |
infra/readmodel/ 8 支、stage_entered_recipients.py、job_batch_complete_query.py、detection_job_notify_query.py |
勾掉審閱能力點批次完成通知不寄;勾回會寄;我的稽核待辦列有 proj.audit.judge 的人 |
opus/high |
| T-3.3 | CM-2984 | 3 | 流程推進讀取換算:_resolve_main_caps、可推進判定、force、進站通知收件人;舊四值與逗號多值測試 |
stage_advance_service.py、stage_entered_notify_service.py、測試 |
進行中舊專案推進行為不變;新範本存能力點名可推進 | opus/high |
| T-3.4 | CM-2985 | 3 | 專案角色 API:roles CRUD 加 kind、pattern 限制、is_default_owner 互斥、刪除前查使用中;GET /project-roles;專案詳情回 my_capabilities;gen_permission_matrix.py 產 PROJ 常數 |
jedi-iam api/+app/、api/project/、scripts/deliverables/gen_permission_matrix.py、FE src/config/projectCapabilities.js |
Swagger 打得通;GET /projects/<uid> 有 my_capabilities;常數檔與 DB 一致 |
opus/medium |
| T-4.1 | CM-2986 | 4 | FE 角色頁兩個 tab+專案角色詳情(照雛形 1、2) | FE views/access/RoleList.vue、RoleDetail.vue、components/access/PermissionResourceMatrix.vue、i18n |
新增「資深稽核員」勾三個能力點存得起來;預設負責人開關互斥;使用中不可刪 | opus/medium |
| T-4.2 | CM-2987 | 4 | FE 參與者下拉讀表+專案內按鈕改 v-can-project(約 30 處)+audit_scope 只對稽核判定角色顯示(照雛形 3) |
FE ParticipantPicker.vue、ProjectParticipantsPanel.vue、ProjectCreateView.vue、directives/vCanProject.js、專案頁約 10 支 |
指派「資深稽核員」後進專案只看得到三個動作;grep "role === 'manager'" 專案頁為零 |
opus[1m]/high |
| T-4.3 | CM-2988 | 4 | FE 流程編輯器「主要角色」改選能力點+BE 存檔驗證 | FE FlowTemplateEditorView.vue、FlowTemplatePreviewDialog.vue、BE workflow_xml_sync_service.py 驗證 |
編輯器下拉列能力點;存舊四值被拒;預覽顯示能力點 i18n | sonnet/medium |
收口:審查、SPEC 三頁、使用手冊、release note 草稿由 CM-3014 做(報告 docs/review/2026-10-22-fr155-review.md);套件發版與還原 pin、e2e 到 test repo 等決策者令。
上版綁定:① 參與者回填 migration(T-2.2)與判定改查能力點(T-3.x)必須同一版上,只上判定,參與者沒有 project_role_id 會一律 404;② jedi-iam 1.7.0、jedi-task-platform 1.4.0、jedi-compliance-audit 1.4.0、jedi-survey 1.3.0 四支新版必須一起進 image(task-platform 的 adapter 新增必填參數,新套件配舊宿主會 TypeError);③ 正式 compose 的 Redis ACL 要含 ~authz:proj:*。
GET /users/me/capabilities 不含 proj.*。main_role 存能力點名;舊範本開啟顯示換算後的能力點。*.*.* 不含 proj.*、test/test_module_boundaries.py、RLS 形狀測試全綠。