diff --git a/server/routes/sessions.ts b/server/routes/sessions.ts
index 2bff077..fe0b619 100644
--- a/server/routes/sessions.ts
+++ b/server/routes/sessions.ts
@@ -296,8 +296,22 @@ sessionRoutes.delete("/api/sessions/:id", async (c) => {
}
}
+ const isAdmin = hasScope(auth, "admin") || hasScope(auth, "*");
+
const session = await sqlWrapper.sql`
- SELECT id FROM sessions WHERE id = ${targetSessionId} AND user_id = ${auth.userId}
+ SELECT s.id
+ FROM sessions s
+ JOIN users u ON s.user_id = u.id
+ WHERE s.id = ${targetSessionId}
+ AND (
+ s.user_id = ${auth.userId}
+ OR ${isAdmin}
+ OR EXISTS (
+ SELECT 1 FROM event_passes ep
+ WHERE ep.created_by = ${auth.userId}
+ AND u.username LIKE 'guest_' || ep.slug || '_%'
+ )
+ )
`.then((res: any) => res[0]);
if (!session) {
diff --git a/server/tests/scopes.test.ts b/server/tests/scopes.test.ts
index 4b7031e..997fc48 100644
--- a/server/tests/scopes.test.ts
+++ b/server/tests/scopes.test.ts
@@ -95,7 +95,7 @@ Deno.test("Zero-Trust Scope Guards", async (t) => {
setMockSql(
(async (strings: any, ..._values: any[]) => {
const q = Array.isArray(strings) ? strings.join("?") : String(strings);
- if (q.includes("SELECT id FROM sessions WHERE id =")) {
+ if (q.includes("SELECT s.id")) {
return [{ id: mockSessionId }];
}
if (q.includes("DELETE FROM sessions")) {
diff --git a/tasks/complete/2026-0826.05.gem.feat.sessions-ui.sessions-and-guest-drawer-overhaul-2023.ph5.md b/tasks/complete/2026-0826.05.gem.feat.sessions-ui.sessions-and-guest-drawer-overhaul-2023.ph5.md
new file mode 100644
index 0000000..faa4e7b
--- /dev/null
+++ b/tasks/complete/2026-0826.05.gem.feat.sessions-ui.sessions-and-guest-drawer-overhaul-2023.ph5.md
@@ -0,0 +1,155 @@
+# TASK METADATA
+
+- **Target Files:** `server/routes/sessions.ts`, `server/routes/events.ts`,
+ `ui/components/SessionsPage.tsx`,
+ `ui/components/sessions/EventCockpitDeck.tsx`,
+ `ui/components/sessions/EventAttendeesDrawer.tsx` (refactored/renamed to
+ `EventGuestsDrawer.tsx`), `ui/components/sessions/SessionDeck.tsx`,
+ `ui/components/sessions/SessionTable.tsx`,
+ `ui/components/sessions/SessionsScript.tsx`, `server/tests/events.test.ts`,
+ `server/tests/scopes.test.ts`, `ui/ui_scripts.test.ts`
+- **Core Objective:** Implement Phase 5 Overhaul: Fix backend attendee session
+ revocation permission checks, build a true fixed slide-over Guest Drawer,
+ standardize dynamic countdown pills, add multi-event compact density toggle,
+ and clean up page/session visual hierarchy.
+- **Dependencies:** None.
+- **Additional Important Notes:** Must remain 100% pure React-free Hono SSR JSX.
+ All client interactions in `SessionsScript.tsx` must use native vanilla
+ JavaScript DOM APIs.
+
+---
+
+## 2. Architectural Considerations & Risks
+
+- **Risks:**
+ - **Zero-Trust Revocation Leakage:** When allowing event creators to
+ delete/revoke attendee sessions via `DELETE /api/sessions/:id`, ensure the
+ query strictly verifies that the session belongs to a guest user claimed
+ under an event pass created by `auth.userId` (or that caller is global
+ admin). Never allow arbitrary session deletions across different event
+ creators.
+ - **DOM Stacking & Focus Trapping:** A fixed slide-over drawer must properly
+ layer (`z-index: 1050`) over the background page and support backdrop
+ dismissal and `Escape` key capture without disrupting background table
+ states.
+ - **Mobile Density Regressions:** Ensure the mobile slide-up bottom sheet does
+ not block necessary viewport scrolling or clip action buttons on small
+ mobile viewports (320px–375px).
+
+- **Alternatives:**
+ - _In-Page Expandable Row vs. Fixed Slide-Over Panel:_ In-page expandable
+ cards cause massive vertical jumping and layout disruption when 5–10 events
+ are active. A dedicated fixed slide-over drawer (right panel on desktop,
+ bottom sheet on mobile) provides isolated context, independent scrolling,
+ and a pinned summary header without disturbing the background dashboard.
+
+---
+
+## 3. Proposed Implementation
+
+### Phase 1: Backend Revocation Permission Fix (`server/routes/sessions.ts`)
+
+1. **Authorize Event Creators in `DELETE /api/sessions/:id`:**
+ - Update `DELETE /api/sessions/:id` to check whether the target session is
+ owned by `auth.userId`, or if caller is `isGlobalAdmin`, OR if the session
+ belongs to a guest user of an event pass created by `auth.userId`:
+ ```sql
+ SELECT s.id
+ FROM sessions s
+ JOIN users u ON s.user_id = u.id
+ WHERE s.id = ${targetSessionId}
+ AND (
+ s.user_id = ${auth.userId}
+ OR ${isAdmin}
+ OR EXISTS (
+ SELECT 1 FROM event_passes ep
+ WHERE ep.created_by = ${auth.userId}
+ AND u.username LIKE 'guest_' || ep.slug || '_%'
+ )
+ )
+ ```
+ - If found, delete the session from Valkey cache (`valkey.del`) and
+ PostgreSQL (`DELETE FROM sessions WHERE id = ${targetSessionId}`), logging
+ the audit event.
+ - Return `{ success: true }`.
+
+### Phase 2: Page Hierarchy & Section Titles (`ui/components/SessionsPage.tsx`)
+
+1. **Page Title:**
+ - Update top `
` in `SessionsPage.tsx` from `Active Sessions & Passes` to
+ **`Sessions & Passes`**.
+2. **Missing Section Headings:**
+ - Section 1: `
Event Passes
` (with compact density toggle).
+ - Section 2: Add a prominent `
Sessions
` heading directly above
+ `SessionTable` and `SessionDeck` with subtitle _"Direct device logins,
+ passkey authentications, and delegated agent tokens."_
+
+### Phase 3: True Fixed Slide-Over Panel (`EventGuestsDrawer.tsx` & `SessionsScript.tsx`)
+
+1. **Drawer Component Overhaul
+ (`ui/components/sessions/EventAttendeesDrawer.tsx` ->
+ `EventGuestsDrawer.tsx`):**
+ - Refactor the component from an in-line `
`
+ - **Close Button:** Accessible close button in top right.
+3. **Streamlined Roster Rows (`SessionsScript.tsx`):**
+ - Discard repeated static expiration timestamps from individual rows.
+ - Render clean, distinct cards for each guest seat:
+ - **Left:** `Seat #[N]` (`guest__`) + relative join timestamp
+ (`Joined 5m ago`).
+ - **Status Badge:** `🟢 Active` / `⏸️ Paused`.
+ - **Right Actions:** Compact `[ ⏸️ Pause ]` / `[ ▶️ Resume ]` toggle and
+ `[ 🗑️ Revoke ]` trigger.
+4. **Standardize Terminology:**
+ - Use **`Event Guests`** and **`Claimed Seats`** across all drawer titles,
+ buttons (`[ 👥 Manage Guests (N) ]`), and notices.
+
+### Phase 4: Dynamic Countdown Standardization & Density Polish (`EventCockpitDeck.tsx`, `SessionDeck.tsx`, `SessionTable.tsx`)
+
+1. **Standardized Countdown Pill:**
+ - Create a reusable countdown formatter rendering:
+ `⏳ 2h 45m left · (Expires 10:39 PM)` (or
+ `⏳ 29d left · (Expires Sep 25)`).
+ - Dynamic status coloring: **Green** (healthy) -> **Amber** (<1h remaining)
+ -> **Red** (expired).
+ - Apply consistently across Event Pass Cards and the Sessions Table/Deck.
+2. **Multi-Event Compact Density Toggle (`EventCockpitDeck.tsx`):**
+ - Add a `[ 🗂️ Grid ]` / `[ 📋 Compact ]` density toggle at the top of the
+ `Event Passes` section.
+ - In **Compact Mode**, collapse each event card into a sleek 1-row summary
+ strip
+ (`Title · 🟢 Active · ⏳ 2h 45m left · 12/50 Seats · PIN: 749-123 · [ 👥 Guests (12) ] · [ ▸ Details ]`),
+ fitting 10+ active events in a single screen.
+3. **Mobile Session Card Optimization (`SessionDeck.tsx`):**
+ - Remove the dedicated full-width bottom row on remote session cards.
+ - Move `[ 🗑️ Revoke ]` inline into the top card header row next to the status
+ badge as a compact button (`btn-outline-danger`), reducing card height by
+ 50% and fitting 4–5 more sessions per screen.
+
+### Phase 5: Quality Gates & Testing
+
+1. **Unit Tests (`server/tests/events.test.ts` & `scopes.test.ts`):**
+ - Test event host revoking a guest attendee session via
+ `DELETE /api/sessions/:id` succeeds (200 OK).
+ - Test non-owner unauthorized user attempting to revoke an attendee session
+ is rejected (404/403).
+ - Test guest list API returns active claimed seats.
+2. **Hermetic UI Validation (`ui/ui_scripts.test.ts`):**
+ - Verify all vanilla JavaScript drawer controllers, density toggles, and
+ countdown scripts in `SessionsScript.tsx` parse and evaluate cleanly with
+ zero syntax errors.
+3. **Formatting & Linting:**
+ - Ensure `deno fmt`, `deno task lint`, `deno task check`, and
+ `deno test --allow-all` execute with 100% green status.
diff --git a/tasks/new/2026-0826.05.gem.feat.sessions-ui.sessions-and-guest-drawer-overhaul-2023.ph5.md b/tasks/new/2026-0826.05.gem.feat.sessions-ui.sessions-and-guest-drawer-overhaul-2023.ph5.md
deleted file mode 100644
index d397ec2..0000000
--- a/tasks/new/2026-0826.05.gem.feat.sessions-ui.sessions-and-guest-drawer-overhaul-2023.ph5.md
+++ /dev/null
@@ -1,96 +0,0 @@
-# TASK METADATA
-
-- **Target Files:** `server/routes/sessions.ts`, `server/routes/events.ts`, `ui/components/SessionsPage.tsx`, `ui/components/sessions/EventCockpitDeck.tsx`, `ui/components/sessions/EventAttendeesDrawer.tsx` (refactored/renamed to `EventGuestsDrawer.tsx`), `ui/components/sessions/SessionDeck.tsx`, `ui/components/sessions/SessionTable.tsx`, `ui/components/sessions/SessionsScript.tsx`, `server/tests/events.test.ts`, `server/tests/scopes.test.ts`, `ui/ui_scripts.test.ts`
-- **Core Objective:** Implement Phase 5 Overhaul: Fix backend attendee session revocation permission checks, build a true fixed slide-over Guest Drawer, standardize dynamic countdown pills, add multi-event compact density toggle, and clean up page/session visual hierarchy.
-- **Dependencies:** None.
-- **Additional Important Notes:** Must remain 100% pure React-free Hono SSR JSX. All client interactions in `SessionsScript.tsx` must use native vanilla JavaScript DOM APIs.
-
----
-
-## 2. Architectural Considerations & Risks
-
-- **Risks:**
- - **Zero-Trust Revocation Leakage:** When allowing event creators to delete/revoke attendee sessions via `DELETE /api/sessions/:id`, ensure the query strictly verifies that the session belongs to a guest user claimed under an event pass created by `auth.userId` (or that caller is global admin). Never allow arbitrary session deletions across different event creators.
- - **DOM Stacking & Focus Trapping:** A fixed slide-over drawer must properly layer (`z-index: 1050`) over the background page and support backdrop dismissal and `Escape` key capture without disrupting background table states.
- - **Mobile Density Regressions:** Ensure the mobile slide-up bottom sheet does not block necessary viewport scrolling or clip action buttons on small mobile viewports (320px–375px).
-
-- **Alternatives:**
- - *In-Page Expandable Row vs. Fixed Slide-Over Panel:* In-page expandable cards cause massive vertical jumping and layout disruption when 5–10 events are active. A dedicated fixed slide-over drawer (right panel on desktop, bottom sheet on mobile) provides isolated context, independent scrolling, and a pinned summary header without disturbing the background dashboard.
-
----
-
-## 3. Proposed Implementation
-
-### Phase 1: Backend Revocation Permission Fix (`server/routes/sessions.ts`)
-
-1. **Authorize Event Creators in `DELETE /api/sessions/:id`:**
- - Update `DELETE /api/sessions/:id` to check whether the target session is owned by `auth.userId`, or if caller is `isGlobalAdmin`, OR if the session belongs to a guest user of an event pass created by `auth.userId`:
- ```sql
- SELECT s.id
- FROM sessions s
- JOIN users u ON s.user_id = u.id
- WHERE s.id = ${targetSessionId}
- AND (
- s.user_id = ${auth.userId}
- OR ${isAdmin}
- OR EXISTS (
- SELECT 1 FROM event_passes ep
- WHERE ep.created_by = ${auth.userId}
- AND u.username LIKE 'guest_' || ep.slug || '_%'
- )
- )
- ```
- - If found, delete the session from Valkey cache (`valkey.del`) and PostgreSQL (`DELETE FROM sessions WHERE id = ${targetSessionId}`), logging the audit event.
- - Return `{ success: true }`.
-
-### Phase 2: Page Hierarchy & Section Titles (`ui/components/SessionsPage.tsx`)
-
-1. **Page Title:**
- - Update top `
` in `SessionsPage.tsx` from `Active Sessions & Passes` to **`Sessions & Passes`**.
-2. **Missing Section Headings:**
- - Section 1: `
Event Passes
` (with compact density toggle).
- - Section 2: Add a prominent `
Sessions
` heading directly above `SessionTable` and `SessionDeck` with subtitle *"Direct device logins, passkey authentications, and delegated agent tokens."*
-
-### Phase 3: True Fixed Slide-Over Panel (`EventGuestsDrawer.tsx` & `SessionsScript.tsx`)
-
-1. **Drawer Component Overhaul (`ui/components/sessions/EventAttendeesDrawer.tsx` -> `EventGuestsDrawer.tsx`):**
- - Refactor the component from an in-line `