# Crew Feature Plan ## Overview The Crew system enables players to form groups of up to 30 members to collaborate, share strategies, and compete in Unite and Fight events. Crews provide a social layer to the application with hierarchical roles, shared content visibility, and performance tracking capabilities. ## Business Requirements ### Core Concepts #### Crew Structure - **Size Limit**: Maximum 30 members per crew - **Roles Hierarchy**: - **Captain**: The crew creator with full administrative privileges - **Subcaptains**: Up to 3 members with elevated permissions - **Members**: Regular crew participants with standard access #### Key Features 1. **Crew Management**: Creation, invitation, membership control 2. **Gamertags**: 4-character tags displayed alongside member names 3. **Unite and Fight**: Event participation and score tracking 4. **Crew Feed**: Private content stream for crew-only parties, guides, and future posts 5. **Performance Analytics**: Historical tracking and visualization of member contributions ### User Stories #### As a Captain - I want to create a crew and invite players to join - I want to appoint up to 3 subcaptains to help manage the crew - I want to remove members who are inactive or problematic - I want to set crew rules that all members can view - I want to track member performance in Unite and Fight events #### As a Subcaptain - I want to help manage crew by inviting new members - I want to update crew rules and information - I want to record Unite and Fight scores for members - I want to set gamertags for crew representation #### As a Member - I want to join a crew via invitation link - I want to view crew rules and information - I want to see my Unite and Fight performance history - I want to choose whether to display my crew's gamertag - I want to see crew-only parties and guides in the feed #### As a System Administrator - I want to add new Unite and Fight events when announced by Cygames - I want to manage event dates and parameters - I want to monitor crew activities for policy violations ## Technical Design ### Database Schema #### crews ```sql - id: uuid (primary key) - name: string (unique, not null) - captain_id: uuid (foreign key to users, not null) - gamertag: string (4 characters, unique, can be null) - rules: text - member_count: integer (counter cache, default 1) - created_at: timestamp - updated_at: timestamp Indexes: - unique index on name - unique index on gamertag (where not null) - index on captain_id - index on created_at ``` #### crew_memberships ```sql - id: uuid (primary key) - crew_id: uuid (foreign key to crews, not null) - user_id: uuid (foreign key to users, not null) - role: integer (0=member, 1=subcaptain, 2=captain) - display_gamertag: boolean (default true) - joined_at: timestamp (default now) - created_at: timestamp - updated_at: timestamp Indexes: - unique index on [crew_id, user_id] - index on crew_id - index on user_id - index on role - index on joined_at ``` #### crew_invitations ```sql - id: uuid (primary key) - crew_id: uuid (foreign key to crews, not null) - invited_by_id: uuid (foreign key to users, not null) - token: string (unique, not null) - expires_at: timestamp (default 7 days from creation) - used_at: timestamp (null) - used_by_id: uuid (foreign key to users, null) - created_at: timestamp - updated_at: timestamp Indexes: - unique index on token - index on crew_id - index on expires_at - index on [crew_id, used_at] (for tracking active invitations) ``` #### unite_and_fights ```sql - id: uuid (primary key) - name: string (not null) - event_number: integer (sequential, unique) - starts_at: timestamp (not null) - ends_at: timestamp (not null) - created_by_id: uuid (foreign key to users, not null) - created_at: timestamp - updated_at: timestamp Indexes: - unique index on event_number - index on starts_at - index on ends_at - index on [starts_at, ends_at] (for finding active events) ``` #### unf_scores ```sql - id: uuid (primary key) - unite_and_fight_id: uuid (foreign key to unite_and_fights, not null) - crew_id: uuid (foreign key to crews, not null) - user_id: uuid (foreign key to users, not null) - honors: bigint (not null, default 0) - recorded_by_id: uuid (foreign key to users, not null) - day_number: integer (1-7, not null) - created_at: timestamp - updated_at: timestamp Indexes: - unique index on [unite_and_fight_id, crew_id, user_id, day_number] - index on unite_and_fight_id - index on crew_id - index on user_id - index on [crew_id, unite_and_fight_id] (for crew performance queries) - index on honors (for rankings) ``` #### crew_feeds (future table for reference) ```sql - id: uuid (primary key) - crew_id: uuid (foreign key to crews, not null) - feedable_type: string (Party, Guide, Post, etc.) - feedable_id: uuid (polymorphic reference) - created_at: timestamp Indexes: - index on [crew_id, created_at] (for feed queries) - index on [feedable_type, feedable_id] ``` ### Model Relationships ```ruby # User model additions has_one :crew_membership, dependent: :destroy has_one :crew, through: :crew_membership has_many :captained_crews, class_name: 'Crew', foreign_key: :captain_id has_many :crew_invitations_sent, class_name: 'CrewInvitation', foreign_key: :invited_by_id has_many :unf_scores has_many :recorded_unf_scores, class_name: 'UnfScore', foreign_key: :recorded_by_id # Crew model belongs_to :captain, class_name: 'User' has_many :crew_memberships, dependent: :destroy has_many :members, through: :crew_memberships, source: :user has_many :crew_invitations, dependent: :destroy has_many :unf_scores, dependent: :destroy has_many :subcaptains, -> { where(crew_memberships: { role: 1 }) }, through: :crew_memberships, source: :user # CrewMembership model belongs_to :crew, counter_cache: :member_count belongs_to :user enum role: { member: 0, subcaptain: 1, captain: 2 } # CrewInvitation model belongs_to :crew belongs_to :invited_by, class_name: 'User' belongs_to :used_by, class_name: 'User', optional: true # UniteAndFight model has_many :unf_scores, dependent: :destroy belongs_to :created_by, class_name: 'User' # UnfScore model belongs_to :unite_and_fight belongs_to :crew belongs_to :user belongs_to :recorded_by, class_name: 'User' ``` ### API Design #### Crew Management ##### Crew CRUD ``` POST /api/v1/crews Body: { name, rules?, gamertag? } Response: Created crew with captain membership GET /api/v1/crews/:id Response: Crew details with members list PUT /api/v1/crews/:id Body: { name?, rules?, gamertag? } Response: Updated crew (captain/subcaptain only) DELETE /api/v1/crews/:id Response: Success (captain only, disbands crew) GET /api/v1/crews/my Response: Current user's crew with full details ``` ##### Member Management ``` GET /api/v1/crews/:id/members Response: Paginated list of crew members with roles POST /api/v1/crews/:id/members/promote Body: { user_id, role: "subcaptain" } Response: Updated membership (captain only) DELETE /api/v1/crews/:id/members/:user_id Response: Success (captain only) PUT /api/v1/crews/:id/members/me Body: { display_gamertag } Response: Updated own membership settings ``` ##### Invitations ``` POST /api/v1/crews/:id/invitations Response: { invitation_url, token, expires_at } Note: Captain/subcaptain only GET /api/v1/crews/:id/invitations Response: List of pending invitations (captain/subcaptain) DELETE /api/v1/crews/:id/invitations/:id Response: Revoke invitation (captain/subcaptain) POST /api/v1/crews/join Body: { token } Response: Joined crew details ``` #### Unite and Fight ##### Event Management (Admin) ``` GET /api/v1/unite_and_fights Response: List of all UnF events POST /api/v1/unite_and_fights Body: { name, event_number, starts_at, ends_at } Response: Created event (requires level 7+ permissions) PUT /api/v1/unite_and_fights/:id Body: { name?, starts_at?, ends_at? } Response: Updated event (admin only) ``` ##### Score Management ``` POST /api/v1/unf_scores Body: { unite_and_fight_id, user_id, honors, day_number } Response: Created/updated score (captain/subcaptain only) GET /api/v1/crews/:crew_id/unf_scores Query: unite_and_fight_id?, user_id? Response: Scores for crew, optionally filtered GET /api/v1/unf_scores/performance Query: crew_id, user_id?, from_date?, to_date? Response: Performance data for graphing ``` #### Crew Feed ``` GET /api/v1/crews/:id/feed Query: page, limit, type? Response: Paginated feed of crew-only content ``` ### Authorization & Permissions #### Permission Matrix | Action | Captain | Subcaptain | Member | Non-member | |--------|---------|------------|---------|------------| | View crew info | ✓ | ✓ | ✓ | ✓ | | View member list | ✓ | ✓ | ✓ | ✗ | | Update crew info | ✓ | ✓ | ✗ | ✗ | | Set gamertag | ✓ | ✓ | ✗ | ✗ | | Invite members | ✓ | ✓ | ✗ | ✗ | | Remove members | ✓ | ✗ | ✗ | ✗ | | Promote to subcaptain | ✓ | ✗ | ✗ | ✗ | | Record UnF scores | ✓ | ✓ | ✗ | ✗ | | View UnF scores | ✓ | ✓ | ✓ | ✗ | | View crew feed | ✓ | ✓ | ✓ | ✗ | | Leave crew | ✗* | ✓ | ✓ | ✗ | *Captain must transfer ownership or disband crew #### Authorization Helpers ```ruby # app/models/user.rb def captain_of?(crew) crew.captain_id == id end def subcaptain_of?(crew) crew_membership&.subcaptain? && crew_membership.crew_id == crew.id end def member_of?(crew) crew_membership&.crew_id == crew.id end def can_manage_crew?(crew) captain_of?(crew) || subcaptain_of?(crew) end def can_invite_to_crew?(crew) can_manage_crew?(crew) end def can_remove_from_crew?(crew) captain_of?(crew) end def can_record_unf_scores?(crew) can_manage_crew?(crew) end ``` ### Security Considerations 1. **Invitation Security**: - Tokens are cryptographically secure random strings - Automatic expiration after 7 days - One-time use only - Rate limiting on join attempts 2. **Member Limits**: - Enforce 30 member maximum at database level - Check before invitation acceptance - Atomic operations for membership changes 3. **Role Management**: - Only captain can promote/demote - Maximum 3 subcaptains enforced - Captain role transfer requires explicit action 4. **Data Privacy**: - Crew-only content respects visibility settings - UnF scores only visible to crew members - Member list public, but details restricted ### Performance Considerations 1. **Caching**: - Cache crew member lists (5-minute TTL) - Cache UnF leaderboards (1-hour TTL) - Cache crew feed content 2. **Database Optimization**: - Counter cache for member_count - Composite indexes for common queries - Partial indexes for active records 3. **Query Optimization**: - Eager load associations - Pagination for member lists and feeds - Batch operations for UnF score updates 4. **Background Jobs**: - Async invitation email sending - Scheduled cleanup of expired invitations - UnF score aggregation calculations ## Implementation Phases ### Phase 1: Core Crew System (Week 1-2) - Database migrations and models - Basic CRUD operations - Captain and member roles - Invitation system ### Phase 2: Advanced Roles & Permissions (Week 3) - Subcaptain functionality - Permission system - Gamertag management - Crew rules ### Phase 3: Unite and Fight Integration (Week 4-5) - UnF event management - Score recording system - Performance queries - Basic reporting ### Phase 4: Feed & Analytics (Week 6) - Crew feed implementation - Integration with parties/guides - Performance graphs - Historical tracking ### Phase 5: Polish & Optimization (Week 7) - Performance tuning - Caching layer - Background jobs - Admin tools ## Success Metrics 1. **Adoption**: 60% of active users join a crew within 3 months 2. **Engagement**: Average crew has 15+ active members 3. **Performance**: All crew operations complete within 200ms 4. **Reliability**: 99.9% uptime for crew services 5. **UnF Participation**: 80% score recording during events ## Future Enhancements 1. **Crew Battles**: Inter-crew competitions outside UnF 2. **Crew Chat**: Real-time messaging system 3. **Crew Achievements**: Badges and milestones 4. **Crew Resources**: Shared guides and strategies library 5. **Crew Recruitment**: Public crew discovery and application system 6. **Officer Roles**: Additional permission tiers 7. **Crew Alliances**: Multi-crew coordination 8. **Automated Scoring**: API integration with game data (if available) 9. **Mobile Notifications**: Push notifications for crew events 10. **Crew Statistics**: Advanced analytics and insights ## Risk Mitigation 1. **Toxic Behavior**: Implement reporting system and moderation tools 2. **Inactive Crews**: Automatic leadership transfer after inactivity 3. **Database Load**: Implement read replicas for heavy queries 4. **Invitation Spam**: Rate limiting and abuse detection 5. **Score Manipulation**: Audit logs and validation rules