Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ alternate writer.

## One writer path

- Surviving API: separate `PUT /projects/{project_id}/review-policy` and
`PUT /projects/{project_id}/revision-policy` routes in the dedicated
- Surviving API: separate
`PUT /api/v1/projects/{project_id}/guides/{guide_id}/review-policy` and
`PUT /api/v1/projects/{project_id}/guides/{guide_id}/revision-policy` routes
in the dedicated
`backend/app/modules/projects/policy_mutation_router.py`, registered once by
`backend/app/api/router.py`, each declaring its exact primary ActionId. This
follows the project-create and guide-mutation router boundary on current main.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ This table is the planning source of truth for the v0.1 review and human-revisio

| ActionId | PermissionId | Principal and scope | Resource family | Surface owner | State | Activation wave |
|---|---|---|---|---|---|---|
| `project.review_policy.update` | `project.review_policy.manage` | Project Manager grant for exact project | draft guide + ReviewPolicy version | project/REV semantics; AUTH mutation | registered planned | `WS-XINT-003-02B` after 02A lineage |
| `project.revision_policy.update` | `project.review_policy.manage` | Project Manager grant for exact project | draft guide + RevisionPolicy version | project/REV semantics; AUTH mutation | registered planned | `WS-XINT-003-02B` after 02A lineage |
| `project.review_policy.update` | `project.review_policy.manage` | Project Manager grant for exact project | draft guide + ReviewPolicy version | project/REV semantics; AUTH mutation | active | `WS-XINT-003-02B` after 02A lineage |
| `project.revision_policy.update` | `project.review_policy.manage` | Project Manager grant for exact project | draft guide + RevisionPolicy version | project/REV semantics; AUTH mutation | active | `WS-XINT-003-02B` after 02A lineage |
| `review.queue.read` | `review.queue.read` | reviewer grant; exact project; self-review denied | concealed current-work view | REV | registered planned | `WS-XINT-003-03A` |
| `review.claim` | `review.claim` | reviewer grant; exact project; self-review denied | queue entry + global reviewer lease state | REV | registered planned | `WS-XINT-003-03A` |
| `review.release` | `review.release` | owning reviewer and active lease | ReviewLease | REV | registered planned | `WS-XINT-003-03A` |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,59 @@
# Discovery: WS-XINT-003 REV-AUTH End-to-End Contract

## WS-XINT-003-02B current-main refresh — 2026-08-02

### Observed merged baseline

- PR #242 merged 02A as `9618b938`; Alembic `0047` makes `ReviewPolicy` and
`RevisionPolicy` append-only identities with generation, digest, predecessor,
and complete/legacy semantics.
- `ProjectGuide` selects exact policy identities, and Task, Submission, and
CheckerRun retain the immutable lineage. Existing reads lock the selected row
through `ProjectRepository.lock_review_policy()` and
`lock_revision_policy()`.
- AUTH already registers strict
`ProjectReviewPolicyMutationResourceContext` and
`ProjectRevisionPolicyMutationResourceContext`; both actions remain planned.
- No `ProjectPolicyMutationService`, policy mutation router, replay repository,
idempotency ledger, provenance/evidence columns, append-only repository
writers, or public mutation routes exist on merged main.

### Existing conventions to preserve

- Project-create and guide-mutation routes use dedicated router/service modules,
caller-owned root transactions, opaque PREP handles, exact locked facts, and
atomic decision evidence.
- Committed idempotent replay is checked before fresh authority; pending or
changed reuse conflicts. A replay-only repository cannot write product rows.
- `policy_lineage.py` owns strict semantic validation and domain-separated
policy digests. `0047` owns immutability and downstream lineage and must not be
weakened or duplicated.

### Gaps owned by 02B

- Add one dedicated two-route API and one orchestration service.
- Add the two internal append-only repository primitives and a replay-only
ledger repository.
- Add migration `0048` for the ledger and nullable historical provenance plus
evidence references.
- Extend the existing PREP evaluator only for the two typed policy resources and
activate exactly their catalogue rows.
- Add PostgreSQL tests for replay, revocation, stale/crossed replacement,
active-guide freeze, immutability, atomic rollback, and migration round trip.

### Risks and resolved assumptions

- A combined persistence/activation chunk was rejected earlier; 02A has now
removed that blocker. 02B does not modify policy meanings or downstream
lineage.
- The API needs an explicit optimistic precondition. The implementation uses an
`If-Match` opaque selector binding the current policy ID, generation, and
canonical digest plus an explicit no-current sentinel for first creation;
wildcard/omitted preconditions deny. Digest-only matching was rejected because
equal policy semantics must not hide predecessor advancement.
- No external credentials are required. PostgreSQL proof runs in hosted CI when
local isolated database variables are absent.

## Canonical references

- [Review lifecycle specification](../../../docs/spec_review_lifecycle.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,32 @@ test semantics mapping. Its post-main-merge review also found that the guide
sufficiency migration test did not protect cleanup when the initial downgrade
failed. All findings were valid and corrected; the exact response is in
`reviews/WS-XINT-003-02A-external-review-response.md`.

## WS-XINT-003-02B guide-bound policy mutation activation

Architecture, security/auth, product/operations, QA/test, senior engineering,
reuse/dedup, docs, test-delta, and CI integrity reviewed the completed runtime
chunk. Valid findings corrected full PREP and denial binding, replay-before-PREP
ordering, same-actor replay, exact opaque selectors, route rollback, database
successor/predecessor custody, live fixture bypasses, and operator docs. All
tracks passed after correction; no finding remains open. Final evidence is in
`reviews/WS-XINT-003-02B-internal-review.md`.

The first hosted Backend run failed the unchanged docstring gate because 22 new
02B callables reduced coverage to 79.7 percent. The new surface was documented,
and the same local gate passes at 80.5 percent without a threshold or workflow
change. External evidence is in
`reviews/WS-XINT-003-02B-external-review-response.md`.

CodeRabbit's first pass found valid replay timestamp immutability, downgrade
locking, historical trigger allow-list, fixture-copy, and replacement-selector
documentation issues. Its related indexing, typing, exact-exception,
constraint-shape, and reservation-branch notes were also valid. All were fixed;
none was deferred.

CodeRabbit's second pass found a valid post-lock guide-version revalidation gap;
it now denies before PREP consumption. Hosted migration evidence then exposed a
stale 0047 head constant and an incorrect unprefixed constraint lookup. The
0048 head is now exact, the installed constraint is behaviorally exercised for
independent and partial selector cases, and the focused isolated PostgreSQL
round trip passes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

## Current status

WS-XINT-003-01 is merged. WS-XINT-003-02A immutable policy identity and
downstream lineage is implemented on its bounded branch and has passed final
internal review. No policy mutation action or public surface is activated.
WS-XINT-003-01 and WS-XINT-003-02A are merged. WS-XINT-003-02B policy mutation
activation is the active bounded implementation chunk from `origin/main` at
`2c24c91d`. No policy mutation action or public surface was active at its
starting baseline.

## Baseline

Expand Down Expand Up @@ -44,5 +45,6 @@ REV-owned semantics with AUTH-owned mutation authorization.

## Next step

Open the 02A PR, run exact-head hosted CI and CodeRabbit, obtain human merge,
and stop. WS-XINT-003-02B requires a new explicit user start after 02A merges.
WS-XINT-003-02B implementation and internal review are complete. Open the PR,
run exact-head hosted review, resolve every valid external finding, obtain human
merge, and stop before review lifecycle activation.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

## Status

Planning skeleton. Refresh only after 02A merges; do not start automatically.
In progress after PR #242 merged 02A and the user explicitly started 02B on
2026-08-02. Refreshed from `origin/main` at merge `6babf81b` before application
code changes and rebased onto `2c24c91d` before external review.

## Goal

Expand All @@ -18,10 +20,18 @@ existing opaque, transaction-bound PREP protocol.
- A replay-only repository may touch only the idempotency ledger.
- Exact committed replay returns the recorded response without new PREP,
policy write, or allowed evidence, including after later grant/link
revocation. Changed or pending replay conflicts without product state.
revocation, but only after current authentication proves the same active
ActorProfile and the replay row matches the actor, action, project, guide,
idempotency key, and canonical request digest. An idempotency key alone never
discloses a response. Changed or pending replay conflicts without product
state.
- Final PREP consumption follows locks on the exact project, draft guide,
selected current policy, reserved replacement identity, actor/link/grant,
operation, request digest, session, and transaction.
- The two typed policy PREP contexts bind the operation identity, canonical
request digest, reserved successor identity and generation, predecessor
identity and digest (or the explicit no-current sentinel), draft-guide
status, exact actor/link/grant/project scope, session, and root transaction.
- New versions persist actor, identity link, matched grant, project scope,
ActionId, decision-event reference, predecessor identity/digest, generation,
and canonical policy digest atomically with selection advancement.
Expand All @@ -30,8 +40,166 @@ existing opaque, transaction-bound PREP protocol.
replacement races fail with no partial policy or audit state.
- No review lifecycle ActionId or behavior is activated.

## Allowed files

```text
backend/app/api/router.py
backend/app/modules/projects/models.py
backend/app/modules/projects/repository.py
backend/app/modules/projects/schemas.py
backend/app/modules/projects/policy_lineage.py
backend/app/modules/projects/policy_mutation_replay_repository.py
backend/app/modules/projects/policy_mutation_service.py
backend/app/modules/projects/policy_mutation_router.py
backend/app/modules/authorization/catalogue.py
backend/app/modules/authorization/kernel.py
backend/app/modules/authorization/prepared.py
backend/app/modules/authorization/runtime.py
backend/alembic/versions/0048_review_revision_policy_authority.py
backend/tests/test_authorization.py
backend/tests/test_project_policy_mutations.py
backend/tests/test_policy_identity_lineage.py
backend/tests/test_projects.py
backend/tests/test_tasks.py
backend/tests/test_artifact_admission.py
backend/tests/project_create_fixtures.py
backend/tests/conftest.py
backend/tests/test_alembic.py
backend/tests/test_artifact_architecture.py
backend/scripts/api_contract_e2e.py
backend/scripts/run_test_lanes.py
docs/spec_authorization_service.md
docs/spec_review_lifecycle.md
docs/operations_authorization_service.md
docs/operations_roles_permissions.md
docs/operations_project_operating_manual.md
.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12D2-guide-bound-policy-mutations.md
.agent-loop/initiatives/WS-REV-001-review-revision-lifecycle/chunks/WS-REV-001-03P-review-revision-policy-persistence.md
.agent-loop/initiatives/WS-XINT-003-rev-auth-end-to-end/**
```

## Not allowed changes

- Queue, lease, review, finding, decision, or revision-execution behavior.
- Artifact, contribution, compensation, reputation, frontend, or token-verifier
behavior.
- Compatibility routes, embedded guide policy fields, duplicate policy models,
alternate writers, generic authorization protocols, or fallback authority.
- Mutation of historical policy versions or rewriting 02A lineage.

## Migration and schema ownership

- `0048_review_revision_policy_authority` follows merged `0047` and adds only
the replay ledger plus nullable historical provenance/evidence columns needed
by newly appended complete policy versions.
- Database constraints require each newly appended complete 02B row to carry a
coherent non-null actor profile, identity link, matched grant, project scope,
ActionId, decision-event reference, predecessor identity/digest (or the
explicit first-version marker), generation, and canonical digest. Existing
legacy rows remain explicitly grandfathered and nullable.
- Existing 02A immutable guards, lineage keys, Task/Submission/CheckerRun locks,
and policy semantic fields remain authoritative.
- `0048` corrects the 02A draft-setup selector shape so the review selector
triple and revision selector triple are each independently all-null or
all-present. This is required because the two policies have separate routes
and may be attached in either order. The active/superseded-guide constraint
continues to require both complete selector triples, and partial triples are
always rejected.
- The replay ledger has one UUID operation identity, action, actor, exact
project/guide, request digest, state, reserved replacement policy identity,
and committed response. It cannot be a policy writer.

## API and transaction contract

- `PUT /api/v1/projects/{project_id}/guides/{guide_id}/review-policy` declares
only `project.review_policy.update`.
- `PUT /api/v1/projects/{project_id}/guides/{guide_id}/revision-policy` declares
only `project.revision_policy.update`.
- Both require an `Idempotency-Key` UUID and an `If-Match` opaque selector that
binds the current policy ID, generation, and canonical digest;
creation requires the exact quoted sentinel `If-Match: "no-current-policy"`,
never an omitted or wildcard precondition.
- The canonical request digest includes the HTTP method, exact ActionId,
project ID, guide ID, policy kind, exact `If-Match` selector or no-current sentinel,
normalized policy semantics, and idempotency operation identity.
- The service owns one root transaction. It checks committed replay before PREP,
prepares exact Project Manager authority, locks project/guide/current policy
and replay reservation, validates server-computed semantics/digest, consumes
PREP, appends one policy, advances the guide selector, stages allowed evidence,
records the response, and commits once.
- Denial and conflict paths expose bounded errors and create no policy row,
selector advancement, committed replay, or allowed evidence.
- Denial/conflict audit evidence, when recorded, is limited to action,
project/guide, reason code, and request digest; it never contains the raw
request body, policy semantics, or internal error details.

## Acceptance criteria

- Exactly the two policy actions become active; no other action availability or
permission mapping changes.
- Exact covered-project Project Manager authority is required and revalidated
against the locked actor, identity link, grant, project, draft guide, current
selected policy, reserved successor, generation, predecessor digest, request,
session, and transaction.
- Complete request semantics are normalized through `policy_lineage.py`; the
server owns the canonical policy digest.
- A successful replacement appends one immutable policy row, records complete
provenance/evidence, and advances only the matching guide selector atomically.
- A draft guide with neither policy may attach review then revision or revision
then review; the intermediate state has one complete selector triple. Guide
activation remains impossible until both complete policies are selected.
- Exact committed replay returns the recorded response without PREP or a new
allowed event, even after grant/link revocation, only for the same currently
authenticated active ActorProfile and exact recorded actor/action/project/
guide/idempotency/request tuple. Pending, changed, crossed, stale, or
differently authenticated replay conflicts without product mutation.
- Active guide, stale `If-Match`, stale selected policy, cross-project/guide,
wrong action, copied/replayed/wrong-session/wrong-transaction handle, revoked
link/grant, and concurrent replacement all fail closed.
- Direct update/delete/truncate remains refused by 02A guards; old mutators and
embedded guide-policy inputs do not reappear.
- OpenAPI and architecture tests prove one router/service/writer path and exact
primary actions.

## Verification commands

```bash
(cd backend && .venv/bin/ruff check app tests scripts)
(cd backend && .venv/bin/pytest -q tests/test_artifact_architecture.py)
(cd backend && WORKSTREAM_TEST_ADMIN_DATABASE_URL="$WORKSTREAM_TEST_ADMIN_DATABASE_URL" \
.venv/bin/python scripts/run_isolated_tests.py \
--metadata-json .ci/xint-003-02b-focused.json --lane xint_003_02b \
-- .venv/bin/pytest -q tests/test_authorization.py \
tests/test_project_policy_mutations.py tests/test_alembic.py)
(cd backend && WORKSTREAM_TEST_DATABASE_URL="$WORKSTREAM_TEST_DATABASE_URL" \
.venv/bin/pytest -q tests/test_project_policy_mutations.py \
--cov=app.modules.projects.policy_mutation_replay_repository \
--cov=app.modules.projects.policy_mutation_service \
--cov=app.modules.projects.policy_mutation_router \
--cov-report=term-missing --cov-fail-under=90)
(cd backend && .venv/bin/python scripts/api_contract_e2e.py)
python3 scripts/check_stale_authorization_docs.py
python3 scripts/check_stale_artifact_contracts.py
python3 scripts/check_stale_workstream_wording.py
python3 scripts/check_markdown_links.py
git diff --check
```

GitHub `Backend / test`, `Agent Gates / agent-gates`, and CodeRabbit must pass
on the exact final head. No local full-suite run is required.

## Required reviewers

Architecture, security/auth, product/operations, QA/test, senior engineering,
reuse/dedup, docs, test-delta, and CI integrity.

## Human review focus

The sole writer path, exact Project Manager/project/guide/policy binding,
append-only semantics, replay-before-PREP behavior, atomic provenance/evidence,
active-guide freeze, and absence of review-lifecycle activation.

## Risk, review, and stop

L1. Exact files and commands must be refreshed from post-02A main. Require all
L1 reviewers, hosted full coverage, CodeRabbit, and human merge. Merge and stop
before 03A.
L1. Require all named reviewers, hosted full coverage, CodeRabbit, and human
merge. Merge and stop before 03A.
Loading
Loading