Feature: Team collaboration — multi-user sessions, RBAC, and conflict resolution As a CleverAgents team lead I want team collaboration features with role-based access control So that multiple users can work concurrently on shared projects # ── TeamRole enum ──────────────────────────────────────────────── Scenario: TeamRole enum contains expected members Given the TeamRole enum Then the TeamRole enum should contain "owner" And the TeamRole enum should contain "admin" And the TeamRole enum should contain "member" And the TeamRole enum should contain "viewer" # ── TeamPermission enum ────────────────────────────────────────── Scenario: TeamPermission enum contains expected members Given the TeamPermission enum Then the TeamPermission enum should contain "read" And the TeamPermission enum should contain "write" And the TeamPermission enum should contain "admin" And the TeamPermission enum should contain "manage_members" # ── ConflictResolutionStrategy enum ────────────────────────────── Scenario: ConflictResolutionStrategy enum contains expected members Given the ConflictResolutionStrategy enum Then the ConflictResolutionStrategy enum should contain "last_writer_wins" And the ConflictResolutionStrategy enum should contain "reject" And the ConflictResolutionStrategy enum should contain "merge" # ── TEAM_ROLE_PERMISSIONS constant ─────────────────────────────── Scenario: OWNER role has all 4 permissions Given the TEAM_ROLE_PERMISSIONS constant Then the "owner" team role should have 4 allowed permissions Scenario: ADMIN role has all 4 permissions Given the TEAM_ROLE_PERMISSIONS constant Then the "admin" team role should have 4 allowed permissions Scenario: MEMBER role has read and write Given the TEAM_ROLE_PERMISSIONS constant Then the "member" team role should have 2 allowed permissions Scenario: VIEWER role has only read Given the TEAM_ROLE_PERMISSIONS constant Then the "viewer" team role should have 1 allowed permissions # ── TeamMember model ───────────────────────────────────────────── Scenario: Create a valid TeamMember Given a team member "alice" with role "owner" and email "alice@example.com" Then the team member user_id should be "alice" And the team member role should be "owner" And the team member email should be "alice@example.com" Scenario: TeamMember email validation rejects missing @ When I try to create a team member with email "invalid-email" Then a team member validation error should be raised Scenario: TeamMember has_permission returns True for allowed action Given a team member "bob" with role "member" and email "bob@example.com" Then the team member should have "read" permission And the team member should have "write" permission Scenario: TeamMember has_permission returns False for denied action Given a team member "carol" with role "viewer" and email "carol@example.com" Then the team member should not have "write" permission And the team member should not have "admin" permission # ── UserSession model ──────────────────────────────────────────── Scenario: Create a valid UserSession Given a user session for user "alice" on project "proj-1" Then the user session user_id should be "alice" And the user session project_id should be "proj-1" And the user session should be active Scenario: Deactivate a UserSession Given a user session for user "alice" on project "proj-1" When the session is deactivated Then the user session should not be active Scenario: Touch updates last_active timestamp Given a user session for user "alice" on project "proj-1" When the session is touched Then the last_active timestamp should be updated # ── VersionStamp model ─────────────────────────────────────────── Scenario: Create a valid VersionStamp Given a version stamp for resource "res-1" by user "alice" Then the version stamp version should be 1 And the version stamp updated_by should be "alice" Scenario: Increment a VersionStamp Given a version stamp for resource "res-1" by user "alice" When the version stamp is incremented by "bob" Then the new version stamp version should be 2 And the new version stamp updated_by should be "bob" Scenario: VersionStamp increment rejects empty user Given a version stamp for resource "res-1" by user "alice" Then incrementing with empty user should raise ValueError # ── ConflictDetectionResult model ──────────────────────────────── Scenario: Create a no-conflict result Given a conflict detection result with no conflict for "res-1" Then the result detected should be False And the result resource_id should be "res-1" # ── VersionConflictError ───────────────────────────────────────── Scenario: VersionConflictError carries version info When a VersionConflictError is raised for "res-1" expected 1 actual 2 Then the error resource_id should be "res-1" And the error expected_version should be 1 And the error actual_version should be 2 Scenario: VersionConflictError rejects empty resource_id Then creating VersionConflictError with empty resource_id raises ValueError # ── SessionRegistry ────────────────────────────────────────────── Scenario: Register and retrieve a session Given a session registry And a user session "sess-1" for user "alice" on project "proj-1" When the session is registered Then the registry should contain the session "sess-1" And the registry active count should be 1 Scenario: Unregister a session Given a session registry And a user session "sess-1" for user "alice" on project "proj-1" When the session is registered And the session "sess-1" is unregistered Then the registry should not contain the session "sess-1" And the registry active count should be 0 Scenario: Get sessions for a specific user Given a session registry And a user session "sess-1" for user "alice" on project "proj-1" And a user session "sess-2" for user "alice" on project "proj-2" And a user session "sess-3" for user "bob" on project "proj-1" When all sessions are registered Then the registry should have 2 sessions for user "alice" And the registry should have 1 sessions for user "bob" Scenario: Get sessions for a specific project Given a session registry And a user session "sess-1" for user "alice" on project "proj-1" And a user session "sess-2" for user "bob" on project "proj-1" And a user session "sess-3" for user "carol" on project "proj-2" When all sessions are registered Then the registry should have 2 sessions for project "proj-1" And the registry should have 1 sessions for project "proj-2" Scenario: Clear all sessions Given a session registry And a user session "sess-1" for user "alice" on project "proj-1" And a user session "sess-2" for user "bob" on project "proj-2" When all sessions are registered And the registry is cleared Then the registry active count should be 0 Scenario: Duplicate session registration raises ValueError Given a session registry And a user session "sess-1" for user "alice" on project "proj-1" When the session is registered Then registering the same session again raises ValueError Scenario: Session registry generate_session_id returns valid ULID Given a session registry Then the generated session ID should be a valid ULID # ── TeamCollaborationService — member management ───────────────── Scenario: Add and retrieve a team member via service Given a team collaboration service When I add member "alice" with role "owner" and email "alice@example.com" Then the service should have 1 members And getting member "alice" should return the member Scenario: Adding duplicate member raises TeamMemberExistsError Given a team collaboration service When I add member "alice" with role "owner" and email "alice@example.com" Then adding member "alice" again raises TeamMemberExistsError Scenario: Remove a team member via service Given a team collaboration service When I add member "alice" with role "owner" and email "alice@example.com" And I remove member "alice" Then the service should have 0 members Scenario: Removing non-existent member raises TeamMemberNotFoundError Given a team collaboration service Then removing member "ghost" raises TeamMemberNotFoundError Scenario: Update a member role Given a team collaboration service When I add member "alice" with role "member" and email "alice@example.com" And I update member "alice" role to "admin" Then member "alice" should have role "admin" # ── TeamCollaborationService — permission checks ───────────────── Scenario: Owner has all permissions Given a team collaboration service When I add member "alice" with role "owner" and email "alice@example.com" Then member "alice" should have "read" team permission And member "alice" should have "write" team permission And member "alice" should have "admin" team permission And member "alice" should have "manage_members" team permission Scenario: Viewer has only read permission Given a team collaboration service When I add member "viewer-user" with role "viewer" and email "view@example.com" Then member "viewer-user" should have "read" team permission And member "viewer-user" should not have "write" team permission Scenario: Enforce permission raises PermissionError for viewer writing Given a team collaboration service When I add member "viewer-user" with role "viewer" and email "view@example.com" Then enforcing "write" on "viewer-user" raises PermissionError # ── TeamCollaborationService — session management ──────────────── Scenario: Register a session for a team member Given a team collaboration service When I add member "alice" with role "owner" and email "alice@example.com" And I register a session for "alice" on project "proj-1" Then the project "proj-1" should have 1 active sessions Scenario: Multiple users have concurrent sessions on same project Given a team collaboration service When I add member "alice" with role "owner" and email "alice@example.com" And I add member "bob" with role "member" and email "bob@example.com" And I register a session for "alice" on project "proj-1" And I register a session for "bob" on project "proj-1" Then the project "proj-1" should have 2 active sessions Scenario: Unregister a session reduces active count Given a team collaboration service When I add member "alice" with role "owner" and email "alice@example.com" And I register a session for "alice" on project "proj-1" And I unregister the last session Then the project "proj-1" should have 0 active sessions Scenario: Removing a member cleans up their sessions Given a team collaboration service When I add member "alice" with role "owner" and email "alice@example.com" And I register a session for "alice" on project "proj-1" And I remove member "alice" Then the project "proj-1" should have 0 active sessions # ── TeamCollaborationService — conflict resolution ─────────────── Scenario: Create and retrieve a version stamp Given a team collaboration service When I create a version stamp for "res-1" by "alice" Then the version stamp for "res-1" should have version 1 Scenario: Update version with matching expected version succeeds Given a team collaboration service When I create a version stamp for "res-1" by "alice" And I update version for "res-1" expecting 1 by "bob" Then the version stamp for "res-1" should have version 2 Scenario: Update version with mismatched version using reject strategy raises error Given a team collaboration service When I create a version stamp for "res-1" by "alice" And I update version for "res-1" expecting 1 by "bob" Then updating "res-1" expecting 1 by "carol" with reject raises VersionConflictError Scenario: Update version with last-writer-wins resolves conflict Given a team collaboration service When I create a version stamp for "res-1" by "alice" And I update version for "res-1" expecting 1 by "bob" And I update "res-1" expecting 1 by "carol" with last_writer_wins Then the conflict result should be detected and resolved And the version stamp for "res-1" should have version 3 Scenario: Detect conflict without resolving Given a team collaboration service When I create a version stamp for "res-1" by "alice" And I update version for "res-1" expecting 1 by "bob" Then detecting conflict on "res-1" expecting 1 should return True And detecting conflict on "res-1" expecting 2 should return False Scenario: Update version with merge strategy resolves conflict Given a team collaboration service When I create a version stamp for "res-1" by "alice" And I update version for "res-1" expecting 1 by "bob" And I update "res-1" expecting 1 by "carol" with merge Then the conflict result should be detected and resolved # ── Error hierarchy ────────────────────────────────────────────── Scenario: TeamMemberNotFoundError is a TeamCollaborationError Then TeamMemberNotFoundError should be a subclass of TeamCollaborationError Scenario: TeamMemberExistsError is a TeamCollaborationError Then TeamMemberExistsError should be a subclass of TeamCollaborationError Scenario: VersionConflictError is a TeamCollaborationError Then VersionConflictError should be a subclass of TeamCollaborationError # ----------------------------------------------------------------------- # Service edge case validations # ----------------------------------------------------------------------- Scenario: Service session_registry property returns the registry Given a fresh TeamCollaborationService Then the service session_registry should be a SessionRegistry instance Scenario: Service add_member rejects empty user_id Given a fresh TeamCollaborationService When I try to add a member with empty user_id via service Then a collab ValueError should be raised mentioning "user_id" Scenario: Service add_member rejects empty display_name Given a fresh TeamCollaborationService When I try to add a member with empty display_name via service Then a collab ValueError should be raised mentioning "display_name" Scenario: Service add_member rejects empty email Given a fresh TeamCollaborationService When I try to add a member with empty email via service Then a collab ValueError should be raised mentioning "email" Scenario: Service remove_member rejects empty user_id Given a fresh TeamCollaborationService When I try to remove a member with empty user_id via service Then a collab ValueError should be raised mentioning "user_id" Scenario: Service get_member rejects empty user_id Given a fresh TeamCollaborationService When I try to get a member with empty user_id via service Then a collab ValueError should be raised mentioning "user_id" Scenario: Service get_member for non-existent user raises error Given a fresh TeamCollaborationService When I try to get member "ghost" via service Then a TeamMemberNotFoundError should be raised Scenario: Service update_member_role rejects empty user_id Given a fresh TeamCollaborationService When I try to update role with empty user_id via service Then a collab ValueError should be raised mentioning "user_id" Scenario: Service register_session rejects empty user_id Given a fresh TeamCollaborationService When I try to register session with empty user_id via service Then a collab ValueError should be raised mentioning "user_id" Scenario: Service register_session rejects empty project_id Given a fresh TeamCollaborationService And a team member "u1" with role "member" When I try to register session with empty project_id for user "u1" Then a collab ValueError should be raised mentioning "project_id" Scenario: Service unregister_session rejects empty session_id Given a fresh TeamCollaborationService When I try to unregister session with empty session_id Then a collab ValueError should be raised mentioning "session_id" Scenario: Service get_active_sessions_for_project rejects empty project_id Given a fresh TeamCollaborationService When I try to get sessions for empty project_id Then a collab ValueError should be raised mentioning "project_id" Scenario: Service create_version_stamp rejects empty resource_id Given a fresh TeamCollaborationService When I try to create version stamp with empty resource_id Then a collab ValueError should be raised mentioning "resource_id" Scenario: Service create_version_stamp rejects empty user_id Given a fresh TeamCollaborationService When I try to create version stamp with empty user_id Then a collab ValueError should be raised mentioning "user_id" Scenario: Service get_version_stamp rejects empty resource_id Given a fresh TeamCollaborationService When I try to get version stamp with empty resource_id Then a collab ValueError should be raised mentioning "resource_id" Scenario: Service update_version rejects empty resource_id Given a fresh TeamCollaborationService When I try to update version with empty resource_id Then a collab ValueError should be raised mentioning "resource_id" Scenario: Service update_version rejects empty user_id Given a fresh TeamCollaborationService When I try to update version with empty user_id Then a collab ValueError should be raised mentioning "user_id" Scenario: Service update_version rejects zero expected_version Given a fresh TeamCollaborationService When I try to update version with expected_version 0 Then a collab ValueError should be raised mentioning "expected_version" Scenario: Service update_version creates stamp for untracked resource Given a fresh TeamCollaborationService When I update version for untracked resource "new-res" by user "u1" expecting version 1 Then the result should indicate no conflict detected And the result details should contain "New resource created" Scenario: Service detect_conflict rejects empty resource_id Given a fresh TeamCollaborationService When I try to detect conflict with empty resource_id Then a collab ValueError should be raised mentioning "resource_id" Scenario: Service detect_conflict rejects zero expected_version Given a fresh TeamCollaborationService When I try to detect conflict with expected_version 0 Then a collab ValueError should be raised mentioning "expected_version" Scenario: Service detect_conflict returns False for untracked resource Given a fresh TeamCollaborationService When I detect conflict for untracked resource "phantom" expecting version 1 Then the conflict result should be False # ----------------------------------------------------------------------- # Domain model edge case validations # ----------------------------------------------------------------------- Scenario: VersionConflictError rejects negative expected_version When I try to create VersionConflictError with expected_version -1 Then a collab ValueError should be raised mentioning "expected_version" Scenario: VersionConflictError rejects negative actual_version When I try to create VersionConflictError with actual_version -1 Then a collab ValueError should be raised mentioning "actual_version" Scenario: SessionRegistry register rejects empty session_id Given a fresh SessionRegistry When I try to register a session with empty session_id Then a collab ValueError should be raised mentioning "session_id" Scenario: SessionRegistry unregister rejects empty session_id Given a fresh SessionRegistry When I try to unregister a session with empty session_id Then a collab ValueError should be raised mentioning "session_id" Scenario: SessionRegistry unregister returns False for unknown session Given a fresh SessionRegistry When I unregister session "nonexistent-id" Then the unregister result should be False Scenario: SessionRegistry get rejects empty session_id Given a fresh SessionRegistry When I try to get session with empty session_id Then a collab ValueError should be raised mentioning "session_id" Scenario: SessionRegistry list_active_sessions returns only active ones Given a fresh SessionRegistry And a registered session "s1" for user "u1" in project "p1" And a registered session "s2" for user "u2" in project "p1" When I deactivate session "s1" in the registry Then get_active_sessions should return 1 session Scenario: SessionRegistry get_sessions_for_user rejects empty user_id Given a fresh SessionRegistry When I try to get sessions for empty user_id Then a collab ValueError should be raised mentioning "user_id" Scenario: SessionRegistry get_sessions_for_project rejects empty project_id Given a fresh SessionRegistry When I try to get sessions for empty project_id via registry Then a collab ValueError should be raised mentioning "project_id"