Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .agent-loop/REVIEW_LOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -30,114 +32,43 @@ 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=<admin-db> .venv/bin/python scripts/run_isolated_tests.py --metadata-json <path> --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=<test-db> .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.
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.
Loading
Loading