Skip to content

AUTH-12C: activate project creation authority - #229

Merged
abiorh-claw merged 13 commits into
mainfrom
codex/ws-auth-001-12c-project-create
Jul 31, 2026
Merged

AUTH-12C: activate project creation authority#229
abiorh-claw merged 13 commits into
mainfrom
codex/ws-auth-001-12c-project-create

Conversation

@Abiorh001

@Abiorh001 Abiorh001 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Chunk

WS-AUTH-001-12C — project.create

Goal

Activate one fail-closed, transaction-bound path for system-scoped Project Managers to create projects.

Human-approved intent

Continue AUTH-11/12 while preserving the landed ART guide-ingestion authorization. Contributor task-submission authorization remains owned by its later dedicated chunks.

What changed

  • Activates only project.create in the closed authorization catalogue.
  • Adds PREP binding, denial evidence, provenance, idempotent replay recovery, and database custody constraints.
  • Makes the route own commit/rollback and validates Idempotency-Key before actor provisioning.
  • Isolates project-create route, service, and reservation boundaries and removes the obsolete token-role create path.
  • Centralizes supported PostgreSQL constraint-name inspection in one database helper.
  • Adds migration 0044, API-contract proof, adversarial tests, documentation, and hosted per-file coverage enforcement.

Scope control

This PR does not activate project update/archive, guide mutations, contributor task submission, submission preparation, or generic artifact access.

Product behavior

Only an active human with an active identity link and effective system-scoped Project Manager project.create grant can create a project. Exact committed retries return the same project. Changed, copied, cross-actor, stale, revoked-new-request, service-actor, contributor, or project-scoped attempts deny safely.

Authorization design

The existing opaque PreparedAuthorizationHandle binds actor, identity link, system grant, action, request digest, idempotency key, operation identity and generation, requested project, session, and transaction. Allowed decision evidence, idempotency completion, and project provenance commit atomically.

Verification

  • Exact head: b9f3a99b1bbf242684407466b45044f4327e767f, current with main.
  • GitHub Backend full semantic lanes and coverage: passed in 14m11s.
  • GitHub Agent Gates: passed.
  • Focused project-create tests: 22 passed.
  • Targeted authorization helper regression: passed.
  • Focused coverage: database helper 100%, create router 100%, create repository 96.43%, create service 94.12%.
  • Ruff, stale authorization docs, stale wording, Markdown links, and diff integrity: passed.
  • No tests removed, skipped, or weakened.

Internal reviewers

Architecture/senior PASS; security PASS; QA PASS; product operations PASS; CI integrity PASS; docs PASS; test delta PASS; reuse/dedup PASS WITH LOW RISKS. All valid findings were fixed.

External review

CodeRabbit reviewed the complete project-create boundary through 6fd23074, raised one valid deduplication comment, and that comment is fixed by ab7584f4 with focused tests and internal re-review. The subsequent exact-head automatic attempt is currently rate-limited; this is not represented as a fresh CodeRabbit review. There are zero unresolved, non-outdated review threads.

Remaining risks

No known code or CI blocker. GitHub reports REVIEW_REQUIRED; a human approval is still required.

Follow-up work

Later AUTH chunks activate remaining project/guide mutations. Contributor submission authorization remains separate and will connect task submission through the ART submission-bundle boundary.

Human review focus

Review migration custody triggers, exact replay semantics, denial evidence after rollback, system-scope-only authority, and atomic project provenance.

Human merge ownership

A human must approve and merge this specific PR.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Project creation now uses active, system-scoped project.create authorization with prepared bindings, audit evidence, idempotent reservations, provenance constraints, replay handling, and migration coverage. The old project-creation route and service method were replaced by dedicated project-create modules.

Changes

Project creation authorization

Layer / File(s) Summary
Authority contracts and custody schema
.agent-loop/..., backend/app/modules/authorization/*, backend/app/modules/projects/models.py, backend/alembic/versions/0044_project_create_authority.py, docs/*
project.create is active. Project creation now requires system-scoped authority, project-bound audit evidence, provenance fields, and idempotency custody constraints.
Prepared project-create authorization
backend/app/modules/authorization/kernel.py, backend/app/modules/authorization/prepared.py, backend/app/api/deps/authorization.py
Prepared bindings validate operation, project, and generation identifiers. Denials retain resource context and persist evidence before HTTP translation.
Reservation-backed project creation
backend/app/api/router.py, backend/app/modules/projects/create_*.py, backend/app/modules/projects/router.py, backend/app/modules/projects/service.py, backend/scripts/api_contract_e2e.py
The new endpoint validates UUID idempotency keys, reserves actor-scoped requests, consumes authorization, creates attributed projects, handles replay, and maps bounded conflicts.
Migration, fixtures, regression, and coverage validation
backend/tests/*, .github/workflows/backend.yml, .agent-loop/.../reviews/*
Migration, authorization, API, replay, concurrency, fixture, coverage, and compatibility tests were expanded for the new custody model.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: abiorh-claw

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ProjectCreateRouter
  participant ProjectCreateService
  participant ProjectCreateRepository
  participant AuthorizationService
  Client->>ProjectCreateRouter: POST /api/v1/projects with Idempotency-Key
  ProjectCreateRouter->>ProjectCreateService: create request
  ProjectCreateService->>ProjectCreateRepository: reserve actor-scoped request
  ProjectCreateService->>AuthorizationService: consume system-scoped PROJECT_CREATE
  ProjectCreateService->>ProjectCreateRepository: complete reservation
  ProjectCreateRouter-->>Client: ProjectResponse or replayed response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the main change: activating project creation authority.
Description check ✅ Passed The description explains the goal, scope, behavior, design, verification results, risks, reviews, and human approval requirement.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/ws-auth-001-12c-project-create

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Abiorh001
Abiorh001 marked this pull request as ready for review July 30, 2026 18:35
@Abiorh001

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (7)
backend/tests/test_audit.py (1)

187-189: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Indentation of these three set members is off by four spaces relative to the rest of the literal. Harmless at runtime, but it makes the added entries look nested.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_audit.py` around lines 187 - 189, Align the three ActionId
members PROJECT_ACTIVE_GUIDE_READ, PROJECT_CREATE, and PROJECT_READ with the
other members in the surrounding set literal by removing the extra four spaces
of indentation.
backend/tests/test_authorization.py (1)

3456-3465: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dead PROJECT_CREATE branches remain after the continue. With the skip at 3456-3457, the conditionals at 3459-3464 can never take the PROJECT_CREATE path; they can be collapsed to the project scope.

♻️ Suggested simplification
         if action_id is ActionId.PROJECT_CREATE:
             continue
         scope = PreparedAuthorityScope(
-            kind=(
-                PreparedAuthorityScopeKind.SYSTEM
-                if action_id is ActionId.PROJECT_CREATE
-                else PreparedAuthorityScopeKind.PROJECT
-            ),
-            project_id=None if action_id is ActionId.PROJECT_CREATE else project_id,
+            kind=PreparedAuthorityScopeKind.PROJECT,
+            project_id=project_id,
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_authorization.py` around lines 3456 - 3465, Remove the
unreachable PROJECT_CREATE branches from the PreparedAuthorityScope construction
after the existing skip in the authorization test, and always use
PreparedAuthorityScopeKind.PROJECT with the existing project_id value.
backend/tests/project_create_fixtures.py (3)

38-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Pre-cutover branch returns a transient Project that is not attached to the session.

The row is inserted via raw SQL, but the returned object is a detached ORM instance with no identity in the session. Callers that only read project_id are fine, but any caller that mutates the return value (or expects session.get(Project, id) to return the same object) will silently diverge from the post-cutover branch, which returns a session-managed instance. Consider returning the persisted instance (e.g. await session.get(Project, project_id) guarded by the historical column set) or documenting the contract difference in the docstring.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/project_create_fixtures.py` around lines 38 - 55, Update the
pre-cutover branch of the project fixture helper to return the persisted,
session-managed Project instance after the raw SQL insert, using
session.get(Project, project_id) while preserving the historical column set.
Ensure the returned object matches the post-cutover branch’s session identity
behavior rather than the transient instance currently constructed locally.

129-137: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

UUID(project_id) makes the post-cutover branch stricter than the pre-cutover branch.

The historical path accepts any 36-char string, while this path raises ValueError for non-UUID ids. All current call sites pass str(uuid4()), so this is only a latent trap for future callers; a short docstring note or an explicit UUID(project_id) validation up front would keep both branches consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/project_create_fixtures.py` around lines 129 - 137, Document
the UUID requirement for project_id in the fixture path around
ProjectCreateResourceContext, or validate it explicitly before constructing the
resource so invalid identifiers fail consistently with the post-cutover
behavior. Keep the existing requested_project_id assignment for valid UUID
strings and ensure both historical and post-cutover branches enforce the same
contract.

105-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The existing-grant lookup can never hit for a freshly minted actor.

actor_id/link are created with a new uuid4() on every call (Lines 56-79), so the project_manager lookup at Lines 105-112 always returns None and a new grant is always inserted. The branch is dead work; either drop the query or reuse an existing fixture actor when one is present.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/project_create_fixtures.py` around lines 105 - 127, The
existing-grant lookup in the project fixture setup is unreachable because each
call creates a fresh actor via the actor/link creation flow. Remove the grant
query and conditional reuse, or change the fixture setup to reuse an existing
actor when available; ensure the project-manager grant is created only when
needed without retaining dead lookup logic.
.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md (1)

162-184: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Ruff/coverage command lists omit app/modules/audit/schemas.py.

It's in the allowed-files list (line 43) and is materially changed by this chunk, but it is neither linted here nor included in the coverage --source, while the criteria at lines 153-154 require every changed module to be covered.

♻️ Proposed adjustment
   app/modules/authorization/runtime.py app/api/deps/authorization.py \
+  app/modules/audit/schemas.py \
   alembic/versions/0044_project_create_authority.py \
-  --source=app.modules.projects,app.modules.authorization \
+  --source=app.modules.projects,app.modules.authorization,app.modules.audit \
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
@.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md
around lines 162 - 184, Update the Ruff command and coverage configuration in
this validation block to include app/modules/audit/schemas.py, adding it to the
linted file list and its module path to coverage --source and --include patterns
so every changed allowed module is linted and covered.
backend/app/modules/authorization/prepared.py (1)

162-169: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the project-create identity comparison.

The same three-field binding/resource comparison is duplicated in consume and deny_unsupported; a small predicate keeps them from drifting.

♻️ Suggested helper
`@staticmethod`
def _project_create_binding_matches(
    binding: _PreparedAuthorizationBinding,
    resource: ProjectCreateResourceContext,
) -> bool:
    return (
        binding.project_create_operation_id == resource.resource_id
        and binding.project_create_project_id == resource.requested_project_id
        and binding.project_create_generation == resource.operation_generation
    )

Also applies to: 191-197

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/modules/authorization/prepared.py` around lines 162 - 169,
Extract the repeated project-create identity comparison into a shared predicate,
such as _project_create_binding_matches, accepting _PreparedAuthorizationBinding
and ProjectCreateResourceContext and returning whether all three fields match.
Replace the inline comparisons in both consume and deny_unsupported with this
helper, preserving the existing invalid-handle behavior when it returns false.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/alembic/versions/0044_project_create_authority.py`:
- Around line 239-261: Update the custody validation in the project-create
trigger so every nullable provenance and evidence comparison uses NULL-safe IS
DISTINCT FROM semantics instead of <>. Apply this to the reservation/project
checks and the evidence fields in the blocks that raise “project create custody
mismatch” and “project create evidence mismatch,” while preserving the existing
expected-value validations and exception behavior.
- Around line 111-120: Update the Alembic migration’s check-constraint
operations for the constraint shown to use the logical name suffix
“creation_authority_shape” rather than the convention-rendered
“ck_projects_creation_authority_shape”. Apply the same logical name to both
create and drop operations so Alembic’s naming convention adds the prefix
exactly once.

In `@backend/app/modules/projects/repository.py`:
- Around line 101-128: The idempotency reservation flow in the repository method
must handle concurrent same-key requests without raising
ProjectRepositoryIntegrityError. Replace the on_conflict_do_nothing path on the
ProjectCreateIdempotencyRecord insert with a no-op on_conflict_do_update that
targets the same actor_profile_id, action_id, and idempotency_key constraint and
returns the existing row ID, causing competing requests to wait for the first
writer; preserve the claimed result for new records and the documented
idempotency_pending result for conflicts.

In `@backend/app/modules/projects/router.py`:
- Around line 173-183: Update the IntegrityError handling around the existing
constraint_name extraction to support asyncpg, psycopg2, and direct
constraint_name shapes by falling back through
exc.orig.__cause__.constraint_name, exc.orig.constraint_name, and
exc.orig.diag.constraint_name. Retain the rollback and re-raise behavior for
unrelated constraints, while ensuring the allowed slug-conflict set includes
ix_projects_slug.

In `@backend/tests/project_create_fixtures.py`:
- Around line 138-161: The project seed fixture creates authority audit events
and system-scoped grants that invalidate absolute-count assertions. In
backend/tests/project_create_fixtures.py:138-161, add an emit_evidence opt-out
to seed_authorized_project and skip both side effects when disabled; in
backend/tests/test_artifact_admission.py:3661-3687 and
backend/tests/test_artifact_recovery.py:180-185, convert audit-event checks to
baseline deltas or appropriate filters; in backend/tests/test_auth.py:1799-1810,
adjust system-scoped grant totals to account for the two fixture-created
project_manager grants.

In `@backend/tests/test_alembic.py`:
- Around line 2181-2192: Update the privacy constraint check in the async
inspection flow to handle a None result from connection.scalar before testing
for substrings. Preserve the existing true condition when the constraint
definition contains both required terms, while returning false when the
constraint is missing so the assertion reports clearly.

In `@backend/tests/test_audit.py`:
- Around line 204-222: Move
test_project_create_audit_event_binds_operation_to_future_project so it appears
after test_action_aware_audit_input_enforces_mapping_and_action_availability has
fully ended, including its artifact digest, invalid-digest, unknown-action, and
ADMIN_ROLE_GRANT_ISSUE_DENIED assertions. Ensure those assertions remain owned
by the mapping test, and keep the project-create test as a separate function
containing only its project binding assertions.

In `@backend/tests/test_projects.py`:
- Around line 8761-8766: Update the pending-reservation assertion in the project
creation test to count all pending ProjectCreateIdempotencyRecord rows for this
slug’s attempts, rather than filtering by project_id != first.json()["id"]. Keep
the assertion expecting zero so NULL project_id reservations are included and
leaked pending rows are detected.

---

Nitpick comments:
In
@.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md:
- Around line 162-184: Update the Ruff command and coverage configuration in
this validation block to include app/modules/audit/schemas.py, adding it to the
linted file list and its module path to coverage --source and --include patterns
so every changed allowed module is linted and covered.

In `@backend/app/modules/authorization/prepared.py`:
- Around line 162-169: Extract the repeated project-create identity comparison
into a shared predicate, such as _project_create_binding_matches, accepting
_PreparedAuthorizationBinding and ProjectCreateResourceContext and returning
whether all three fields match. Replace the inline comparisons in both consume
and deny_unsupported with this helper, preserving the existing invalid-handle
behavior when it returns false.

In `@backend/tests/project_create_fixtures.py`:
- Around line 38-55: Update the pre-cutover branch of the project fixture helper
to return the persisted, session-managed Project instance after the raw SQL
insert, using session.get(Project, project_id) while preserving the historical
column set. Ensure the returned object matches the post-cutover branch’s session
identity behavior rather than the transient instance currently constructed
locally.
- Around line 129-137: Document the UUID requirement for project_id in the
fixture path around ProjectCreateResourceContext, or validate it explicitly
before constructing the resource so invalid identifiers fail consistently with
the post-cutover behavior. Keep the existing requested_project_id assignment for
valid UUID strings and ensure both historical and post-cutover branches enforce
the same contract.
- Around line 105-127: The existing-grant lookup in the project fixture setup is
unreachable because each call creates a fresh actor via the actor/link creation
flow. Remove the grant query and conditional reuse, or change the fixture setup
to reuse an existing actor when available; ensure the project-manager grant is
created only when needed without retaining dead lookup logic.

In `@backend/tests/test_audit.py`:
- Around line 187-189: Align the three ActionId members
PROJECT_ACTIVE_GUIDE_READ, PROJECT_CREATE, and PROJECT_READ with the other
members in the surrounding set literal by removing the extra four spaces of
indentation.

In `@backend/tests/test_authorization.py`:
- Around line 3456-3465: Remove the unreachable PROJECT_CREATE branches from the
PreparedAuthorityScope construction after the existing skip in the authorization
test, and always use PreparedAuthorityScopeKind.PROJECT with the existing
project_id value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d8bcc265-377c-4c4c-a287-23d5acfcc74d

📥 Commits

Reviewing files that changed from the base of the PR and between 57c6711 and 473e793.

📒 Files selected for processing (27)
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md
  • .github/workflows/backend.yml
  • backend/alembic/versions/0044_project_create_authority.py
  • backend/app/api/deps/authorization.py
  • backend/app/modules/audit/schemas.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/app/modules/projects/models.py
  • backend/app/modules/projects/repository.py
  • backend/app/modules/projects/router.py
  • backend/app/modules/projects/service.py
  • backend/scripts/api_contract_e2e.py
  • backend/tests/conftest.py
  • backend/tests/project_create_fixtures.py
  • backend/tests/test_alembic.py
  • backend/tests/test_api_controls.py
  • backend/tests/test_artifact_admission.py
  • backend/tests/test_artifact_recovery.py
  • backend/tests/test_audit.py
  • backend/tests/test_auth.py
  • backend/tests/test_authorization.py
  • backend/tests/test_guide_bindings.py
  • backend/tests/test_projects.py
  • docs/operations_authorization_service.md
  • docs/spec_authorization_service.md

Comment thread backend/alembic/versions/0044_project_create_authority.py
Comment thread backend/alembic/versions/0044_project_create_authority.py
Comment thread backend/app/modules/projects/repository.py Outdated
Comment thread backend/app/modules/projects/router.py Outdated
Comment thread backend/tests/project_create_fixtures.py
Comment thread backend/tests/test_alembic.py
Comment thread backend/tests/test_audit.py Outdated
Comment thread backend/tests/test_projects.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/app/modules/projects/router.py (1)

169-172: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Finalize the transaction before raising service errors.

reserve_project_create() performs a write before it can return idempotency_pending or idempotency_mismatch, but this branch raises without a rollback. Explicitly roll back before propagating non-success service errors; if denied audit evidence must persist, commit that narrowly and test it rather than relying on dependency teardown.

Proposed fix
     except PermissionDenied as exc:
+        await session.rollback()
         raise permission_http_error(exc) from exc
     except ProjectServiceError as exc:
+        await session.rollback()
         raise project_http_error(exc) from exc
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/modules/projects/router.py` around lines 169 - 172, Update the
error-handling path around reserve_project_create so every non-success
ProjectServiceError finalizes its transaction before project_http_error is
raised, including idempotency_pending and idempotency_mismatch; roll back
ordinary failures, while preserving denied-audit evidence only through an
explicit narrow commit, and cover both behaviors with tests.
🧹 Nitpick comments (1)
backend/tests/test_projects.py (1)

705-730: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use the shared grant_system_project_manager helper for both grants.

The admission call is aligned with task_client/checker_client, but project_client still manually creates both the access_administrator bootstrap and the scoped project_manager grant. Delegate those steps to backend/tests/project_create_fixtures.py:grant_system_project_manager, including its AuthorityControl.bootstrap_grant_id handling, so this fixture cannot drift from the grant/evidence semantics used by the shared fixtures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_projects.py` around lines 705 - 730, The project_client
fixture should stop manually creating the access_administrator bootstrap and
project_manager grant. Replace that setup with the shared
grant_system_project_manager helper from project_create_fixtures.py, using its
returned actor/link/grant values as needed and preserving
AuthorityControl.bootstrap_grant_id handling so both grants follow shared
grant/evidence semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/app/modules/projects/router.py`:
- Around line 169-172: Update the error-handling path around
reserve_project_create so every non-success ProjectServiceError finalizes its
transaction before project_http_error is raised, including idempotency_pending
and idempotency_mismatch; roll back ordinary failures, while preserving
denied-audit evidence only through an explicit narrow commit, and cover both
behaviors with tests.

---

Nitpick comments:
In `@backend/tests/test_projects.py`:
- Around line 705-730: The project_client fixture should stop manually creating
the access_administrator bootstrap and project_manager grant. Replace that setup
with the shared grant_system_project_manager helper from
project_create_fixtures.py, using its returned actor/link/grant values as needed
and preserving AuthorityControl.bootstrap_grant_id handling so both grants
follow shared grant/evidence semantics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9436e899-2979-4aa1-8500-4253a36d7b4b

📥 Commits

Reviewing files that changed from the base of the PR and between 473e793 and de77977.

📒 Files selected for processing (16)
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-12C-external-review-response.md
  • backend/alembic/versions/0044_project_create_authority.py
  • backend/app/modules/authorization/prepared.py
  • backend/app/modules/projects/repository.py
  • backend/app/modules/projects/router.py
  • backend/tests/conftest.py
  • backend/tests/project_create_fixtures.py
  • backend/tests/test_alembic.py
  • backend/tests/test_artifact_admission.py
  • backend/tests/test_audit.py
  • backend/tests/test_auth.py
  • backend/tests/test_authorization.py
  • backend/tests/test_checkers.py
  • backend/tests/test_projects.py
  • backend/tests/test_tasks.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/tests/test_authorization.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/tests/test_alembic.py (1)

2269-2273: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert that the historical project survived the upgrade.

At Line 2270, the migration runs, but no assertion verifies historical_id still exists; the later DELETE is a no-op if migration 0044 removed it. A destructive migration would therefore pass this test.

Proposed test tightening
             asyncio.run(seed_historical())
             command.upgrade(config, "head")
+            async def assert_historical_survives() -> None:
+                engine = create_async_engine(isolated_database_env)
+                try:
+                    async with engine.connect() as connection:
+                        assert await connection.scalar(
+                            text("select exists(select 1 from projects where id=:id)"),
+                            {"id": historical_id},
+                        )
+                finally:
+                    await engine.dispose()
+
+            asyncio.run(assert_historical_survives())
             asyncio.run(
                 _assert_0044_rejects_new_unattributed_project(isolated_database_env)
             )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_alembic.py` around lines 2269 - 2273, Update the migration
test around seed_historical and command.upgrade to explicitly verify that the
historical project identified by historical_id still exists after upgrading to
head, before invoking _assert_0044_rejects_new_unattributed_project. Keep the
existing rejection assertion unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/tests/test_alembic.py`:
- Around line 2269-2273: Update the migration test around seed_historical and
command.upgrade to explicitly verify that the historical project identified by
historical_id still exists after upgrading to head, before invoking
_assert_0044_rejects_new_unattributed_project. Keep the existing rejection
assertion unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 083ed569-ee23-4c42-91ab-76d44c877686

📥 Commits

Reviewing files that changed from the base of the PR and between de77977 and badb3d1.

📒 Files selected for processing (12)
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-12C-external-review-response.md
  • backend/tests/project_create_fixtures.py
  • backend/tests/test_alembic.py
  • backend/tests/test_artifact_admission.py
  • backend/tests/test_artifact_internal_authorization.py
  • backend/tests/test_artifact_recovery.py
  • backend/tests/test_auth.py
  • backend/tests/test_authorization.py
  • backend/tests/test_guide_bindings.py
  • backend/tests/test_outbox.py
  • backend/tests/test_projects.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-12C-external-review-response.md
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md

@Abiorh001

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
backend/app/modules/projects/create_router.py (1)

105-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the constraint-name lookup into one shared helper.

The three-shape extraction (asyncpg __cause__, direct attribute, psycopg diag) is now duplicated in this module and in backend/app/modules/authorization/project_role_service.py. Move it to one shared helper and call it from both places. That keeps future driver shapes in a single location.

♻️ Proposed shape
# backend/app/db/errors.py
def integrity_constraint_name(exc: IntegrityError) -> str | None:
    """Resolve the violated constraint across supported PostgreSQL drivers."""
    orig = exc.orig
    return (
        getattr(getattr(orig, "__cause__", None), "constraint_name", None)
        or getattr(orig, "constraint_name", None)
        or getattr(getattr(orig, "diag", None), "constraint_name", None)
    )
-        constraint_name = getattr(
-            getattr(exc.orig, "__cause__", None), "constraint_name", None
-        ) or getattr(exc.orig, "constraint_name", None)
-        if constraint_name is None:
-            constraint_name = getattr(
-                getattr(exc.orig, "diag", None), "constraint_name", None
-            )
+        constraint_name = integrity_constraint_name(exc)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/modules/projects/create_router.py` around lines 105 - 119,
Extract the duplicated three-shape constraint lookup from the create-router
IntegrityError handling and project-role service into a shared
integrity_constraint_name helper in the database errors module. Update both
callers to use this helper while preserving the existing constraint validation
and re-raise behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/app/modules/projects/create_router.py`:
- Around line 105-119: Extract the duplicated three-shape constraint lookup from
the create-router IntegrityError handling and project-role service into a shared
integrity_constraint_name helper in the database errors module. Update both
callers to use this helper while preserving the existing constraint validation
and re-raise behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: df5ebf1e-24bd-469e-a062-a9c1638d271c

📥 Commits

Reviewing files that changed from the base of the PR and between badb3d1 and 6fd2307.

📒 Files selected for processing (11)
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-12C-external-review-response.md
  • .github/workflows/backend.yml
  • backend/app/api/router.py
  • backend/app/modules/projects/create_repository.py
  • backend/app/modules/projects/create_router.py
  • backend/app/modules/projects/create_service.py
  • backend/app/modules/projects/router.py
  • backend/app/modules/projects/service.py
  • backend/tests/test_authorization.py
  • backend/tests/test_projects.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/backend.yml
  • .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-12C-project-create.md

@abiorh-claw
abiorh-claw self-requested a review July 31, 2026 05:35
@abiorh-claw
abiorh-claw merged commit 67f2c14 into main Jul 31, 2026
4 checks passed
@abiorh-claw
abiorh-claw deleted the codex/ws-auth-001-12c-project-create branch July 31, 2026 07:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants