Merge pull request 'Document the IAM U4 role policy authority design' (#114) from feature/iam-u4-role-authority into main
governance / governance (push) Successful in 19s
governance / governance (push) Successful in 19s
This commit was merged in pull request #114.
This commit is contained in:
@@ -0,0 +1,393 @@
|
||||
# Focused Role and Permission System Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Deliver the requested single-effective-role position model and enforce system, role, and per-project permissions from one server-side action catalog.
|
||||
|
||||
**Architecture:** Backend owns roles, the single active user binding, the permission catalog, system grants, and role defaults/caps. RDM consumes that public authority while retaining project membership, project overrides, creator ownership, and existing document duties. Frontend uses Backend role APIs for the single-role and matrix views and uses RDM only for project-scoped configuration.
|
||||
|
||||
**Tech Stack:** Go 1.27, SQLite, PostgreSQL, HTTP JSON APIs, compiled extension Host contracts, React 19, TypeScript, Vitest, Playwright.
|
||||
|
||||
**Spec:** `docs/proposals/2026-10-15-iam-focused-role-permission-spec.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- One user has at most one effective role; one role may be assigned to many users.
|
||||
- Role and user stable IDs never change when display data changes.
|
||||
- System administrators do not gain implicit access to project business content.
|
||||
- A project creator can manage only the project they created.
|
||||
- Unknown actions, missing providers, stale versions, disabled accounts, and disabled roles fail closed.
|
||||
- Role defaults never exceed role caps; project allows never exceed role or platform caps.
|
||||
- UI configuration uses a draft, preview, and atomic batch commit; hidden rows do not lose draft edits.
|
||||
- Account recovery, multi-role bindings, Agent permissions, workflow redesign, production migration, and deployment remain outside this plan.
|
||||
|
||||
## Review Focus
|
||||
|
||||
- Reassigning a user while two administrators submit concurrently must leave exactly one active binding and return one conflict.
|
||||
- Disabling a role between preview and commit must reject the commit without partial writes.
|
||||
- A project creator switching from project A to B must not retain A-derived management authority in B.
|
||||
- A filtered matrix batch must contain explicit action IDs and must not apply to hidden or newly registered actions.
|
||||
- RDM provider absence or version mismatch must deny authorization and must not fall back to the legacy local role table.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Backend action catalog and role schema
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Backend/internal/authz/catalog.go`
|
||||
- Create: `Agent-System-Backend/internal/store/role_policy.go`
|
||||
- Modify: `Agent-System-Backend/internal/store/migrate.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_migration_test.go`
|
||||
- Test: `Agent-System-Backend/internal/authz/catalog_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `authz.Descriptor`, `authz.DescriptorByID(string) (Descriptor, bool)`, `store.RoleRecord`, `store.RoleBinding`, and schema v35 tables.
|
||||
- Consumes: existing `users(id, owner, enabled, state_version)` and migration transaction machinery.
|
||||
|
||||
- [ ] **Step 1: Add failing catalog tests**
|
||||
|
||||
```go
|
||||
func TestCatalogRejectsUnknownAndProtectsManagementActions(t *testing.T) {
|
||||
if _, ok := DescriptorByID("invented.action"); ok { t.Fatal("unknown action registered") }
|
||||
got, ok := DescriptorByID("system.roles.manage")
|
||||
if !ok || got.Scope != ScopeSystem || !got.Protected { t.Fatalf("descriptor=%+v", got) }
|
||||
create, ok := DescriptorByID("project.create")
|
||||
if !ok || create.Scope != ScopeSystem || create.Protected { t.Fatalf("project.create=%+v", create) }
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused test and confirm the missing package failure**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/authz -run TestCatalogRejectsUnknownAndProtectsManagementActions -count=1`
|
||||
Expected: FAIL because `internal/authz` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the fixed action catalog**
|
||||
|
||||
```go
|
||||
type Scope string
|
||||
const ( ScopeSystem Scope = "system"; ScopeProject Scope = "project" )
|
||||
type Descriptor struct { ActionID, Label, Description, Group string; Scope Scope; Protected bool }
|
||||
func DescriptorByID(id string) (Descriptor, bool)
|
||||
func Catalog() []Descriptor
|
||||
```
|
||||
|
||||
Register only the system and project actions listed in the spec. Return copies so callers cannot mutate the registry.
|
||||
|
||||
- [ ] **Step 4: Add schema v35 and migration tests for both dialects**
|
||||
|
||||
Create `roles`, `role_permissions`, `user_role_bindings`, `system_grants`, `authority_state`, and `policy_receipts`. Enforce one active binding per user with a unique `user_id` key; preserve inactive history through versioned replacement rows in the same transaction. Use the existing boolean representation per dialect and foreign keys to `users(id)`.
|
||||
|
||||
Run: `GOWORK=off go test ./internal/store -run 'TestRolePolicyMigration|TestMigrate' -count=1`
|
||||
Expected: PASS on SQLite; the existing PG migration suite later verifies PostgreSQL.
|
||||
|
||||
- [ ] **Step 5: Commit the schema and catalog**
|
||||
|
||||
```bash
|
||||
git add internal/authz/catalog.go internal/authz/catalog_test.go internal/store/role_policy.go internal/store/role_policy_migration_test.go internal/store/migrate.go
|
||||
git commit -m "Add the role permission catalog and schema"
|
||||
```
|
||||
|
||||
### Task 2: Atomic role, binding, and permission store
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Backend/internal/store/role_policy.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_test.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_pg_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `authz.DescriptorByID`, current account state, store transaction helpers.
|
||||
- Produces:
|
||||
- `CreateRole(actor string, in RoleCreate) (RoleRecord, error)`
|
||||
- `PatchRole(actor, roleID string, expected int64, patch RolePatch) (RoleRecord, error)`
|
||||
- `ReplaceRolePermissions(actor, roleID string, expected int64, changes []RolePermissionChange, key string) (PolicyReceipt, error)`
|
||||
- `AssignUserRole(actor, userID, roleID string, expectedState int64, key string) (RoleBinding, error)`
|
||||
- `RoleForUser(userID string) (RoleBindingView, error)`
|
||||
- `ReplaceSystemGrants(actor, userID string, expectedEpoch int64, actions []string, key string) (PolicyReceipt, error)`
|
||||
|
||||
- [ ] **Step 1: Write failing transaction and concurrency tests**
|
||||
|
||||
```go
|
||||
func TestAssignUserRoleKeepsOneEffectiveBinding(t *testing.T) {
|
||||
first := mustAssign(t, st, admin, userID, engineerID, 1, "assign-1")
|
||||
second := mustAssign(t, st, admin, userID, reviewerID, first.UserStateVersion, "assign-2")
|
||||
if second.RoleID != reviewerID { t.Fatalf("binding=%+v", second) }
|
||||
if got := countActiveBindings(t, st, userID); got != 1 { t.Fatalf("active=%d", got) }
|
||||
}
|
||||
```
|
||||
|
||||
Also test two concurrent replacements with the same expected state, duplicate idempotency keys, disabled users, disabled roles, unknown actions, `allowed=true/capAllowed=false`, stale role versions, a multi-change batch containing one invalid item, and rejection when a system-grant batch removes the final effective `system.permissions.manage` holder.
|
||||
|
||||
- [ ] **Step 2: Run tests and confirm missing methods fail**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/store -run 'TestAssignUserRole|TestReplaceRolePermissions' -count=1`
|
||||
Expected: FAIL with undefined store methods.
|
||||
|
||||
- [ ] **Step 3: Implement transaction-scoped mutations**
|
||||
|
||||
Lock the actor, target account, role, and `authority_state` in the existing account-before-policy lock order. Validate the complete batch before any write. Increment `role_version` and `authority_epoch` once per successful transaction and persist one `policy_receipts` row per idempotency key.
|
||||
|
||||
- [ ] **Step 4: Verify SQLite and PostgreSQL behavior**
|
||||
|
||||
Run SQLite: `GOWORK=off go test ./internal/store -run 'Role|Policy|Binding' -count=1`
|
||||
Run PostgreSQL through the isolated PG harness after it creates the mode-0600 DSN file: `AGENT_SYSTEM_TEST_PG_DSN_FILE=/tmp/iam-u4-role-policy-pg-dsn GOWORK=off go test ./internal/store -run 'Role|Policy|Binding' -count=1`
|
||||
Expected: both pass with identical conflict and rollback assertions.
|
||||
|
||||
- [ ] **Step 5: Commit the store service**
|
||||
|
||||
```bash
|
||||
git add internal/store/role_policy.go internal/store/role_policy_test.go internal/store/role_policy_pg_test.go
|
||||
git commit -m "Add atomic role and binding policies"
|
||||
```
|
||||
|
||||
### Task 3: Backend authorization service and HTTP API
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Backend/internal/authz/service.go`
|
||||
- Create: `Agent-System-Backend/internal/server/roles.go`
|
||||
- Modify: `Agent-System-Backend/internal/server/server.go`
|
||||
- Modify: `Agent-System-Backend/internal/server/accounts.go`
|
||||
- Test: `Agent-System-Backend/internal/server/roles_test.go`
|
||||
- Test: `Agent-System-Backend/internal/server/role_authorization_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 2 store methods and existing authenticated actor resolution.
|
||||
- Produces: the HTTP endpoints in the spec and `Service.AuthorizeSystem(actor, actionID string) Decision`.
|
||||
|
||||
- [ ] **Step 1: Write failing authorization and HTTP tests**
|
||||
|
||||
```go
|
||||
func TestRoleManagerCannotGrantProtectedPermission(t *testing.T) {
|
||||
cookie := loginAs(t, h, "role-manager")
|
||||
rec := do(h, "PUT", "/api/admin/roles/"+roleID+"/permissions",
|
||||
`{"expectedRoleVersion":1,"idempotencyKey":"p1","changes":[{"actionId":"system.permissions.manage","allowed":true,"capAllowed":true}]}`, cookie)
|
||||
if rec.Code != http.StatusForbidden { t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String()) }
|
||||
}
|
||||
```
|
||||
|
||||
Cover role description create/edit, exact and fuzzy role search, status/action filtering, pagination, one role assigned to two users, user reassignment, stale versions, fixed-ID batches, disabled role assignment, system-grant listing/replacement, final permission-manager protection, and account profile fields remaining outside authorization.
|
||||
|
||||
- [ ] **Step 2: Run tests and confirm routes are absent**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/server -run 'Role|PermissionCatalog|UserRole' -count=1`
|
||||
Expected: FAIL with 404 or missing handlers.
|
||||
|
||||
- [ ] **Step 3: Implement service authorization and handlers**
|
||||
|
||||
Protected actions require explicit `system_grants`; `project.create` may also use the current enabled role permission. Provide `GET/PUT /api/admin/users/{userId}/system-grants` with expected epoch and idempotency key. Replace `requireAdmin` only for the new role endpoints in this task; existing unrelated admin endpoints remain unchanged until their explicit action is wired in Task 4.
|
||||
|
||||
- [ ] **Step 4: Wire account management actions without changing recovery**
|
||||
|
||||
Map account list/create/profile/state handlers to `system.users.view/create/edit/disable`. Do not touch recovery routes or credential behavior. Preserve first-admin bootstrap as the protected root recovery path.
|
||||
|
||||
- [ ] **Step 5: Run server and full Backend tests**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/authz ./internal/server ./internal/store -count=1`
|
||||
Run: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 6: Commit the Backend API**
|
||||
|
||||
```bash
|
||||
git add internal/authz/service.go internal/server/roles.go internal/server/server.go internal/server/accounts.go internal/server/roles_test.go internal/server/role_authorization_test.go
|
||||
git commit -m "Expose role and system permission APIs"
|
||||
```
|
||||
|
||||
### Task 4: Public role provider for RDM
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Backend/pkg/extension/extension.go`
|
||||
- Modify: `Agent-System-Backend/internal/app/hostcaps.go`
|
||||
- Test: `Agent-System-Backend/internal/app/role_provider_test.go`
|
||||
- Create: `Agent-System-RDM/internal/perms/role_provider.go`
|
||||
- Modify: `Agent-System-RDM/internal/rdmapp/perms_api.go`
|
||||
- Test: `Agent-System-RDM/internal/rdmapp/role_provider_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces public `extension.RolePolicyProvider` methods `RolePolicyForActor(actor string)`, `RolePolicyForUser(userID string)`, and `CheckRoleAction(userID, actionID string)` returning versioned, non-secret snapshots.
|
||||
- Consumes the actual request actor from the trusted Host; caller-supplied actor fields never select authority.
|
||||
|
||||
- [ ] **Step 1: Add provider contract tests on both sides**
|
||||
|
||||
Backend tests verify stable user IDs, enabled state, role version, action default/cap, authority epoch, and rejection when the Host has no authenticated actor. RDM tests verify provider missing, wrong version, disabled role, and unknown action all deny without consulting the legacy tables.
|
||||
|
||||
- [ ] **Step 2: Run the contract tests red**
|
||||
|
||||
Run Backend: `GOWORK=off go test ./internal/app -run RoleProvider -count=1`
|
||||
Run RDM: `GOWORK=off go test ./internal/rdmapp -run RoleProvider -count=1`
|
||||
Expected: FAIL with missing public provider.
|
||||
|
||||
- [ ] **Step 3: Implement the public interface and RDM adapter**
|
||||
|
||||
The provider returns only role and permission metadata needed for authorization. It must not return user contact fields. RDM's evaluator must use the provider whenever a role binding exists; absence is a denial after migration readiness is enabled.
|
||||
|
||||
- [ ] **Step 4: Run both component suites**
|
||||
|
||||
Run Backend: `GOWORK=off go test ./... -count=1`
|
||||
Run RDM with the reviewed Backend module overlay during development: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS; overlay evidence remains SELF until a trusted Backend tag is published.
|
||||
|
||||
- [ ] **Step 5: Commit each repository separately**
|
||||
|
||||
Backend commit: `Expose the role policy provider`.
|
||||
RDM commit: `Consume the Backend role authority`.
|
||||
|
||||
### Task 5: RDM migration adapter and creator-only project management
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-RDM/internal/perms/role_migration.go`
|
||||
- Modify: `Agent-System-RDM/internal/perms/roles.go`
|
||||
- Modify: `Agent-System-RDM/internal/rdmapp/perms_api.go`
|
||||
- Modify: `Agent-System-RDM/plugins/rdm/plugin.go`
|
||||
- Test: `Agent-System-RDM/internal/perms/role_migration_test.go`
|
||||
- Test: `Agent-System-RDM/internal/rdmapp/project_creator_permissions_test.go`
|
||||
- Test: `Agent-System-RDM/plugins/rdm/roles_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Task 4 provider and Backend import endpoint/command.
|
||||
- Produces a versioned export manifest for existing RDM roles and a compatibility forwarding layer for `rdm.role.*` writes.
|
||||
|
||||
- [ ] **Step 1: Write migration and cross-project denial tests**
|
||||
|
||||
```go
|
||||
func TestCreatorCannotManageAnotherCreatorsProject(t *testing.T) {
|
||||
projectA := createProjectAs(t, "alice")
|
||||
projectB := createProjectAs(t, "bob")
|
||||
requireAllowed(t, setProjectPermission("alice", projectA, "carol", ActDocRead, "allow"))
|
||||
requireDenied(t, setProjectPermission("alice", projectB, "carol", ActDocRead, "allow"))
|
||||
}
|
||||
```
|
||||
|
||||
Also assert role IDs, descriptions, bindings, defaults, caps, disabled state, and counts survive export/import; once provider readiness is set, legacy local writes and fallback reads are rejected.
|
||||
|
||||
- [ ] **Step 2: Implement export and forwarding without production migration**
|
||||
|
||||
Add dry-run export and compare functions. Forward `rdm.role.create/action/assign/disable/list` to Backend through the trusted Host. Keep old tables read-only for verification; do not delete them.
|
||||
|
||||
- [ ] **Step 3: Tighten project management to immutable creator ownership**
|
||||
|
||||
For member, permission, and duty configuration, load the actual project and compare its immutable `created_by` to the authenticated actor before applying role and platform caps. Do not add delegated project-manager behavior.
|
||||
|
||||
- [ ] **Step 4: Run RDM tests**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/perms ./internal/rdmapp ./plugins/rdm -count=1`
|
||||
Run: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 5: Commit the RDM consumer**
|
||||
|
||||
```bash
|
||||
git add internal/perms/role_migration.go internal/perms/roles.go internal/rdmapp/perms_api.go plugins/rdm/plugin.go internal/perms/role_migration_test.go internal/rdmapp/project_creator_permissions_test.go plugins/rdm/roles_test.go
|
||||
git commit -m "Move RDM role writes to the Backend authority"
|
||||
```
|
||||
|
||||
### Task 6: Frontend role API and shared draft model
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Frontend/src/api.ts`
|
||||
- Create: `Agent-System-Frontend/src/rolePermissions.ts`
|
||||
- Test: `Agent-System-Frontend/test/role-permissions.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Backend HTTP endpoints from Task 3.
|
||||
- Produces `RoleRecord`, `PermissionDescriptor`, `RolePermissionDraft`, `mergeRoleDraft`, `visiblePatch`, and typed API methods.
|
||||
|
||||
- [ ] **Step 1: Write failing draft-model tests**
|
||||
|
||||
Test that filtered rows retain edits, `visiblePatch` includes only explicit action IDs, single-role and matrix edits produce identical changes, stale server refresh does not replace a newer draft generation, and `allowed=true/capAllowed=false` is rejected before submit.
|
||||
|
||||
- [ ] **Step 2: Implement the typed API and pure draft helpers**
|
||||
|
||||
```ts
|
||||
export type RolePermissionValue = { allowed: boolean; capAllowed: boolean }
|
||||
export type RolePermissionDraft = Record<string, RolePermissionValue>
|
||||
export function visiblePatch(draft: RolePermissionDraft, visibleActionIds: string[]): RolePermissionChange[]
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run tests and build**
|
||||
|
||||
Run: `npx vitest run test/role-permissions.test.ts`
|
||||
Run: `npm run build`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Commit the client contract**
|
||||
|
||||
```bash
|
||||
git add src/api.ts src/rolePermissions.ts test/role-permissions.test.ts
|
||||
git commit -m "Add the role permission client model"
|
||||
```
|
||||
|
||||
### Task 7: Single-role and matrix configuration UI
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Frontend/src/components/RolePermissionsPanel.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/components/AdminZone.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/components/AccountUsersPanel.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/styles.css`
|
||||
- Test: `Agent-System-Frontend/test/role-permissions-ui.test.tsx`
|
||||
- Test: `Agent-System-Frontend/test/design-guard.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Task 6 API and draft helpers plus existing `@ui` components and tokens.
|
||||
- Produces the “角色与权限” system-management section and one-role assignment in user details.
|
||||
|
||||
- [ ] **Step 1: Re-read Dev `DESIGN.md` and add failing interaction tests**
|
||||
|
||||
Cover exact/fuzzy role search, state filtering, description edit, binding count, single-role edit, matrix edit, grouped paging, hidden-row draft retention, diff preview, conflict preservation, one user role replacement, system-grant editing with final-manager protection, and a late response discarded after switching roles.
|
||||
|
||||
- [ ] **Step 2: Implement the panel using existing design tokens**
|
||||
|
||||
Use `Collapsible` from `@ui`; consume `@ui/tokens.css` and `@ui/motion.css`; do not add raw colors, private keyframes, `<details>`, or inline font sizes. Keep one request sequence owner for list/detail/preview/commit results.
|
||||
|
||||
- [ ] **Step 3: Replace the RDM role admin entry**
|
||||
|
||||
Change the section label from “RDM 角色权限” to “角色与权限” and bind it to Backend APIs. Keep RDM project permission and document-duty components in project settings.
|
||||
|
||||
- [ ] **Step 4: Add the user role selector**
|
||||
|
||||
Display the current role and explicit system grants in account details. Allow role replacement only when the server advertises `system.roles.assign`; submit stable `userId`, `roleId`, expected state version, and idempotency key. Allow system-grant editing only with `system.permissions.manage`, using the server epoch and fixed action IDs.
|
||||
|
||||
- [ ] **Step 5: Run Frontend and design checks**
|
||||
|
||||
Run: `npm test`
|
||||
Run: `npm run build`
|
||||
Run paired UI: `npm test` in `Agent-System-UI`.
|
||||
Expected: all pass with no design-guard regression.
|
||||
|
||||
- [ ] **Step 6: Commit the UI**
|
||||
|
||||
```bash
|
||||
git add src/components/RolePermissionsPanel.tsx src/components/AdminZone.tsx src/components/AccountUsersPanel.tsx src/styles.css test/role-permissions-ui.test.tsx
|
||||
git commit -m "Add role and permission configuration views"
|
||||
```
|
||||
|
||||
### Task 8: Fixed-combination verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Dev/modules/backend`
|
||||
- Modify: `Agent-System-Dev/modules/rdm`
|
||||
- Modify: `Agent-System-Dev/modules/frontend`
|
||||
- Modify: `Agent-System-Dev/modules/ui`
|
||||
- Test evidence: `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes reviewed component commits and trusted published tags where required.
|
||||
- Produces one fixed Dev combination and requirement-to-evidence map.
|
||||
|
||||
- [ ] **Step 1: Run component verification on exact commits**
|
||||
|
||||
Backend: full SQLite, isolated PostgreSQL, migration rollback/retry, build.
|
||||
RDM: full tests/build against the reviewed Backend public version.
|
||||
Frontend/UI: full tests/build and design guards.
|
||||
|
||||
- [ ] **Step 2: Run a real browser scenario on an isolated database**
|
||||
|
||||
Create two ordinary users, one “软件工程师” role with a description, assign both users, replace one user's role, configure `project.create`, create projects A and B under different creators, and verify A's creator cannot manage B. Verify an unassigned system administrator cannot read either project's business content. Exercise exact/fuzzy search, filters, single-role edit, matrix edit, preview, conflict, and draft retention.
|
||||
|
||||
- [ ] **Step 3: Run the Dev combination**
|
||||
|
||||
Run: `make test`
|
||||
Expected: PASS on the exact component SHAs recorded by `make evidence`.
|
||||
|
||||
- [ ] **Step 4: Record remaining boundaries**
|
||||
|
||||
Mark production migration, deployment, account recovery, multi-role, Agent permissions, and workflow changes `NOT_RUN / out of scope`. Do not claim them from component tests.
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: 聚焦版角色与分层权限系统规格
|
||||
date: 2026-10-15
|
||||
type: feature
|
||||
status: approved
|
||||
approved_at: 2026-10-15
|
||||
supersedes_scope: 2026-10-13-identity-role-permission-plan.md U4-U7 中超出本文的部分
|
||||
---
|
||||
|
||||
# 聚焦版角色与分层权限系统规格
|
||||
|
||||
## 目标
|
||||
|
||||
实现用户要求的角色与权限系统,不引入账号恢复、多职位、Agent 授权或新的工作流能力。
|
||||
|
||||
首期角色等同企业职位。角色与用户分离:同一角色可分配给多个用户,每个用户同时只有一个生效角色。角色包含稳定 `roleId`、名称、可选描述、启用状态和版本。用户继续使用稳定 `userId`,姓名、电话、邮箱、工号、部门和备注均为可选资料,不参与授权主键。
|
||||
|
||||
权限分为三个配置面,但使用同一服务端动作目录和判定结果:
|
||||
|
||||
1. 系统权限控制系统设置、账号管理、角色管理、权限上限和项目创建。
|
||||
2. 角色权限描述一个职位默认可以执行的动作及其上限,例如创建项目、查看获准目录信息。
|
||||
3. 项目权限只在一个项目内生效,由该项目创建者维护;A 项目的管理权不能用于 B 项目。
|
||||
|
||||
系统管理员负责账号、角色和平台权限上限。系统管理员没有他人项目业务内容的隐式访问权,读取项目正文、原件、草稿、业务审计或导出仍需该项目的明确授权。
|
||||
|
||||
## 范围
|
||||
|
||||
### 角色与用户
|
||||
|
||||
- `Role`:`roleId`、名称、描述、状态、`roleVersion`、创建者、修改者和时间。
|
||||
- `UserRoleBinding`:`bindingId`、`userId`、`roleId`、状态、版本、任命者和任命时间。
|
||||
- 一个用户最多一条有效 binding;同一角色允许任意多个有效用户 binding。
|
||||
- 改名、编辑描述和停用角色不改变用户、项目或历史记录的稳定 ID。
|
||||
- 停用角色时保留 binding 和审计记录,但其角色授权立即失效;不回落到更宽的默认权限。
|
||||
- 任命停用角色、未知角色或停用账号均拒绝。
|
||||
|
||||
### 权限目录与三层授权
|
||||
|
||||
动作目录由受信注册表定义 `actionId`、中文名、说明、scope、分组、风险级别和是否受保护。请求不能通过动作字符串或请求体自行声明 scope。
|
||||
|
||||
首期系统动作至少包括:
|
||||
|
||||
| 分组 | 动作 |
|
||||
|---|---|
|
||||
| 系统设置 | `system.settings.read`、`system.settings.write` |
|
||||
| 用户管理 | `system.users.view`、`system.users.create`、`system.users.edit`、`system.users.disable` |
|
||||
| 角色管理 | `system.roles.view`、`system.roles.manage`、`system.roles.assign` |
|
||||
| 权限管理 | `system.permissions.view`、`system.permissions.manage` |
|
||||
| 项目入口 | `project.create` |
|
||||
|
||||
受保护的角色、权限和管理员管理动作只能来自明确系统授权,普通角色配置不能授予。`project.create` 可以来自明确系统授权或当前有效角色的允许项,但仍受平台上限约束。
|
||||
|
||||
项目动作至少包括 `project.settings`、`project.members.manage`、`project.permissions.manage`、`document.duties.assign`、`document.read`、`document.author`、`document.review`、`document.approve`。项目动作必须同时满足:账号有效、项目关系有效、当前角色有效、角色动作允许且未超过角色上限、平台未禁止、项目未明确拒绝、资源关系和文档状态允许。
|
||||
|
||||
项目创建者只获得自己项目的管理默认,且仍受平台和角色上限约束。创建者关系不授予其他项目权限,也不自动授予项目业务内容读取或代签能力。
|
||||
|
||||
### 角色权限
|
||||
|
||||
每个角色的动作配置包含 `allowed` 与 `capAllowed`:
|
||||
|
||||
- `allowed=true` 表示该角色默认允许动作。
|
||||
- `capAllowed=false` 表示该角色不能通过项目覆盖获得该动作。
|
||||
- `allowed=true && capAllowed=false` 是非法配置,服务端拒绝。
|
||||
- 未知动作和未配置动作默认拒绝。
|
||||
- 角色默认和上限不得写入超出操作者可委派范围的受保护动作。
|
||||
|
||||
首期一个用户只有一个有效角色,因此一次判定只使用该 binding,不进行跨角色动作或范围拼接。
|
||||
|
||||
### 项目权限和文档三权
|
||||
|
||||
RDM 保留项目成员、项目创建者、项目内覆盖和现有文档三权的领域权威。Backend 负责角色、binding、系统动作和平台/角色上限。
|
||||
|
||||
项目覆盖使用 `inherit | allow | deny`:`allow` 不能突破平台或角色上限,`deny` 明确阻断。项目创建者只能修改自己创建项目的成员、权限和文档三权。
|
||||
|
||||
文档编制、审核、批准继续使用现有 RDM 三权记录;分配候选必须是该项目有效成员,账号和角色有效,并具有对应动作上限。角色只提供资格,不能代替具体文档职责。
|
||||
|
||||
## 服务端接口
|
||||
|
||||
Backend 成为角色和系统权限的唯一写入权威,提供版本化接口:
|
||||
|
||||
- `GET /api/admin/permissions/catalog`
|
||||
- `GET /api/admin/roles`,支持名称精确/模糊搜索、状态和动作过滤、分页
|
||||
- `POST /api/admin/roles`
|
||||
- `GET /api/admin/roles/{roleId}`
|
||||
- `PATCH /api/admin/roles/{roleId}`,使用 `expectedRoleVersion`
|
||||
- `PUT /api/admin/roles/{roleId}/permissions`,批量提交固定 actionId 列表和 `expectedRoleVersion`
|
||||
- `PUT /api/admin/users/{userId}/role`,替换该用户唯一有效角色,使用用户状态版本和幂等键
|
||||
- `GET /api/admin/users/{userId}/role`
|
||||
- `GET /api/admin/users/{userId}/system-grants`
|
||||
- `PUT /api/admin/users/{userId}/system-grants`,批量提交明确系统动作并使用 `expectedAuthorityEpoch` 和幂等键
|
||||
- `GET /api/authz/explain`,仅解释调用者自己或其有权管理的对象
|
||||
|
||||
批量权限修改在单库事务内全部成功或全部失败;任一未知动作、越权、越上限或版本冲突不得部分写入。响应返回新版本、实际差异和审计回执标识。
|
||||
|
||||
RDM 通过公开、固定版本的 Backend 角色读取/判定契约消费角色数据。RDM 原 `rdm.role.*` 写命令在迁移期转发 Backend;切换完成后不得继续写 RDM 旧角色表。读取失败、提供者缺失或版本不兼容时拒绝权限动作。
|
||||
|
||||
## 配置界面
|
||||
|
||||
系统管理区增加“角色与权限”入口:
|
||||
|
||||
- 角色列表显示名称、描述、状态和绑定人数,支持精确搜索、模糊搜索和状态过滤。
|
||||
- 单角色视图编辑名称、描述、状态及权限。
|
||||
- 角色矩阵以角色为行、权限为列,按权限分组和分页展示。
|
||||
- 单角色视图与矩阵使用同一草稿模型和同一批量接口。
|
||||
- 过滤隐藏的单元格不丢失草稿;全选只作用于当前明确显示的固定 actionId 集合。
|
||||
- 提交前显示差异;冲突保留草稿并要求刷新,不逐格即时写库。
|
||||
|
||||
用户详情显示当前角色和明确系统授权来源,并允许有 `system.roles.assign` 权限的操作者从有效角色中选择。有 `system.permissions.manage` 权限的操作者可以编辑目标用户的系统授权,但不能撤销最后一个有效权限管理者。用户可选资料继续用于展示、搜索和辨识,不参与权限计算。
|
||||
|
||||
项目设置继续使用现有成员和权限入口,显示角色默认、角色上限、项目覆盖及最终允许/拒绝原因。项目创建者无法切换到其他项目后沿用原项目管理权。
|
||||
|
||||
## 迁移与兼容
|
||||
|
||||
1. 导出 RDM 当前角色、动作配置和账号单角色绑定,包含来源版本与摘要。
|
||||
2. 以原 `roleId` 导入 Backend;为每个账号生成一个稳定 binding。
|
||||
3. 对角色数量、绑定数量、动作默认/上限和引用逐项比对。
|
||||
4. 在迁移窗口内禁止双主写;RDM 写入口改为转发 Backend。
|
||||
5. 切换消费者后保留旧表只读,确认无回退依赖后再另行批准清理。
|
||||
|
||||
迁移失败不得启用部分角色数据;恢复时保留已撤销、停用和拒绝事实,不能复活旧权限。
|
||||
|
||||
## 验收条件
|
||||
|
||||
- 创建“软件工程师”角色并分配给两个用户,两人保留各自稳定 userId。
|
||||
- 同一用户第二次任命替换原生效角色,不产生两个同时有效的 binding。
|
||||
- 角色描述可编辑,用户可选资料全空仍合法。
|
||||
- 未知动作、默认超过上限、停用角色任命、无授权角色管理和最后系统权限管理者撤销均拒绝。
|
||||
- 普通角色可被授予 `project.create`,但不能因此修改系统设置、用户、角色或权限上限。
|
||||
- A 项目创建者管理 A 的成员、项目权限和文档三权成功;操作 B 全部拒绝。
|
||||
- 系统管理员未获项目授权时不能读取 A 或 B 的业务内容。
|
||||
- 单角色配置和角色矩阵产生等价的固定 actionId 批量 patch;过滤和分页不丢草稿。
|
||||
- SQLite 与 PostgreSQL 的版本冲突、事务回滚和迁移结果一致。
|
||||
|
||||
## 明确排除
|
||||
|
||||
本增量不实现一人多职、账号恢复、SSO/组织树、Agent 权限、工作流重建、新的文档审签流程、生产迁移或部署。现有文档三权只接入新的角色资格和项目权限判定。
|
||||
@@ -0,0 +1,368 @@
|
||||
---
|
||||
title: IAM U4 Stage 7-10 Delivery - Plan
|
||||
type: feat
|
||||
date: 2026-10-15
|
||||
artifact_contract: ce-unified-plan/v1
|
||||
product_contract_source: specialized-project-spec
|
||||
execution: code
|
||||
origin: docs/proposals/2026-10-15-iam-focused-role-permission-spec.md
|
||||
---
|
||||
|
||||
# IAM U4 Stage 7-10 Delivery - Plan
|
||||
|
||||
## Goal Capsule
|
||||
|
||||
- **Objective:** The approved IAM U4 role and permission behavior is accepted in a real browser, merged through each protected repository at reviewed commits, and closed with traceable evidence and clean workspaces.
|
||||
- **Means:** Use the repository's existing Playwright stack against an isolated RDM product composition, then deliver Backend, Frontend, Documents, RDM, and Dev in dependency order with exact-head review and CI. (KTD1–KTD5)
|
||||
- **Authority:** The focused specification approved at Documents `e9ce9a5` owns behavior; this plan owns delivery mechanics; `Agent-System-Dev/policy/delivery.md` and `policy/merge-queue.md` own stage and merge gates; the task card owns current evidence.
|
||||
- **Execution profile:** The agent prepares and performs testing, review, PRs, conflict resolution, CI checks, merges, and cleanup. The user retains stage 7 and stage 10 decisions and personally publishes any trusted tag.
|
||||
- **Stop conditions:** Stop on a changed requirement, cross-project access success, stale or mismatched candidate SHA, unresolved browser or CI failure, a conflicting merge window, a mismatched existing tag, or any request to touch production data or deployment.
|
||||
|
||||
---
|
||||
|
||||
## Product Contract
|
||||
|
||||
### Summary
|
||||
|
||||
Stage 7 proves the implemented role model and permission boundaries through the real React interface and live Backend/RDM authorization path.
|
||||
Stages 8–10 then carry the same behavior through component PRs, a trusted Backend release tag, RDM pins, the Dev fixed combination, protected-branch CI, and safe task closure.
|
||||
|
||||
### Problem Frame
|
||||
|
||||
The implementation candidate has passed SQLite, isolated PostgreSQL, RDM overlay, Frontend/UI, and Dev combination checks, but those checks do not prove browser behavior or protected-repository delivery.
|
||||
The repositories also move independently: RDM consumes a published Backend extension contract, while Dev pins Backend, Frontend, UI, and Documents but does not pin RDM.
|
||||
Delivery must preserve those dependency and evidence boundaries without turning production migration or deployment into an implied part of acceptance.
|
||||
|
||||
### Requirements
|
||||
|
||||
**Candidate identity and formal acceptance**
|
||||
|
||||
- DR1. Stage 7 starts only after each task branch is synchronized with its current `origin/main` and the tested component commits are recorded together.
|
||||
- DR2. Formal browser acceptance runs on a fresh isolated data root and random loopback ports, never on a registered or production service.
|
||||
- DR3. Browser evidence uses the existing Frontend Playwright stack against the exact Backend, RDM, Frontend, and UI sources under review.
|
||||
- DR4. Every acceptance result records the command, component SHAs, environment shape, exit code, sanitized screenshots or traces where applicable, and evidence source. Browser credentials are synthetic; artifacts use owner-restricted external storage and expire after stage 10.
|
||||
- DR5. Stage 7 requires user review of the requirement-to-evidence map before any component merge begins.
|
||||
|
||||
**Role, user, and permission behavior**
|
||||
|
||||
- DR6. The browser creates and edits a role with a description while its stable `roleId` remains unchanged. Before disabling a role, the UI names it, shows the bound-user count and authorization impact, supports cancellation without a request, and sends one versioned request only after confirmation; success preserves a usable selection, while conflict or failure preserves context for retry.
|
||||
- DR7. Two users can share one role, one user can be moved to another role, and the system never exposes two active roles for one user.
|
||||
- DR8. Name, phone, email, staff number, department, and notes remain optional and searchable without becoming authorization identifiers.
|
||||
- DR9. Exact and fuzzy role search, status and action filters, pagination, single-role editing, matrix default/cap editing, visible-page batch changes, preview, and 409 rebasing work through the rendered UI. Drafts are keyed by `roleId`: permission grouping/paging, role filters, role paging, and single/matrix switching preserve them; switching roles never transfers a draft; leaving the administration area or refreshing requires an explicit save/discard decision; a 409 rebases only the same role's draft.
|
||||
- DR10. Delegated system actions expose only their permitted user, role, or permission controls; a manager without view authority cannot replace data with an empty set. Loading, stale, empty, partial-failure, retry, and success states are distinct; destructive controls remain unavailable without authoritative data. The core flow works by keyboard with named controls, announced status changes, stable focus after save/conflict/error, and a usable narrow viewport including matrix navigation.
|
||||
- DR11. `project.create` works only through an explicit system grant or an enabled role default within its cap.
|
||||
- DR12. Project A's creator can manage A's members, overrides, and document duties, but A's creator relation and an untrusted owner label never authorize project B. A separate valid B-side membership, current role grant, cap, and project override continue to authorize only the B actions they explicitly allow.
|
||||
- DR13. System administrator identity alone cannot read another user's project business content, private conversations, originals, drafts, controlled document bodies, business audit records, or exports, and cannot create a business-content export artifact.
|
||||
- DR14. Document author, reviewer, and approver assignments require current account and role eligibility, and approval remains separate from document publication.
|
||||
- DR15. Missing providers, incompatible schemas, stale authority epochs, disabled accounts, disabled roles, unknown actions, and out-of-cap overrides fail closed. Version-conflict, unknown-action, and out-of-cap batch updates perform zero partial writes, with the same resulting state and migration outcome on SQLite and PostgreSQL.
|
||||
|
||||
**Protected delivery**
|
||||
|
||||
- DR16. Component branches merge in dependency order; a downstream pin never points to an unmerged commit or unpublished required tag.
|
||||
- DR17. Backend's trusted tag is created only by the user after the reviewed Backend PR merge commit is verified on `origin/main`; an existing tag is never rewritten.
|
||||
- DR18. RDM updates its pins only after Backend and Frontend commits are merged, then builds the product from committed Git objects or a strictly clean tracked/untracked/ignored source tree, records digests of the copied content as well as Git SHAs, and replays affected acceptance. Dirty or untracked source must fail before packaging.
|
||||
- DR19. Dev enters the manual merge queue only after all component dependencies are merged and the final gitlinks point to their authoritative commits.
|
||||
- DR20. Dev merge requires exact-head `test`, `test-pg`, and `svc-realcombo` GATE success, current Review, no conflicting main advance, and an explicit queue claim.
|
||||
- DR21. Task closure records the actual merge commits and retains every failed, skipped, and not-run result at its real evidence level. Any source, base, pin, candidate manifest, or test-definition change after stage 7 updates the manifest, replays affected evidence, and requires a renewed stage 7 conclusion before the first component merge.
|
||||
|
||||
### Actors
|
||||
|
||||
- A1. **System administrator:** manages platform accounts, roles, explicit system grants, and platform permission ceilings.
|
||||
- A2. **Delegated manager:** holds a selected subset of system actions and sees only the matching administration controls.
|
||||
- A3. **Project creator:** manages only the project identified by immutable creation metadata.
|
||||
- A4. **Project member:** receives project behavior from the current professional role, project override, resource relation, lifecycle state, and document duty.
|
||||
- A5. **Delivery agent:** executes tests, review, PRs, CI checks, merges, and cleanup within the approved scope.
|
||||
- A6. **User reviewer:** approves stage 7 evidence, personally publishes a trusted tag, and approves stage 10 cleanup.
|
||||
|
||||
### Key Decisions
|
||||
|
||||
- **One effective role remains the product model.** The delivery does not introduce multi-role assignment. Governs DR7.
|
||||
- **System administration does not grant business-content access.** Project and resource authorization remain explicit. Governs DR12–DR15.
|
||||
- **Production migration and deployment remain outside this delivery.** The migration machinery is tested only on isolated databases. Governs DR2, DR21.
|
||||
|
||||
### Key Flows
|
||||
|
||||
- F1. **Formal acceptance**
|
||||
- **Trigger:** The synchronized component candidate is fixed.
|
||||
- **Actors:** A1–A5.
|
||||
- **Steps:** Build the isolated composition; seed accounts and projects; drive role, user, system grant, project, and document flows in Chromium; capture traces and API decisions; replay SQLite, PostgreSQL, and component suites.
|
||||
- **Outcome:** DR6–DR15 have exact-candidate evidence or stage 7 stops.
|
||||
- **Covers:** DR1–DR15.
|
||||
|
||||
- F2. **Serial protected delivery**
|
||||
- **Trigger:** The user approves stage 7.
|
||||
- **Actors:** A5, A6, Gitea.
|
||||
- **Steps:** Merge Backend; publish and verify its tag; merge Frontend and Documents; update and merge RDM pins; fix the Dev gitlinks; claim the Dev queue window; pass exact-head CI; merge and verify; finish registered worktrees; close the task card through its own reviewed PR.
|
||||
- **Outcome:** Every protected branch contains the reviewed behavior and the task record identifies the actual commits.
|
||||
- **Covers:** DR16–DR21.
|
||||
|
||||
### Acceptance Examples
|
||||
|
||||
- AE1. **Shared position:** Given two enabled users and one enabled software-engineer role, assigning both users succeeds; replacing one user's role leaves one active binding for each user and the original role still has one member. Covers DR7.
|
||||
- AE2. **Fine-grained manager:** Given `system.users.view` without role or permission view, the manager opens user details without a failing role request and cannot edit roles or grants. Covers DR10.
|
||||
- AE3. **Draft conflict:** Given a local matrix draft and a concurrent role update, saving returns 409, reloads the latest role version, replays the local draft against it, and never applies role A's draft to role B. Covers DR9.
|
||||
- AE4. **Creator isolation:** Given projects A and B with different creators, A's creator can change A's members, overrides, and author/reviewer/approver duties. With no valid B-side management grant, the same actor is denied for all three mutation families in B and B remains unchanged, even if the actor is a B member or carries an untrusted owner label. A separate fixture proves that a valid B-side grant authorizes only its declared B actions. Covers DR12, DR14.
|
||||
- AE5. **Administrator privacy:** Given an administrator with no project content grant, direct authenticated requests to B's private conversations, originals, drafts, controlled bodies, business-audit list/detail, and export create/status/download routes are denied with no payload or export artifact. A separately authorized project actor supplies positive controls for the same routes. Covers DR13.
|
||||
- AE6. **Fail-closed refresh:** Given an allowed cached role snapshot, tightening the role cap changes the authority epoch; the next project or knowledge read consumes the current Backend snapshot and denies the old permission. Covers DR15.
|
||||
- AE7. **Migration rehearsal:** Given a provenance-recorded frozen v35 fixture and oracle covering active and disabled roles, inactive bindings, revoked grants, explicit denies, owner mappings, and orphan/reference failures, a trusted non-production harness invokes the real Backend/RDM stores to freeze legacy writes, stage stable IDs and full owner-to-role mappings, compare source hashes and expected results, and activate by epoch. Compare mismatch or a concurrent legacy write blocks activation; a lost success response can be retried without duplicate bindings. SQLite and PostgreSQL produce the same final state. Covers DR2, DR15, DR21.
|
||||
|
||||
### Scope Boundaries
|
||||
|
||||
Included:
|
||||
|
||||
- Repeatable UI and live-composition browser acceptance for the approved U4 requirements.
|
||||
- Test-only adjustments needed to make that acceptance deterministic and reviewable.
|
||||
- Component sync, PRs, CI, Backend tag handoff, RDM pin updates, Dev queue delivery, and task cleanup.
|
||||
|
||||
Excluded:
|
||||
|
||||
- Production schema migration, legacy-role cutover, deployment, or 8788 service changes.
|
||||
- Account recovery, password flows beyond isolated fixture setup, multi-role support, Agent permissions, organization trees, SSO, and workflow redesign.
|
||||
- Branch-protection changes, tag rewriting, runner changes, or merge-queue automation.
|
||||
|
||||
---
|
||||
|
||||
## Planning Contract
|
||||
|
||||
### Key Technical Decisions
|
||||
|
||||
- KTD1. **Use repository-native Playwright as the formal browser gate.** `Agent-System-Frontend/playwright.config.ts` and the existing `e2e/` suite already define the project's real-browser contract. `agent-browser` may add an exploratory visible smoke when installed, but its absence does not replace or invalidate the repeatable Playwright run.
|
||||
- KTD2. **Run two browser layers.** Deterministic routed tests cover UI permission combinations, pagination, draft retention, and response ordering; a live isolated RDM product covers authentication, storage, Backend policy, RDM project boundaries, and embedded Frontend behavior.
|
||||
- KTD3. **Freeze evidence by component SHA and environment.** A branch sync, pin change, test-harness change, or target-main advance invalidates only the affected results; no receipt is copied to a different object.
|
||||
- KTD4. **Deliver dependencies serially.** Backend merges and receives a trusted tag before RDM updates its Backend pin. Frontend and Documents merge before RDM fixes its product pins. Dev pins only merged component commits and enters its final queue window last.
|
||||
- KTD5. **Keep high-consequence operations dual-keyed.** The agent prepares and verifies the proposed Backend tag command; the user executes tag publication. Production migration and deployment have no execution unit in this plan.
|
||||
- KTD6. **Treat missing component CI honestly.** Backend, RDM, and Dev consume their repository workflows. Frontend and Documents use their repository verification plus downstream Dev GATE when no repository-local workflow exists; they are not labeled GATE without a real platform run.
|
||||
|
||||
### High-Level Technical Design
|
||||
|
||||
The diagrams are delivery guidance rather than implementation specifications.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
BE[Backend reviewed commit] --> BET[User-published Backend tag]
|
||||
BET --> RP[RDM Backend pin]
|
||||
FE[Frontend reviewed commit] --> RP
|
||||
UI[UI commit already on main] --> RP
|
||||
DOC[Documents plan/spec commit] --> DEV[Dev final gitlinks]
|
||||
BE --> DEV
|
||||
FE --> DEV
|
||||
UI --> DEV
|
||||
RP --> RDM[RDM protected main]
|
||||
DEV --> DCI[Dev test + test-pg + svc-realcombo]
|
||||
DCI --> DM[Dev protected main]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Agent as Delivery agent
|
||||
participant Browser as Playwright Chromium
|
||||
participant Product as Isolated RDM product
|
||||
participant User as User reviewer
|
||||
participant Gitea as Protected repositories
|
||||
Agent->>Product: Build exact component composition
|
||||
Agent->>Browser: Run deterministic and live U4 scenarios
|
||||
Browser->>Product: Role, user, grant, project, and duty requests
|
||||
Product-->>Browser: Current policy decisions and rendered results
|
||||
Agent->>User: Stage 7 requirement-to-evidence map
|
||||
User-->>Agent: Stage 7 conclusion
|
||||
Agent->>Gitea: Review, CI, and serial component PR merges
|
||||
Agent->>User: Exact Backend merge commit and tag command
|
||||
User-->>Gitea: Publish trusted tag
|
||||
Agent->>Gitea: RDM pins, Dev queue CI, merge, and verification
|
||||
```
|
||||
|
||||
### Sequencing
|
||||
|
||||
1. Fix and accept the synchronized stage 7 candidate.
|
||||
2. Complete stage 7 browser and database evidence and obtain the user's conclusion.
|
||||
3. Re-review any files changed by acceptance work.
|
||||
4. Merge Backend, then complete the user-owned tag gate.
|
||||
5. Merge Frontend and Documents.
|
||||
6. Sync RDM, update pins, replay the product and browser gates, then merge RDM.
|
||||
7. Fix Dev gitlinks, claim the manual queue window, run exact-head CI, and merge Dev.
|
||||
8. Verify merged content, preserve receipts, clean registered worktrees, and then close the task card through its own reviewed PR after stage 10 approval.
|
||||
|
||||
### Risks and Dependencies
|
||||
|
||||
- Backend, RDM, Documents, and Dev main may advance before their windows. Each affected branch must sync once at its delivery boundary and rerun affected evidence.
|
||||
- RDM currently trails its authoritative main substantially. Conflict resolution must preserve upstream behavior and may invalidate the pre-sync RDM review.
|
||||
- Browser execution requires the repository Playwright runtime and Chromium. If the runtime cannot launch, stage 7 stops with the launch error; another browser stack is not substituted.
|
||||
- The recommended Backend version is `v0.6.0` because U4 adds public surface 2.15 and schema v36. If that remote tag exists or the release convention has changed, stop for a new user-selected version and never rewrite the ref.
|
||||
- Gitea API or Actions may fail transiently. A retry is allowed only after reading the actual PR/run state; unknown results are reconciled before resubmission.
|
||||
- The current Dev merge queue has no active window after the recorded release of the workbench visual task. This fact must be re-read immediately before U8 claims a new window.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Units
|
||||
|
||||
### U1. Record approval and fix the stage 7 candidate
|
||||
|
||||
- **Goal:** Record the user's stage 6 approval and produce one synchronized candidate manifest for formal acceptance.
|
||||
- **Requirements:** DR1, DR4, DR5.
|
||||
- **Files:** `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`, `Agent-System-Documents/docs/proposals/2026-10-15-iam-u4-stage7-10-delivery-plan.md`, `Agent-System-Documents/docs/proposals/INDEX.md`.
|
||||
- **Approach:** Update the stage table with the approved component versions and date. Run the required task sync in each branch before its evidence is treated as current. Record base, head, component commits, test definitions, and browser-harness commit in one candidate manifest.
|
||||
- **Test scenarios:** A clean same-base sync leaves the candidate unchanged; a moving target updates the manifest and invalidates affected evidence; a dirty or drifting component stops the unit.
|
||||
- **Verification:** Dev precheck/task-resume receipt, `make task-sync` evidence, recursive worktree verification, and Documents `git diff --check`.
|
||||
- **Dependencies:** None.
|
||||
|
||||
### U2. Add the repeatable IAM browser acceptance slice
|
||||
|
||||
- **Goal:** Make DR6–DR15 reproducible in the existing browser-test stack without production services.
|
||||
- **Requirements:** DR2–DR4, DR6–DR15.
|
||||
- **Files:** `Agent-System-Frontend/e2e/iam-role-authority.playwright.ts`, `Agent-System-RDM/scripts/iam-u4-role-acceptance.sh`, and only the smallest supporting test fixtures required by those files.
|
||||
- **Approach:** Add deterministic route-driven cases for fine-grained navigation, filters, matrix edits, role-scoped draft lifetime, 409 rebasing, pagination, late-response rejection, role-disable impact confirmation, and independently loading role/permission data. Cover keyboard operation, focus, accessible names and announcements, and a narrow viewport with horizontal matrix navigation. Add a live harness that materializes exact committed candidate sources into a temporary build root, builds the embedded RDM product, initializes a fresh SQLite database, starts on loopback random ports, and runs the same Playwright file in live mode. Synthetic credentials and activation material remain in the temporary root and are deleted on exit; traces and screenshots are sanitized before owner-restricted retention.
|
||||
- **Execution note:** Add failure-first cases for the previously reviewed permission combinations and race paths before changing the harness or UI assertions.
|
||||
- **Test scenarios:** Roles-only viewer; users-only viewer; permissions viewer without manager; permissions manager without view; shared-role assignment and reassignment; exact/fuzzy/action/status filters; multi-page role selection; matrix default/cap and visible-page batch edits; draft-preserving and explicit-discard transitions; concurrent 409 rebase; stale response after role switch; role-disable cancel/success/conflict/failure; initial loading; filtered and catalog-empty states; independent role-list and permission-catalog failure; failed create/save retry; keyboard-only core flow; narrow viewport; current epoch denial; isolated service startup failure and cleanup.
|
||||
- **Verification:** Focused Playwright run with trace retention, Frontend unit tests/build/design guard, harness shell check, and a clean-process check after success and failure.
|
||||
- **Dependencies:** U1.
|
||||
|
||||
### U3. Execute stage 7 formal acceptance
|
||||
|
||||
- **Goal:** Produce the requirement-to-evidence map the user can approve without reading implementation details.
|
||||
- **Requirements:** DR2–DR15.
|
||||
- **Files:** `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`; screenshots, traces, and receipts remain outside source and are linked by digest or durable run URL.
|
||||
- **Approach:** Run deterministic Playwright and the live isolated product. Replay Backend SQLite and isolated PostgreSQL role-policy tests, RDM full tests against the fixed Backend, Frontend/UI suites, and Dev combination tests. Exercise migration through an isolated cross-component harness that calls the real trusted Backend/RDM store path for freeze, stage, compare, activate, failure, and retry against the frozen v35 fixture and oracle. Record each DR-ID as PASS, FAIL, SKIP, or NOT_RUN against the exact candidate.
|
||||
- **Test scenarios:** AE1–AE7 plus missing provider, incompatible schema, disabled account/role, final permission-manager protection, project-override cap, document-duty cap, administrator audit/export denial, dirty product-source rejection, and SQLite/PostgreSQL batch failures for version conflict, unknown action, and out-of-cap change with zero partial writes.
|
||||
- **Verification:** Evidence receipts and browser artifacts match the candidate manifest; an independent replay covers the minimum critical set; the user records the stage 7 conclusion in the task card.
|
||||
- **Dependencies:** U2.
|
||||
|
||||
### U4. Re-review and prepare component PRs
|
||||
|
||||
- **Goal:** Turn the stage 7 candidate into current, reviewable component branches without changing accepted behavior.
|
||||
- **Requirements:** DR4, DR16, DR21.
|
||||
- **Files:** Component diffs and task records in Backend, Frontend, Documents, and RDM.
|
||||
- **Approach:** Sync each component with its current main at its own delivery boundary. Resolve conflicts without weakening authorization. If source, base, dependency pin, candidate manifest, or test definition changes, update the manifest, rerun the affected U2/U3 evidence, and obtain a renewed stage 7 conclusion before any component merge. Then simplify only changed code, conduct independent code review, push the branch, and create a draft PR with the final behavior and evidence.
|
||||
- **Test scenarios:** Clean sync; source conflict; changed public surface; changed Frontend API type; changed RDM provider contract; target advance after review.
|
||||
- **Verification:** Each PR identifies base/head, component dependencies, review findings and closure, and current applicable CI or explicit absence of repository-local CI.
|
||||
- **Dependencies:** U3 and stage 7 user approval.
|
||||
|
||||
### U5. Merge Backend and publish its trusted tag
|
||||
|
||||
- **Goal:** Establish the authoritative public role-policy contract RDM can consume.
|
||||
- **Requirements:** DR16, DR17.
|
||||
- **Files:** Backend PR and release tag; no production service files.
|
||||
- **Approach:** Confirm Backend PR HEAD contains current main, public surface 2.15, schema v36, staged migration, and the reviewed tests. Require the `vet-and-test` GATE on that exact HEAD, complete agent Review, merge without force, and verify the merge commit on `origin/main`. Prepare an annotated `v0.6.0` command bound to that merge commit; the user executes it. Read the remote peeled tag before proceeding.
|
||||
- **Test scenarios:** CI failure; main advance; merge conflict; tag absent; tag already exists at the same commit; tag exists at another commit; push result unknown.
|
||||
- **Verification:** PR merge URL, exact merge SHA, successful exact-head CI, remote annotated tag and peeled commit equality.
|
||||
- **Dependencies:** U4. The user-owned tag action is an internal completion gate after the Backend merge.
|
||||
|
||||
### U6. Merge Frontend and Documents
|
||||
|
||||
- **Goal:** Land the accepted administration UI and its authoritative records before RDM fixes product pins.
|
||||
- **Requirements:** DR3, DR6–DR10, DR16, DR21.
|
||||
- **Files:** Frontend PR, Documents PR, `Agent-System-Documents/docs/proposals/INDEX.md`.
|
||||
- **Approach:** Sync and revalidate Frontend, including the IAM Playwright slice, then merge the reviewed PR. Sync Documents, verify links and claims, and merge its PR. UI requires no U4 PR when `e295cae` remains on authoritative main.
|
||||
- **Test scenarios:** Frontend main changes affecting AdminZone, styles, API types, or test harness; Documents link drift; UI main no longer contains the expected token commit.
|
||||
- **Verification:** Frontend tests/build/Playwright and platform status, Documents `git diff --check`, merged commits on each main, and UI ancestry check.
|
||||
- **Dependencies:** U4; Backend may merge before or during this unit, but RDM pinning waits for both U5 and U6.
|
||||
|
||||
### U7. Update and merge the RDM composition
|
||||
|
||||
- **Goal:** Replace the development overlay with authoritative merged pins and prove the real product composition.
|
||||
- **Requirements:** DR12–DR18, DR21.
|
||||
- **Files:** `Agent-System-RDM/pins/agent-system-backend`, `pins/agent-system-frontend`, `pins/agent-system-ui`, public-contract references, product manifest, and RDM task evidence.
|
||||
- **Approach:** Sync the RDM branch with current main and resolve its known divergence. Pin Backend to the verified tagged merge commit, Frontend to its merged commit, and UI to current authoritative main. Remove reliance on the temporary `go.work` overlay. Harden product assembly so it materializes committed Git objects, or rejects any tracked, untracked, or relevant ignored drift before copying, and records copied-content digests in addition to Git SHAs. Run the full RDM suite, product build, product smoke, migration rehearsal, and live IAM Playwright acceptance; review the resulting pin diff and merge the RDM PR after `build-and-test` succeeds.
|
||||
- **Test scenarios:** Pin mismatch; tag/commit mismatch; tracked, untracked, or ignored source drift; copied-content digest mismatch; missing embedded assets; migration staging failure; source-hash mismatch; provider schema mismatch; browser regression after real pins.
|
||||
- **Verification:** `PRODUCT_MANIFEST.json` matches the three gitlinks, RDM HEAD, and actual copied-content digests; a dirty-source negative test fails before packaging; full local receipts and exact-head RDM CI pass; merged RDM main contains the reviewed pins.
|
||||
- **Dependencies:** U5, U6.
|
||||
|
||||
### U8. Deliver the final Dev combination through the manual queue
|
||||
|
||||
- **Goal:** Merge one Dev candidate that pins only authoritative component commits and carries current evidence.
|
||||
- **Requirements:** DR16, DR19, DR20, DR21.
|
||||
- **Files:** `Agent-System-Dev/modules/backend`, `modules/frontend`, `modules/ui`, `modules/documents`, task card, PR, and merge-queue issue #518.
|
||||
- **Approach:** Re-read #518 and claim the only Dev main window. Run `make task-sync`, update gitlinks to merged component commits, fix the candidate, and store final receipts outside source. Run local `make test` and `make build`, complete final independent Review, push the Dev branch, and require `test`, `test-pg`, and `svc-realcombo` on the exact PR HEAD. Recheck main/head immediately before a non-force PR merge, then read back main and the component tree.
|
||||
- **Test scenarios:** Active competing window; main advance after CI; component SHA drift; CI transient failure within retry budget; repeated failure; unknown merge response; merged main content mismatch.
|
||||
- **Verification:** Queue claim/release entries, current base/head, three successful GATE jobs, review receipt, PR merge URL, final main SHA, and exact `git ls-tree` component values.
|
||||
- **Dependencies:** U7 and merged component records from U5–U6.
|
||||
|
||||
### U9. Verify, close, and clean the task
|
||||
|
||||
- **Goal:** Leave authoritative records and no abandoned IAM workspaces or branches.
|
||||
- **Requirements:** DR21.
|
||||
- **Files:** Dev task card and archive entry; component task records where applicable.
|
||||
- **Approach:** Verify actual merged contents and component ancestry. Record browser, SQLite, PostgreSQL, component CI, Dev CI, tag, PR, and merge evidence with their real source levels, then request stage 10 approval. Preserve cleanup receipts outside the worktree; after all writers have exited, run `TASK_WRITERS_STOPPED=1 make task-finish` from the registered task worktree. Then run `make task-close` from clean main and deliver its card-close PR through the applicable queue, Review, CI, merge, and readback path. Never force-delete a refused cleanup.
|
||||
- **Test scenarios:** Uncommitted or untracked file; live writer; unmerged local or remote commit; missing task-card PR link; cleanup refusal; post-merge content mismatch.
|
||||
- **Verification:** Stage 10 conclusion, archived task card on main, clean recursive state, merged ancestry, removed task worktrees, and preserved external receipts.
|
||||
- **Dependencies:** U8. Stage 10 approval is an internal gate before cleanup.
|
||||
|
||||
---
|
||||
|
||||
## Verification Contract
|
||||
|
||||
| Gate | Command or platform check | Applies to | Done signal |
|
||||
|---|---|---|---|
|
||||
| Backend SQLite | `GOWORK=off go test ./... -count=1` | U3, U5 | All packages pass at the recorded Backend HEAD |
|
||||
| Backend PostgreSQL | Isolated PostgreSQL 16 with `AGENT_PG_TEST_DSN`, then role, migration, delegation, and lock-order store tests | U3, U5 | No skips in the selected PG set; container removed |
|
||||
| Cross-database atomicity | Force version-conflict, unknown-action, and out-of-cap batch failures against SQLite and isolated PostgreSQL | U3 | Both stores retain identical pre-request state with zero partial writes |
|
||||
| Migration oracle | Trusted isolated Backend/RDM store harness against the frozen v35 fixture and expected oracle | U3, U7 | Freeze/stage/compare/activate/failure/retry results match on SQLite and PostgreSQL; mismatches block activation |
|
||||
| Frontend unit/build | `npm test && npm run build && node scripts/check-knip.mjs` | U2, U3, U6 | Tests pass, build succeeds, knip stays within baseline |
|
||||
| UI | `npm test` plus the design guard through the Frontend/Dev suite | U3, U6 | UI tests and design guard pass at the fixed UI commit |
|
||||
| Browser deterministic | `npm run e2e -- e2e/iam-role-authority.playwright.ts` with isolated preview | U2, U3, U6 | Required scenarios pass; failure traces retained |
|
||||
| Browser live product | `scripts/iam-u4-role-acceptance.sh` with exact source SHAs | U3, U7 | Embedded product scenarios pass on a fresh data root; screenshot and manifest digests recorded |
|
||||
| RDM overlay | `GOWORK=<fixed overlay> go test ./... -count=1` | U3 | Development contract passes before authoritative pins exist |
|
||||
| RDM pinned product | Dirty-source negative test, then `make test`, `make product`, and `make product-smoke` from committed sources | U7 | Drift is rejected; full suite, embedded product, copied-content digests, and manifest smoke pass without overlay |
|
||||
| Documents | `git diff --check` plus link and version review | U1, U6 | No malformed diff or stale reference |
|
||||
| Dev local combination | `make test && make build` after `make task-sync` | U3, U8 | Exact fixed combination passes and builds |
|
||||
| Component CI | Backend `vet-and-test`; RDM `build-and-test`; current platform-required Frontend/Documents checks when present | U5–U7 | Each available GATE is successful on the merged candidate HEAD |
|
||||
| Dev CI | Gitea `test`, `test-pg`, `svc-realcombo` | U8 | All three jobs succeed on the same final PR HEAD |
|
||||
| Merge integrity | PR head/base readback, non-force merge, `origin/main` and component tree readback | U5–U8 | Actual protected main matches the reviewed candidate |
|
||||
| Cleanup | External receipt preservation; guarded `task-finish`; then `task-close` PR Review/CI/merge/readback | U9 | No writer, unique file, unmerged commit, active lock, or cleanup result is lost |
|
||||
|
||||
Evidence rules:
|
||||
|
||||
- Local commands remain `SELF` unless an independent reviewer replays them.
|
||||
- Gitea Actions counts as `GATE` only when the run identity, job, exact SHA, and result are recorded.
|
||||
- A Playwright screenshot or trace proves only the scenario and build identified by its receipt.
|
||||
- `SKIP` and `NOT_RUN` remain visible; they are never converted to PASS by another layer's test.
|
||||
- Any source, base, pin, manifest, or test-definition change updates the named candidate and reruns the gates it affects. A post-stage-7 change also requires a renewed stage 7 conclusion before component merge; a final Dev HEAD change invalidates its three required CI jobs.
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done
|
||||
|
||||
Global completion requires all of the following:
|
||||
|
||||
- DR1–DR21 all have PASS evidence. These delivery requirements are mandatory; SKIP, NOT_RUN, and exclusions block completion.
|
||||
- The user has approved stage 7 and stage 10 on named candidate versions.
|
||||
- Independent final Review has no unresolved Critical or Important finding.
|
||||
- Backend, Frontend, Documents, RDM, and Dev PRs are merged in dependency order at the recorded commits.
|
||||
- The trusted Backend tag exists remotely and peels to the reviewed Backend merge commit.
|
||||
- RDM's product manifest uses authoritative merged pins and no development overlay.
|
||||
- Dev's final PR HEAD has successful `test`, `test-pg`, and `svc-realcombo` jobs.
|
||||
- Actual protected main branches and Dev gitlinks match the reviewed candidates.
|
||||
- Production migration and deployment remain NOT_RUN unless separately authorized.
|
||||
- Failed experiments, temporary build roots, credentials, processes, branches, and worktrees are removed or retained with an explicit reason; no abandoned attempt code remains in the diffs.
|
||||
- Task cards are archived through the guarded workflow and external receipts remain available.
|
||||
|
||||
Per-unit completion:
|
||||
|
||||
| Unit | Completion signal |
|
||||
|---|---|
|
||||
| U1 | Stage 6 approval and synchronized candidate manifest are recorded |
|
||||
| U2 | Repeatable deterministic and live IAM browser slices exist and pass locally |
|
||||
| U3 | User approves the exact stage 7 requirement-to-evidence map |
|
||||
| U4 | Current component PR candidates have valid review and applicable checks |
|
||||
| U5 | Backend is merged and the user-published tag is verified |
|
||||
| U6 | Frontend and Documents are merged; UI ancestry remains valid |
|
||||
| U7 | RDM is merged with authoritative pins and a matching product manifest |
|
||||
| U8 | Dev is merged through an exclusive queue window with three exact-head GATE jobs |
|
||||
| U9 | Stage 10 is approved, records are archived, and guarded cleanup succeeds |
|
||||
|
||||
---
|
||||
|
||||
## Appendix
|
||||
|
||||
### Sources
|
||||
|
||||
- `docs/proposals/2026-10-15-iam-focused-role-permission-spec.md`
|
||||
- `docs/proposals/2026-10-15-iam-focused-role-permission-implementation-plan.md`
|
||||
- `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`
|
||||
- `Agent-System-Dev/policy/delivery.md`
|
||||
- `Agent-System-Dev/policy/merge-queue.md`
|
||||
- `Agent-System-Dev/.gitea/workflows/ci.yaml`
|
||||
- `Agent-System-Backend/.gitea/workflows/ci.yml`
|
||||
- `Agent-System-RDM/.gitea/workflows/ci.yml`
|
||||
- `Agent-System-RDM/scripts/build-product.sh`
|
||||
- `Agent-System-Frontend/playwright.config.ts`
|
||||
- `Agent-System-Frontend/e2e/rdm-duties.playwright.ts`
|
||||
@@ -15,6 +15,10 @@
|
||||
|
||||
## 先看哪一份
|
||||
|
||||
- [IAM U4 阶段 7–10 验收与交付计划](2026-10-15-iam-u4-stage7-10-delivery-plan.md):
|
||||
基于已批准的聚焦角色权限实现,安排真实浏览器验收、跨仓 Review/PR/CI、Backend 受信 tag、RDM 权威 pin、Dev 手工合入窗口及安全收口。
|
||||
生产迁移、部署、多角色、账号恢复、Agent 权限和工作流改造不在本计划内。
|
||||
|
||||
- [IAM I1 权限边界与公开扩展合同 r1](2026-10-13-iam-i1-permission-contract.md):
|
||||
依据已批准的[用户、职位角色与分层权限统一方案 r1](2026-10-13-identity-role-permission-plan.md)
|
||||
(Documents `a088809`)冻结系统/项目/资源边界、管理员无业务 bypass、创建者 cap、独立 `audit.read` 与公开扩展四要素。
|
||||
|
||||
Reference in New Issue
Block a user