Skip to content

feat(cloud): workspace bootstrap, resolution & management API (C2)#367

Open
OBenner wants to merge 7 commits into
developfrom
claude/cloud-workspace-context-c2
Open

feat(cloud): workspace bootstrap, resolution & management API (C2)#367
OBenner wants to merge 7 commits into
developfrom
claude/cloud-workspace-context-c2

Conversation

@OBenner

@OBenner OBenner commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Second slice of Track C (cloud multitenancy — see docs/strategy/roadmap.md), building on the merged C1 models/permissions. C2 is the workspace-context layer that makes those models usable.

What's delivered

  • CLOUD_MODE config flag (single | team, validated; default single).
  • services/workspace_service.py: get_or_create_personal_workspace (idempotent single-mode default), list_accessible_workspaces (owned ∪ member), create_workspace.
  • get_current_workspace dependency (core/permissions.py): single mode → the caller's auto-created Personal workspace; team mode → ?workspace_id with a >= viewer access check (400 if missing, 403 if denied). Extracted a shared _current_user_id helper (now reused by require_workspace_access).
  • Workspaces API (api/routes/workspaces.py, registered in main.py): GET /api/workspaces (list with role), GET /api/workspaces/current, POST /api/workspaces — the data the frontend workspace-switcher needs.
  • Bootstrap hooked into /register + /login (idempotent; backfills pre-C2 accounts).

Tests

11 workspace tests (bootstrap idempotency, owned∪member listing, single/team resolution incl. 400/403, plus an end-to-end HTTP test of the router). Existing auth/user route tests (49) unaffected; ruff clean; full app imports cleanly and the routes resolve in the OpenAPI schema. Also restores the owner-access test's membership-absence assertion that the C1 squash-merge dropped.

Scope note

No schema change. Per-resource scoping (specs/runs/repos by workspace_id) lands as each resource gains DB persistence — today only repositories is a DB model and it has no CRUD routes yet, so resource scoping is deferred to C3 (runs/AgentExecution) and C6 (OAuth repos), per the roadmap.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added workspace management endpoints: list accessible workspaces, resolve the active workspace, and create workspaces.
    • New accounts now automatically get a “Personal” workspace; older accounts are backfilled on next sign-in if missing.
    • In team mode, added workspace member management (list, add, update role, remove) with role-based access.
    • Added support for a configurable workspace mode (“single” vs “team”) that affects authorization and workspace resolution.
  • Bug Fixes

    • Enforced at most one “Personal” workspace per owner and improved permission/access handling for workspace ownership vs membership.

Second slice of Track C — the workspace-context layer that makes the C1 models usable.

- CLOUD_MODE config flag (single|team, validated; default single).
- services/workspace_service.py: get_or_create_personal_workspace (idempotent single-mode default), list_accessible_workspaces (owned ∪ member), create_workspace.
- core/permissions.py: get_current_workspace dependency — single mode resolves the caller's auto-created Personal workspace; team mode requires ?workspace_id and enforces >= viewer access. Extracted a shared _current_user_id helper (reused by require_workspace_access).
- api/routes/workspaces.py: GET /api/workspaces (list with role), GET /api/workspaces/current, POST /api/workspaces; registered in main.py.
- Personal-workspace bootstrap hooked into register + login (idempotent; backfills pre-C2 accounts).
- Tests: bootstrap idempotency, owned∪member listing, single/team resolution (+400/403), and an end-to-end HTTP test of the router. Restores the owner-access test's membership-absence assertion dropped by the C1 squash. 11 workspace tests pass; existing auth/user route tests (49) unaffected; ruff clean.

No schema change: per-resource scoping (repos/runs) lands as each gains DB persistence (C3/C6); today only 'repositories' is a DB model and has no CRUD routes yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds workspace schemas, cloud-mode validation, persistence helpers, workspace resolution, new workspace routes, app wiring, and tests for bootstrap, access, and member management.

Changes

Workspace multitenancy

Layer / File(s) Summary
Workspace schemas, mode, and constraint
apps/web-backend/api/models/workspace.py, apps/web-backend/core/config.py, apps/web-backend/migrations/versions/004_unique_personal_workspace.py
Adds workspace request/response models, validates CLOUD_MODE, and adds the partial unique index for personal workspaces.
Workspace persistence helpers
apps/web-backend/services/workspace_service.py
Adds helpers for personal-workspace bootstrapping, accessible-workspace listing, team workspace creation, and member row CRUD.
Workspace resolution and bootstrap
apps/web-backend/core/permissions.py, apps/web-backend/api/routes/users.py, apps/web-backend/main.py
Centralizes JWT subject parsing, resolves the current workspace by mode, bootstraps personal workspaces during register/login, and registers the router.
Workspace routes and integration tests
apps/web-backend/api/routes/workspaces.py, apps/web-backend/tests/test_workspaces.py
Adds workspace list/current/create/member endpoints and covers workspace bootstrap, access, and member-management flows in tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • OBenner/Auto-Coding#365: Introduces the workspace access and role logic that this PR builds on for current-workspace resolution and member permissions.

Poem

🐇 I bounced through single mode, then team,
And found new workspaces in the stream.
A Personal nest now starts on cue,
With members, roles, and routes anew.
Thump-thump! My ears approve the theme.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.91% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: cloud workspace bootstrap, resolution, and management APIs for C2.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/cloud-workspace-context-c2

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.

Comment thread apps/web-backend/services/workspace_service.py Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@apps/web-backend/api/routes/workspaces.py`:
- Around line 63-67: The workspace listing in the route that builds
WorkspaceListResponse has an N+1 query pattern because the loop over
list_accessible_workspaces calls user_role_in_workspace for each workspace. Fix
this by deriving the role without re-fetching each row in the loop, using
ws.owner_id to detect OWNER and a single batched membership lookup keyed by
workspace_id for the remaining workspaces, then pass the resolved role into
_to_response.
- Line 56: The FastAPI route signatures in workspaces.py should be updated to
match the newer idioms flagged by SonarCloud: remove the redundant
response_model from the `@router.get/`@router.post decorators where the return
type annotation already defines the response, and convert any dependency
parameters that currently use default-value Depends(...) to Annotated[T,
Depends(...)] in the affected route functions. Apply this consistently to the
route handlers identified by the decorated endpoints and their dependency
parameters so the signatures are idiomatic without changing behavior.

In `@apps/web-backend/core/permissions.py`:
- Around line 149-157: The workspace fetch in the team-mode permission path is
duplicated because `check_workspace_access` already performs the `Workspace`
lookup via `user_role_in_workspace`. Update the resolver around the existing
`check_workspace_access` call to avoid calling
`db.query(Workspace).filter(...).first()` up front; instead, perform the access
check first and only fetch/reuse the `Workspace` after it succeeds, while
keeping the same `HTTPException` behavior for denied access.

In `@apps/web-backend/services/workspace_service.py`:
- Around line 33-52: The get_or_create_personal_workspace flow is vulnerable to
duplicate inserts because the existing lookup and insert are not atomic. Update
the Workspace creation path to be idempotent by using a database-level
uniqueness guarantee on the personal workspace identity (for example, owner_id
plus name) and handle conflicts in get_or_create_personal_workspace, or switch
the Workspace insert to an upsert/ON CONFLICT pattern so concurrent calls cannot
create multiple Personal rows for the same user.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4deee8f8-d264-4a2c-99da-7fe59fc69e9c

📥 Commits

Reviewing files that changed from the base of the PR and between 28bac44 and 7f31d5a.

📒 Files selected for processing (8)
  • apps/web-backend/api/models/workspace.py
  • apps/web-backend/api/routes/users.py
  • apps/web-backend/api/routes/workspaces.py
  • apps/web-backend/core/config.py
  • apps/web-backend/core/permissions.py
  • apps/web-backend/main.py
  • apps/web-backend/services/workspace_service.py
  • apps/web-backend/tests/test_workspaces.py

Comment thread apps/web-backend/api/routes/workspaces.py
Comment thread apps/web-backend/api/routes/workspaces.py
Comment thread apps/web-backend/core/permissions.py
Comment thread apps/web-backend/services/workspace_service.py Outdated
OBenner and others added 2 commits June 25, 2026 13:25
… (C2 review)

- workspace_service.create_workspace: stop logging the user-supplied name (CodeQL log-injection); log id + owner_id only.
- workspaces.list_workspaces: replace the per-row user_role_in_workspace call (N+1) with a single membership query; owners derived from owner_id.
- permissions.get_current_workspace (team mode): check access first, fetch the workspace row only on success (no wasted lookup on denial).
- workspaces routes: drop redundant return-type annotations (keep response_model= in the decorator, matching the rest of the codebase) to clear the SonarCloud duplication hint.
Skipped (with reason): Annotated[...] deps (whole codebase uses = Depends; trivial/stylistic) and a unique-constraint for concurrent Personal-workspace creation (needs a migration, out of C2's no-schema scope; low-probability + reads self-heal). 11 tests pass; ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rounds out the workspace API with team-member management — and the first real consumer of C1's require_workspace_access dependency (listing needs >= viewer, mutations need owner).

- Endpoints (api/routes/workspaces.py): GET /api/workspaces/{id}/members (owner first), POST (add by user_id+role), PATCH /{user_id} (change role), DELETE /{user_id} (remove). Validates: unknown workspace/user -> 404, adding the owner -> 400, duplicate member -> 409.
- Service (services/workspace_service.py): list_workspace_members, get_membership, add_member, update_member_role, remove_member.
- Models: WorkspaceMemberAddRequest/UpdateRequest (role validated via Literal) + member response schemas.
- Tests: service CRUD + an end-to-end HTTP flow (add/list/dup-409/owner-400/update/non-owner-403/viewer-can-list/remove-204). 13 workspace tests pass; ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added size/L and removed size/M labels Jun 25, 2026
list_workspace_members now joinedload's WorkspaceUser.user so the members route reads wu.user.email without a per-row query — same fix as list_workspaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread apps/web-backend/tests/test_workspaces.py Fixed
Comment thread apps/web-backend/services/workspace_service.py Fixed
Comment thread apps/web-backend/services/workspace_service.py Fixed
Comment thread apps/web-backend/services/workspace_service.py Fixed
OBenner and others added 3 commits June 25, 2026 13:53
The 'Insufficient workspace permissions' literal is now used 3x in permissions.py; hoist it to a module constant. (The remaining Sonar S8410 'use Annotated for deps' hints are left as-is: the whole web-backend uses = Depends(...), per the convention CodeRabbit recorded as a repo learning.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…in asserts (C2)

- workspace_service.add_member: remove the info log (user_id/role/workspace_id are request-derived → CodeQL log-injection; member-change audit is C3/C7).
- tests: extract client.post/get/delete calls out of assert expressions so python -O can't strip the action (CodeQL py/side-effect-in-assert). 13 tests pass; ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…p (C2)

Closes the concurrent-duplicate edge case flagged in review (previously deferred). Adds a partial unique index uq_personal_workspace_per_owner on workspaces(owner_id) WHERE name='Personal' (model __table_args__ + migration 004; SQLite + Postgres partial-WHERE). get_or_create_personal_workspace is now truly idempotent under concurrency: the race loser catches IntegrityError and reuses the winner's workspace. Test asserts a second 'Personal' is rejected while other names stay unconstrained.

Note: this adds the first C2 migration (004), upgrading the slice from no-schema to a single additive index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
apps/web-backend/api/routes/workspaces.py (1)

109-117: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Gate team-only workspace management when CLOUD_MODE=single.

single mode is defined as one auto-created Personal workspace per user, but these routes still allow creating additional workspaces and sharing/managing members. Add a team-mode guard to the create and member-management endpoints.

Proposed fix
+def require_team_mode() -> None:
+    if settings.CLOUD_MODE != "team":
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="Workspace management is only available in team mode",
+        )
+
+
 async def create_workspace_endpoint(
     request: WorkspaceCreateRequest,
+    _team_mode: None = Depends(require_team_mode),
     auth: dict = Depends(require_auth),
     db: Session = Depends(get_db),
 ):
@@
 async def list_members(
     workspace_id: int,
+    _team_mode: None = Depends(require_team_mode),
     _role: WorkspaceRole = Depends(require_workspace_access(WorkspaceRole.VIEWER)),
     db: Session = Depends(get_db),
 ):
@@
 async def add_workspace_member(
     workspace_id: int,
     request: WorkspaceMemberAddRequest,
+    _team_mode: None = Depends(require_team_mode),
     _role: WorkspaceRole = Depends(require_workspace_access(WorkspaceRole.OWNER)),
     db: Session = Depends(get_db),
 ):
@@
 async def update_workspace_member(
     workspace_id: int,
     user_id: int,
     request: WorkspaceMemberUpdateRequest,
+    _team_mode: None = Depends(require_team_mode),
     _role: WorkspaceRole = Depends(require_workspace_access(WorkspaceRole.OWNER)),
     db: Session = Depends(get_db),
 ):
@@
 async def remove_workspace_member(
     workspace_id: int,
     user_id: int,
+    _team_mode: None = Depends(require_team_mode),
     _role: WorkspaceRole = Depends(require_workspace_access(WorkspaceRole.OWNER)),
     db: Session = Depends(get_db),
 ):

Also applies to: 130-220

🤖 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 `@apps/web-backend/api/routes/workspaces.py` around lines 109 - 117, Add a
team-mode guard to the workspace creation and member-management routes so they
are unavailable when CLOUD_MODE is single. Update create_workspace_endpoint and
the related member/sharing handlers in the same routes module to check the cloud
mode before calling create_workspace or modifying members, and reject the
request with the same auth/validation style used elsewhere in the API. Use the
existing workspace route helpers and symbols like create_workspace_endpoint,
_require_user_id, and the member-management endpoint functions to place the
guard consistently.
🤖 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 `@apps/web-backend/api/routes/workspaces.py`:
- Around line 177-182: The duplicate-member check in the workspace member route
is still race-prone because concurrent requests can both pass get_membership()
before add_member() commits. Update the handler around add_member() to run in a
transaction and catch the database IntegrityError from the insert; on that
failure, roll back and return the same 409 Conflict response instead of letting
it surface as a 500. Keep the existing get_membership() pre-check, but make
add_member() the authoritative commit-time guard in this route.

In `@apps/web-backend/migrations/versions/004_unique_personal_workspace.py`:
- Around line 33-42: The `upgrade()` migration in
`004_unique_personal_workspace.py` can fail when duplicate `Personal` workspaces
already exist for the same `owner_id`. Add a cleanup step before
`op.create_index` that identifies duplicate `workspaces` rows for `name =
'Personal'`, keeps one row per `owner_id`, and deletes the older extras so the
unique partial index can be created successfully. Use the
`PERSONAL_WORKSPACE_NAME` and `Workspace` creation path in
`workspace_service.py` as the context for the affected records.

---

Outside diff comments:
In `@apps/web-backend/api/routes/workspaces.py`:
- Around line 109-117: Add a team-mode guard to the workspace creation and
member-management routes so they are unavailable when CLOUD_MODE is single.
Update create_workspace_endpoint and the related member/sharing handlers in the
same routes module to check the cloud mode before calling create_workspace or
modifying members, and reject the request with the same auth/validation style
used elsewhere in the API. Use the existing workspace route helpers and symbols
like create_workspace_endpoint, _require_user_id, and the member-management
endpoint functions to place the guard consistently.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 46c2d3f7-5415-4a73-accd-cef82ab4ce22

📥 Commits

Reviewing files that changed from the base of the PR and between 7f31d5a and 6ece550.

📒 Files selected for processing (6)
  • apps/web-backend/api/models/workspace.py
  • apps/web-backend/api/routes/workspaces.py
  • apps/web-backend/core/permissions.py
  • apps/web-backend/migrations/versions/004_unique_personal_workspace.py
  • apps/web-backend/services/workspace_service.py
  • apps/web-backend/tests/test_workspaces.py

Comment on lines +177 to +182
if get_membership(db, workspace_id, request.user_id) is not None:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User is already a member"
)
membership = add_member(db, workspace_id, request.user_id, request.role)
return _member_response(membership.user_id, user.email, membership.role)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 'UniqueConstraint\("workspace_id", "user_id"|def add_member|IntegrityError|add_workspace_member' apps/web-backend

Repository: OBenner/Auto-Coding

Length of output: 6743


🏁 Script executed:

head -35 apps/web-backend/api/routes/workspaces.py

Repository: OBenner/Auto-Coding

Length of output: 1136


🏁 Script executed:

sed -n '126,135p' apps/web-backend/services/workspace_service.py

Repository: OBenner/Auto-Coding

Length of output: 501


🏁 Script executed:

grep -n "add_member" apps/web-backend/api/routes/workspaces.py

Repository: OBenner/Auto-Coding

Length of output: 257


🏁 Script executed:

head -45 apps/web-backend/api/routes/workspaces.py | grep -A 5 "from api.models"

Repository: OBenner/Auto-Coding

Length of output: 359


🏁 Script executed:

sed -n '170,190p' apps/web-backend/api/routes/workspaces.py

Repository: OBenner/Auto-Coding

Length of output: 954


🏁 Script executed:

sed -n '1,45p' apps/web-backend/api/routes/workspaces.py

Repository: OBenner/Auto-Coding

Length of output: 1372


Handle the duplicate-member race at commit time.

The pre-check at line 177 is subject to a race condition. If concurrent requests pass the check simultaneously, the second call to add_member() at line 183 will trigger a database IntegrityError, resulting in a 500 Internal Server Error instead of a clean 409 Conflict. Wrap the insertion in a transaction handler to catch the error, rollback, and return the proper status code.

Suggested implementation
+from sqlalchemy.exc import IntegrityError
 from fastapi import APIRouter, Depends, HTTPException, status
 from services.workspace_service import (
     add_member,
@@
     membership = add_member(db, workspace_id, request.user_id, request.role)
+    try:
+        membership = add_member(db, workspace_id, request.user_id, request.role)
+    except IntegrityError:
+        db.rollback()
+        if get_membership(db, workspace_id, request.user_id) is not None:
+            raise HTTPException(
+                status_code=status.HTTP_409_CONFLICT, detail="User is already a member"
+            )
+        raise
     return _member_response(membership.user_id, user.email, membership.role)
🤖 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 `@apps/web-backend/api/routes/workspaces.py` around lines 177 - 182, The
duplicate-member check in the workspace member route is still race-prone because
concurrent requests can both pass get_membership() before add_member() commits.
Update the handler around add_member() to run in a transaction and catch the
database IntegrityError from the insert; on that failure, roll back and return
the same 409 Conflict response instead of letting it surface as a 500. Keep the
existing get_membership() pre-check, but make add_member() the authoritative
commit-time guard in this route.

Comment on lines +33 to +42
def upgrade() -> None:
"""Add a partial unique index: at most one 'Personal' workspace per owner."""
op.create_index(
_INDEX_NAME,
"workspaces",
["owner_id"],
unique=True,
sqlite_where=sa.text("name = 'Personal'"),
postgresql_where=sa.text("name = 'Personal'"),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for any earlier callers/creation of "Personal" workspaces and any dedup migration.
rg -nP --type=py 'PERSONAL_WORKSPACE_NAME|name\s*=\s*["'\'']Personal["'\'']' -C2
echo "----- migrations -----"
fd -e py . --full-path -p 'migrations/versions' --exec rg -nl 'Personal|dedup|delete' {} \;

Repository: OBenner/Auto-Coding

Length of output: 157


🏁 Script executed:

#!/bin/bash
# 1. List migration files to understand sequence
echo "=== Migration Files ==="
fd -e py --full-path 'migrations/versions' | head -20

# 2. Search for 'Personal' constant definition
echo "=== Constant Definitions ==="
grep -rn "PERSONAL" --include="*.py" | head -10

# 3. Search for direct "Personal" string usage in non-test python
echo "=== Usage Context ==="
grep -rn "\"Personal\"" --include="*.py" | grep -v test | grep -v __pycache__ | head -15

# 4. Check the specific migration file content
echo "=== Target Migration Content ==="
cat apps/web-backend/migrations/versions/004_unique_personal_workspace.py

Repository: OBenner/Auto-Coding

Length of output: 3098


🌐 Web query:

Albumentations installation command

💡 Result:

To install the current version of the library, known as AlbumentationsX, use the following command: pip install albumentationsx [1][2][3] Important Installation Notes: 1. OpenCV Dependency: Starting with version 2.0.14, OpenCV is no longer installed automatically [1]. You should choose an installation command based on your environment: - For headless environments (e.g., servers, Docker, CI/CD): pip install albumentationsx[headless] [1][2] - For environments requiring GUI support (e.g., local development): pip install opencv-python && pip install albumentationsx [1][2] - For environments needing extra algorithms (OpenCV contrib modules): pip install albumentationsx[contrib] [1] 2. Python Requirements: AlbumentationsX requires Python 3.10 or higher [1][2]. 3. Migration: If you have an older version of the library installed, it is recommended to uninstall the original package before installing AlbumentationsX [4]: pip uninstall albumentations pip install albumentationsx You can verify the installation by running [1]: python -c "import albumentations as A; print(A.version)"

Citations:


Migration 004 will fail if "Personal" workspaces are duplicated.

This migration creates a unique partial index on owner_id where name='Personal'. The workspace_service.py creates "Personal" workspaces but lacks atomic constraints (e.g., get_or_create) or data cleanup logic to prevent duplicates. If multiple "Personal" rows exist for a single owner_id (e.g., from repeated bootstrap calls or race conditions), the CREATE UNIQUE INDEX operation will fail, blocking the upgrade.

Add a data cleanup step to delete older duplicates for each owner_id before creating the index.

Relevant code context

apps/web-backend/migrations/versions/004_unique_personal_workspace.py

def upgrade() -> None:
    """Add a partial unique index: at most one 'Personal' workspace per owner."""
    op.create_index(
        _INDEX_NAME,
        "workspaces",
        ["owner_id"],
        unique=True,
        sqlite_where=sa.text("name = 'Personal'"),
        postgresql_where=sa.text("name = 'Personal'"),
    )

apps/web-backend/services/workspace_service.py

PERSONAL_WORKSPACE_NAME = "Personal"
# ...
workspace = Workspace(name=PERSONAL_WORKSPACE_NAME, owner_id=user_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 `@apps/web-backend/migrations/versions/004_unique_personal_workspace.py` around
lines 33 - 42, The `upgrade()` migration in `004_unique_personal_workspace.py`
can fail when duplicate `Personal` workspaces already exist for the same
`owner_id`. Add a cleanup step before `op.create_index` that identifies
duplicate `workspaces` rows for `name = 'Personal'`, keeps one row per
`owner_id`, and deletes the older extras so the unique partial index can be
created successfully. Use the `PERSONAL_WORKSPACE_NAME` and `Workspace` creation
path in `workspace_service.py` as the context for the affected records.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants