From 25b6ae134e3e3db4350fbcbb5c7cfeaa9e261044 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 17:21:13 +0100 Subject: [PATCH 1/4] docs(auth): split project role reads into rate and disclosure chunks --- .../CHUNK_MAP.md | 8 +- .../DECISIONS.md | 27 +++ .../DISCOVERY.md | 10 + .../INTENT.md | 8 +- .../PLAN.md | 12 +- .../RISKS.md | 2 + ...S-AUTH-001-10B-project-role-grant-reads.md | 117 ++-------- ...01-10B1-authorization-read-rate-control.md | 122 ++++++++++ ...-AUTH-001-10B2-project-role-grant-reads.md | 210 ++++++++++++++++++ .../merge-intents/WS-AUTH-001-10B.json | 9 + 10 files changed, 422 insertions(+), 103 deletions(-) create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B1-authorization-read-rate-control.md create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B2-project-role-grant-reads.md create mode 100644 .agent-loop/merge-intents/WS-AUTH-001-10B.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 cf5125704..d493e52c4 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 @@ -42,8 +42,10 @@ stopped. | `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 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-10B` | Project Role Grant Read Planning Parent | L1 | Active start split before runtime implementation | +| `WS-AUTH-001-10B1` | Durable Authorization Read Rate Control | L1 | Proposed successor after 10B planning merge/memory | +| `WS-AUTH-001-10B2` | Privacy-Safe Project Role Grant Reads | L1 | Proposed after 10B1 | +| `WS-AUTH-001-10C` | Project Role Grant Mutations | L1 | Proposed after 10B2 | | `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 | @@ -108,6 +110,8 @@ WS-AUTH-001-PLAN -> WS-AUTH-001-10 -> WS-AUTH-001-10A -> WS-AUTH-001-10B +-> WS-AUTH-001-10B1 +-> WS-AUTH-001-10B2 -> WS-AUTH-001-10C -> WS-AUTH-001-11 -> WS-AUTH-001-12 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 ca618f4a3..993a51b18 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 @@ -716,3 +716,30 @@ unchanged. 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. + +## D33: Split AUTH-10B rate control from privacy-safe disclosure + +Status: accepted by the user on 2026-07-21 after required AUTH-10B L1 plan +review rejected the inherited read-control and concealment boundary. + +AUTH-10B becomes a planning-only parent and splits into two sequential +same-initiative children. AUTH-10B1 extends the existing durable PostgreSQL +API-rate-control system with the closed `authorization_read` scope, dedicated +limit/window configuration, and an unattached FastAPI dependency. It owns +forward migration `0032_authorization_read_rate_control`; it adds or activates +no authorization read route. Historical migration `0017` remains immutable, +and existing first-access and administrative-mutation behavior is unchanged. + +AUTH-10B2 then activates exactly the candidate, project-role-grant list, and +project-role-grant detail actions and attaches the shared authorization-read +control exactly once to each route. It owns the distinct required pagination +cursor secret and signed keyset cursor. It also adds action-aware centralized +public concealment for these three actions while preserving the existing +rollback, denial-evidence restaging, and commit path. Other actions retain +their existing public error mapping. + +Unauthorized or nonexistent projects, terminal or archived candidate projects, +and missing or cross-project grants have one identical public response. No +route catches and discards `AuthorizationDenied`, no response exposes a total, +and no candidate or grant row is queried before canonical resolution and +authorization succeed. AUTH-10C follows only after 10B2. 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 ed5aae3be..2755a0217 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 @@ -43,6 +43,16 @@ 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. +Required AUTH-10B preimplementation review then proved that the promised +existing read-rate control did not exist. The durable counter accepts only +`first_access` and `admin_mutation` in typed code and its PostgreSQL constraint. +Reusing either would couple unrelated traffic; adding `authorization_read` +requires a forward migration. Review also found that current authorization HTTP +translation exposes 403 permission/scope denials separately from 404 missing +resources, while route-local interception would bypass denial-evidence +restaging. The user approved D33: 10B1 owns durable read control, and 10B2 owns +the three reads plus action-aware audited concealment. + ## 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/INTENT.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/INTENT.md index 8aed7b144..9fcd97ac0 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 @@ -164,9 +164,11 @@ Resolved: - 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. + privacy-safe reads second, and PREP-bound mutations third. D33 further splits + 10B into 10B1 durable authorization-read rate control and 10B2 privacy-safe + disclosure after review proved no reusable read scope existed. Migration + `0031` belongs to 10A, `0032` belongs to 10B1, and actor authorization-context + disclosure remains deferred 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 6c3275753..c0d642a36 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,12 +191,14 @@ 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. Close planning parent AUTH-10, then deliver project-role authority in three - separately started children: 10A establishes migration `0031`, immutable +12. Close planning parent AUTH-10, then deliver project-role authority through + separately started children. 10A establishes migration `0031`, immutable 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. + planned action rows with exact 10B/10C custody but no active surface. The + planning-only 10B parent splits into 10B1 migration `0032` and a dedicated + durable authorization-read rate-control scope, followed by 10B2 activation + of candidate/list/detail reads with audited concealment and signed cursors. + 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/RISKS.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/RISKS.md index 21f736777..53303f0e9 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/RISKS.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/RISKS.md @@ -45,6 +45,8 @@ | A29 | PR #132 convergence drops reviewed migration or cleanup repairs | Migration replay imports mutable runtime code, wheel installs fail, or cleanup hides the original error | Preserve frozen packaged contract, script-owned root, location-independent replay, same-loop cleanup, and original-exception precedence | Exact-head internal re-review plus wheel replay and cancellation/cleanup tests | | A30 | Provisioning fabricates service-token verification | An untested service identity appears recently authenticated | Nullable service-link verification timestamp in `0024`, explicit human timestamp writes, and AUTH-09E-only service verification updates | Upgrade/downgrade, create/replay/denial/failure timestamp state-table tests | | A31 | Service provisioning crosses inverse actor/link locks | Administrative and lifecycle mutations deadlock or admit stale authority | Canonical AuthorityControl -> profile -> exact link -> exact grant order before fixed-identity and issuer/subject advisory locks | Independent-session same-key, identity collision, revoke/lifecycle crossing, rollback, and no-deadlock proof | +| A32 | Sensitive authorization reads have no dedicated abuse control or leak resource existence through errors | Enumeration, privacy breach, or cross-project intelligence disclosure | Add one durable `authorization_read` scope before routes; map only the three read actions to one audited not-found boundary | Migration/concurrency/rate tests plus identical-response and persisted-denial-evidence tests | +| A33 | Pagination cursors replay across actions, projects, or filters | Hidden rows disclose or pagination crosses authority scope | Distinct required 32-byte HMAC key, canonical versioned payload, complete query binding, and constant-time verification | Tamper, cross-scope/filter/limit, equal-boundary, no-gap, and no-query tests | ## Required reviewers 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 7d1da867e..656a99038 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 @@ -1,27 +1,29 @@ -# Chunk Contract: WS-AUTH-001-10B - Project Role Grant Read And Candidate Surfaces +# Chunk Contract: WS-AUTH-001-10B — Project Role Grant Read Planning Parent ## Parent initiative `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 -through three exact read actions after 10A establishes durable truth. +Split durable read abuse control from privacy-safe project-role disclosure. ## Why this chunk exists -Read disclosure, candidate eligibility, filtering-before-count, and cursor -binding can be reviewed independently from mutation locking. +Required L1 review proved there is no existing read-rate scope and that current +403/404 translation cannot provide concealment without preserving centralized +denial-evidence handling. The user approved separate 10B1 and 10B2 children. + +## 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` +- Decision: D33 ## Risk class -L1 authorization, privacy, and API disclosure. +L1 authorization and privacy planning. ## SLA @@ -30,103 +32,32 @@ 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/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 .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 +## Not allowed ```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 +application, migration, workflow, test, or public documentation changes +action activation, API routes, or runtime behavior ``` -## 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 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 -- 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. -- 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 - 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. -- No write, PREP, idempotency, invalidation, or migration behavior changes. +- D33 records the accepted split and repository-backed rationale. +- 10B1 and 10B2 have exact PR-sized contracts, dependency order, proof, and stop conditions. +- 10B1 is the only declared successor; 10B2 follows 10B1 and 10C follows 10B2. +- No application action changes availability. ## Verification commands ```bash -(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 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 @@ -134,10 +65,10 @@ 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. +Review that the split preserves durable abuse control, audited concealment, and +the exact three-read boundary without runtime edits. ## Stop conditions -Stop on client-supplied project truth, post-query authorization, count leakage, -new persistence, or any contributor capability implied by a read. +Stop if either child mixes rate-control persistence with public disclosure, if +10B2 can start before 10B1, or if this parent requires a runtime edit. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B1-authorization-read-rate-control.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B1-authorization-read-rate-control.md new file mode 100644 index 000000000..0a75df9d1 --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B1-authorization-read-rate-control.md @@ -0,0 +1,122 @@ +# Chunk Contract: WS-AUTH-001-10B1 — Durable Authorization Read Rate Control + +## Parent initiative + +`WS-AUTH-001` — Workstream Authorization Service + +## Goal + +Extend the existing privacy-keyed PostgreSQL API rate-control system with one +closed `authorization_read` scope before sensitive authorization reads exist. + +## Why this chunk exists + +First-access and administrative-mutation counters have different threat and +capacity semantics. Sensitive reads need their own cross-replica abuse budget +without an ad hoc limiter or a public route in the migration change. + +## 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` +- Decision: D33 + +## Risk class + +L1 authorization, privacy, migration, and API abuse control. + +## SLA + +P1 + +## Allowed files + +```text +backend/alembic/versions/0032_authorization_read_rate_control.py +backend/app/modules/api_controls/** +backend/app/api/deps/api_controls.py +backend/app/core/config.py +backend/tests/test_alembic.py +backend/tests/test_api_rate_controls.py +backend/tests/test_config.py +.github/workflows/backend.yml +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-10B1.json +.agent-loop/REVIEW_LOG.md +``` + +## Not allowed + +```text +authorization candidate/grant routes or action activation +pagination cursor implementation or secret +project-role read queries or response schemas +issue/revoke, PREP, idempotency, or invalidation changes +new counter table, cache, in-memory limiter, or external rate service +workflow changes except adding the API-controls 90 percent coverage gate +lowering, replacing, or broadening exclusions in any coverage threshold +``` + +## Acceptance criteria + +- Migration `0032` changes only the current counter scope constraint from exact + `first_access|admin_mutation` to exact + `first_access|admin_mutation|authorization_read`. Historical migrations stay + immutable; upgrade preserves rows; downgrade refuses while new-scope rows + exist and otherwise restores the prior constraint. Downgrade takes an + appropriate table lock before its row check and DDL so no concurrent consumer + can cross the refusal check. Deterministic lock-order/concurrency proof uses + independent sessions and no timing sleeps. +- Typed `RATE_SCOPES` gains exactly `authorization_read`; existing HMAC domain, + framed issuer/subject digest, committed session, database-time window, + pruning, and fail-closed behavior are reused unchanged. +- Dedicated `api_authorization_read_rate_limit` defaults to 120 and accepts + 1..10,000. `api_authorization_read_rate_window_seconds` defaults to 60 and + accepts 1..3,600. They use the existing API-rate key only; cursor/auth secrets + are never fallbacks. Tests cover defaults, bounds, underflow, overflow, and + independence from both existing scopes. +- One unattached `enforce_authorization_read_rate_limit` dependency consumes the + exact scope and preserves canonical 429/Retry-After and retryable 503 behavior. +- Tests prove scope separation, identity privacy, exact limit/exceeded/reset, + concurrent sessions, cross-replica behavior, commit/database/missing-secret + failures, bounded config, migration round trip/refusal, and unchanged scopes. +- No permission, action, route, or OpenAPI availability changes. +- GitHub Backend adds a distinct 90 percent coverage report covering + `app/modules/api_controls/*` and `app/api/deps/api_controls.py`; existing + reports and thresholds remain byte-for-byte unchanged. Zero action/OpenAPI + delta is asserted. +- Operations/spec docs cover `0032` upgrade and downgrade preflight, refusal + with live read counters, forward recovery, limit/window settings, shared + privacy HMAC key and secret separation, 429/Retry-After, retryable 503, and + the intentionally unattached/no-action rollout state. + +## Verification commands + +```bash +(cd backend && .venv/bin/python -m ruff check app/modules/api_controls app/api/deps/api_controls.py app/core/config.py tests/test_api_rate_controls.py tests/test_config.py tests/test_alembic.py alembic/versions/0032_authorization_read_rate_control.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_api_rate_controls.py tests/test_config.py tests/test_alembic.py -k 'authorization_read or api_rate_control') +python3 scripts/check_stale_authorization_docs.py +python3 scripts/check_markdown_links.py +git diff --check +``` + +GitHub owns the full sharded suite, aggregate 78 percent coverage, the new exact +API-controls 90 percent gate, migration proof, API E2E, and Agent Gates. + +## Required reviewers + +Senior engineering, QA/test, security/auth, product/ops, architecture, CI +integrity, docs, reuse/dedup, and test delta. + +## Human review focus + +Review one-scope migration, privacy-key reuse, capacity settings, cross-replica +proof, downgrade refusal, and absence of routes. + +## Stop conditions + +Stop on a second limiter/store, raw identity persistence, non-database time, +shared first-access/mutation budget, route attachment, or migration data loss. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B2-project-role-grant-reads.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B2-project-role-grant-reads.md new file mode 100644 index 000000000..4e9c1c564 --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-10B2-project-role-grant-reads.md @@ -0,0 +1,210 @@ +# Chunk Contract: WS-AUTH-001-10B2 — Privacy-Safe Project Role Grant Reads + +## Parent initiative + +`WS-AUTH-001` — Workstream Authorization Service + +## Goal + +Expose privacy-safe contributor candidates and project-role grant history +through three exact authenticated, rate-controlled read actions. + +## Why this chunk exists + +After 10A establishes durable truth and 10B1 establishes read abuse control, +disclosure, concealment, minimal schemas, and cursor binding form one reviewable +API boundary independent from mutation locking. + +## 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` +- Decision: D33 + +## 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/deps/authorization.py +backend/app/api/router.py +backend/app/core/api_controls.py +backend/app/core/config.py +backend/app/main.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 +.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/** +.agent-loop/merge-intents/WS-AUTH-001-10B2.json +.agent-loop/REVIEW_LOG.md +``` + +## Not allowed + +```text +migration or durable schema changes +issue/revoke routes or mutation behavior +PREP, idempotency, or invalidation changes +task/review/project product cutover +identity-link, contact, skills, reputation, or cross-project activity disclosure +CI/workflow or coverage-threshold changes +``` + +## 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, registered as planned by 10A, and owned by +`ActionOwner.AUTH_10B`. 10B2 activates exactly these three rows with their +routes. System grants cover every canonical project for the exact permission; +project scope must equal the server-loaded project. Services, agents, and Space +principals deny before product-row lookup. + +## Acceptance criteria + +- Each route consumes the 10B1 `authorization_read` dependency exactly once. + Missing secret/database failure returns established retryable 503; exhaustion + returns established 429/Retry-After before product-row lookup. Tests cover + allowed, denied, and concealed requests, prove no nested-dependency double + consumption, and prove 429/503 occurs before project/grant/candidate SQL. +- Add strict project collection and grant resource contexts. Collection + `resource_id` equals `scope_project_id` and carries server-loaded project + status; detail binds grant ID and canonical project ID. Add only these actions + to the closed human admin read path, action/context map, scope extraction, and + guards. Candidate project-state eligibility is evaluated inside the kernel + before its decision completes, producing bounded denial evidence and the + centralized 404. Grant list/detail deliberately impose no project-state guard. +- Sequence: verified human resolution, non-locking canonical project load, + exact authorization, lifecycle guard, cursor validation, then row query. + Detail queries by both project and grant ID. Nothing serializes before allow. +- Central action-aware HTTP translation maps permission/scope denial, + nonexistent project, terminal/archived candidate project, missing grant, and + project/grant mismatch for these actions to one identical structured 404. + Existing rollback, denial restaging/audit commit, and other mappings remain. + Permission/scope denials persist the existing bounded authorization-denial + evidence before public 404 translation. Missing projects/grants, path + mismatches, and lifecycle concealment do not fabricate authorization-denial + evidence except candidate terminal/archived status, which is an evidenced + kernel resource-guard denial after canonical project load. Tests prove each path. +- A composed human-only prelookup dependency rejects fixed services before + `ProjectRepository.get_project`; agent and Space kinds remain rejected by the + existing actor-resolution boundary. Because no canonical project exists at + this point, this prelookup rejection does not fabricate an action decision or + authority audit event. It returns the same public 404 and tests prove zero + project/candidate/grant query. It never constructs a canonical context from + the path parameter and never catches `AuthorizationDenied` in the route. +- Candidate SQL filters active human profile, active identity link, and caller + exclusion before keyset and `limit + 1`; orders ascending by + `(created_at, actor_profile_id)`; executes no count; and returns only ID plus + nullable display name. Active-link eligibility uses `EXISTS`, not a + multiplicative join, so each profile occurs once even if bad/legacy data + exposes multiple eligible links; regression proof covers uniqueness. Only + draft, active, and paused projects qualify. +- Grant list/detail remain readable in every existing project state. List + applies optional status/role before keyset and `limit + 1`, orders ascending + by `(granted_at, grant_id)`, and executes no count. +- Candidate accepts only `limit` (default 50, 1..100) and cursor (maximum 512). + Grant list accepts only optional `status=active|revoked`, optional + `role=submitter|reviewer|adjudicator`, limit, and cursor. List envelopes are + exactly `{items,next_cursor}` with no total. +- Candidate item is exactly `{actor_profile_id,display_name}` with nullable + display name. Grant list/detail use `extra="forbid"` and contain 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 final + three revocation fields are present and nullable. The nested snapshot is also + strict and contains exactly `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`, and + `captured_at`. Each skills/reputation object contains exactly `availability`, + `reference_ids`, and nullable `unavailable_reason`. No issuer, subject, link, + contact, raw claims, secrets, or cross-project activity is exposed. +- Required private `WORKSTREAM_PAGINATION_CURSOR_HMAC_SECRET` is canonical + Base64 decoding to exactly 32 bytes. Missing/malformed/short/long values fail + settings/startup. It is absent from dumps, repr, errors, and logs and never + falls back to auth or rate secrets. +- Cursor is canonical Base64url over a strict versioned payload and HMAC-SHA256. + Canonical digest binds action, project, normalized optional status/role, + limit, and ordering; boundary binds aware timestamp and canonical UUID. + Constant-time verification and bounded decoding reject unknown keys/types, + encoding/timestamp/UUID noncanonicality, tampering, and cross-action/project/ + filter/limit replay before row query. +- The negative cursor matrix explicitly covers malformed encoding, more than + the bounded decoded bytes, unknown version, unknown/missing/extra key, wrong + field type, noncanonical Base64url, timestamp, or UUID, bad signature, and + cross-action/project/status/role/limit/order replay. Every case fails before + row SQL and returns the same invalid-cursor response. +- Both ascending keysets use boundary tuple `(timestamp, UUID)` and SQL + `> (timestamp, UUID)` semantics (or the equivalent strict disjunction), fetch + `limit + 1`, and encode the last visible item only when another row exists. + Query digest derives from normalized action/project/status/role/limit/order, + never raw query ordering or the cursor itself. +- Persistence ownership is exact: `ProjectRepository` owns canonical project + lookup; `ActorRepository` owns candidate eligibility/keyset SQL; the + authorization repository owns grant/snapshot list/detail SQL; authorization + service/router own orchestration and schema conversion. Reuse canonical + hashing, secret sanitation, and Base64 framing patterns, but do not reuse or + extend the unsigned administrative-grant cursor codec. +- Tests cover page boundaries/equal timestamps/no gaps, all eligibility and + project states, PM versus Audit Authority, system versus project scope, + nonhuman denial, identical concealment plus persisted bounded denial evidence, + strict schemas/no counts, cursor failures, and secret non-serialization. +- Exactly one active OpenAPI declaration exists per surface and manifest delta + is exactly three. AUTH-10C actions stay planned and authorization-context is absent. +- Operations/spec docs cover cursor-key generation, provisioning, coordinated + rotation and startup failure; diagnostics never log cursors/secrets or reveal + concealment distinctions. They enumerate exact routes, actions, authorities, + project-state behavior, strict fields/no totals, and the unchanged + no-migration/no-PREP/no-mutation boundary. +- Hosted `api_contract_e2e.py` provisions an isolated cursor secret and exercises + all three real routes, practical 429/503 mappings, identical 404 concealment, + OpenAPI action IDs, strict shapes, and absence of extra surfaces. The merge + intent names only 10B2 and its successor 10C. + +## Verification commands + +```bash +(cd backend && .venv/bin/python -m ruff check app/modules/actors app/modules/authorization app/modules/projects app/api/deps/authorization.py app/core/api_controls.py app/core/config.py app/main.py tests/test_actors.py tests/test_authorization.py tests/test_projects.py tests/test_config.py scripts/api_contract_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 tests/test_config.py -k 'project_role or contributor_candidate or pagination_cursor or authorization_read') +(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 full shards, aggregate 78 percent coverage, authorization subsystem +90 percent coverage, API E2E, and Agent Gates. + +## Required reviewers + +Senior engineering, QA/test, security/auth, product/ops, architecture, CI +integrity, docs, reuse/dedup, and test delta. + +## Human review focus + +Review rate-before-lookup, canonical scope, identical audited concealment, +filter-before-keyset behavior, minimal fields, cursor binding, and exact activation. + +## Stop conditions + +Stop on client project truth, authorization after row query, count leakage, +unaudited route-local denial interception, persistence, or capability from read. diff --git a/.agent-loop/merge-intents/WS-AUTH-001-10B.json b/.agent-loop/merge-intents/WS-AUTH-001-10B.json new file mode 100644 index 000000000..b1816c1ef --- /dev/null +++ b/.agent-loop/merge-intents/WS-AUTH-001-10B.json @@ -0,0 +1,9 @@ +{ + "chunk_id": "WS-AUTH-001-10B", + "chunk_title": "Project Role Grant Read Planning Parent", + "initiative_id": "WS-AUTH-001", + "next_chunk_id": "WS-AUTH-001-10B1", + "next_chunk_title": "Durable Authorization Read Rate Control", + "next_requires_explicit_start": true, + "schema_version": 2 +} From ca7743a2d83212ac091b761a0c544916bb0c8a70 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 17:23:48 +0100 Subject: [PATCH 2/4] docs(auth): record 10B planning review evidence --- .agent-loop/REVIEW_LOG.md | 15 +++++ ...S-AUTH-001-10B-internal-review-evidence.md | 66 +++++++++++++++++++ .../WS-AUTH-001-10B-pr-trust-bundle.md | 54 +++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-internal-review-evidence.md create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-pr-trust-bundle.md diff --git a/.agent-loop/REVIEW_LOG.md b/.agent-loop/REVIEW_LOG.md index f5bfc47a3..0050d9a89 100644 --- a/.agent-loop/REVIEW_LOG.md +++ b/.agent-loop/REVIEW_LOG.md @@ -1,5 +1,20 @@ # Review Log +## 2026-07-21 - WS-AUTH-001-10B Internal Plan Review + +The inherited AUTH-10B contract failed required L1 review before runtime edits: +the repository had no read-rate scope to reuse, and existing 403/404 translation +could not conceal sensitive project/grant existence without preserving denial +evidence. The user approved D33 and the sequential 10B1/10B2 repair. + +Exact planning SHA `25b6ae134e3e3db4350fbcbb5c7cfeaa9e261044` passes senior +engineering, QA/test, security/auth, product/ops, architecture, CI integrity, +docs, reuse/dedup, and test-delta review. 10B1 owns only durable +`authorization_read` control and migration `0032`; 10B2 owns exactly three +rate-controlled reads, audited concealment, strict schemas, and signed cursors. +Markdown links, stale scans, merge-intent validation, and diff integrity pass. +GitHub, CodeRabbit, and explicit human review remain. + ## 2026-07-21 - WS-ENG-005 Parallel Initiative Execution Plan The user approved maximum safe concurrent work across distinct initiatives. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-internal-review-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-internal-review-evidence.md new file mode 100644 index 000000000..c8402b38b --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-internal-review-evidence.md @@ -0,0 +1,66 @@ +# WS-AUTH-001-10B Internal Review Evidence + +Reviewed code SHA: `25b6ae134e3e3db4350fbcbb5c7cfeaa9e261044` + +Reviewed against trusted main: `f2aa57a45f9088a91e8f7adcf79ec7e05a2b5734` + +Reviewed at: `2026-07-21T16:22:47Z` + +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 + +## Scope + +AUTH-10B is a planning-only parent. It records D33 and splits implementation +into 10B1 durable authorization-read rate control followed by 10B2 privacy-safe +project-role disclosures. It changes no runtime, migration, workflow, test, +public documentation, route, action availability, or authored live status. + +## Deterministic evidence + +- Stale authorization documentation: PASS. +- Stale Workstream wording: PASS. +- Markdown links: PASS for all nine changed Markdown files. +- Merge-intent validation: PASS; the only new schema-v2 intent names exact + same-initiative successor `WS-AUTH-001-10B1` and requires explicit start. +- `git diff --check`: PASS. +- `STATUS.md` is byte-identical to trusted main; signed automation remains the + sole owner of live projection. + +## Reviewer results + +| Reviewer | Result | Blocking findings | Notes | +|---|---|---|---| +| senior engineering | PASS AFTER FIXES | none | Durable counter and disclosure boundaries are independently reviewable. | +| QA/test | PASS AFTER FIXES | none | Migration races, rate ordering, concealment, cursors, schemas, and E2E proof are exact. | +| security/auth | PASS AFTER FIXES | none | Abuse control, audited concealment, minimal disclosure, and cursor secret isolation are fail closed. | +| product/ops | PASS AFTER FIXES | none | Rollout and recovery preserve submitter and project-owner data custody. | +| architecture | PASS AFTER FIXES | none | 10B1 owns persistence/control only; 10B2 owns exactly three reads. | +| CI integrity | PASS AFTER FIXES | none | 10B1 must add a new API-controls 90 percent gate without weakening existing gates. | +| docs | PASS AFTER FIXES | none | D33, plan, map, risks, and child contracts agree. | +| reuse/dedup | PASS AFTER FIXES | none | Existing PostgreSQL limiter is extended; no second limiter or unsigned cursor reuse is allowed. | +| test delta | PASS | none | This planning parent changes no tests or thresholds. | + +## Findings resolved + +Initial review failed because no reusable read-rate scope existed and current +403/404 translation could not conceal sensitive resources without preserving +denial evidence. The repaired plan adds 10B1 migration `0032` for one durable +`authorization_read` scope and makes 10B2 depend on it. Further review froze +downgrade locking, exact capacity bounds, hosted 90 percent API-controls +coverage, action-aware concealment, nonhuman prelookup behavior, unique +candidate SQL, repository ownership, strict response fields, exact keyset +semantics, complete cursor rejection, operations guidance, and hosted E2E. + +Valid findings addressed: yes + +Open sub-agent sessions: none after evidence publication + +## Remaining gate + +GitHub Agent Gates, CodeRabbit, and explicit human review remain. After merge, +signed memory must stop at 10B1; implementation requires a fresh protected +explicit start on exact `main`. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-pr-trust-bundle.md new file mode 100644 index 000000000..e77a5ddda --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-pr-trust-bundle.md @@ -0,0 +1,54 @@ +# WS-AUTH-001-10B PR Trust Bundle + +## Chunk + +`WS-AUTH-001-10B` — Project Role Grant Read Planning Parent + +Merge intent: `.agent-loop/merge-intents/WS-AUTH-001-10B.json` + +## Goal and human-approved intent + +Protect entrusted submitter and project-owner authorization data by separating +durable cross-replica read abuse control from privacy-sensitive disclosure. The +user approved the 10B1/10B2 split after required review found the inherited +10B contract unsafe to implement as written. + +## What changed and why + +- D33 makes 10B planning-only and declares 10B1 as its only successor. +- 10B1 extends the existing PostgreSQL counter with one locked + `authorization_read` scope, independent capacity settings, an unattached + dependency, migration rollback safety, and hosted 90 percent coverage. +- 10B2 attaches that control exactly once to three reads and owns strict minimal + responses, canonical resource scope, audited concealment, signed keyset + cursors, exact three-action activation, and hosted API proof. +- 10C follows only after 10B2 and retains all mutation/PREP work. + +## Scope and product behavior + +This PR changes planning/process artifacts and one merge intent only. It adds no +migration, route, runtime behavior, test, workflow, public documentation, or +action activation. Authored `STATUS.md` is unchanged because signed automation +owns live state. + +## Evidence and review + +Exact commit `25b6ae134e3e3db4350fbcbb5c7cfeaa9e261044` passed all nine required +review tracks against trusted main `f2aa57a4`. Stale scans, Markdown links, +merge-intent validation, and diff integrity pass. No test or CI threshold was +changed or weakened. + +## Remaining risks and follow-up + +10B1 must prove migration locking and cross-replica counters before any read +surface exists. 10B2 must prove rate-before-lookup, indistinguishable public +404 behavior with correct evidence semantics, no counts, minimal schemas, +cursor replay resistance, and exact action/OpenAPI activation. Each child needs +its own protected start, implementation review, GitHub full suite/coverage, +CodeRabbit review, and human merge approval. + +## Human review focus and merge ownership + +Review the 10B1/10B2 boundary, migration `0032` ownership, action-aware +concealment, cursor threat model, hosted coverage requirements, and exact +successor order. The user retains approval for this specific PR and merge. From dd8afb790ddbe0e04581ee717e5fc03952e6b23e Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 17:25:18 +0100 Subject: [PATCH 3/4] docs(auth): clarify 10B merge authority --- .../reviews/WS-AUTH-001-10B-pr-trust-bundle.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-pr-trust-bundle.md index e77a5ddda..fce735421 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-pr-trust-bundle.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-pr-trust-bundle.md @@ -51,4 +51,5 @@ CodeRabbit review, and human merge approval. Review the 10B1/10B2 boundary, migration `0032` ownership, action-aware concealment, cursor threat model, hosted coverage requirements, and exact -successor order. The user retains approval for this specific PR and merge. +successor order. The user retains final approval authority for this specific PR +and merge. From a1953b754befedbd5c3ca5ce32daddbae57b5135 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Tue, 21 Jul 2026 17:26:29 +0100 Subject: [PATCH 4/4] docs(auth): bind 10B integrated review evidence --- .../reviews/WS-AUTH-001-10B-internal-review-evidence.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-internal-review-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-internal-review-evidence.md index c8402b38b..02421b259 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-internal-review-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-10B-internal-review-evidence.md @@ -1,10 +1,12 @@ # WS-AUTH-001-10B Internal Review Evidence -Reviewed code SHA: `25b6ae134e3e3db4350fbcbb5c7cfeaa9e261044` +Reviewed code SHA: `dd8afb790ddbe0e04581ee717e5fc03952e6b23e` + +Reviewed planning SHA: `25b6ae134e3e3db4350fbcbb5c7cfeaa9e261044` Reviewed against trusted main: `f2aa57a45f9088a91e8f7adcf79ec7e05a2b5734` -Reviewed at: `2026-07-21T16:22:47Z` +Reviewed at: `2026-07-21T16:26:06Z` Reviewer run IDs: `auth10_plan_core`, `auth10_plan_security_qa`, `auth10_plan_ops_ci_docs`