From 5ad6e11692d271f08352b0e16a4fd7ef892117bb Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 09:58:11 +0100 Subject: [PATCH 01/14] docs(auth): repair AUTH-10 implementation contract --- .../CHUNK_MAP.md | 2 +- .../DISCOVERY.md | 39 ++++++++ .../STATUS.md | 12 +-- .../WS-AUTH-001-10-project-role-grants.md | 88 ++++++++++++++++++- 4 files changed, 132 insertions(+), 9 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md index 59ed1bdae..c7346c7f5 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md @@ -40,7 +40,7 @@ stopped. | `WS-AUTH-001-ART-CUSTODY` | ART Activation Custody Transfer | L1 | Merged through PR #158 as `be2a79a2`; all 25 ART actions remain planned | | `WS-AUTH-001-REV-CUSTODY` | REV Activation Custody Transfer | L1 | Merged through PR #160 as `fe0e4492`; all 19 REV actions remain planned | | `WS-AUTH-001-PREP` | Prepared Mutation Authorization Protocol | L1 | Merged through PR #162 as `c559d556`; no feature consumer or activation | -| `WS-AUTH-001-10` | Project Qualification And Contributor Role Grants | L1 | Proposed | +| `WS-AUTH-001-10` | Project Qualification And Contributor Role Grants | L1 | Active contract repair; runtime blocked on required L1 plan review | | `WS-AUTH-001-11` | Project Identity, Guide, Source, And Visibility Cutover | L1 | Proposed | | `WS-AUTH-001-12` | Project Policy And Setup Mutation Cutover | L1 | Proposed | | `WS-AUTH-001-13` | Task Management And Assignment Cutover | L1 | Proposed | diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DISCOVERY.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DISCOVERY.md index a1240fe4b..7d5ac1a51 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DISCOVERY.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DISCOVERY.md @@ -3,6 +3,45 @@ Discovery is read-only. No application code was changed while producing this artifact. +## AUTH-10 delta discovery - 2026-07-21 + +Trusted `main` is `5a8a924d`; Alembic head is +`0030_artifact_verification_fencing`, making `0031` the only current migration +candidate. PREP is merged and supports actor-self plus active administrative +mutations, but deliberately rejects ProjectRoleGrant preparation because no +project grant table, resource context, or evaluator branch exists. + +The permission catalogue already contains `project.role_grant.read` and +`project.role_grant.manage`. Project Manager has both permissions and may be +system- or exact-project-scoped; Audit Authority has read only. No matching +ProjectRoleGrant ActionIds or owner exist. Five surfaces therefore require five +new action declarations: candidate list, grant list, grant detail, issue, and +revoke. The existing authorization router is mounted directly under +`/api/v1`, and AdminRoleGrant routes provide the closest route/idempotency/audit +convention, but AUTH-10 mutations must use PREP rather than copying their older +single-phase `require()` flow. + +`ActorProfile` already contains the only candidate fields AUTH-10 may expose: +canonical ID and nullable display name. It also contains contact and lifecycle +metadata that candidate responses must not disclose. `ActorIdentityLink` is +one-to-one and supplies the active-link eligibility predicate without exposing +issuer, subject, or link metadata. `ProjectRepository.get_project(..., +for_update=...)` is the canonical project loader; AUTH must not add a duplicate +project repository. + +Typed audit/idempotency foundations already reserve project issue/revoke +operations, but still admit superseded `both`, `ProjectRoleGrantReplaced`, and +replacement evidence. AUTH-10 must replace those current validators in typed +code and migration `0031`, while leaving historical migrations immutable and +refusing unsafe upgrade/downgrade rather than converting evidence. + +The inherited contract combined schema, five active routes, candidate privacy, +PREP extension, idempotency, audit/invalidation, concurrency, and migration +round-trip proof without the D27 surface inventory. The repaired contract now +freezes those boundaries for required L1 plan review; reviewers must still +decide whether this remains one reviewable implementation chunk or requires a +same-initiative split before runtime edits. + ## AUTH-09 delta discovery - 2026-07-16 Merged AUTH-08 provides `AuthorityControl`, immutable AdminRoleGrants, seven diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md index 587663f90..2f7952922 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md @@ -111,16 +111,18 @@ or consumer feature action is active. ## Active planning chunk -None. `WS-AUTH-001-XINT` merged through PR #140. +`WS-AUTH-001-10`. Signed start event +`github-actions:29815937933:start` activated the exact recorded successor on +2026-07-21. D27 contract repair and required L1 plan review are in progress. ## Active implementation chunk -None. `WS-AUTH-001-09E` merged through PR #157 as `42a89b2d` without changing -feature action availability. +None. AUTH-10 runtime and migration edits remain blocked until the repaired +contract passes required L1 plan review. ## Current review branch -None. +`codex/ws-auth-001-10-project-role-grants`. ## Chunk status @@ -155,7 +157,7 @@ None. | `WS-AUTH-001-ART-CUSTODY` | Merged | `codex/ws-auth-001-art-custody` | #158 | Merged as `be2a79a2` on 2026-07-20; all 25 ART actions remain planned. | | `WS-AUTH-001-REV-CUSTODY` | Merged | `codex/ws-auth-001-rev-custody` | #160 | Merged as `fe0e4492` on 2026-07-20; all 19 REV actions remain planned. | | `WS-AUTH-001-PREP` | Merged | `codex/ws-auth-001-prep` | #162 | Merged as `c559d556` on 2026-07-21; adds no feature consumer or activation. | -| `WS-AUTH-001-10` | Proposed | - | - | Project contributor grants. | +| `WS-AUTH-001-10` | Active planning | `codex/ws-auth-001-10-project-role-grants` | - | Signed start passed; exact D27 inventory is under required L1 review before runtime edits. | | `WS-AUTH-001-11` | Proposed | - | - | Project identity/guide/source/read cutover. | | `WS-AUTH-001-12` | Proposed | - | - | Project policy/setup mutation cutover. | | `WS-AUTH-001-13` | Proposed | - | - | Task management and assignment cutover. | diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md index 97e2192db..c23d89477 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md @@ -2,9 +2,10 @@ ## Status -Proposed and inactive. Before implementation review, this contract must add the -exact ActionId/target/guard inventory required by D27. AUTH-PREP is a hard -runtime prerequisite. +Active contract repair after signed start event +`github-actions:29815937933:start`. Runtime implementation remains blocked +until the exact repaired contract passes required L1 plan review. AUTH-PREP is +merged through PR #162 and is the hard runtime prerequisite. ## Parent initiative @@ -35,6 +36,79 @@ L1 P1 +## Trusted-main baseline and migration custody + +- Reviewed baseline: protected `main` at + `5a8a924d9b3b347d4cc74b4682865518539c837e`. +- Current Alembic head: `0030_artifact_verification_fencing`. +- AUTH-10 owns only the next forward migration, + `0031_project_role_grants`. It does not reserve a later number and does not + edit any historical migration. + +## Exact action, target, guard, surface, and revalidation inventory + +All five actions are human-only, owned by `WS-AUTH-001-10`, active in the same +commit that exposes their route, and use only the retained permissions shown +below. A service principal is rejected before grant lookup. Every route has +exactly one OpenAPI `x-workstream-action-id` declaration. + +| ActionId | PermissionId | Canonical target and facts | Candidate authority | Required guards and revalidation | Exact surface | +|---|---|---|---|---|---| +| `project.contributor_candidate.list` | `project.role_grant.manage` | locked/read canonical `Project(id, status)`; page cursor is not authority | active `AdminRoleGrant(project_manager)` whose system scope or exact `scope_project_id` covers the loaded project | active human caller and link; re-resolve caller grant and exact project scope before querying; return only active human profiles with active links; exclude caller before page count/cursor construction | `GET /api/v1/projects/{project_id}/contributor-candidates` | +| `project_role_grant.list` | `project.role_grant.read` | canonical `Project`; filters are status and one exact role only | covered Project Manager or covered Audit Authority permission candidate | active human caller/link; canonical project scope; authorization precedes count, cursor, and row query; response contains grant/snapshot identifiers and role history but no identity-link or contact fields | `GET /api/v1/projects/{project_id}/role-grants` | +| `project_role_grant.read` | `project.role_grant.read` | loaded `ProjectRoleGrant` joined to its canonical project; path project must equal row project | covered Project Manager or covered Audit Authority permission candidate | active human caller/link; exact-project scope; mismatch/not-found remains concealed before disclosure; replay is not applicable to this read | `GET /api/v1/projects/{project_id}/role-grants/{grant_id}` | +| `project_role_grant.issue` | `project.role_grant.manage` | canonical project plus locked target `ActorProfile`/exact link, exact requested role, and newly staged composite-owned qualification snapshot | active covered `AdminRoleGrant(project_manager)` only | PREP locks caller profile/link/matched manager grant first; feature then locks project, target profile/link, and active exact-role selector; target is a different active human with active link; role is one of `submitter`, `reviewer`, `adjudicator`; no active same-role row; consume recomposes all final facts before snapshot/grant/evidence flush | `POST /api/v1/projects/{project_id}/role-grants` | +| `project_role_grant.revoke` | `project.role_grant.manage` | loaded and locked grant, its canonical project, actor, exact role, status, and snapshot reference | active covered `AdminRoleGrant(project_manager)` only | PREP locks caller authority first; feature then locks project and grant; path project must match; active grant only; caller may not revoke a grant whose contributor is caller; consume revalidates exact role/status/project and stages role-specific invalidation | `POST /api/v1/projects/{project_id}/role-grants/{grant_id}/revoke` | + +`project_role_grant.issue` and `.revoke` extend PREP with an exact-project +scope and a locked `ProjectRoleGrant` candidate path. Preparation never locks a +feature row, and feature code never chooses or supplies its authorizing grant. +The route owns the root transaction and commits once. Reads continue through +`AuthorizationService.require()`. + +## Exact qualification snapshot and grant contract + +`ProjectRoleQualificationSnapshot` is authorization-owned and contains: + +```text +id, project_id, actor_profile_id, requested_role, +skills_snapshot, reputation_snapshot, +prior_project_work_refs, external_expertise_refs, +captured_by_actor_profile_id, captured_by_admin_role_grant_id, captured_at +``` + +`skills_snapshot` and `reputation_snapshot` are closed privacy-bounded objects +with `availability = available | unavailable`; unavailable records a bounded +reason token and no inferred score. References are caller-supplied UUID/string +evidence identifiers only after validation, are never dereferenced as +authority, and exclude contact data, issuer subjects, raw claims, secrets, and +free-form personal profiles. The composite key +`(id, actor_profile_id, project_id, requested_role)` is the ownership target of +the grant foreign key. + +`ProjectRoleGrant` contains one immutable issuance row with exact role, +`status = active | revoked`, `grant_method = manual`, the composite snapshot +reference, granting manager/profile and matched manager-grant provenance, +database timestamps, bounded issue reason, and terminal revocation provenance. +Only lifecycle fields may change once, from active version 1 to revoked version +2. No update changes project, actor, role, snapshot, or issuance provenance. + +## Exact route and pagination disclosure contract + +- Candidate items expose only `actor_profile_id` and nullable `display_name`. + They never expose `contact_email`, issuer, subject, identity-link ID/status, + last-seen timestamps, skills, reputation, or activity in another project. +- Candidate filtering for active human/profile/link and caller exclusion occurs + in SQL before `total` and keyset cursor calculation. +- Grant list/detail responses expose durable grant provenance and the bounded + snapshot captured for that grant, but never external identity metadata or + contact fields. +- Cursors are opaque, signed/canonical query digests bound to project, status, + role filter, page boundary, and limit. Cross-filter or cross-project reuse is + rejected as `invalid_request` without counts. +- Candidate/list/detail routes use the existing read-rate control; issue and + revoke use the existing administrative mutation rate control. + ## Allowed files ```text @@ -48,6 +122,7 @@ backend/app/modules/audit/** backend/alembic/versions/_*.py backend/tests/test_actors.py backend/tests/test_projects.py +backend/tests/test_authorization.py backend/tests/test_auth.py backend/tests/test_alembic.py backend/scripts/api_contract_e2e.py @@ -72,6 +147,9 @@ project/task/checker authorization cutover `both`, compatibility alias, replacement event/reason, `replaced_grant_id`, or silent conversion of combined/replacement evidence editing migrations `0018`, `0019`, or `0022` +adding project-role routes without one exact active ActionId declaration +using Audit Authority or any system role to issue or revoke contributor grants +committing inside PREP, the authorization kernel, or a repository ``` ## Acceptance criteria @@ -114,6 +192,10 @@ editing migrations `0018`, `0019`, or `0022` role from the locked grant and replay reloads/re-authorizes before disclosure. - State, idempotency result, audit event, and invalidation event commit in one transaction. +- Issuance stages exactly two success events in the same transaction: + `ProjectRoleQualificationSnapshotCaptured` and `ProjectRoleGrantIssued`. + Revocation stages `ProjectRoleGrantRevoked` and one linked + `AuthorityInvalidationRequested`; there is no replacement event. - Only manual creation is enabled; automated schema value cannot be emitted. - Revocation is visible on the next authorization context build. - Revocation evidence and invalidation identify the exact revoked role; From 5a53757474ddcf8bf509cca6f61c70215d169815 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 11:47:09 +0100 Subject: [PATCH 02/14] docs(auth): split project role grant delivery --- .../CHUNK_MAP.md | 8 +- .../DECISIONS.md | 30 ++ .../DISCOVERY.md | 9 +- .../INTENT.md | 4 + .../PLAN.md | 8 +- .../STATUS.md | 8 +- .../WS-AUTH-001-10-project-role-grants.md | 271 ++++-------------- ...01-10A-project-role-grant-data-evidence.md | 130 +++++++++ ...S-AUTH-001-10B-project-role-grant-reads.md | 112 ++++++++ ...TH-001-10C-project-role-grant-mutations.md | 143 +++++++++ .agent-loop/merge-intents/WS-AUTH-001-10.json | 9 + ...and-authorization-service-specification.md | 79 ++--- 12 files changed, 548 insertions(+), 263 deletions(-) create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md create mode 100644 .agent-loop/merge-intents/WS-AUTH-001-10.json diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md index c7346c7f5..cf5125704 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md @@ -40,7 +40,10 @@ stopped. | `WS-AUTH-001-ART-CUSTODY` | ART Activation Custody Transfer | L1 | Merged through PR #158 as `be2a79a2`; all 25 ART actions remain planned | | `WS-AUTH-001-REV-CUSTODY` | REV Activation Custody Transfer | L1 | Merged through PR #160 as `fe0e4492`; all 19 REV actions remain planned | | `WS-AUTH-001-PREP` | Prepared Mutation Authorization Protocol | L1 | Merged through PR #162 as `c559d556`; no feature consumer or activation | -| `WS-AUTH-001-10` | Project Qualification And Contributor Role Grants | L1 | Active contract repair; runtime blocked on required L1 plan review | +| `WS-AUTH-001-10` | Project Qualification And Contributor Role Grants | L1 | Active planning-only parent; split approved after failed L1 combined review | +| `WS-AUTH-001-10A` | Project Role Grant Data And Evidence Foundation | L1 | Proposed successor; migration `0031`, no active surface | +| `WS-AUTH-001-10B` | Project Role Grant Read And Candidate Surfaces | L1 | Proposed after 10A | +| `WS-AUTH-001-10C` | Project Role Grant Mutations | L1 | Proposed after 10B | | `WS-AUTH-001-11` | Project Identity, Guide, Source, And Visibility Cutover | L1 | Proposed | | `WS-AUTH-001-12` | Project Policy And Setup Mutation Cutover | L1 | Proposed | | `WS-AUTH-001-13` | Task Management And Assignment Cutover | L1 | Proposed | @@ -103,6 +106,9 @@ WS-AUTH-001-PLAN -> WS-AUTH-001-ART-CUSTODY and WS-AUTH-001-REV-CUSTODY -> WS-AUTH-001-PREP -> WS-AUTH-001-10 +-> WS-AUTH-001-10A +-> WS-AUTH-001-10B +-> WS-AUTH-001-10C -> WS-AUTH-001-11 -> WS-AUTH-001-12 -> WS-AUTH-001-13 diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DECISIONS.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DECISIONS.md index cb9f65f05..bc8568638 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DECISIONS.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DECISIONS.md @@ -684,3 +684,33 @@ The foundation allocates only the then-current next migration from trusted `main`. This decision supersedes only the future fixed-number reservation clauses in D29, D30, and earlier decisions; their other boundaries remain in force. Merged migration ownership through AUTH-09D-A `0026` is unchanged. +## D32: Split project-role truth, reads, and mutations + +Status: accepted by the user on 2026-07-21 after required AUTH-10 L1 plan +review rejected the combined runtime boundary. + +AUTH-10 is a planning-only parent and splits into exactly three sequential +same-initiative children. AUTH-10A owns migration +`0031_project_role_grants`, the immutable qualification/grant aggregates, and +the typed/PostgreSQL three-role evidence clean cut; it activates no action or +route. AUTH-10B owns exactly the candidate, grant-list, and grant-detail read +actions and privacy-safe pagination. AUTH-10C owns exactly issue and revoke, +multi-principal PREP ordering, idempotency, audit, invalidation, and mutation +concurrency. Each child requires a separate signed start and stop. + +The project roles are independent `submitter`, `reviewer`, and `adjudicator` +rows. `both`, replacement, automated issuance, conversion, and one-active-role- +total semantics are removed from current contracts. Discovery and issuance are +allowed only for draft, active, and paused projects. Historical grant reads and +revocation remain possible for every existing project state so evidence cannot +be hidden and authority cannot become irremovable. + +The current trusted-main Alembic head is `0030`; this decision assigns `0031` +only to AUTH-10A and supersedes every older inactive AUTH-10 migration-number +reservation in D21, D27, D28, D29, and D30. Historical migration ownership is +unchanged. + +`GET /api/v1/actors/me/authorization-context` moves to AUTH-11. AUTH-10B does +not expose a context that advertises still-inactive project/task/review actions; +AUTH-11 must enumerate that surface with its exact ActionId, target, guards, +and disclosure contract before implementation. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DISCOVERY.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DISCOVERY.md index 7d5ac1a51..ed5aae3be 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DISCOVERY.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DISCOVERY.md @@ -37,10 +37,11 @@ refusing unsafe upgrade/downgrade rather than converting evidence. The inherited contract combined schema, five active routes, candidate privacy, PREP extension, idempotency, audit/invalidation, concurrency, and migration -round-trip proof without the D27 surface inventory. The repaired contract now -freezes those boundaries for required L1 plan review; reviewers must still -decide whether this remains one reviewable implementation chunk or requires a -same-initiative split before runtime edits. +round-trip proof without the D27 surface inventory. Required L1 review rejected +that combined boundary and identified crossed-manager lock ordering, replay, +privacy-shape, lifecycle, reference-spec, and local-versus-hosted proof gaps. +The user approved the resulting 10A durable-truth, 10B read, and 10C mutation +split on 2026-07-21. D32 records the exact boundaries and successor order. ## AUTH-09 delta discovery - 2026-07-16 diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/INTENT.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/INTENT.md index dae8de64e..8aed7b144 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/INTENT.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/INTENT.md @@ -163,6 +163,10 @@ Resolved: on 2026-07-11 after planning and post-merge memory closed. - D20-D22 establish service ActorProfiles, independent three-role project grants, and fixed service runtime admission. +- D32 records the user-approved AUTH-10A/10B/10C split: durable truth first, + privacy-safe reads second, and PREP-bound mutations third. It assigns current + migration `0031` to 10A and defers actor authorization-context disclosure to + AUTH-11. External deployment details such as issuer URL, JWKS URL, approved algorithms, claim names, and introspection policy are configuration inputs. Their absence diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md index 8354e48b5..dc9fed9dc 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md @@ -191,9 +191,11 @@ proving the same issuer role metadata alone no longer authorizes. 11. Transfer all current ART/REV owner labels to exact AUTH custodians without changing availability, then add the prepared mutation authorization protocol. -12. Implement qualification snapshots and independent contributor grants scoped - to the exact project, including the typed/PostgreSQL clean cut from `both` - and replacement evidence. +12. Close planning parent AUTH-10, then deliver project-role authority in three + separately started children: 10A establishes migration `0031`, immutable + snapshots/grants, and the typed/PostgreSQL three-role clean cut without an + active surface; 10B activates candidate/list/detail reads; 10C activates + PREP-bound issue/revoke mutations and concurrency proof. 13. Cut project identity, guide, source, and visibility queries over to local permissions. 14. Cut project policy mutations, approvals, activation, and setup operations diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md index 2f7952922..6ea507645 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md @@ -113,7 +113,8 @@ or consumer feature action is active. `WS-AUTH-001-10`. Signed start event `github-actions:29815937933:start` activated the exact recorded successor on -2026-07-21. D27 contract repair and required L1 plan review are in progress. +2026-07-21. Required L1 plan review rejected the combined runtime scope; the +user approved the planning-only 10A/10B/10C split, now under exact review. ## Active implementation chunk @@ -157,7 +158,10 @@ contract passes required L1 plan review. | `WS-AUTH-001-ART-CUSTODY` | Merged | `codex/ws-auth-001-art-custody` | #158 | Merged as `be2a79a2` on 2026-07-20; all 25 ART actions remain planned. | | `WS-AUTH-001-REV-CUSTODY` | Merged | `codex/ws-auth-001-rev-custody` | #160 | Merged as `fe0e4492` on 2026-07-20; all 19 REV actions remain planned. | | `WS-AUTH-001-PREP` | Merged | `codex/ws-auth-001-prep` | #162 | Merged as `c559d556` on 2026-07-21; adds no feature consumer or activation. | -| `WS-AUTH-001-10` | Active planning | `codex/ws-auth-001-10-project-role-grants` | - | Signed start passed; exact D27 inventory is under required L1 review before runtime edits. | +| `WS-AUTH-001-10` | Active planning split | `codex/ws-auth-001-10-project-role-grants` | - | Planning-only parent; user approved 10A/10B/10C after combined L1 review failed. | +| `WS-AUTH-001-10A` | Proposed | - | - | Migration `0031`, data/evidence foundation, no active surface. | +| `WS-AUTH-001-10B` | Proposed | - | - | Candidate/list/detail read surfaces after 10A. | +| `WS-AUTH-001-10C` | Proposed | - | - | PREP-bound issue/revoke mutations after 10B. | | `WS-AUTH-001-11` | Proposed | - | - | Project identity/guide/source/read cutover. | | `WS-AUTH-001-12` | Proposed | - | - | Project policy/setup mutation cutover. | | `WS-AUTH-001-13` | Proposed | - | - | Task management and assignment cutover. | diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md index c23d89477..534d52a29 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md @@ -1,272 +1,105 @@ -# Chunk Contract: WS-AUTH-001-10 - Project Qualification And Contributor Role Grants +# Chunk Contract: WS-AUTH-001-10 — Project Qualification And Contributor Role Grants ## Status -Active contract repair after signed start event -`github-actions:29815937933:start`. Runtime implementation remains blocked -until the exact repaired contract passes required L1 plan review. AUTH-PREP is -merged through PR #162 and is the hard runtime prerequisite. +Active planning-only parent. Signed start event +`github-actions:29815937933:start` activated this exact chunk. Required L1 plan +review rejected the combined runtime scope at `5ad6e116`; the user approved the +10A/10B/10C design on 2026-07-21. This parent changes planning and contracts +only and names `WS-AUTH-001-10A` as its same-initiative successor. ## Parent initiative -`WS-AUTH-001` - Workstream Authorization Service +`WS-AUTH-001` — Workstream Authorization Service ## Goal -Implement immutable qualification snapshots and independent -`ProjectRoleGrant(submitter|reviewer|adjudicator)` create, list, and revoke -behavior scoped to the exact project under Project Manager authority. +Freeze the reviewed three-stage delivery design for independent project-role +authority before any AUTH-10 runtime or migration edit. ## Why this chunk exists -Project contributor authority must be durable, explicit, revocable, and -separate from both token claims and administrative roles. - -## Approved plan reference - -- INTENT: `.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/INTENT.md` -- PLAN: `.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md` -- CHUNK_MAP: `.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/CHUNK_MAP.md` +The inherited chunk mixed durable schema, a validator clean cut, five active +surfaces, privacy-sensitive pagination, PREP multi-principal locking, mutations, +and concurrency proof. Those boundaries require separate review and rollback. ## Risk class -L1 +L1 planning and authorization architecture. ## SLA P1 -## Trusted-main baseline and migration custody - -- Reviewed baseline: protected `main` at - `5a8a924d9b3b347d4cc74b4682865518539c837e`. -- Current Alembic head: `0030_artifact_verification_fencing`. -- AUTH-10 owns only the next forward migration, - `0031_project_role_grants`. It does not reserve a later number and does not - edit any historical migration. - -## Exact action, target, guard, surface, and revalidation inventory - -All five actions are human-only, owned by `WS-AUTH-001-10`, active in the same -commit that exposes their route, and use only the retained permissions shown -below. A service principal is rejected before grant lookup. Every route has -exactly one OpenAPI `x-workstream-action-id` declaration. - -| ActionId | PermissionId | Canonical target and facts | Candidate authority | Required guards and revalidation | Exact surface | -|---|---|---|---|---|---| -| `project.contributor_candidate.list` | `project.role_grant.manage` | locked/read canonical `Project(id, status)`; page cursor is not authority | active `AdminRoleGrant(project_manager)` whose system scope or exact `scope_project_id` covers the loaded project | active human caller and link; re-resolve caller grant and exact project scope before querying; return only active human profiles with active links; exclude caller before page count/cursor construction | `GET /api/v1/projects/{project_id}/contributor-candidates` | -| `project_role_grant.list` | `project.role_grant.read` | canonical `Project`; filters are status and one exact role only | covered Project Manager or covered Audit Authority permission candidate | active human caller/link; canonical project scope; authorization precedes count, cursor, and row query; response contains grant/snapshot identifiers and role history but no identity-link or contact fields | `GET /api/v1/projects/{project_id}/role-grants` | -| `project_role_grant.read` | `project.role_grant.read` | loaded `ProjectRoleGrant` joined to its canonical project; path project must equal row project | covered Project Manager or covered Audit Authority permission candidate | active human caller/link; exact-project scope; mismatch/not-found remains concealed before disclosure; replay is not applicable to this read | `GET /api/v1/projects/{project_id}/role-grants/{grant_id}` | -| `project_role_grant.issue` | `project.role_grant.manage` | canonical project plus locked target `ActorProfile`/exact link, exact requested role, and newly staged composite-owned qualification snapshot | active covered `AdminRoleGrant(project_manager)` only | PREP locks caller profile/link/matched manager grant first; feature then locks project, target profile/link, and active exact-role selector; target is a different active human with active link; role is one of `submitter`, `reviewer`, `adjudicator`; no active same-role row; consume recomposes all final facts before snapshot/grant/evidence flush | `POST /api/v1/projects/{project_id}/role-grants` | -| `project_role_grant.revoke` | `project.role_grant.manage` | loaded and locked grant, its canonical project, actor, exact role, status, and snapshot reference | active covered `AdminRoleGrant(project_manager)` only | PREP locks caller authority first; feature then locks project and grant; path project must match; active grant only; caller may not revoke a grant whose contributor is caller; consume revalidates exact role/status/project and stages role-specific invalidation | `POST /api/v1/projects/{project_id}/role-grants/{grant_id}/revoke` | - -`project_role_grant.issue` and `.revoke` extend PREP with an exact-project -scope and a locked `ProjectRoleGrant` candidate path. Preparation never locks a -feature row, and feature code never chooses or supplies its authorizing grant. -The route owns the root transaction and commits once. Reads continue through -`AuthorizationService.require()`. - -## Exact qualification snapshot and grant contract - -`ProjectRoleQualificationSnapshot` is authorization-owned and contains: - -```text -id, project_id, actor_profile_id, requested_role, -skills_snapshot, reputation_snapshot, -prior_project_work_refs, external_expertise_refs, -captured_by_actor_profile_id, captured_by_admin_role_grant_id, captured_at -``` - -`skills_snapshot` and `reputation_snapshot` are closed privacy-bounded objects -with `availability = available | unavailable`; unavailable records a bounded -reason token and no inferred score. References are caller-supplied UUID/string -evidence identifiers only after validation, are never dereferenced as -authority, and exclude contact data, issuer subjects, raw claims, secrets, and -free-form personal profiles. The composite key -`(id, actor_profile_id, project_id, requested_role)` is the ownership target of -the grant foreign key. - -`ProjectRoleGrant` contains one immutable issuance row with exact role, -`status = active | revoked`, `grant_method = manual`, the composite snapshot -reference, granting manager/profile and matched manager-grant provenance, -database timestamps, bounded issue reason, and terminal revocation provenance. -Only lifecycle fields may change once, from active version 1 to revoked version -2. No update changes project, actor, role, snapshot, or issuance provenance. +## Approved split -## Exact route and pagination disclosure contract +1. `WS-AUTH-001-10A` — Project Role Grant Data And Evidence Foundation. +2. `WS-AUTH-001-10B` — Project Role Grant Read And Candidate Surfaces. +3. `WS-AUTH-001-10C` — Project Role Grant Mutations. -- Candidate items expose only `actor_profile_id` and nullable `display_name`. - They never expose `contact_email`, issuer, subject, identity-link ID/status, - last-seen timestamps, skills, reputation, or activity in another project. -- Candidate filtering for active human/profile/link and caller exclusion occurs - in SQL before `total` and keyset cursor calculation. -- Grant list/detail responses expose durable grant provenance and the bounded - snapshot captured for that grant, but never external identity metadata or - contact fields. -- Cursors are opaque, signed/canonical query digests bound to project, status, - role filter, page boundary, and limit. Cross-filter or cross-project reuse is - rejected as `invalid_request` without counts. -- Candidate/list/detail routes use the existing read-rate control; issue and - revoke use the existing administrative mutation rate control. +Each child requires its own signed start, internal review, merge intent, hosted +checks, human merge approval, signed merge memory, and stop. No child begins +automatically. ## Allowed files ```text -backend/app/modules/actors/** -backend/app/modules/authorization/** -backend/app/modules/projects/models.py -backend/app/modules/projects/repository.py -backend/app/api/router.py -backend/app/db/models.py -backend/app/modules/audit/** -backend/alembic/versions/_*.py -backend/tests/test_actors.py -backend/tests/test_projects.py -backend/tests/test_authorization.py -backend/tests/test_auth.py -backend/tests/test_alembic.py -backend/scripts/api_contract_e2e.py -docs/operations_authorization_service.md -docs/spec_authorization_service.md .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/** .agent-loop/merge-intents/WS-AUTH-001-10.json -.agent-loop/LOOP_STATE.md -.agent-loop/WORK_QUEUE.md -.agent-loop/REVIEW_LOG.md +docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md ``` -## Not allowed +## Not allowed changes ```text -automated grants from skills or reputation -self-grant or self-revoke of the issuer's own contributor grant through the -administrative project-grant operation -admin roles satisfying submitter/reviewer/adjudicator permissions -task/review lifecycle implementation -project/task/checker authorization cutover -`both`, compatibility alias, replacement event/reason, `replaced_grant_id`, or -silent conversion of combined/replacement evidence -editing migrations `0018`, `0019`, or `0022` -adding project-role routes without one exact active ActionId declaration -using Audit Authority or any system role to issue or revoke contributor grants -committing inside PREP, the authorization kernel, or a repository +backend or frontend runtime +database models or migrations +ActionId or permission availability +API routes or schemas +CI workflows or dependencies +starting AUTH-10A before this parent merges and signed memory stops ``` ## Acceptance criteria -- Snapshot is immutable, actor/project/requested-role-bound, privacy-bounded, - and records unavailable evidence explicitly. -- Qualification snapshots and ProjectRoleGrants are owned by the authorization - module; ActorProfile/IdentityLink remain actor-owned, and ProjectRepository - remains the canonical project loader. No duplicate grant/project repository - is introduced. -- Only a Project Manager whose active grant covers the project can issue or - revoke a contributor grant. -- The same covered `project.role_grant.manage` permission provides a scoped, - paginated contributor-candidate lookup for the grant workflow. It returns - only minimal actor fields, filters unauthorized rows before totals/cursors, - cannot enumerate unrelated project activity, and never exposes issuer - subject, identity-link metadata, contact data, skills, or reputation as - authority. -- Target must be an active human and cannot be the issuing manager. -- A manager who separately holds a contributor grant cannot revoke that grant - through their own administrative request; denial is stable and audited. -- A partial unique index on `(actor_profile_id, project_id, role) WHERE status = - 'active'` permits at most one active grant for the same exact role while a - contributor may hold active submitter, reviewer, and adjudicator rows - concurrently. -- Issue never revokes another role. Regrant after revocation creates a new - immutable row. -- Typed schemas, audit facts, idempotency evidence, and current PostgreSQL - validators accept only `submitter`, `reviewer`, and `adjudicator`; only issued - and revoked success events remain. `both`, replacement fields/events/reasons, - aliases, and conversion branches are absent. -- Snapshot ownership is composite across snapshot ID, actor, project, and exact - requested role. There is no replacement/supersession column. -- Create and revoke require canonical request hashing: same key and - same request returns the committed graph; same key with different request is - rejected. -- Issue hashing includes the exact requested role. Same key/different role is - `idempotency_mismatch`; a new-key duplicate same-role issue is a stable audited - conflict; distinct keys may issue different roles concurrently. Revoke derives - role from the locked grant and replay reloads/re-authorizes before disclosure. -- State, idempotency result, audit event, and invalidation event commit in one - transaction. -- Issuance stages exactly two success events in the same transaction: - `ProjectRoleQualificationSnapshotCaptured` and `ProjectRoleGrantIssued`. - Revocation stages `ProjectRoleGrantRevoked` and one linked - `AuthorityInvalidationRequested`; there is no replacement event. -- Only manual creation is enabled; automated schema value cannot be emitted. -- Revocation is visible on the next authorization context build. -- Revocation evidence and invalidation identify the exact revoked role; - downstream consumers reconcile only the matching task, review, or future - adjudication responsibility. -- The linked invalidation retains exact grant and cause-event references. A - submitter revocation creates only the task-assignment obligation and reviewer - revocation may create only its exact REV-owned review obligation. Adjudicator - revocation persists exact invalidation only and creates or consumes no - adjudication obligation until that separately approved lifecycle is active. - No path changes another project role or an AdminRoleGrant. -- Project manager/admin role alone never creates contributor capability. -- PostgreSQL concurrency tests cover identical-role creates, concurrent - different-role creates, regrant versus revoke, and revocation versus - authorization. -- `POST/GET /api/v1/projects/{project_id}/role-grants`, grant detail, and grant - revoke routes have multi-role, self-revoke, scope, privacy, rate-limit, - replay, and negative tests. -- Every protected grant/candidate route declares one active `ActionId` mapped to - `project.role_grant.read` or `project.role_grant.manage` against the - canonically loaded project/grant target. Generated manifest-delta tests prove - every surface introduced here has exactly one declaration. -- Contributor-candidate lookup has covered-manager allow, uncovered/cross- - project deny, pagination/count concealment, minimal-field, rate-limit, and - inactive/non-human exclusion tests; no UUID must be recovered from logs or - direct database access. -- The then-current migration enforces exact three-role checks, composite snapshot/grant - ownership, partial unique/supporting indexes, database-time fields, and - immutability. It refuses upgrade when obsolete combined or - replacement evidence exists and never converts or deletes those rows. It - replaces current audit/idempotency validators without editing historical - migrations and refuses an unsafe downgrade without mutating evidence. - Prior-head, fresh replay, preserved history, and both refusal paths are tested. +- D32 records the user-approved 10A/10B/10C design and exact dependency order. +- Every child has a complete contract with allowed files, exclusions, + acceptance criteria, verification, reviewers, human focus, and stop rules. +- Migration `0031` belongs only to 10A; 10B and 10C add no migration. +- 10A activates no action or route; 10B owns exactly three read actions; 10C + owns exactly two mutation actions. +- Project lifecycle behavior is frozen: discovery and issuance allow draft, + active, and paused projects; list/detail and revocation remain available for + every existing project state so evidence is inspectable and authority can + always be removed. +- `/api/v1/actors/me/authorization-context` is explicitly deferred to AUTH-11, + where the first complete project read cutover can expose useful contributor + context without advertising inactive task/review actions. +- The canonical reference specification removes `both`, replacement, automated + issuance, one-active-role-total, and noncanonical AUTH-10 route prefixes. +- Exactly one merge intent names 10A as successor with explicit start required. ## Verification commands ```bash -(cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/alembic upgrade head) -(cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/alembic downgrade -1) -(cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/alembic upgrade head) -(cd backend && .venv/bin/python -m ruff check app tests) -(cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/python -m pytest -q \ - tests/test_authorization.py tests/test_auth.py tests/test_actors.py \ - tests/test_projects.py --cov=app.modules.authorization \ - --cov-report=term-missing --cov-fail-under=90) -(cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/python -m pytest -q) -(cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/python scripts/api_contract_e2e.py) python3 scripts/check_stale_workstream_wording.py +python3 scripts/check_stale_authorization_docs.py python3 scripts/check_markdown_links.py +python3 scripts/update_post_merge_memory.py validate-merge-intent --base-ref origin/main git diff --check ``` ## Required reviewers -- senior engineering -- QA/test -- security/auth -- product/ops -- architecture -- CI integrity -- docs -- reuse/dedup -- test delta +Senior engineering, QA/test, security/auth, product/ops, architecture, CI +integrity, docs, reuse/dedup, and test delta. ## Human review focus -Review exact-project scope, self-grant protection, role-specific snapshot -privacy, independent issue/revoke semantics, and absence of implicit grants. +Confirm the three boundaries, exact successor order, migration custody, +authorization-context deferral, and absence of runtime changes. ## Stop conditions -Stop if contributor authority depends on a token role, inferred qualification, -project ID supplied without canonical database resolution, compatibility for -`both`, evidence conversion, or a mutation path that bypasses AUTH-PREP. +Stop if the planning PR changes runtime, combines child boundaries again, +starts a child automatically, or names a cross-initiative successor. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md new file mode 100644 index 000000000..b65b4e362 --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md @@ -0,0 +1,130 @@ +# Chunk Contract: WS-AUTH-001-10A - Project Role Grant Data And Evidence Foundation + +## Parent initiative + +`WS-AUTH-001` — Workstream Authorization Service + +## Goal + +Create the immutable qualification-snapshot and independent three-role grant +truth plus typed/PostgreSQL evidence parity, without exposing a route or active +action. + +## Why this chunk exists + +Reads and mutations must depend on database-enforced ownership and exact-role +history rather than shipping schema and behavior together. + +## Risk class + +L1 schema, authorization evidence, and privacy. + +## SLA + +P1 + +## Allowed files + +```text +backend/app/modules/authorization/models.py +backend/app/modules/authorization/schemas.py +backend/app/modules/authorization/repository.py +backend/app/modules/audit/** +backend/app/db/models.py +backend/alembic/versions/0031_project_role_grants.py +backend/tests/test_authorization.py +backend/tests/test_alembic.py +docs/operations_authorization_service.md +docs/spec_authorization_service.md +.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/** +.agent-loop/merge-intents/WS-AUTH-001-10A.json +.agent-loop/REVIEW_LOG.md +``` + +## Not allowed changes + +```text +API routes or OpenAPI declarations +new or active ActionId/ActionOwner rows +authorization kernel or PREP behavior +candidate/list/detail/issue/revoke services +project/task/review lifecycle behavior +`both`, replacement, automated issuance, aliases, or evidence conversion +editing a historical migration +``` + +## Exact durable contract + +`ProjectRoleQualificationSnapshot` contains `id`, `project_id`, +`actor_profile_id`, exact `requested_role`, two structured availability +snapshots, bounded prior-work UUID references, bounded external-expertise +reference tokens, capturer profile/grant provenance, and database capture time. + +Each availability snapshot is exactly: + +```text +availability: available | unavailable +reference_ids: array[string], 0..20 items, each 1..120 UTF-8 bytes after + rejecting leading/trailing whitespace and control characters +unavailable_reason: not_collected | source_unavailable | no_record | null +``` + +`available` requires one or more reference IDs and null reason. `unavailable` +requires no references and one reason. Prior-work references are 0..20 UUIDs. +External-expertise references are 0..20 tokens with the same byte/whitespace +rules. No free-form narrative, score, contact field, issuer subject, raw claim, +secret, URL credential, or automatically inferred authority is stored. + +`ProjectRoleGrant` is one immutable issuance row for exactly `submitter`, +`reviewer`, or `adjudicator`, with active/revoked lifecycle, manual method, +composite snapshot reference, issuer profile/grant provenance, bounded reason, +database timestamps, and terminal revocation provenance. Only active version 1 +to revoked version 2 may mutate lifecycle fields. + +## Acceptance criteria + +- Migration `0031` creates both authorization-owned tables and no route/action. +- Composite key `(snapshot_id, actor_profile_id, project_id, requested_role)` + is referenced by the matching grant facts. +- Partial uniqueness permits one active actor/project/exact-role row while all + three distinct roles may coexist. +- Regrant after revocation creates a new row; issuance provenance is immutable. +- Typed and PostgreSQL audit/idempotency validators accept only the three exact + roles and issued/revoked success events. Replacement fields/events/reasons + and `both` are absent. +- Upgrade refuses, without mutation, any obsolete combined/replacement evidence + in current authority audit/idempotency rows. Downgrade refuses without + mutation when either new table contains any row or new three-role evidence + cannot be represented by the prior validator. +- Fresh install, prior-head upgrade, downgrade/refusal, replay, constraints, + immutability, and preserved unrelated history are proven on PostgreSQL. +- No migration, model, schema, or fixture accepts automated creation. + +## Verification commands + +```bash +(cd backend && .venv/bin/python -m ruff check app/modules/authorization app/modules/audit tests/test_authorization.py tests/test_alembic.py) +(cd backend && WORKSTREAM_TEST_ADMIN_DATABASE_URL= .venv/bin/python scripts/run_isolated_tests.py --metadata-json --timeout-seconds 300 -- .venv/bin/python -m pytest -q tests/test_alembic.py tests/test_authorization.py -k 'project_role or qualification') +python3 scripts/check_stale_authorization_docs.py +python3 scripts/check_markdown_links.py +git diff --check +``` + +The full suite, all shards, aggregate 78 percent coverage, authorization 90 +percent coverage, API E2E, and Agent Gates run only on GitHub before PR +readiness. + +## Required reviewers + +Senior engineering, QA/test, security/auth, product/ops, architecture, CI +integrity, docs, reuse/dedup, and test delta. + +## Human review focus + +Review privacy shapes, composite ownership, independent-role uniqueness, +immutability, refusal predicates, and absence of exposed behavior. + +## Stop conditions + +Stop if migration data would be converted/deleted, evidence becomes free-form, +an action/route becomes active, or one role affects another. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md new file mode 100644 index 000000000..df826dc5c --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md @@ -0,0 +1,112 @@ +# Chunk Contract: WS-AUTH-001-10B - Project Role Grant Read And Candidate Surfaces + +## Parent initiative + +`WS-AUTH-001` — Workstream Authorization Service + +## Goal + +Expose privacy-safe contributor candidates and project-role grant history +through three exact read actions after 10A establishes durable truth. + +## Why this chunk exists + +Read disclosure, candidate eligibility, filtering-before-count, and cursor +binding can be reviewed independently from mutation locking. + +## Risk class + +L1 authorization, privacy, and API disclosure. + +## SLA + +P1 + +## Allowed files + +```text +backend/app/modules/actors/repository.py +backend/app/modules/authorization/** +backend/app/modules/projects/repository.py +backend/app/api/router.py +backend/tests/test_actors.py +backend/tests/test_authorization.py +backend/tests/test_projects.py +backend/scripts/api_contract_e2e.py +docs/operations_authorization_service.md +docs/spec_authorization_service.md +.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/** +.agent-loop/merge-intents/WS-AUTH-001-10B.json +.agent-loop/REVIEW_LOG.md +``` + +## Not allowed changes + +```text +migration or durable schema changes +issue/revoke routes or mutation behavior +PREP extension +task/review/project product cutover +identity-link, contact, skills, reputation, or cross-project activity disclosure +``` + +## Exact surface inventory + +| ActionId | PermissionId | Authority | Canonical target | Surface | +|---|---|---|---|---| +| `project.contributor_candidate.list` | `project.role_grant.manage` | covered Project Manager only | canonical project | `GET /api/v1/projects/{project_id}/contributor-candidates` | +| `project_role_grant.list` | `project.role_grant.read` | covered Project Manager or Audit Authority | canonical project | `GET /api/v1/projects/{project_id}/role-grants` | +| `project_role_grant.read` | `project.role_grant.read` | covered Project Manager or Audit Authority | grant joined to canonical project | `GET /api/v1/projects/{project_id}/role-grants/{grant_id}` | + +All are human-only, owned by `WS-AUTH-001-10B`, active with their route, and +use non-locking canonical project reads. System-scoped candidates cover every +project only for the retained permission; project scope must equal the loaded +project. Services, agents, and Space principals deny before lookup. + +## Acceptance criteria + +- Candidate SQL filters active human profile, active link, and caller exclusion + before total/cursor calculation and returns only ID plus nullable display name. +- Candidate discovery allows draft, active, and paused projects and conceals + terminal/archived, nonexistent, and unauthorized projects equivalently. +- Grant list/detail remain readable for every existing project state so + immutable history is inspectable; unauthorized/nonexistent/path-mismatch + cases are indistinguishable before disclosure. +- List/detail expose bounded grant/snapshot provenance but no external identity + or contact metadata. +- Existing cursor convention is reused and binds project, status, exact optional + role, limit, and boundary. Malformed/tampered/cross-project/cross-filter + cursors reveal neither count nor rows. +- Existing read rate control is reused. Every surface has exactly one active + OpenAPI declaration and manifest-delta proof. +- `/actors/me/authorization-context` remains absent and planned for AUTH-11. +- No write, PREP, idempotency, invalidation, or migration behavior changes. + +## Verification commands + +```bash +(cd backend && .venv/bin/python -m ruff check app/modules/actors app/modules/authorization app/modules/projects tests/test_actors.py tests/test_authorization.py tests/test_projects.py) +(cd backend && WORKSTREAM_TEST_ADMIN_DATABASE_URL= .venv/bin/python scripts/run_isolated_tests.py --metadata-json --timeout-seconds 300 -- .venv/bin/python -m pytest -q tests/test_actors.py tests/test_authorization.py tests/test_projects.py -k 'project_role or contributor_candidate') +(cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/python scripts/api_contract_e2e.py) +python3 scripts/check_stale_authorization_docs.py +python3 scripts/check_markdown_links.py +git diff --check +``` + +GitHub owns the full sharded suite, aggregate and subsystem coverage, API E2E, +and Agent Gates before PR readiness. + +## Required reviewers + +Senior engineering, QA/test, security/auth, product/ops, architecture, CI +integrity, docs, reuse/dedup, and test delta. + +## Human review focus + +Review scope coverage, filter-before-count, lifecycle concealment, minimal +candidate fields, cursor binding, and absence of mutation behavior. + +## Stop conditions + +Stop on client-supplied project truth, post-query authorization, count leakage, +new persistence, or any contributor capability implied by a read. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md new file mode 100644 index 000000000..acffc6bf5 --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md @@ -0,0 +1,143 @@ +# Chunk Contract: WS-AUTH-001-10C - Project Role Grant Mutations + +## Parent initiative + +`WS-AUTH-001` — Workstream Authorization Service + +## Goal + +Issue and revoke independent project contributor roles through PREP-bound, +idempotent, auditable, exact-project transactions. + +## Why this chunk exists + +Multi-principal locking, absence serialization, replay disclosure, audit, +invalidation, and failure atomicity require a mutation-only security review. + +## Risk class + +L1 authorization mutation and concurrency. + +## SLA + +P1 + +## Allowed files + +```text +backend/app/modules/actors/repository.py +backend/app/modules/authorization/** +backend/app/modules/projects/repository.py +backend/app/api/router.py +backend/tests/test_actors.py +backend/tests/test_authorization.py +backend/tests/test_projects.py +backend/scripts/api_contract_e2e.py +backend/scripts/auth_api_e2e.py +docs/operations_authorization_service.md +docs/spec_authorization_service.md +.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/** +.agent-loop/merge-intents/WS-AUTH-001-10C.json +.agent-loop/REVIEW_LOG.md +``` + +## Not allowed changes + +```text +migration or durable schema changes +read/candidate surface redesign +task assignment or review reconciliation implementation +admin/service/project-role authority substitution +`both`, replacement, automated grants, or role conversion +commit inside PREP, kernel, service, or repository +``` + +## Exact surface inventory + +| ActionId | PermissionId | Authority | Surface | +|---|---|---|---| +| `project_role_grant.issue` | `project.role_grant.manage` | covered Project Manager only | `POST /api/v1/projects/{project_id}/role-grants` | +| `project_role_grant.revoke` | `project.role_grant.manage` | covered Project Manager only | `POST /api/v1/projects/{project_id}/role-grants/{grant_id}/revoke` | + +Both are human-only, owned by `WS-AUTH-001-10C`, active with their route, and +use existing mutation rate controls. Audit Authority, Access Administrator, +Operator, Finance Authority, contributor grants, services, agents, and Space do +not substitute for Project Manager authority. + +## Exact lock and transaction order + +PREP is extended to accept the caller plus the known target human principal for +issuance. It sorts distinct ActorProfile IDs lexically and locks each profile +then its exact active link. It then locks the caller's one deterministic covered +Project Manager AdminRoleGrant. `AuthorityControl` is not locked because these +operations cannot remove final Access Administrator authority. + +After PREP returns, issue locks canonical project, then takes one transaction +advisory key for `(actor_profile_id, project_id, requested_role)` to serialize +absence, then reloads the target/scope facts and active exact-role selector. +Revoke locks canonical project then the exact grant. Consume recomposes every +fact and evaluates once; repositories flush only. The route commits once. + +Crossed-manager tests prove two managers targeting one another cannot deadlock +because principal locks share the same global ordering. + +## Acceptance criteria + +- Issue permits draft, active, and paused projects; terminal/archived, + nonexistent, and unauthorized projects deny without target disclosure. +- Revoke remains available for every existing project state so authority can + always be removed. +- Target is a different active human with active link. Agent, Space, service, + self-grant, and self-revoke deny stably before disclosure. +- Issue creates one exact-role snapshot, one manual grant, + `ProjectRoleQualificationSnapshotCaptured`, and `ProjectRoleGrantIssued` in + one transaction. It never changes another role. +- Revoke changes only the locked exact role and creates + `ProjectRoleGrantRevoked` plus one linked `AuthorityInvalidationRequested` + carrying grant, actor, project, role, and cause-event references. +- Submitter invalidation names only the future AUTH-13 assignment obligation; + reviewer names only its REV-owned obligation; adjudicator creates no product + obligation until separately activated. +- Same key/same request replay reloads canonical project, grant, and snapshot + and reauthorizes the current caller before response disclosure. Suspended, + revoked-link, removed/out-of-scope manager, or now-concealed project replay + denies without returning stale response data. +- Same key/different request or role is `idempotency_mismatch`; new-key active + same-role issue is one audited stable conflict; distinct roles may issue + concurrently. +- Cancellation and injected failure at snapshot, grant, idempotency, audit, + invalidation, flush, and commit leave no orphan row or partial evidence. +- PostgreSQL tests cover identical-role issue, different-role issue, crossed + managers, revoke versus regrant, revoke versus authorization, replay after + authority loss, timeout, and cancellation. +- Live API proof uses the same unexpired token to issue, exercise visibility, + revoke, and observe immediate denial without direct database edits. + +## Verification commands + +```bash +(cd backend && .venv/bin/python -m ruff check app/modules/actors app/modules/authorization app/modules/projects tests/test_actors.py tests/test_authorization.py tests/test_projects.py scripts/auth_api_e2e.py) +(cd backend && WORKSTREAM_TEST_ADMIN_DATABASE_URL= .venv/bin/python scripts/run_isolated_tests.py --metadata-json --timeout-seconds 300 -- .venv/bin/python -m pytest -q tests/test_actors.py tests/test_authorization.py tests/test_projects.py -k 'project_role_grant') +(cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/python scripts/auth_api_e2e.py) +python3 scripts/check_stale_authorization_docs.py +python3 scripts/check_markdown_links.py +git diff --check +``` + +GitHub owns the full sharded suite, aggregate and authorization coverage, API +E2E, and Agent Gates before PR readiness. + +## Required reviewers + +Senior engineering, QA/test, security/auth, product/ops, architecture, CI +integrity, docs, reuse/dedup, and test delta. + +## Human review focus + +Review multi-principal lock order, no-self controls, exact-project scope, +replay reauthorization, role-specific invalidation, and single commit ownership. + +## Stop conditions + +Stop on target-after-caller deadlock ordering, unlocked absence checks, stale +replay disclosure, cross-role mutation, lifecycle implementation, or PREP bypass. diff --git a/.agent-loop/merge-intents/WS-AUTH-001-10.json b/.agent-loop/merge-intents/WS-AUTH-001-10.json new file mode 100644 index 000000000..81a7d138f --- /dev/null +++ b/.agent-loop/merge-intents/WS-AUTH-001-10.json @@ -0,0 +1,9 @@ +{ + "chunk_id": "WS-AUTH-001-10", + "chunk_title": "Project Qualification And Contributor Role Grants", + "initiative_id": "WS-AUTH-001", + "next_chunk_id": "WS-AUTH-001-10A", + "next_chunk_title": "Project Role Grant Data And Evidence Foundation", + "next_requires_explicit_start": true, + "schema_version": 2 +} diff --git a/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md b/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md index 3eb3c2fba..ed0781023 100644 --- a/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md +++ b/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md @@ -77,7 +77,8 @@ The coding agent is not permitted to reinterpret the following decisions. 8. Every active human ActorProfile belongs to the Contributor domain by default. 9. Contributor-domain membership is not task, submission, or review authority. 10. Project work requires an active `ProjectRoleGrant` for that exact project. -11. Project contributor roles are `submitter`, `reviewer`, or `both`. +11. Project contributor roles are independent `submitter`, `reviewer`, or + `adjudicator` grants; there is no combined role. 12. Project roles are explicitly granted by an authorized Project Manager in v0.1. 13. Skills and reputation may inform a ProjectRoleGrant but do not automatically create one. 14. Administrative authority is represented by separate `AdminRoleGrant` records. @@ -116,7 +117,7 @@ ActorProfile (one local Workstream actor) | | | +-- ProjectRoleGrant(project A, submitter) | +-- ProjectRoleGrant(project B, reviewer) - | +-- ProjectRoleGrant(project C, both) + | +-- ProjectRoleGrant(project C, adjudicator) | +-- Zero or more AdminRoleGrants +-- access_administrator @@ -305,11 +306,10 @@ ProjectRoleGrant: id: uuid project_id: uuid contributor_id: uuid - role: enum [submitter, reviewer, both] + role: enum [submitter, reviewer, adjudicator] status: enum [active, revoked] - grant_method: enum [manual, automated] - automation_policy_ref: uuid | null + grant_method: literal [manual] qualification_snapshot_ref: uuid granted_by: actor_id @@ -328,14 +328,15 @@ ProjectRoleGrant: - The grant is valid for one project only. - A Project Manager whose effective scope covers the project may issue or revoke it. - The issuing Project Manager cannot grant a ProjectRoleGrant to themselves. -- `grant_method = manual` is the only creation path enabled in v0.1. -- `grant_method = automated` is schema-reserved and MUST NOT be emitted by v0.1 code. -- `automation_policy_ref` is null for a manual grant. +- `grant_method = manual` is the only accepted creation value in v0.1. - `qualification_snapshot_ref` is mandatory and must match the same project and contributor. -- At most one active ProjectRoleGrant exists for a contributor in a project. -- Changing `submitter`, `reviewer`, or `both` requires revoking the active grant and creating a new grant in one transaction. -- A submitter action requires `role IN (submitter, both)`. -- A reviewer action requires `role IN (reviewer, both)`. +- At most one active ProjectRoleGrant exists for a contributor, project, and + exact role. All three distinct roles may be active concurrently. +- Revoking or regranting one role never changes another role. +- A submitter action requires an active exact `submitter` grant. +- A reviewer action requires an active exact `reviewer` grant. +- An `adjudicator` grant creates no adjudication capability until WS-REV defines + that lifecycle and AUTH activates its exact actions. - AdminRoleGrant does not satisfy either requirement. - Revocation does not delete or invalidate historical work completed while the grant was active. @@ -791,7 +792,7 @@ It may not claim tasks, create submissions, read reviewer queues, or review work ### 13.2 Submitter grant -An active `ProjectRoleGrant(submitter|both)` supplies candidate permissions for the same project: +An active exact `ProjectRoleGrant(submitter)` supplies candidate permissions for the same project: ```text project.read @@ -806,7 +807,7 @@ TaskAssignment state, task ban, task availability, policy state, and other submi ### 13.3 Reviewer grant -An active `ProjectRoleGrant(reviewer|both)` supplies candidate permissions for the same project: +An active exact `ProjectRoleGrant(reviewer)` supplies candidate permissions for the same project: ```text project.read @@ -821,9 +822,11 @@ review.chain.read Self-review, active-lease capacity, preferred-reviewer routing, lease state, and decision-state guards still apply. -### 13.4 Both grant +### 13.4 Adjudicator grant -`both` is the union of submitter and reviewer project permissions. It does not bypass self-review or any other resource guard. +An `adjudicator` grant is independently persisted authority evidence. It grants +no adjudication action until the separately approved WS-REV lifecycle and exact +AUTH activation exist. --- @@ -1118,25 +1121,31 @@ The transaction MUST: 3. require an active human target; 4. prohibit self-grant; 5. create the immutable qualification snapshot; -6. revoke an existing active grant only when the request explicitly replaces it; -7. create the new manual ProjectRoleGrant; -8. append audit and invalidation events where replacement occurred; -9. commit. +6. reject an existing active grant for the same exact role; +7. create the new manual ProjectRoleGrant without changing another role; +8. append snapshot and grant-issued audit evidence; +9. commit once. --- ## 21. API Contract -All endpoints use the independent `/v1` API namespace. This does not change the product release from Workstream v0.1. +Repository implementation uses the canonical `/api/v1` namespace. Any older +`/v1` example in this imported reference is superseded by that repository +decision. ### 21.1 Current actor ```http -GET /v1/actors/me -PATCH /v1/actors/me -GET /v1/actors/me/authorization-context?project_id={project_id} +GET /api/v1/actors/me +PATCH /api/v1/actors/me +GET /api/v1/actors/me/authorization-context?project_id={project_id} ``` +The authorization-context surface is assigned to AUTH-11, not AUTH-10. It +ships only with the first complete project-read cutover and must not advertise +inactive task or review actions. + PATCH request: ```json @@ -1221,10 +1230,11 @@ Create request: ### 21.6 Project-role grants ```http -POST /v1/projects/{project_id}/role-grants -GET /v1/projects/{project_id}/role-grants -GET /v1/projects/{project_id}/role-grants/{grant_id} -POST /v1/projects/{project_id}/role-grants/{grant_id}/revoke +GET /api/v1/projects/{project_id}/contributor-candidates +POST /api/v1/projects/{project_id}/role-grants +GET /api/v1/projects/{project_id}/role-grants +GET /api/v1/projects/{project_id}/role-grants/{grant_id} +POST /api/v1/projects/{project_id}/role-grants/{grant_id}/revoke ``` Create request: @@ -1241,7 +1251,7 @@ Create request: "prior_project_work_refs": [], "external_expertise_refs": [] }, - "replace_active_grant": false + "role": "submitter" } ``` @@ -1286,7 +1296,7 @@ Errors MUST use the Workstream structured error envelope. | 409 | `actor_deactivated_terminal` | Transition attempted from deactivated state | | 409 | `last_access_administrator` | Operation would remove final Access Administrator | | 409 | `admin_role_grant_exists` | Identical effective grant already active | -| 409 | `project_role_grant_exists` | Active project grant exists and replacement not requested | +| 409 | `project_role_grant_exists` | Active exact-role project grant exists | | 409 | `identity_link_conflict` | Issuer subject already maps to another ActorProfile | | 409 | `resource_project_mismatch` | Related resources belong to different projects | | 409 | `idempotency_mismatch` | Idempotency key reused with different request | @@ -1346,7 +1356,7 @@ AdminRoleGrant: access_administrator/operator => system scope ProjectRoleGrant: - role IN ('submitter', 'reviewer', 'both') + role IN ('submitter', 'reviewer', 'adjudicator') status IN ('active', 'revoked') manual => automation_policy_ref IS NULL ``` @@ -1384,7 +1394,9 @@ Two concurrent identical grant requests result in one active grant. An exact ide ### 24.3 Duplicate ProjectRoleGrant race -Two concurrent project-grant requests for one contributor/project result in one active grant. Replacement requires locking the active grant selector and an explicit replacement request. +Two concurrent same-role project-grant requests for one contributor/project +result in one active exact-role grant. Concurrent distinct-role requests may +each succeed and never replace one another. ### 24.4 Last Access Administrator race @@ -1448,7 +1460,6 @@ All events are append-only. - `ProjectRoleQualificationSnapshotCaptured` - `ProjectRoleGrantIssued` -- `ProjectRoleGrantReplaced` - `ProjectRoleGrantRevoked` ### Authorization @@ -1831,7 +1842,7 @@ Exit: actor-state and service-actor tests pass. - qualification snapshot; - project grant schema/constraints; - Project Manager authority; -- create/replace/revoke APIs; +- independent create/read/revoke APIs; - project permission candidates. Exit: project-grant and concurrency tests pass. From f9765c38d0213fb290ed8ba3d393057a672fbbcb Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 11:55:43 +0100 Subject: [PATCH 03/14] docs(auth): close project role grant plan gaps --- ...01-10A-project-role-grant-data-evidence.md | 24 ++++-- ...S-AUTH-001-10B-project-role-grant-reads.md | 22 +++-- ...TH-001-10C-project-role-grant-mutations.md | 46 +++++++++-- ...and-authorization-service-specification.md | 82 ++++++++++++------- 4 files changed, 124 insertions(+), 50 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md index b65b4e362..b3368c1e1 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md @@ -4,6 +4,11 @@ `WS-AUTH-001` — Workstream Authorization Service +## Status and prerequisite + +Proposed and inactive. Start only after planning parent AUTH-10 merges, signed +memory names 10A, and a fresh explicit start event activates this exact child. + ## Goal Create the immutable qualification-snapshot and independent three-role grant @@ -64,15 +69,16 @@ Each availability snapshot is exactly: ```text availability: available | unavailable -reference_ids: array[string], 0..20 items, each 1..120 UTF-8 bytes after - rejecting leading/trailing whitespace and control characters +reference_ids: array[string], 0..20 items, each matching + ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$ and containing no `://` unavailable_reason: not_collected | source_unavailable | no_record | null ``` `available` requires one or more reference IDs and null reason. `unavailable` requires no references and one reason. Prior-work references are 0..20 UUIDs. -External-expertise references are 0..20 tokens with the same byte/whitespace -rules. No free-form narrative, score, contact field, issuer subject, raw claim, +External-expertise references are 0..20 tokens with the same grammar. Issue and +revoke reasons are 1..500 UTF-8 bytes, must equal Python `str.strip()`, and +reject Unicode control characters. No free-form evidence narrative, score, contact field, issuer subject, raw claim, secret, URL credential, or automatically inferred authority is stored. `ProjectRoleGrant` is one immutable issuance row for exactly `submitter`, @@ -92,10 +98,12 @@ to revoked version 2 may mutate lifecycle fields. - Typed and PostgreSQL audit/idempotency validators accept only the three exact roles and issued/revoked success events. Replacement fields/events/reasons and `both` are absent. -- Upgrade refuses, without mutation, any obsolete combined/replacement evidence - in current authority audit/idempotency rows. Downgrade refuses without - mutation when either new table contains any row or new three-role evidence - cannot be represented by the prior validator. +- Upgrade refuses, without mutation, an audit/idempotency row whose project-role + facts contain `role='both'`, a replacement/supersession key, operation/event + `ProjectRoleGrantReplaced`, or replacement reason token. Downgrade refuses + without mutation when either new table contains any row or an authority audit + or idempotency row contains `role='adjudicator'`. Each predicate and the + combined predicate have no-mutation proof. - Fresh install, prior-head upgrade, downgrade/refusal, replay, constraints, immutability, and preserved unrelated history are proven on PostgreSQL. - No migration, model, schema, or fixture accepts automated creation. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md index df826dc5c..f18b6b244 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md @@ -4,6 +4,11 @@ `WS-AUTH-001` — Workstream Authorization Service +## Status and prerequisite + +Proposed and inactive. Start only after 10A merges, signed memory names 10B, +and a fresh explicit start event activates this exact child. + ## Goal Expose privacy-safe contributor candidates and project-role grant history @@ -29,9 +34,11 @@ backend/app/modules/actors/repository.py backend/app/modules/authorization/** backend/app/modules/projects/repository.py backend/app/api/router.py +backend/app/core/config.py backend/tests/test_actors.py backend/tests/test_authorization.py backend/tests/test_projects.py +backend/tests/test_config.py backend/scripts/api_contract_e2e.py docs/operations_authorization_service.md docs/spec_authorization_service.md @@ -74,9 +81,14 @@ project. Services, agents, and Space principals deny before lookup. cases are indistinguishable before disclosure. - List/detail expose bounded grant/snapshot provenance but no external identity or contact metadata. -- Existing cursor convention is reused and binds project, status, exact optional - role, limit, and boundary. Malformed/tampered/cross-project/cross-filter - cursors reveal neither count nor rows. +- 10B introduces one shared authorization pagination codec using HMAC-SHA256 and + a required base64-decoded 32-byte + `WORKSTREAM_PAGINATION_CURSOR_HMAC_SECRET`. The versioned cursor payload binds + action, project, status, exact optional role, limit, boundary timestamp/UUID, + and canonical query digest. Signature comparison is constant-time; + malformed/tampered/cross-project/cross-filter cursors reveal neither count + nor rows. The secret is configuration-only, never logged, serialized, stored, + or reused from auth/rate-limit secrets. - Existing read rate control is reused. Every surface has exactly one active OpenAPI declaration and manifest-delta proof. - `/actors/me/authorization-context` remains absent and planned for AUTH-11. @@ -85,8 +97,8 @@ project. Services, agents, and Space principals deny before lookup. ## Verification commands ```bash -(cd backend && .venv/bin/python -m ruff check app/modules/actors app/modules/authorization app/modules/projects tests/test_actors.py tests/test_authorization.py tests/test_projects.py) -(cd backend && WORKSTREAM_TEST_ADMIN_DATABASE_URL= .venv/bin/python scripts/run_isolated_tests.py --metadata-json --timeout-seconds 300 -- .venv/bin/python -m pytest -q tests/test_actors.py tests/test_authorization.py tests/test_projects.py -k 'project_role or contributor_candidate') +(cd backend && .venv/bin/python -m ruff check app/modules/actors app/modules/authorization app/modules/projects app/core/config.py tests/test_actors.py tests/test_authorization.py tests/test_projects.py tests/test_config.py) +(cd backend && WORKSTREAM_TEST_ADMIN_DATABASE_URL= .venv/bin/python scripts/run_isolated_tests.py --metadata-json --timeout-seconds 300 -- .venv/bin/python -m pytest -q tests/test_actors.py tests/test_authorization.py tests/test_projects.py tests/test_config.py -k 'project_role or contributor_candidate or pagination_cursor') (cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/python scripts/api_contract_e2e.py) python3 scripts/check_stale_authorization_docs.py python3 scripts/check_markdown_links.py diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md index acffc6bf5..875359e44 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md @@ -4,6 +4,11 @@ `WS-AUTH-001` — Workstream Authorization Service +## Status and prerequisite + +Proposed and inactive. Start only after 10B merges, signed memory names 10C, +and a fresh explicit start event activates this exact child. + ## Goal Issue and revoke independent project contributor roles through PREP-bound, @@ -66,11 +71,12 @@ not substitute for Project Manager authority. ## Exact lock and transaction order -PREP is extended to accept the caller plus the known target human principal for -issuance. It sorts distinct ActorProfile IDs lexically and locks each profile -then its exact active link. It then locks the caller's one deterministic covered -Project Manager AdminRoleGrant. `AuthorityControl` is not locked because these -operations cannot remove final Access Administrator authority. +PREP first locks `AuthorityControl(id=1)` as the shared authorization-order +barrier used by administrative lifecycle mutations. It then accepts the caller +plus the known target human principal for issuance, sorts distinct ActorProfile +IDs lexically, and locks each profile then its exact active link. It finally +locks the caller's one deterministic covered Project Manager AdminRoleGrant. +The barrier is ordering coordination, not final-admin permission or safety. After PREP returns, issue locks canonical project, then takes one transaction advisory key for `(actor_profile_id, project_id, requested_role)` to serialize @@ -78,8 +84,27 @@ absence, then reloads the target/scope facts and active exact-role selector. Revoke locks canonical project then the exact grant. Consume recomposes every fact and evaluates once; repositories flush only. The route commits once. -Crossed-manager tests prove two managers targeting one another cannot deadlock -because principal locks share the same global ordering. +Crossed tests prove two managers targeting one another, issue versus target +profile/link lifecycle, and issue versus caller-grant revocation cannot deadlock +because all paths share the barrier and compatible principal order. + +## Exact request and response contract + +Issue requires `Idempotency-Key: ` and a strict body containing exactly +`target_actor_profile_id`, `role`, `qualification`, and `reason`. +`qualification` contains the two exact availability objects and reference lists +defined by 10A. Revoke requires the same header and exactly `reason`. Reasons +are 1..500 UTF-8 bytes, equal their Python `str.strip()` result, and contain no +Unicode control character. Issue returns HTTP 201 with exactly `{id, +qualification_snapshot_id, project_id, actor_profile_id, role, status: +"active", version: 1}`. Revoke returns HTTP 200 with the same fields, the +original snapshot ID, `status: "revoked"`, and `version: 2`. Strict response +schemas reject undeclared fields, including identity-link and contact data. +Stable errors are HTTP 400 `invalid_request`, HTTP 403 +`self_grant_forbidden` or `self_role_revoke_forbidden`, concealed HTTP 404 +`resource_not_found`, HTTP 409 `idempotency_mismatch` or +`project_role_grant_exists`, HTTP 422 `qualification_snapshot_invalid`, and +fail-closed HTTP 503 `service_unavailable`. ## Acceptance criteria @@ -110,8 +135,11 @@ because principal locks share the same global ordering. - PostgreSQL tests cover identical-role issue, different-role issue, crossed managers, revoke versus regrant, revoke versus authorization, replay after authority loss, timeout, and cancellation. -- Live API proof uses the same unexpired token to issue, exercise visibility, - revoke, and observe immediate denial without direct database edits. +- Live API proof uses the same unexpired manager token to issue, read the active + grant through 10B, revoke, read the historical revoked grant, and prove a + second revoke and an old replay are reauthorized/denied without direct + database edits. Contributor-capability denial remains explicitly deferred to + AUTH-11/13 where a real consumer action exists. ## Verification commands diff --git a/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md b/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md index ed0781023..39eeecbc6 100644 --- a/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md +++ b/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md @@ -278,15 +278,22 @@ The qualification snapshot records the evidence visible when a Project Manager m ProjectRoleQualificationSnapshot: id: uuid project_id: uuid - contributor_id: uuid + actor_profile_id: uuid + requested_role: enum [submitter, reviewer, adjudicator] - skills_snapshot: object - reputation_snapshot: object + skills_snapshot: QualificationAvailabilitySnapshot + reputation_snapshot: QualificationAvailabilitySnapshot prior_project_work_refs: list[uuid] - external_expertise_refs: list[string] + external_expertise_refs: list[bounded_reference_token] - captured_by: actor_id + captured_by_actor_profile_id: uuid + captured_by_admin_role_grant_id: uuid captured_at: timestamp + +QualificationAvailabilitySnapshot: + availability: enum [available, unavailable] + reference_ids: list[bounded_reference_token] + unavailable_reason: enum [not_collected, source_unavailable, no_record] | null ``` #### Qualification snapshot invariants @@ -296,6 +303,13 @@ ProjectRoleQualificationSnapshot: - Missing reputation does not block a manual v0.1 grant. - The snapshot is evidence for the grant decision, not an authorization decision by itself. - Snapshot payloads MUST exclude secrets and unnecessary personal information. +- Each availability `reference_ids` list contains zero to 20 tokens. A bounded + reference token is one to 120 characters, matches + `^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$`, and MUST NOT contain `://`. +- `available` requires at least one reference and a null unavailable reason; + `unavailable` requires no references and exactly one unavailable reason. +- `prior_project_work_refs` contains zero to 20 UUIDs and + `external_expertise_refs` contains zero to 20 bounded reference tokens. ### 6.5 ProjectRoleGrant @@ -305,19 +319,19 @@ ProjectRoleQualificationSnapshot: ProjectRoleGrant: id: uuid project_id: uuid - contributor_id: uuid + actor_profile_id: uuid role: enum [submitter, reviewer, adjudicator] status: enum [active, revoked] grant_method: literal [manual] - qualification_snapshot_ref: uuid + qualification_snapshot_id: uuid - granted_by: actor_id + granted_by_actor_profile_id: uuid granted_by_admin_role_grant_id: uuid granted_at: timestamp grant_reason: string - revoked_by: actor_id | null + revoked_by_actor_profile_id: uuid | null revoked_at: timestamp | null revoked_reason: string | null ``` @@ -329,10 +343,13 @@ ProjectRoleGrant: - A Project Manager whose effective scope covers the project may issue or revoke it. - The issuing Project Manager cannot grant a ProjectRoleGrant to themselves. - `grant_method = manual` is the only accepted creation value in v0.1. -- `qualification_snapshot_ref` is mandatory and must match the same project and contributor. +- `qualification_snapshot_id` is mandatory and its snapshot must match the same + project, actor profile, and exact requested role. - At most one active ProjectRoleGrant exists for a contributor, project, and exact role. All three distinct roles may be active concurrently. - Revoking or regranting one role never changes another role. +- Grant and revocation reasons are one to 500 UTF-8 bytes, equal their Python + `str.strip()` result, and contain no Unicode control character. - A submitter action requires an active exact `submitter` grant. - A reviewer action requires an active exact `reviewer` grant. - An `adjudicator` grant creates no adjudication capability until WS-REV defines @@ -1241,25 +1258,31 @@ Create request: ```json { - "contributor_id": "uuid", - "role": "reviewer", - "grant_method": "manual", - "grant_reason": "Qualified project reviewer", + "target_actor_profile_id": "uuid", + "role": "submitter", + "reason": "Qualified project submitter", "qualification": { - "skills_snapshot": {}, - "reputation_snapshot": {"status": "unavailable"}, + "skills_snapshot": { + "availability": "available", + "reference_ids": ["skill:python"], + "unavailable_reason": null + }, + "reputation_snapshot": { + "availability": "unavailable", + "reference_ids": [], + "unavailable_reason": "no_record" + }, "prior_project_work_refs": [], "external_expertise_refs": [] - }, - "role": "submitter" + } } ``` ### 21.7 Permission catalog ```http -GET /v1/authorization/permissions -GET /v1/authorization/admin-role-definitions +GET /api/v1/authorization/permissions +GET /api/v1/authorization/admin-role-definitions ``` These endpoints return registered definitions. They do not expose or accept arbitrary dynamic policy code. @@ -1326,7 +1349,7 @@ AdminRoleGrant: WHERE status = 'active' AND scope_type = 'project' ProjectRoleGrant: - partial UNIQUE(project_id, contributor_id) + partial UNIQUE(project_id, actor_profile_id, role) WHERE status = 'active' ProjectRoleQualificationSnapshot: @@ -1358,7 +1381,7 @@ AdminRoleGrant: ProjectRoleGrant: role IN ('submitter', 'reviewer', 'adjudicator') status IN ('active', 'revoked') - manual => automation_policy_ref IS NULL + grant_method = 'manual' ``` Foreign keys MUST prevent project/snapshot/contributor mismatches. Composite foreign keys MUST be used where necessary. @@ -1371,9 +1394,9 @@ ActorProfile(last_seen_at) ActorIdentityLink(issuer, subject, status) AdminRoleGrant(actor_profile_id, status) AdminRoleGrant(role, scope_type, scope_project_id, status) -ProjectRoleGrant(project_id, contributor_id, status) -ProjectRoleGrant(contributor_id, status) -ProjectRoleQualificationSnapshot(project_id, contributor_id, captured_at) +ProjectRoleGrant(project_id, actor_profile_id, role, status) +ProjectRoleGrant(actor_profile_id, role, status) +ProjectRoleQualificationSnapshot(project_id, actor_profile_id, requested_role, captured_at) AuditEvent(actor_profile_id, occurred_at) AuditEvent(project_id, occurred_at) ``` @@ -1702,10 +1725,12 @@ Alert on: - Missing reputation may be recorded unavailable. - Submitter grant cannot review. - Reviewer grant cannot claim submission tasks. -- Both grant supplies union but not self-review bypass. +- Concurrent active submitter and reviewer grants supply the union of their + exact permissions but do not bypass self-review guards. - Admin role alone cannot submit or review. - Duplicate active project grant is prevented. -- Replacement creates a new grant and revokes old history. +- Regrant after revocation creates a new row and preserves revoked history; + issuance never implicitly replaces or revokes another row. - Revocation is visible on next request. ### 31.6 Permission matrix tests @@ -1732,7 +1757,8 @@ For every permission identifier, create table-driven tests proving: - Concurrent human first access leaves one profile/link. - Concurrent identical AdminRoleGrant leaves one active grant. -- Concurrent ProjectRoleGrant leaves one active grant. +- Concurrent same-exact-role ProjectRoleGrant issue leaves one active grant; + concurrent distinct-role issue may leave each independent role active. - Concurrent final-admin suspension/revocation attempts leave at least one effective Access Administrator. - Grant revocation versus sensitive command produces one serializable authority outcome. From 83816ff2a659c75310da25c0f918f9181b35b50b Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 11:58:56 +0100 Subject: [PATCH 04/14] docs(auth): freeze project role grant interfaces --- ...01-10A-project-role-grant-data-evidence.md | 21 +++++++++++++------ ...S-AUTH-001-10B-project-role-grant-reads.md | 20 ++++++++++++++++-- ...TH-001-10C-project-role-grant-mutations.md | 16 +++++++++----- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md index b3368c1e1..aba2cd227 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md @@ -98,12 +98,21 @@ to revoked version 2 may mutate lifecycle fields. - Typed and PostgreSQL audit/idempotency validators accept only the three exact roles and issued/revoked success events. Replacement fields/events/reasons and `both` are absent. -- Upgrade refuses, without mutation, an audit/idempotency row whose project-role - facts contain `role='both'`, a replacement/supersession key, operation/event - `ProjectRoleGrantReplaced`, or replacement reason token. Downgrade refuses - without mutation when either new table contains any row or an authority audit - or idempotency row contains `role='adjudicator'`. Each predicate and the - combined predicate have no-mutation proof. +- Upgrade inspects exact existing storage and refuses before DDL when any + `audit_events` row with `event_domain='authority'` has + `before_facts->>'role'='both'`, `after_facts->>'role'='both'`, a + `replaced_grant_id` key in either facts object, + `event_type='ProjectRoleGrantReplaced'`, or + `reason='authority_replacement'`. It also refuses any + `authority_idempotency_records.operation` in + `('project_role_grant.issue','project_role_grant.revoke')`, because those + records contain only a digest/resource reference and cannot prove an + independent-role request safely. No other replacement/supersession key or + fuzzy reason search is implied. +- Downgrade refuses before DDL when either new table contains any row or an + authority audit row has `before_facts->>'role'='adjudicator'` or + `after_facts->>'role'='adjudicator'`. Each individual predicate and their + combined form have transaction-level no-mutation proof. - Fresh install, prior-head upgrade, downgrade/refusal, replay, constraints, immutability, and preserved unrelated history are proven on PostgreSQL. - No migration, model, schema, or fixture accepts automated creation. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md index f18b6b244..c80b90912 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md @@ -79,8 +79,24 @@ project. Services, agents, and Space principals deny before lookup. - Grant list/detail remain readable for every existing project state so immutable history is inspectable; unauthorized/nonexistent/path-mismatch cases are indistinguishable before disclosure. -- List/detail expose bounded grant/snapshot provenance but no external identity - or contact metadata. +- Candidate accepts only `limit` (default 50, range 1..100) and `cursor` (at + most 512 characters). Its item is exactly `{actor_profile_id, display_name}` + with nullable display name. Grant list accepts only `status` (optional exact + `active|revoked`), `role` (optional exact + `submitter|reviewer|adjudicator`), `limit` (default 50, range 1..100), and + `cursor` (at most 512 characters). Both list envelopes are exactly + `{items, next_cursor}` and intentionally expose no total count. +- Grant list items and detail responses share one strict schema containing + exactly `id`, `project_id`, `actor_profile_id`, `role`, `status`, `version`, + `grant_method`, `qualification_snapshot`, + `granted_by_actor_profile_id`, `granted_by_admin_role_grant_id`, `granted_at`, + `grant_reason`, `revoked_by_actor_profile_id`, `revoked_at`, and + `revoked_reason`. The nested snapshot contains exactly `id`, `requested_role`, + the two 10A availability objects, `prior_project_work_refs`, + `external_expertise_refs`, `captured_by_actor_profile_id`, + `captured_by_admin_role_grant_id`, and `captured_at`. Nullable revocation + fields remain present. Strict schemas reject all other fields, especially + identity issuer/subject/link status, contact data, raw claims, and secrets. - 10B introduces one shared authorization pagination codec using HMAC-SHA256 and a required base64-decoded 32-byte `WORKSTREAM_PAGINATION_CURSOR_HMAC_SECRET`. The versioned cursor payload binds diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md index 875359e44..6680ee5d2 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md @@ -103,8 +103,11 @@ schemas reject undeclared fields, including identity-link and contact data. Stable errors are HTTP 400 `invalid_request`, HTTP 403 `self_grant_forbidden` or `self_role_revoke_forbidden`, concealed HTTP 404 `resource_not_found`, HTTP 409 `idempotency_mismatch` or -`project_role_grant_exists`, HTTP 422 `qualification_snapshot_invalid`, and -fail-closed HTTP 503 `service_unavailable`. +`project_role_grant_exists` or `project_role_grant_already_revoked`, HTTP 422 +`qualification_snapshot_invalid`, and fail-closed HTTP 503 +`service_unavailable`. A same-key/same-body revoke replay reauthorizes and +returns the prior canonical HTTP 200 revoked response; a new-key revoke of an +already-revoked grant returns HTTP 409 `project_role_grant_already_revoked`. ## Acceptance criteria @@ -137,9 +140,12 @@ fail-closed HTTP 503 `service_unavailable`. authority loss, timeout, and cancellation. - Live API proof uses the same unexpired manager token to issue, read the active grant through 10B, revoke, read the historical revoked grant, and prove a - second revoke and an old replay are reauthorized/denied without direct - database edits. Contributor-capability denial remains explicitly deferred to - AUTH-11/13 where a real consumer action exists. + same-key/same-body revoke replay returns the prior HTTP 200 response after + reauthorization while a new-key second revoke returns HTTP 409 + `project_role_grant_already_revoked`; an old issue replay after revocation is + reauthorized and denied without stale disclosure or direct database edits. + Contributor-capability denial remains explicitly deferred to AUTH-11/13 where + a real consumer action exists. ## Verification commands From 77cdda43cef9bb99ea4e2caf3dce73f9283d63e2 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:00:52 +0100 Subject: [PATCH 05/14] docs(auth): define grant replay state conflict --- ...UTH-001-10C-project-role-grant-mutations.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md index 6680ee5d2..4e88497d0 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md @@ -103,11 +103,16 @@ schemas reject undeclared fields, including identity-link and contact data. Stable errors are HTTP 400 `invalid_request`, HTTP 403 `self_grant_forbidden` or `self_role_revoke_forbidden`, concealed HTTP 404 `resource_not_found`, HTTP 409 `idempotency_mismatch` or -`project_role_grant_exists` or `project_role_grant_already_revoked`, HTTP 422 +`project_role_grant_exists`, `project_role_grant_already_revoked`, or +`project_role_grant_replay_state_changed`, HTTP 422 `qualification_snapshot_invalid`, and fail-closed HTTP 503 -`service_unavailable`. A same-key/same-body revoke replay reauthorizes and -returns the prior canonical HTTP 200 revoked response; a new-key revoke of an -already-revoked grant returns HTTP 409 `project_role_grant_already_revoked`. +`service_unavailable`. A same-key/same-body issue replay reauthorizes and +returns the prior canonical HTTP 201 response while the grant remains active; +after that grant is revoked it returns HTTP 409 +`project_role_grant_replay_state_changed` without the stale response. A +same-key/same-body revoke replay reauthorizes and returns the prior canonical +HTTP 200 revoked response; a new-key revoke of an already-revoked grant returns +HTTP 409 `project_role_grant_already_revoked`. ## Acceptance criteria @@ -143,7 +148,10 @@ already-revoked grant returns HTTP 409 `project_role_grant_already_revoked`. same-key/same-body revoke replay returns the prior HTTP 200 response after reauthorization while a new-key second revoke returns HTTP 409 `project_role_grant_already_revoked`; an old issue replay after revocation is - reauthorized and denied without stale disclosure or direct database edits. + reauthorized and returns HTTP 409 `project_role_grant_replay_state_changed` + without stale disclosure or direct database edits. Unit and PostgreSQL tests + assert the same-key active issue replay remains HTTP 201 and the post-revoke + issue replay changes to that exact closed conflict. Contributor-capability denial remains explicitly deferred to AUTH-11/13 where a real consumer action exists. From 2956dec0b0c370cd4786a43b57569122a1dc6280 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:01:37 +0100 Subject: [PATCH 06/14] docs(auth): reserve project grant evidence vocabulary --- ...01-10A-project-role-grant-data-evidence.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md index aba2cd227..b01df88af 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md @@ -98,6 +98,26 @@ to revoked version 2 may mutate lifecycle fields. - Typed and PostgreSQL audit/idempotency validators accept only the three exact roles and issued/revoked success events. Replacement fields/events/reasons and `both` are absent. +- Migration `0031` reserves availability-neutral typed and PostgreSQL evidence + parity for exactly these future action/permission pairs, without adding an + `ActionDefinition`, `ActionOwner`, route, or callable behavior: + + ```text + project.contributor_candidate.list -> project.role_grant.manage + project_role_grant.list -> project.role_grant.read + project_role_grant.read -> project.role_grant.read + project_role_grant.issue -> project.role_grant.manage + project_role_grant.revoke -> project.role_grant.manage + ``` + + It likewise reserves exactly the future denial codes + `project_role_grant_already_revoked` and + `project_role_grant_replay_state_changed` in the typed and PostgreSQL denial + vocabularies. Reservation does not make an action active; 10B and 10C own + their respective action definitions, owners, routes, and emissions. +- Migration and schema tests prove all five pairs and both denial codes are + admitted, a neighboring unreserved value is rejected, and the action + registry/owner manifest remains unchanged by 10A. - Upgrade inspects exact existing storage and refuses before DDL when any `audit_events` row with `event_domain='authority'` has `before_facts->>'role'='both'`, `after_facts->>'role'='both'`, a From fd851cbe514eb1c16e3fc0e076cb7f67ba1aa134 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:05:22 +0100 Subject: [PATCH 07/14] docs(auth): assign planned action catalogue custody --- .../DECISIONS.md | 8 +++-- .../PLAN.md | 7 ++-- .../WS-AUTH-001-10-project-role-grants.md | 5 +-- ...01-10A-project-role-grant-data-evidence.md | 33 ++++++++++--------- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DECISIONS.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DECISIONS.md index bc8568638..ca618f4a3 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DECISIONS.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/DECISIONS.md @@ -692,9 +692,11 @@ review rejected the combined runtime boundary. AUTH-10 is a planning-only parent and splits into exactly three sequential same-initiative children. AUTH-10A owns migration `0031_project_role_grants`, the immutable qualification/grant aggregates, and -the typed/PostgreSQL three-role evidence clean cut; it activates no action or -route. AUTH-10B owns exactly the candidate, grant-list, and grant-detail read -actions and privacy-safe pagination. AUTH-10C owns exactly issue and revoke, +the typed/PostgreSQL three-role evidence clean cut. It registers all five +project-role actions as planned with exact AUTH-10B/AUTH-10C owners but activates +no action or route. AUTH-10B owns activation of exactly the candidate, +grant-list, and grant-detail read actions and privacy-safe pagination. AUTH-10C +owns activation of exactly issue and revoke, multi-principal PREP ordering, idempotency, audit, invalidation, and mutation concurrency. Each child requires a separate signed start and stop. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md index dc9fed9dc..6c3275753 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/PLAN.md @@ -193,9 +193,10 @@ proving the same issuer role metadata alone no longer authorizes. protocol. 12. Close planning parent AUTH-10, then deliver project-role authority in three separately started children: 10A establishes migration `0031`, immutable - snapshots/grants, and the typed/PostgreSQL three-role clean cut without an - active surface; 10B activates candidate/list/detail reads; 10C activates - PREP-bound issue/revoke mutations and concurrency proof. + snapshots/grants, the typed/PostgreSQL three-role clean cut, and all five + planned action rows with exact 10B/10C owners but no active surface; 10B + activates candidate/list/detail reads; 10C activates PREP-bound + issue/revoke mutations and concurrency proof. 13. Cut project identity, guide, source, and visibility queries over to local permissions. 14. Cut project policy mutations, approvals, activation, and setup operations diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md index 534d52a29..a46fd567b 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md @@ -66,8 +66,9 @@ starting AUTH-10A before this parent merges and signed memory stops - Every child has a complete contract with allowed files, exclusions, acceptance criteria, verification, reviewers, human focus, and stop rules. - Migration `0031` belongs only to 10A; 10B and 10C add no migration. -- 10A activates no action or route; 10B owns exactly three read actions; 10C - owns exactly two mutation actions. +- 10A registers all five actions as planned with exact 10B/10C owner values and + activates no action or route; 10B owns activation of exactly three read + actions; 10C owns activation of exactly two mutation actions. - Project lifecycle behavior is frozen: discovery and issuance allow draft, active, and paused projects; list/detail and revocation remain available for every existing project state so evidence is inspectable and authority can diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md index b01df88af..2c574a3d8 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md @@ -12,8 +12,8 @@ memory names 10A, and a fresh explicit start event activates this exact child. ## Goal Create the immutable qualification-snapshot and independent three-role grant -truth plus typed/PostgreSQL evidence parity, without exposing a route or active -action. +truth plus typed/PostgreSQL evidence parity and planned action registrations, +without exposing a route or active action. ## Why this chunk exists @@ -50,7 +50,7 @@ docs/spec_authorization_service.md ```text API routes or OpenAPI declarations -new or active ActionId/ActionOwner rows +active ActionDefinition rows or callable action behavior authorization kernel or PREP behavior candidate/list/detail/issue/revoke services project/task/review lifecycle behavior @@ -98,26 +98,29 @@ to revoked version 2 may mutate lifecycle fields. - Typed and PostgreSQL audit/idempotency validators accept only the three exact roles and issued/revoked success events. Replacement fields/events/reasons and `both` are absent. -- Migration `0031` reserves availability-neutral typed and PostgreSQL evidence - parity for exactly these future action/permission pairs, without adding an - `ActionDefinition`, `ActionOwner`, route, or callable behavior: +- 10A adds the five `ActionId` enum members and closed `ActionDefinition` rows + below with `ActionAvailability.PLANNED`; it adds exact `ActionOwner.AUTH_10B` + and `ActionOwner.AUTH_10C` enum values and assigns each row to its named + future owner. Migration `0031` reserves matching PostgreSQL evidence parity. + No route or callable behavior is added: ```text - project.contributor_candidate.list -> project.role_grant.manage - project_role_grant.list -> project.role_grant.read - project_role_grant.read -> project.role_grant.read - project_role_grant.issue -> project.role_grant.manage - project_role_grant.revoke -> project.role_grant.manage + project.contributor_candidate.list -> project.role_grant.manage -> AUTH_10B + project_role_grant.list -> project.role_grant.read -> AUTH_10B + project_role_grant.read -> project.role_grant.read -> AUTH_10B + project_role_grant.issue -> project.role_grant.manage -> AUTH_10C + project_role_grant.revoke -> project.role_grant.manage -> AUTH_10C ``` It likewise reserves exactly the future denial codes `project_role_grant_already_revoked` and `project_role_grant_replay_state_changed` in the typed and PostgreSQL denial - vocabularies. Reservation does not make an action active; 10B and 10C own - their respective action definitions, owners, routes, and emissions. + vocabularies. Planned registration does not make an action active; 10B and + 10C own the transition to active availability, routes, and emissions for + their respective rows. - Migration and schema tests prove all five pairs and both denial codes are - admitted, a neighboring unreserved value is rejected, and the action - registry/owner manifest remains unchanged by 10A. + admitted, a neighboring unreserved value is rejected, every new catalogue row + is planned with its exact owner, and no route/OpenAPI surface exists in 10A. - Upgrade inspects exact existing storage and refuses before DDL when any `audit_events` row with `event_domain='authority'` has `before_facts->>'role'='both'`, `after_facts->>'role'='both'`, a From ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:08:43 +0100 Subject: [PATCH 08/14] docs(auth): permit planned catalogue registration --- .../WS-AUTH-001-10A-project-role-grant-data-evidence.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md index 2c574a3d8..0d223dfa6 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md @@ -34,6 +34,7 @@ P1 backend/app/modules/authorization/models.py backend/app/modules/authorization/schemas.py backend/app/modules/authorization/repository.py +backend/app/modules/authorization/catalogue.py backend/app/modules/audit/** backend/app/db/models.py backend/alembic/versions/0031_project_role_grants.py @@ -89,7 +90,8 @@ to revoked version 2 may mutate lifecycle fields. ## Acceptance criteria -- Migration `0031` creates both authorization-owned tables and no route/action. +- Migration `0031` creates both authorization-owned tables; 10A adds no route, + active action, or callable behavior. - Composite key `(snapshot_id, actor_profile_id, project_id, requested_role)` is referenced by the matching grant facts. - Partial uniqueness permits one active actor/project/exact-role row while all From e0af0a6dddb5ede4707d309d2894409344733446 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:12:31 +0100 Subject: [PATCH 09/14] docs(auth): add AUTH-10 planning trust evidence --- .agent-loop/REVIEW_LOG.md | 15 ++++ ...WS-AUTH-001-10-internal-review-evidence.md | 62 +++++++++++++++ .../reviews/WS-AUTH-001-10-pr-trust-bundle.md | 77 +++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md diff --git a/.agent-loop/REVIEW_LOG.md b/.agent-loop/REVIEW_LOG.md index e6bd76f0d..405e08207 100644 --- a/.agent-loop/REVIEW_LOG.md +++ b/.agent-loop/REVIEW_LOG.md @@ -2719,3 +2719,18 @@ any branch-policy-required renewed approval remain after publication. The later documentation-only audit reconciliation is reviewed independently and bound by the chunk's internal-review evidence; it does not replace `acc15aa5` as the canonical implementation and closeout evidence head. +## 2026-07-21 - WS-AUTH-001-10 Internal Plan Review + +The inherited combined AUTH-10 runtime contract failed L1 plan review because +it mixed migration/evidence, privacy-sensitive reads, and PREP-bound mutations. +The user approved a sequential 10A/10B/10C design. The repaired plan freezes +independent submitter/reviewer/adjudicator grants, exact API and privacy shapes, +deterministic lock/replay behavior, migration refusal predicates, and planned +catalogue/evidence custody without activating a route or changing runtime. + +Planning candidate `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` passes +senior engineering, QA/test, security/auth, product/ops, architecture, CI +integrity, docs, reuse/dedup, and test-delta review with no open finding. Stale +authorization wording, Markdown links, merge-intent validation, and diff +integrity pass. GitHub Agent Gates, CodeRabbit, and human review remain; 10A +requires a separate signed start after this planning parent merges. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md new file mode 100644 index 000000000..468e2e463 --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md @@ -0,0 +1,62 @@ +# WS-AUTH-001-10 Internal Review Evidence + +Reviewed planning SHA: `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` + +Reviewed against trusted main: `5a8a924d9b3b347d4cc74b4682865518539c837e` + +Reviewed at: `2026-07-21T11:10:59Z` + +Reviewer run IDs: `auth10_plan_core`, `auth10_plan_security_qa`, +`auth10_plan_ops_ci_docs` + +Reviewer tracks: senior engineering, QA/test, security/auth, product/ops, +architecture, CI integrity, docs, reuse/dedup, and test delta + +## Deterministic Evidence + +- `python3 scripts/check_stale_authorization_docs.py`: PASS. +- `python3 scripts/check_markdown_links.py`: PASS for all changed Markdown. +- `python3 scripts/update_post_merge_memory.py validate-merge-intent --base-ref origin/main`: + PASS for `WS-AUTH-001-10`, with same-initiative successor + `WS-AUTH-001-10A` and explicit start required. +- `git diff --check`: PASS. +- This parent changes planning/specification artifacts only. No runtime, + migration, test, CI, dependency, threshold, or product behavior changed. +- GitHub remains the owner of the full sharded suite, aggregate 78 percent and + changed-authorization 90 percent coverage gates, API E2E, and Agent Gates for + each implementation child. + +## Reviewer Results + +| Reviewer | Result | Blocking findings | Notes | +|---|---|---|---| +| senior engineering | PASS AFTER FIXES | none | The parent and three children are executable and correctly sequenced. | +| QA/test | PASS AFTER FIXES | none | Exact migration predicates, API schemas, replay states, and proof obligations are frozen. | +| security/auth | PASS AFTER FIXES | none | Privacy, lock ordering, replay reauthorization, disclosure, and fail-closed behavior are explicit. | +| product/ops | PASS AFTER FIXES | none | Independent roles, project lifecycle availability, and downstream consumer deferrals are coherent. | +| architecture | PASS AFTER FIXES | none | 10A owns data and planned catalogue registration; 10B/10C exclusively activate their rows. | +| CI integrity | PASS | none | No workflow, threshold, command, dependency, or hosted-test ownership was weakened. | +| docs | PASS AFTER FIXES | none | Parent, children, D32, plan, and canonical reference specification agree. | +| reuse/dedup | PASS AFTER FIXES | none | The design reuses the closed catalogue, PREP, repositories, evidence, and idempotency conventions. | +| test delta | PASS | none | No test was changed, removed, skipped, or weakened. | + +## Findings Resolved + +The initial combined runtime contract was rejected as too broad and split into +10A data/evidence, 10B reads, and 10C mutations. Review then closed stale +combined-role specification language, privacy and pagination ambiguity, +migration refusal predicates, action/evidence migration custody, PREP lock +ordering, strict request/response schemas, and issue/revoke replay states. The +final repair places all five future actions in the existing closed catalogue as +planned rows with exact 10B/10C owners; no parallel allowlist or active surface +is created. + +Valid findings addressed: yes + +Open sub-agent sessions: none after evidence publication review + +## Remaining Risk And Gate + +GitHub Agent Gates, CodeRabbit, and explicit human review remain. This parent +does not authorize migration or runtime work. After merge and signed memory +generation, 10A still requires a separate exact-main explicit start event. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md new file mode 100644 index 000000000..6200dfef9 --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md @@ -0,0 +1,77 @@ +# WS-AUTH-001-10 PR Trust Bundle + +## Chunk + +`WS-AUTH-001-10` - Project Qualification And Contributor Role Grants Planning + +Merge intent: `.agent-loop/merge-intents/WS-AUTH-001-10.json` + +## Goal And Human-Approved Intent + +Turn the approved independent project-role design into executable, separately +gated implementation contracts without shipping a migration or runtime surface. +The user approved the 10A/10B/10C split and asked to start this planning chunk. + +## What Changed And Why + +- Recasts AUTH-10 as a planning-only parent and declares 10A as its exact + same-initiative successor. +- Defines 10A for migration `0031`, immutable qualification/grant truth, + availability-neutral evidence parity, and five planned catalogue rows. +- Defines 10B for the three privacy-safe candidate/list/detail reads and a + signed, filter-bound pagination codec. +- Defines 10C for PREP-bound issue/revoke mutations, deterministic locking, + idempotency, audit/invalidation, replay, failure atomicity, and live API proof. +- Reconciles D32 and the canonical reference specification with independent + submitter, reviewer, and adjudicator grants. + +The split was chosen because schema/evidence, privacy-sensitive reads, and +multi-principal mutations have different risks and proof boundaries. One +combined implementation PR was rejected during L1 plan review. + +## Scope And Product Behavior + +Only initiative planning, chunk contracts, the canonical reference spec, one +merge intent, review evidence, and this trust bundle change. No Workstream +product behavior, migration, action availability, route, CI workflow, or test +changes in this parent. + +## Acceptance Proof And Test Delta + +- Stale authorization documentation: PASS. +- Markdown links: PASS. +- Merge-intent validation: PASS. +- Diff integrity: PASS. +- All nine required internal review tracks pass exact planning SHA + `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` after fixes. +- No tests were added, modified, removed, skipped, or weakened because this is + a planning-only parent. Each child owns focused local tests; GitHub owns its + full suite and coverage proof. + +## CI Integrity + +Coverage floors, lint/typecheck commands, workflow behavior, dependencies, and +test selection are unchanged. Child contracts preserve GitHub ownership of the +full sharded suite, aggregate 78 percent coverage, authorization 90 percent +coverage, API E2E, and Agent Gates. + +## External Review And Remaining Risks + +CodeRabbit and GitHub checks are pending publication. Remaining implementation +risk is intentionally isolated into the three children: migration privacy and +refusal in 10A, read disclosure/cursors in 10B, and mutation concurrency/replay +in 10C. None may start from this PR alone. + +## Follow-Up And Human Review Focus + +After this parent merges, trusted automation must name 10A and a fresh explicit +start event must activate it. Review the independent-role model, exact child +boundaries, 10A planned-versus-active distinction, lock ordering, privacy-safe +read schemas, replay state machine, and the absence of runtime or migration +changes in this parent. + +## Human Merge Ownership + +The agent may publish and repair this branch but may not merge it. Only the user +may approve this specific PR for merge. Trusted-main automation owns signed +post-merge memory generation. From 1623e5b2dd85cc65df92af89989fda2ce7881bd0 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:19:47 +0100 Subject: [PATCH 10/14] docs(auth): repair AUTH-10 external gates --- .agent-loop/REVIEW_LOG.md | 11 +++++ .../STATUS.md | 16 +++---- ...WS-AUTH-001-10-external-review-response.md | 42 +++++++++++++++++++ ...WS-AUTH-001-10-internal-review-evidence.md | 2 +- .../reviews/WS-AUTH-001-10-pr-trust-bundle.md | 12 ++++-- 5 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md diff --git a/.agent-loop/REVIEW_LOG.md b/.agent-loop/REVIEW_LOG.md index 405e08207..b9903d140 100644 --- a/.agent-loop/REVIEW_LOG.md +++ b/.agent-loop/REVIEW_LOG.md @@ -2734,3 +2734,14 @@ integrity, docs, reuse/dedup, and test-delta review with no open finding. Stale authorization wording, Markdown links, merge-intent validation, and diff integrity pass. GitHub Agent Gates, CodeRabbit, and human review remain; 10A requires a separate signed start after this planning parent merges. + +## 2026-07-21 - WS-AUTH-001-10 External Gate Repair + +PR #168 preflight rejected a noncanonical internal-evidence SHA heading, while +Agent Gates rejected branch-local live state in AUTH `STATUS.md`. The repair +uses the required `Reviewed code SHA` provenance label and restores the authored +status file to trusted-main state; signed automation remains the sole owner of +live active/review projection. The Backend aggregate failure was consequential: +preflight blocked shards and API E2E, so no runtime test failed. All 88 Agent +Gate regression tests pass locally. Exact-SHA internal repair review, an +evidence-only binding descendant, fresh GitHub checks, and CodeRabbit remain. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md index 6ea507645..587663f90 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/STATUS.md @@ -111,19 +111,16 @@ or consumer feature action is active. ## Active planning chunk -`WS-AUTH-001-10`. Signed start event -`github-actions:29815937933:start` activated the exact recorded successor on -2026-07-21. Required L1 plan review rejected the combined runtime scope; the -user approved the planning-only 10A/10B/10C split, now under exact review. +None. `WS-AUTH-001-XINT` merged through PR #140. ## Active implementation chunk -None. AUTH-10 runtime and migration edits remain blocked until the repaired -contract passes required L1 plan review. +None. `WS-AUTH-001-09E` merged through PR #157 as `42a89b2d` without changing +feature action availability. ## Current review branch -`codex/ws-auth-001-10-project-role-grants`. +None. ## Chunk status @@ -158,10 +155,7 @@ contract passes required L1 plan review. | `WS-AUTH-001-ART-CUSTODY` | Merged | `codex/ws-auth-001-art-custody` | #158 | Merged as `be2a79a2` on 2026-07-20; all 25 ART actions remain planned. | | `WS-AUTH-001-REV-CUSTODY` | Merged | `codex/ws-auth-001-rev-custody` | #160 | Merged as `fe0e4492` on 2026-07-20; all 19 REV actions remain planned. | | `WS-AUTH-001-PREP` | Merged | `codex/ws-auth-001-prep` | #162 | Merged as `c559d556` on 2026-07-21; adds no feature consumer or activation. | -| `WS-AUTH-001-10` | Active planning split | `codex/ws-auth-001-10-project-role-grants` | - | Planning-only parent; user approved 10A/10B/10C after combined L1 review failed. | -| `WS-AUTH-001-10A` | Proposed | - | - | Migration `0031`, data/evidence foundation, no active surface. | -| `WS-AUTH-001-10B` | Proposed | - | - | Candidate/list/detail read surfaces after 10A. | -| `WS-AUTH-001-10C` | Proposed | - | - | PREP-bound issue/revoke mutations after 10B. | +| `WS-AUTH-001-10` | Proposed | - | - | Project contributor grants. | | `WS-AUTH-001-11` | Proposed | - | - | Project identity/guide/source/read cutover. | | `WS-AUTH-001-12` | Proposed | - | - | Project policy/setup mutation cutover. | | `WS-AUTH-001-13` | Proposed | - | - | Task management and assignment cutover. | diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md new file mode 100644 index 000000000..005d4533e --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md @@ -0,0 +1,42 @@ +# WS-AUTH-001-10 External Review Response + +PR: `#168` + +Pre-repair published head: `e0af0a6dddb5ede4707d309d2894409344733446` + +## Comments addressed + +- GitHub preflight run `29825312611` rejected the internal evidence because it + used `Reviewed planning SHA` instead of the gate's canonical provenance label + `Reviewed code SHA`. The evidence now uses the exact required label. +- Agent Gates run `29825436222` found branch-local current-state prose in the + authored AUTH `STATUS.md`. That file is restored to trusted-main state; + signed automation, not a PR branch, owns live active/review projection. +- The Backend aggregate `test` job failed only because preflight failed and its + shards/API E2E prerequisites were skipped. No runtime test executed and no + backend defect was reported. + +## Comments deferred + +- CodeRabbit review remains in progress; no actionable comment exists yet. + +## Human decisions needed + +None. + +## Commands rerun + +```bash +python3 scripts/test_agent_gates.py +python3 scripts/check_internal_review_evidence.py +``` + +Agent Gate regression result: 88 passed. The evidence check intentionally +requires exact-SHA repair review and an evidence-only descendant before it can +pass; that binding is completed after the repair review. + +## Remaining risks + +Fresh GitHub preflight, Backend orchestration, Agent Gates, CodeRabbit, and +human review remain. No runtime, migration, CI, test, or coverage behavior was +changed by this repair. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md index 468e2e463..335e81bbf 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md @@ -1,6 +1,6 @@ # WS-AUTH-001-10 Internal Review Evidence -Reviewed planning SHA: `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` +Reviewed code SHA: `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` Reviewed against trusted main: `5a8a924d9b3b347d4cc74b4682865518539c837e` diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md index 6200dfef9..cbc880c49 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md @@ -57,10 +57,14 @@ coverage, API E2E, and Agent Gates. ## External Review And Remaining Risks -CodeRabbit and GitHub checks are pending publication. Remaining implementation -risk is intentionally isolated into the three children: migration privacy and -refusal in 10A, read disclosure/cursors in 10B, and mutation concurrency/replay -in 10C. None may start from this PR alone. +Initial GitHub preflight rejected a noncanonical reviewed-SHA heading, and Agent +Gates rejected branch-authored live status. The external response records the +minimal repairs: use the canonical evidence label and restore `STATUS.md` to +trusted-main state because signed automation owns live projection. All 88 Agent +Gate regression tests pass locally. CodeRabbit and fresh GitHub checks remain. +Implementation risk is intentionally isolated into the three children: +migration privacy/refusal in 10A, read disclosure/cursors in 10B, and mutation +concurrency/replay in 10C. None may start from this PR alone. ## Follow-Up And Human Review Focus From 2c5fda4869df8d7b0ec54cbe66ab5d488e9ac67d Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:22:16 +0100 Subject: [PATCH 11/14] docs(auth): bind AUTH-10 gate repair review --- .../WS-AUTH-001-10-external-review-response.md | 5 +++-- .../WS-AUTH-001-10-internal-review-evidence.md | 12 +++++++++--- .../reviews/WS-AUTH-001-10-pr-trust-bundle.md | 4 +++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md index 005d4533e..a0f450dea 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md @@ -32,8 +32,9 @@ python3 scripts/check_internal_review_evidence.py ``` Agent Gate regression result: 88 passed. The evidence check intentionally -requires exact-SHA repair review and an evidence-only descendant before it can -pass; that binding is completed after the repair review. +required exact-SHA repair review and an evidence-only descendant; internal +review passed repair SHA `1623e5b2dd85cc65df92af89989fda2ce7881bd0`, and the +reviewed-code binding now names that SHA. ## Remaining risks diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md index 335e81bbf..05482db3e 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md @@ -1,6 +1,8 @@ # WS-AUTH-001-10 Internal Review Evidence -Reviewed code SHA: `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` +Reviewed code SHA: `1623e5b2dd85cc65df92af89989fda2ce7881bd0` + +Reviewed planning SHA: `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` Reviewed against trusted main: `5a8a924d9b3b347d4cc74b4682865518539c837e` @@ -22,6 +24,10 @@ architecture, CI integrity, docs, reuse/dedup, and test delta - `git diff --check`: PASS. - This parent changes planning/specification artifacts only. No runtime, migration, test, CI, dependency, threshold, or product behavior changed. +- The exact external-gate repair restores `STATUS.md` to trusted-main state and + uses the canonical evidence provenance label. All three reviewer groups + passed repair SHA `1623e5b2dd85cc65df92af89989fda2ce7881bd0` with no + remaining finding; 88 Agent Gate regression tests pass locally. - GitHub remains the owner of the full sharded suite, aggregate 78 percent and changed-authorization 90 percent coverage gates, API E2E, and Agent Gates for each implementation child. @@ -47,9 +53,9 @@ The initial combined runtime contract was rejected as too broad and split into combined-role specification language, privacy and pagination ambiguity, migration refusal predicates, action/evidence migration custody, PREP lock ordering, strict request/response schemas, and issue/revoke replay states. The -final repair places all five future actions in the existing closed catalogue as +final planning repair places all five future actions in the existing closed catalogue as planned rows with exact 10B/10C owners; no parallel allowlist or active surface -is created. +is created. The later external-gate repair changes no approved plan semantics. Valid findings addressed: yes diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md index cbc880c49..acd5632ef 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md @@ -43,7 +43,9 @@ changes in this parent. - Merge-intent validation: PASS. - Diff integrity: PASS. - All nine required internal review tracks pass exact planning SHA - `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` after fixes. + `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` after fixes, and all required + tracks pass external-gate repair SHA + `1623e5b2dd85cc65df92af89989fda2ce7881bd0`. - No tests were added, modified, removed, skipped, or weakened because this is a planning-only parent. Each child owns focused local tests; GitHub owns its full suite and coverage proof. From c26e2bb1edeb8bd614b3d2bb657d088592b9e5d4 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:36:13 +0100 Subject: [PATCH 12/14] docs(auth): resolve AUTH-10 review findings --- .agent-loop/REVIEW_LOG.md | 7 ++ .../WS-AUTH-001-10-project-role-grants.md | 1 + ...01-10A-project-role-grant-data-evidence.md | 6 +- ...S-AUTH-001-10B-project-role-grant-reads.md | 11 +-- ...TH-001-10C-project-role-grant-mutations.md | 11 +-- ...WS-AUTH-001-10-external-review-response.md | 20 ++++-- .../reviews/WS-AUTH-001-10-pr-trust-bundle.md | 6 +- ...and-authorization-service-specification.md | 72 +++++++++++-------- 8 files changed, 89 insertions(+), 45 deletions(-) diff --git a/.agent-loop/REVIEW_LOG.md b/.agent-loop/REVIEW_LOG.md index b9903d140..a935506df 100644 --- a/.agent-loop/REVIEW_LOG.md +++ b/.agent-loop/REVIEW_LOG.md @@ -2745,3 +2745,10 @@ live active/review projection. The Backend aggregate failure was consequential: preflight blocked shards and API E2E, so no runtime test failed. All 88 Agent Gate regression tests pass locally. Exact-SHA internal repair review, an evidence-only binding descendant, fresh GitHub checks, and CodeRabbit remain. + +CodeRabbit later posted five valid planning/specification findings. The repair +adds review-log scope to the parent contract, persists the exact active-1/ +revoked-2 grant version, keeps child actions planned until their owning child +activates them with routes, records the complete PREP/idempotency/advisory-lock +issue order, and makes `/api/v1` the sole endpoint namespace. Exact-SHA internal +review and fresh external checks remain. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md index a46fd567b..bb35c7bb4 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10-project-role-grants.md @@ -46,6 +46,7 @@ automatically. ```text .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/** .agent-loop/merge-intents/WS-AUTH-001-10.json +.agent-loop/REVIEW_LOG.md docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md ``` diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md index 0d223dfa6..2e6a8b216 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10A-project-role-grant-data-evidence.md @@ -85,8 +85,10 @@ secret, URL credential, or automatically inferred authority is stored. `ProjectRoleGrant` is one immutable issuance row for exactly `submitter`, `reviewer`, or `adjudicator`, with active/revoked lifecycle, manual method, composite snapshot reference, issuer profile/grant provenance, bounded reason, -database timestamps, and terminal revocation provenance. Only active version 1 -to revoked version 2 may mutate lifecycle fields. +database timestamps, terminal revocation provenance, and a persisted integer +`version`. The invariant is exact: active grants persist version 1 and revoked +grants persist version 2. Only the active-version-1 to revoked-version-2 +transition may mutate lifecycle fields. ## Acceptance criteria diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md index c80b90912..7d1da867e 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B-project-role-grant-reads.md @@ -65,10 +65,13 @@ identity-link, contact, skills, reputation, or cross-project activity disclosure | `project_role_grant.list` | `project.role_grant.read` | covered Project Manager or Audit Authority | canonical project | `GET /api/v1/projects/{project_id}/role-grants` | | `project_role_grant.read` | `project.role_grant.read` | covered Project Manager or Audit Authority | grant joined to canonical project | `GET /api/v1/projects/{project_id}/role-grants/{grant_id}` | -All are human-only, owned by `WS-AUTH-001-10B`, active with their route, and -use non-locking canonical project reads. System-scoped candidates cover every -project only for the retained permission; project scope must equal the loaded -project. Services, agents, and Space principals deny before lookup. +All are human-only and registered as planned by 10A with +`ActionOwner.AUTH_10B`. They remain planned and non-callable until this exact +10B child is separately started and implements them; 10B then changes each row +to active atomically with its route. They use non-locking canonical project +reads. System-scoped candidates cover every project only for the retained +permission; project scope must equal the loaded project. Services, agents, and +Space principals deny before lookup. ## Acceptance criteria diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md index 4e88497d0..cbdce3001 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10C-project-role-grant-mutations.md @@ -64,10 +64,13 @@ commit inside PREP, kernel, service, or repository | `project_role_grant.issue` | `project.role_grant.manage` | covered Project Manager only | `POST /api/v1/projects/{project_id}/role-grants` | | `project_role_grant.revoke` | `project.role_grant.manage` | covered Project Manager only | `POST /api/v1/projects/{project_id}/role-grants/{grant_id}/revoke` | -Both are human-only, owned by `WS-AUTH-001-10C`, active with their route, and -use existing mutation rate controls. Audit Authority, Access Administrator, -Operator, Finance Authority, contributor grants, services, agents, and Space do -not substitute for Project Manager authority. +Both are human-only and registered as planned by 10A with +`ActionOwner.AUTH_10C`. They remain planned and non-callable until this exact +10C child is separately started and implements them; 10C then changes each row +to active atomically with its route. They use existing mutation rate controls. +Audit Authority, Access Administrator, Operator, Finance Authority, contributor +grants, services, agents, and Space do not substitute for Project Manager +authority. ## Exact lock and transaction order diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md index a0f450dea..c9aac5a6a 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md @@ -15,10 +15,17 @@ Pre-repair published head: `e0af0a6dddb5ede4707d309d2894409344733446` - The Backend aggregate `test` job failed only because preflight failed and its shards/API E2E prerequisites were skipped. No runtime test executed and no backend defect was reported. +- CodeRabbit posted five valid contract findings. The parent now explicitly + allows `.agent-loop/REVIEW_LOG.md`; the grant `version` is persisted with the + exact active-1/revoked-2 invariant; 10B/10C state that their 10A registrations + remain planned until the owning child activates them with routes; the + canonical issue operation includes idempotency, AuthorityControl/PREP locks, + advisory absence serialization, and one route commit; and every endpoint + example uses `/api/v1`. ## Comments deferred -- CodeRabbit review remains in progress; no actionable comment exists yet. +None. ## Human decisions needed @@ -29,6 +36,11 @@ None. ```bash python3 scripts/test_agent_gates.py python3 scripts/check_internal_review_evidence.py +python3 scripts/check_stale_authorization_docs.py +python3 scripts/check_stale_workstream_wording.py +python3 scripts/check_markdown_links.py +python3 scripts/update_post_merge_memory.py validate-merge-intent --base-ref origin/main +git diff --check ``` Agent Gate regression result: 88 passed. The evidence check intentionally @@ -38,6 +50,6 @@ reviewed-code binding now names that SHA. ## Remaining risks -Fresh GitHub preflight, Backend orchestration, Agent Gates, CodeRabbit, and -human review remain. No runtime, migration, CI, test, or coverage behavior was -changed by this repair. +Fresh GitHub checks, CodeRabbit re-review, and human review remain. No runtime, +migration, CI, test, or coverage behavior was changed by these documentation +repairs. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md index acd5632ef..718c7c97d 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md @@ -63,7 +63,11 @@ Initial GitHub preflight rejected a noncanonical reviewed-SHA heading, and Agent Gates rejected branch-authored live status. The external response records the minimal repairs: use the canonical evidence label and restore `STATUS.md` to trusted-main state because signed automation owns live projection. All 88 Agent -Gate regression tests pass locally. CodeRabbit and fresh GitHub checks remain. +Gate regression tests pass locally. CodeRabbit then identified five valid +planning/specification gaps: review-log scope, persisted grant version, +planned-versus-active child wording, canonical PREP/idempotency ordering, and +API namespace consistency. Each is corrected without runtime or migration +changes. CodeRabbit re-review and fresh GitHub checks remain. Implementation risk is intentionally isolated into the three children: migration privacy/refusal in 10A, read disclosure/cursors in 10B, and mutation concurrency/replay in 10C. None may start from this PR alone. diff --git a/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md b/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md index 39eeecbc6..0e2539d3a 100644 --- a/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md +++ b/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md @@ -322,6 +322,7 @@ ProjectRoleGrant: actor_profile_id: uuid role: enum [submitter, reviewer, adjudicator] status: enum [active, revoked] + version: integer grant_method: literal [manual] qualification_snapshot_id: uuid @@ -348,6 +349,9 @@ ProjectRoleGrant: - At most one active ProjectRoleGrant exists for a contributor, project, and exact role. All three distinct roles may be active concurrently. - Revoking or regranting one role never changes another role. +- `version` is persisted. The invariant is exact: active grants persist version + 1 and revoked grants persist version 2. Only the active-version-1 to + revoked-version-2 transition may mutate lifecycle fields. - Grant and revocation reasons are one to 500 UTF-8 bytes, equal their Python `str.strip()` result, and contain no Unicode control character. - A submitter action requires an active exact `submitter` grant. @@ -955,7 +959,7 @@ Effects: - bearer token verification may succeed; - ActorResolver returns the suspended ActorProfile; - all business mutations are denied with `actor_suspended`; -- `/v1/actors/me` may return minimal status and support information; +- `/api/v1/actors/me` may return minimal status and support information; - active AdminRoleGrants and ProjectRoleGrants remain recorded but ineffective; - active review leases and other exclusive work claims are invalidated through `AuthorityInvalidationRequested` and consuming lifecycle reconciliation; - historical records remain unchanged. @@ -1042,7 +1046,7 @@ The first Access Administrator cannot be created through an endpoint that alread ### 19.2 Bootstrap sequence -1. The initial human signs in normally and calls `/v1/actors/me`. +1. The initial human signs in normally and calls `/api/v1/actors/me`. 2. Workstream creates the human ActorProfile and ActorIdentityLink. 3. An authorized deployment operator runs the Workstream-local bootstrap management operation with the exact ActorProfile ID. 4. The operation verifies that no active Access Administrator exists. @@ -1133,23 +1137,31 @@ The transaction MUST: The transaction MUST: -1. authorize `project.role_grant.manage` for the exact project; -2. lock the target ActorProfile and project active-grant selector; -3. require an active human target; -4. prohibit self-grant; -5. create the immutable qualification snapshot; -6. reject an existing active grant for the same exact role; -7. create the new manual ProjectRoleGrant without changing another role; -8. append snapshot and grant-issued audit evidence; -9. commit once. +1. require and reserve the typed `Idempotency-Key` in the caller/operation + namespace, rejecting a mismatched replay; +2. run PREP by locking `AuthorityControl(id=1)`, then locking distinct caller + and target ActorProfiles in lexical ID order, each followed by its exact + active identity link, and finally locking the caller's deterministic covered + Project Manager grant; +3. authorize `project.role_grant.manage` for the exact project; +4. lock the canonical project; +5. take the transaction advisory key for + `(actor_profile_id, project_id, requested_role)` to serialize absence; +6. reload the target, scope, and active exact-role selector under those locks; +7. require an active human target and prohibit self-grant; +8. reject an existing active grant for the same exact role; +9. create the immutable qualification snapshot and new manual ProjectRoleGrant + without changing another role; +10. append snapshot and grant-issued audit evidence and commit the idempotency + response reference; +11. commit once at the route boundary. --- ## 21. API Contract -Repository implementation uses the canonical `/api/v1` namespace. Any older -`/v1` example in this imported reference is superseded by that repository -decision. +Repository implementation and every endpoint example below use the canonical +`/api/v1` namespace. ### 21.1 Current actor @@ -1190,11 +1202,11 @@ Capabilities are computed server-side from a registered allowlist. Clients canno ### 21.2 Actor administration ```http -GET /v1/admin/actors -GET /v1/admin/actors/{actor_profile_id} -POST /v1/admin/actors/{actor_profile_id}/suspend -POST /v1/admin/actors/{actor_profile_id}/reactivate -POST /v1/admin/actors/{actor_profile_id}/deactivate +GET /api/v1/admin/actors +GET /api/v1/admin/actors/{actor_profile_id} +POST /api/v1/admin/actors/{actor_profile_id}/suspend +POST /api/v1/admin/actors/{actor_profile_id}/reactivate +POST /api/v1/admin/actors/{actor_profile_id}/deactivate ``` State-change request: @@ -1208,9 +1220,9 @@ State-change request: ### 21.3 Identity-link administration ```http -GET /v1/admin/actors/{actor_profile_id}/identity-link -POST /v1/admin/identity-links/{identity_link_id}/revoke -POST /v1/admin/identity-links/{identity_link_id}/reactivate +GET /api/v1/admin/actors/{actor_profile_id}/identity-link +POST /api/v1/admin/identity-links/{identity_link_id}/revoke +POST /api/v1/admin/identity-links/{identity_link_id}/reactivate ``` No endpoint adds a second human identity link in v0.1. @@ -1218,18 +1230,18 @@ No endpoint adds a second human identity link in v0.1. ### 21.4 Service actors ```http -POST /v1/admin/service-actors -GET /v1/admin/service-actors -GET /v1/admin/service-actors/{actor_profile_id} +POST /api/v1/admin/service-actors +GET /api/v1/admin/service-actors +GET /api/v1/admin/service-actors/{actor_profile_id} ``` ### 21.5 Admin-role grants ```http -POST /v1/admin-role-grants -GET /v1/admin-role-grants -GET /v1/actors/{actor_profile_id}/admin-role-grants -POST /v1/admin-role-grants/{grant_id}/revoke +POST /api/v1/admin-role-grants +GET /api/v1/admin-role-grants +GET /api/v1/actors/{actor_profile_id}/admin-role-grants +POST /api/v1/admin-role-grants/{grant_id}/revoke ``` Create request: @@ -1779,7 +1791,7 @@ The live drill MUST use supported Workstream APIs and the bootstrap operation. D ### 32.1 Drill sequence 1. Start Workstream with no ActorProfiles or Access Administrators. -2. Human A presents a valid issuer token and calls `/v1/actors/me`. +2. Human A presents a valid issuer token and calls `/api/v1/actors/me`. 3. Prove Human A has one active ActorProfile, Contributor domain, and no roles/grants. 4. Run the one-time bootstrap operation for Human A. 5. Prove Human A now has system-scoped `access_administrator` only. From 6a89dd5018d28be149dc6e77f1466a0b3c707296 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:37:52 +0100 Subject: [PATCH 13/14] docs(auth): align canonical PREP consumption order --- ...and-authorization-service-specification.md | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md b/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md index 0e2539d3a..e338e7257 100644 --- a/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md +++ b/docs/reference_specs/WS-AUTH-001-actor-profile-role-and-authorization-service-specification.md @@ -1142,19 +1142,23 @@ The transaction MUST: 2. run PREP by locking `AuthorityControl(id=1)`, then locking distinct caller and target ActorProfiles in lexical ID order, each followed by its exact active identity link, and finally locking the caller's deterministic covered - Project Manager grant; -3. authorize `project.role_grant.manage` for the exact project; -4. lock the canonical project; -5. take the transaction advisory key for + Project Manager grant; preparation validates candidate authority for the + normalized requested project scope but does not make the final decision; +3. lock and load the canonical project; +4. take the transaction advisory key for `(actor_profile_id, project_id, requested_role)` to serialize absence; -6. reload the target, scope, and active exact-role selector under those locks; -7. require an active human target and prohibit self-grant; -8. reject an existing active grant for the same exact role; -9. create the immutable qualification snapshot and new manual ProjectRoleGrant +5. reload the target, caller scope, and active exact-role selector under those + locks, require an active human target, prohibit self-grant, and reject an + existing active grant for the same exact role; +6. consume the exact transaction-bound PREP handle, recompose the final locked + authority and canonical-project facts, and evaluate + `project.role_grant.manage` exactly once; no product, evidence, or + idempotency response mutation is staged before successful consumption; +7. create the immutable qualification snapshot and new manual ProjectRoleGrant without changing another role; -10. append snapshot and grant-issued audit evidence and commit the idempotency +8. append snapshot and grant-issued audit evidence and commit the idempotency response reference; -11. commit once at the route boundary. +9. commit once at the route boundary. --- From d3101a613ad5c59f13b02a311c913525a7cb446b Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 12:39:57 +0100 Subject: [PATCH 14/14] docs(auth): bind CodeRabbit repair review --- .../reviews/WS-AUTH-001-10-external-review-response.md | 7 ++++++- .../reviews/WS-AUTH-001-10-internal-review-evidence.md | 6 +++++- .../reviews/WS-AUTH-001-10-pr-trust-bundle.md | 3 ++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md index c9aac5a6a..1b98d4cb0 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-external-review-response.md @@ -46,7 +46,12 @@ git diff --check Agent Gate regression result: 88 passed. The evidence check intentionally required exact-SHA repair review and an evidence-only descendant; internal review passed repair SHA `1623e5b2dd85cc65df92af89989fda2ce7881bd0`, and the -reviewed-code binding now names that SHA. +initial evidence-only descendant bound that SHA. + +All required internal tracks subsequently passed the five-finding CodeRabbit +repair at exact SHA `6a89dd5018d28be149dc6e77f1466a0b3c707296`; the final +PREP wording was repaired once more before that pass so final authorization +occurs only through `consume()` after canonical feature facts are locked. ## Remaining risks diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md index 05482db3e..f90c57c01 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-internal-review-evidence.md @@ -1,6 +1,6 @@ # WS-AUTH-001-10 Internal Review Evidence -Reviewed code SHA: `1623e5b2dd85cc65df92af89989fda2ce7881bd0` +Reviewed code SHA: `6a89dd5018d28be149dc6e77f1466a0b3c707296` Reviewed planning SHA: `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` @@ -28,6 +28,10 @@ architecture, CI integrity, docs, reuse/dedup, and test delta uses the canonical evidence provenance label. All three reviewer groups passed repair SHA `1623e5b2dd85cc65df92af89989fda2ce7881bd0` with no remaining finding; 88 Agent Gate regression tests pass locally. +- All three reviewer groups also pass CodeRabbit repair SHA + `6a89dd5018d28be149dc6e77f1466a0b3c707296`. The five findings close parent + scope, persisted versioning, planned activation wording, canonical PREP + consumption/idempotency order, and `/api/v1` namespace consistency. - GitHub remains the owner of the full sharded suite, aggregate 78 percent and changed-authorization 90 percent coverage gates, API E2E, and Agent Gates for each implementation child. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md index 718c7c97d..6cc51db9a 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10-pr-trust-bundle.md @@ -45,7 +45,8 @@ changes in this parent. - All nine required internal review tracks pass exact planning SHA `ca52fd6a6c51f78b3e3a10faf77f4ab235843ad2` after fixes, and all required tracks pass external-gate repair SHA - `1623e5b2dd85cc65df92af89989fda2ce7881bd0`. + `1623e5b2dd85cc65df92af89989fda2ce7881bd0` and CodeRabbit repair SHA + `6a89dd5018d28be149dc6e77f1466a0b3c707296`. - No tests were added, modified, removed, skipped, or weakened because this is a planning-only parent. Each child owns focused local tests; GitHub owns its full suite and coverage proof.