# Phase 7: Attendee Relational Decoupling & Immutable Identity ## 1. Context & Rationale Currently, guest attendee sessions are bound to their originating event via brittle string matching on `users.username` (e.g., `WHERE username LIKE 'guest_' || ep.slug || '_%'`). This creates two major architectural flaws: 1. **Rotation Breakage:** If an event host rotates the event's ingress PIN/slug (e.g., `deno-lab-1a2b` to `deno-lab-9x8z`), the backend loses track of previously joined guests because their usernames retain the old slug suffix. 2. **Unwieldy Data:** Guest usernames inherit the full event slug, leading to massive, unreadable usernames in logs and the database (e.g., `guest_annual-cyber-security-training-workshop-4f8x_12`). ## 2. Objective Implement a robust **"A+B Architecture"** to permanently decouple guest session lifecycle from transient URL slugs: - **Option A (Relational Integrity):** Link guests directly to the Event UUID via a strict Foreign Key (`event_pass_id`). - **Option B (Immutable Identity):** Mint short, deterministic, and immutable usernames using the Event UUID prefix. ## 3. Scope & Acceptance Criteria ### 3.1 Database Migration (`server/db.ts`) - [ ] Add column: `ALTER TABLE users ADD COLUMN IF NOT EXISTS event_pass_id UUID REFERENCES event_passes(id) ON DELETE CASCADE;` - [ ] Ensure backward compatibility with existing standalone users (column should be nullable, as admins/owners won't have an `event_pass_id`). ### 3.2 Immutable Username Minting (`server/routes/events.ts`) - [ ] In `POST /api/join`: Change guest username generation from `guest_${event.slug}_${seat}` to `guest_${event.id.split('-')[0]}_${seat}`. - [ ] In `POST /api/join`: Insert `event.id` into the new `event_pass_id` column for the newly minted user. ### 3.3 Query Refactoring (Zero String Parsing) - [ ] **`GET /api/events/:id/attendees`**: Refactor the query to use `WHERE u.event_pass_id = ${eventId}`. - [ ] **`POST /api/events/:id/extend`**: Refactor the query to target sessions belonging to `u.event_pass_id = ${eventId}`. - [ ] **`POST /api/events/:id/end`**: Refactor the revocation query to target `u.event_pass_id = ${eventId}`. - [ ] **`DELETE /api/sessions/:id`**: Refactor the ownership check to verify `u.event_pass_id = ${eventId}` instead of string parsing. ### 3.4 Quality Gates - [ ] Run `deno fmt`, `deno task lint`, `deno task check`. - [ ] All 67+ backend tests must pass. (Update `server/tests/events.test.ts` to mock/handle the new `event_pass_id` column and short username generation). ## 4. Anti-Patterns to Avoid - **No `LIKE 'guest_%'` queries** anywhere in the backend logic. - **No updating usernames on slug rotation.** The `POST /api/events/:id/rotate-pin` endpoint should ONLY touch `event_passes.slug` and `event_passes.pin_code`, leaving attendees completely unaffected.