Open
Conversation
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add locked_by_sub and locked_at columns to projects table for session locking - Create project_members table with FK to projects for N:M user-project relationships - Add indexes on user_sub and user_email for membership queries - Update FakeHasuraClient to track table creation in tests
- Add ProjectMember dataclass with project_id, user_sub, user_email, added_at, added_by_sub - Add add_project_member() to add members by email (with optional user_sub) - Add list_project_members() to list all members of a project - Add remove_project_member() to remove members (prevents removing last member) - Add is_project_member() to check membership by sub or email - Update create_project() to add creator as first member - Update FakeHasuraClient to handle member INSERT/SELECT/DELETE
- Update get_project_by_id() to check is_project_member() instead of owner_sub - Update get_project_by_slug() to check membership - Update list_projects() to use JOIN with project_members table - Users can now access projects they are members of, not just projects they own - Update FakeHasuraClient with proper membership JOIN and is_project_member handlers
- Add ProjectLock dataclass with project_id, locked_by_sub, locked_by_email, locked_at - Add get_project_lock() to check current lock status - Add acquire_project_lock() to acquire lock (with optional force=True for takeover) - Add release_project_lock() to release lock if held by user - Update FakeHasuraClient with lock query handlers
Verify that acquire_project_lock() with force=True allows taking over a lock held by another user.
… projects - Fix race condition in acquire_project_lock with atomic conditional UPDATE - Change project_members PK to (project_id, user_email) for email-only invites - Add migration SQL to add existing project owners as members - Update ensure_project_for_id to check membership instead of ownership - Update FakeHasuraClient to simulate atomic conditional UPDATE
Add GET/POST/DELETE /api/projects/{id}/members endpoints for managing
project members. These allow listing members, adding members by email,
and removing members by user_sub.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add SESSION_CLAIMED message type to backend and frontend - Acquire project lock during WebSocket INIT - If locked by another user, return project_locked error (unless force=True) - When force-claiming, notify previous session with SESSION_CLAIMED message - Release lock on WebSocket disconnect - Track ws -> (session_id, user_sub) in module-level map for cleanup Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update mark_project_deleted to verify membership instead of ownership - Update hard_delete_project_row to verify membership instead of ownership - Update rename_project to remove owner_sub check (membership already verified) - Add tests for member-based delete permissions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create ProjectMembers.tsx component for managing project members - Lists current members with email and avatar - Add member by email with validation - Remove member button (disabled when only one member remains) - Shows "Pending" badge for invited users who haven't logged in yet - Create ProjectLockedModal.tsx for locked project handling - Shows which user is editing the project - "Go back" button returns to project list - "Take over session" button with confirmation warning - Create SessionClaimedModal.tsx for session takeover notification - Shows message when another user takes over the session - Redirects to project list on dismiss - Integrate modals into Create screen - Handle ERROR with project_locked code to show ProjectLockedModal - Handle SESSION_CLAIMED message to show SessionClaimedModal - Add handlers for take over and go back actions
Critical fixes: - ERROR message: use `code` instead of `error`, send `locked_by` as object - Force-claim: accept both `force_claim` and `force` parameters - SESSION_CLAIMED: use `claimed_by_email` field name Additional fixes: - set_project_slug/set_gitlab_metadata: check membership instead of ownership - list_projects: use EXISTS instead of JOIN to avoid potential duplicates - Add remove_project_member_by_email for pending invitations - Update ProjectMembers.tsx to allow removing pending members Update FakeHasuraClient for new EXISTS query pattern.
Backend fixes: - Normalize email to lowercase in set_project_slug/set_gitlab_metadata - Wrap lock acquisition in try/except, send structured error on failure - Log exception in lock release instead of silently swallowing - Store locked_by_email directly in projects table (avoids member lookup failure for email-only/pending members) - Return added_at timestamp from add_project_member - Add locked_by_email column to schema + migration Frontend fixes: - Fix force-claim: reconnect with initExtra instead of sending on closed socket. WebSocketBus now accepts initExtra merged into INIT payload, and skips auto-reconnect on code 1008 (policy violation) - Expose connectWithExtra from useMessageBus for force-claim reconnection - Wire ProjectMembers component into Create screen with a Share button in the sidebar header Test fixes: - Remove unused MagicMock import - Patch _get_owner_from_request to raise PermissionError so auth tests work regardless of AUTH_MODE - Extend FakeHasuraClient to handle email-based member deletion - Update fake for locked_by_email column in lock operations - Remove unused owner2 variable
- Fix React.FormEvent import to use named FormEvent import - Remove overly-restrictive NOT EXISTS in migration, use LOWER() for emails - Allow add_project_member upsert to backfill user_sub for pending invites - Add atomic membership check (EXISTS) in rename_project UPDATE SQL - Validate target member exists before reporting successful removal - Wrap WS handler loop in try/finally for reliable lock release on all exits - Log force-claim notification failures instead of silent pass - Fix B023 lint: bind loop variables in _check_and_acquire_lock closure - Store asyncio.create_task reference to avoid RUF006 lint warning
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
52912ff to
cfa460f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.