Skip to content

feat(buddy): web buddy flow — Daily room, video layout, join errors (#106)#128

Merged
auerbachb merged 2 commits into
mainfrom
issue-106-buddy-web-ui-flow
Apr 14, 2026
Merged

feat(buddy): web buddy flow — Daily room, video layout, join errors (#106)#128
auerbachb merged 2 commits into
mainfrom
issue-106-buddy-web-ui-flow

Conversation

@auerbachb
Copy link
Copy Markdown
Owner

@auerbachb auerbachb commented Apr 14, 2026

Summary

Implements buddy MVP web flow pieces for #106: Daily room at session start, BuddyVideo with auto-connect, desktop timer-left / video-right layout, Daily teardown on complete/host abandon, and user-visible deep-link join failures.

Closes #106

Test plan

  • npx drizzle-kit push (or apply drizzle/buddy_sessions_daily_room_incremental.sql) so new columns exist
  • DAILY_API_KEY set for start → active path
  • Create/join buddy → waiting → ready → start: video connects without extra click; desktop shows video right of timer; narrow viewport stacks timer then video
  • Complete sit → personal completion flow → home; solo session unchanged
  • Invalid ?buddy= shows dismissible alert and cleans URL
  • npm run build

Notes

  • Snapshot adds dailyRoomUrl while state === "active".
  • Polling unchanged: 1500ms in BuddySessionRoom.
  • Admin strict delete: use deleteRoom(name, { ignoreMissing: false }) from daily.ts when required (runtime teardown uses true).

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Automatic private video room provisioning for active buddy sessions with join tokens.
    • In-session video embedded in the session view, shown when available.
  • User Experience

    • Responsive session layout refined for mobile and desktop.
    • Join flow now surfaces clearer invite and video-token errors with a dismissible alert.
  • Bug Fixes

    • Video rooms and related data are cleaned up when sessions end or hosts leave, reducing orphaned rooms.

- Add buddy_sessions.daily_room_name and daily_room_url; snapshot exposes dailyRoomUrl when active
- POST …/start creates a private Daily room before activating; race cleanup deletes orphan room
- Teardown Daily room on active→completed reconcile and host abandon; clear DB fields
- Active buddy UI: desktop grid timer left / video right; mobile stack timer then video
- Mount BuddyVideo with default auto-connect; message when URL missing
- Deep-link buddy join failures show dismissible alert and clean URL

Made-with: Cursor
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
still-point Ready Ready Preview, Comment Apr 14, 2026 3:42am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 34dc78f9-1980-46b1-8957-6d8848468d74

📥 Commits

Reviewing files that changed from the base of the PR and between 186ee4c and 102872a.

📒 Files selected for processing (6)
  • src/app/api/buddy/sessions/[id]/meeting-token/route.ts
  • src/app/api/buddy/sessions/[id]/start/route.ts
  • src/components/BuddySessionRoom.tsx
  • src/components/BuddyVideo.tsx
  • src/lib/api.ts
  • src/lib/daily.ts

📝 Walkthrough

Walkthrough

Private Daily.co rooms are provisioned for buddy sessions on start, meeting tokens are minted for participants, room name/URL are persisted to the DB, and rooms are torn down and cleared when sessions end or the host abandons them; the UI and snapshot types surface the video URL and token usage.

Changes

Cohort / File(s) Summary
Daily Room Lifecycle
src/app/api/buddy/sessions/[id]/start/route.ts, src/app/api/buddy/sessions/[id]/leave/route.ts
Start handler creates a private Daily room (buddy-${sessionId}), persists dailyRoomName/dailyRoomUrl, retries on name-conflict, and cleans up the room if DB update fails. Leave handler tears down the room when host abandons and nulls room fields.
Meeting Token API
src/app/api/buddy/sessions/[id]/meeting-token/route.ts, src/lib/daily.ts, src/lib/api.ts
Adds POST meeting-token route with membership checks and token minting via createMeetingToken. Implements createMeetingToken, isDailyRoomNameConflict, and exposes api.getBuddyMeetingToken.
Database Schema
drizzle/buddy_sessions_daily_room_incremental.sql, src/db/schema.ts
Adds nullable daily_room_name varchar(128) and daily_room_url text columns to buddy_sessions (migration uses IF NOT EXISTS) and updates schema types.
Buddy Session Utilities
src/lib/buddySession.ts
Adds teardownBuddyDailyRoomByName(), runs teardown during reconciliation when active sessions expire, clears room fields on completion, and includes dailyRoomUrl in snapshots for active sessions.
UI: Session & Video Components
src/components/BuddySessionRoom.tsx, src/components/BuddyVideo.tsx
Restructures active session layout (responsive columns), adds logic to fetch meeting token and render BuddyVideo; BuddyVideo accepts optional meetingToken and joins with token when provided.
Client App Invite UX
src/app/app/page.tsx
Adds buddyInviteError state, captures ApiError messages on join failures, shows dismissible alert when not in-session, and resets history/state on error or success.
Misc Types/Exports
src/lib/api.ts
Extends BuddySnapshot to include `dailyRoomUrl: string

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AppAPI as App API
    participant Auth as Auth/Users
    participant DB as Database
    participant Daily as Daily.co API

    Client->>AppAPI: POST /buddy/sessions/[id]/start
    AppAPI->>Daily: createRoom(name="buddy-{id}", privacy="private")
    alt createRoom OK
        Daily-->>AppAPI: { name, url }
        AppAPI->>DB: UPDATE buddy_sessions set dailyRoomName,dailyRoomUrl,state="active"
        alt DB update OK
            DB-->>AppAPI: 1 row
            AppAPI-->>Client: 200 active
        else DB update failed
            AppAPI->>Daily: deleteRoom(name, {ignoreMissing:true})
            Daily-->>AppAPI: ok/ignored
            AppAPI-->>Client: 500
        end
    else createRoom DailyApiError
        alt name-conflict
            AppAPI->>Daily: deleteRoom(conflictName,{ignoreMissing:true})
            Daily-->>AppAPI: ok/ignored
            AppAPI->>Daily: createRoom(...) retry
        end
        Daily-->>AppAPI: error -> AppAPI->>Client: 503
    end
Loading
sequenceDiagram
    participant Client
    participant AppAPI as App API
    participant Auth as Auth/Users
    participant DB as Database
    participant Daily as Daily.co API

    Client->>AppAPI: POST /buddy/sessions/[id]/meeting-token
    AppAPI->>Auth: getCurrentUser()
    Auth-->>AppAPI: user
    AppAPI->>DB: verify participant active
    DB-->>AppAPI: membership ok
    AppAPI->>AppAPI: reconcileBuddySession(id)
    AppAPI->>DB: SELECT dailyRoomName FROM buddy_sessions
    DB-->>AppAPI: roomName
    AppAPI->>Daily: POST /meeting-tokens {room_name, user_name, user_id, exp}
    alt token OK
        Daily-->>AppAPI: { token }
        AppAPI-->>Client: 200 { token }
    else Daily error
        AppAPI-->>Client: 503
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

  • #92: Implements the buddy web flow and active-session video placement; these changes add Daily integration, meeting tokens, and UI/video placement matching the MVP.
  • Buddy meditation (web MVP parent) #92: Adds the Daily.co room/token and session lifecycle pieces referenced by the MVP; directly relevant to this PR's goals.

Possibly related PRs

  • #127: Extends the same Daily.co integration primitives (room create/delete, DailyApiError) and BuddyVideo — this PR wires those primitives into session lifecycle and token flows.
  • #123: Modifies buddy session route handlers and host-abandon policy helpers used by this PR; strong code-level relation.

Poem

🐰 A rabbit hops into the code,
Rooms spin up where buddies go,
Tokens minted, video glows,
Timers tick and presence shows.
Rooms tidy up when sessions close.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: implementing the web buddy flow with Daily room creation, video layout, and join error handling.
Linked Issues check ✅ Passed The changes comprehensively address all coding requirements from issue #106: extends app state for buddy flow, implements active session view with video to the right of timer on desktop, auto-connects video when session becomes active, handles Daily room creation/teardown, and surfaces join errors to users.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue objectives: database schema updates for Daily room tracking, server-side room creation/teardown logic, API type extensions, UI layout and error handling, and responsive video embedding.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📋 Issue Planner

Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).

View plan for ticket: #106

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-106-buddy-web-ui-flow

Comment @coderabbitai help to get the list of available commands and usage tips.

@auerbachb
Copy link
Copy Markdown
Owner Author

@cursor review

@auerbachb
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/app/api/buddy/sessions/[id]/start/route.ts (1)

59-71: Consider handling the duplicate room name edge case.

If a previous start attempt created the Daily room but the database update failed, the race condition cleanup attempts to delete the orphan room. However, if that cleanup doesn't fully execute or there's a timing window, a retry would attempt to create a room with the same buddy-${sessionId} name. Daily's API returns HTTP 400 for duplicate room names within a domain, which would be caught as DailyApiError and return a 503 response—masking the underlying issue.

For a more robust flow, consider:

  1. Attempting to fetch/reuse an existing room with that name before creating
  2. Including a unique suffix (e.g., timestamp) in the room name

The current behavior is acceptable for an MVP since the race cleanup handles orphan rooms, but this edge case is worth hardening in future iterations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/buddy/sessions/`[id]/start/route.ts around lines 59 - 71, The
createRoom call using roomName (`const roomName = \`buddy-${sessionId}\`` and
`createRoom`) can fail with a duplicate-name DailyApiError; update the flow to
first try to locate/reuse an existing room with that name (e.g., call the Daily
API list/get-by-name before creating) and only call createRoom if none exists,
and as a simpler alternative append a unique suffix (timestamp/UUID) to roomName
before calling createRoom so retries won't hit the duplicate-name 400; keep the
DailyApiError handling but only treat it as fatal after attempting reuse or
using a unique-suffixed name to avoid masking the real-state when a prior room
already exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/app/api/buddy/sessions/`[id]/start/route.ts:
- Around line 59-71: The createRoom call using roomName (`const roomName =
\`buddy-${sessionId}\`` and `createRoom`) can fail with a duplicate-name
DailyApiError; update the flow to first try to locate/reuse an existing room
with that name (e.g., call the Daily API list/get-by-name before creating) and
only call createRoom if none exists, and as a simpler alternative append a
unique suffix (timestamp/UUID) to roomName before calling createRoom so retries
won't hit the duplicate-name 400; keep the DailyApiError handling but only treat
it as fatal after attempting reuse or using a unique-suffixed name to avoid
masking the real-state when a prior room already exists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d398e96b-3059-4fdf-b29b-fd64a9da8e40

📥 Commits

Reviewing files that changed from the base of the PR and between 7c6b50c and 186ee4c.

📒 Files selected for processing (8)
  • drizzle/buddy_sessions_daily_room_incremental.sql
  • src/app/api/buddy/sessions/[id]/leave/route.ts
  • src/app/api/buddy/sessions/[id]/start/route.ts
  • src/app/app/page.tsx
  • src/components/BuddySessionRoom.tsx
  • src/db/schema.ts
  • src/lib/api.ts
  • src/lib/buddySession.ts

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 186ee4c. Configure here.

const roomName = `buddy-${sessionId}`;
let dailyRoom: { name: string; url: string };
try {
dailyRoom = await createRoom({ name: roomName, privacy: "private" });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private Daily room join fails without meeting token

High Severity

The room is created with privacy: "private", but no Daily meeting token is ever generated or passed to the client. In BuddyVideoInner, daily.join({ url, userName }) is called without a token parameter. Daily's private rooms require a valid meeting token for participants to join — without one the join call will be rejected, making the video feature completely non-functional.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 186ee4c. Configure here.

- Add createMeetingToken + isDailyRoomNameConflict in daily.ts
- POST …/meeting-token for active participants; BuddySessionRoom fetches before join
- BuddyVideo passes token to daily.join for private rooms
- Start: on name conflict delete orphan room once and recreate (CodeRabbit)
- Addresses Bugbot: private rooms require meeting tokens

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Buddy MVP] Child C: Web buddy UI flow in /app

1 participant