Skip to content

[EPIC-003] Story 1: Planning Poker Session Management#48

Merged
TheEagleByte merged 11 commits intomainfrom
issue-17-session-management
Oct 1, 2025
Merged

[EPIC-003] Story 1: Planning Poker Session Management#48
TheEagleByte merged 11 commits intomainfrom
issue-17-session-management

Conversation

@TheEagleByte
Copy link
Copy Markdown
Owner

@TheEagleByte TheEagleByte commented Oct 1, 2025

Summary

Implements Story 1 of EPIC-003 Planning Poker Core: Session Management. This PR adds the foundation for planning poker sessions with unique URLs, configurable settings, session listing/history, and rejoin capabilities.

Changes

Database Schema

  • ✅ Add poker_sessions table with unique URLs and settings
  • ✅ Add poker_stories table for stories to estimate
  • ✅ Add poker_participants table for session participants
  • ✅ Add poker_votes table for estimate votes
  • ✅ Implement RLS policies for anonymous and authenticated access
  • ✅ Enable real-time on all poker tables

Backend

  • ✅ Server actions for session CRUD operations
  • ✅ Cookie-based session tracking for anonymous users
  • ✅ Session rejoin capability after disconnection
  • ✅ Participant management actions
  • ✅ Real-time channel support for poker sessions

Frontend

  • ✅ Session creation form with estimation sequence selection
  • ✅ Session list component with archive/delete functionality
  • /poker route for session listing
  • /poker/new route for creating sessions
  • /poker/[sessionUrl] route for viewing sessions
  • ✅ React hooks for poker sessions with TanStack Query
  • ✅ Session statistics and info cards

Features

  • ✅ Estimation sequences: Fibonacci, T-shirt sizes, Linear, Powers of 2
  • ✅ Session settings: auto-reveal, allow revoting, show voter names
  • ✅ Session status management: active, ended, archived
  • ✅ Delete sessions with undo capability
  • ✅ Archive filtering in session list

Documentation

  • ✅ Update CLAUDE.md with poker features
  • ✅ Update CHANGELOG.md
  • ✅ Code follows existing patterns (similar to retrospective boards)

Testing Notes

Important: This PR includes a database migration that needs to be applied before the build will pass. After merging:

  1. Apply the migration: supabase/migrations/20250930000001_planning_poker_schema.sql
  2. Regenerate TypeScript types from the database schema
  3. Run the build again

The linter passes with only existing warnings from other files.

Test Plan

  • Create a new poker session with different estimation sequences
  • Configure session settings (auto-reveal, revoting, voter names)
  • View session listing with active/archived filter
  • End a session
  • Archive a session
  • Delete a session (with undo)
  • View session details page

Future Work

Remaining stories in EPIC-003:

  • Story 2: Story/Ticket Management
  • Story 3: Voting Interface
  • Story 4: Real-time Voting Status
  • Story 5: Vote Reveal System
  • Story 6: Estimation Sequences (custom)
  • Story 7: Statistics & Analytics
  • Story 8: Timer & Discussion Tools
  • Story 9: Mobile Optimization

Related Issues

Closes #17

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Planning Poker: create/share sessions with configurable estimation sequences and settings, session history, join/rejoin and anonymous participant support.
  • UI

    • Session creation form, session detail view, sessions list with active/archived views, stats, and actions (open, end, archive, delete with undo).
    • Retrospective board: description field removed from settings and display.
  • Realtime

    • Live updates for sessions, stories, participants, votes, and basic timer events.
  • Client-side

    • Optimistic updates, undoable deletes, local-storage anonymous tracking.
  • Documentation

    • CHANGELOG and product docs updated.
  • Chores

    • Added Supabase dependency and DB migrations.

- Create poker_sessions, poker_stories, poker_participants, poker_votes tables
- Add RLS policies for anonymous and authenticated access
- Implement server actions for session management
- Add React hooks for poker sessions with TanStack Query
- Create estimation sequences (Fibonacci, T-shirt, Linear, Powers of 2)
- Add realtime channel support for poker sessions
- Implement session URL generation and cookie-based tracking
- Add utility functions for voting statistics and validation
- Create PokerSessionForm with estimation sequence selection
- Add PokerSessionList component with session management
- Implement /poker main page with session listing
- Add /poker/new page for creating sessions
- Add /poker/[sessionUrl] page for viewing sessions
- Support session status badges and settings display
- Add archive/delete functionality with confirmations
- Include session statistics and info cards
- Add planning poker database schema to CLAUDE.md
- Document planning poker features and capabilities
- Update real-time features list
- Add planning poker entry to CHANGELOG.md
- Fix apostrophe escaping in PokerSessionForm
- Remove unused imports in PokerSessionList
- Fix formatEstimateValue signature
- Import types from types file instead of actions file

Note: Build will pass once migration is applied and types are regenerated
Copilot AI review requested due to automatic review settings October 1, 2025 02:21
@vercel
Copy link
Copy Markdown

vercel bot commented Oct 1, 2025

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

Project Deployment Preview Comments Updated (UTC)
scrumkit Ready Ready Preview Comment Oct 1, 2025 0:12am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 1, 2025

Warning

Rate limit exceeded

@TheEagleByte has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 5 minutes and 0 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 54049ea and d7968b4.

📒 Files selected for processing (1)
  • src/lib/poker/actions.ts (1 hunks)

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a Planning Poker feature: DB migration and RLS policies, server-side Supabase actions and types, realtime channel wiring, client hooks with optimistic updates/undo, UI pages/components/forms for session creation/listing/viewing, utilities for sequences/voting, and documentation/changelog updates.

Changes

Cohort / File(s) Summary
Changelog & Docs
CHANGELOG.md, CLAUDE.md
Add Planning Poker entries and update documentation to list new features, schema tables, and realtime capabilities.
App Pages: Poker
src/app/poker/page.tsx, src/app/poker/new/page.tsx, src/app/poker/[sessionUrl]/page.tsx
Add poker listing (client), new session creation page, and server-rendered session overview page with session lookup and notFound handling.
Poker UI Components
src/components/poker/PokerSessionForm.tsx, src/components/poker/PokerSessionList.tsx
New session creation form (react-hook-form + zod) and animated session list/cards with actions (open/end/archive/delete) and confirmation dialogs.
Hooks: Poker Sessions
src/hooks/use-poker-session.ts
React Query hooks and key helpers for listing, creating, updating, ending, archiving, and deleting sessions with optimistic updates and undo via localStorage/toasts.
Server Actions
src/lib/poker/actions.ts
Server-side Supabase-backed CRUD actions, participant join/rejoin, permission checks, cookie tracking, and path revalidation for poker sessions.
Types
src/lib/poker/types.ts, src/lib/supabase/types.ts
Add TypeScript domain types for sessions/stories/participants/votes and update Supabase types to include new poker tables; remove description from retrospective types.
Utils: Sequences & Voting
src/lib/poker/utils.ts
Estimation sequences, sequence selection/validation, default session settings, URL generator, voting stats calculations, and status guards.
Realtime Channels
src/lib/realtime/channels.ts
Add poker channel names/events and helpers to create/setup poker session channels and subscriptions for sessions/stories/participants/votes.
Database Migration
supabase/migrations/20250930000001_planning_poker_schema.sql
New migration creating poker_sessions, poker_stories, poker_participants, poker_votes with indexes, triggers, FKs, CHECKs, and comprehensive RLS policies.
Retrospectives Adjustments
src/components/RetrospectiveBoard.tsx, src/components/retro/BoardCustomizationDialog.tsx
Remove usage and UI of retrospective description (field removed from settings and persistence).
Dependencies
package.json
Add supabase dependency (v^2.47.2).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Form as PokerSessionForm (client)
  participant Hook as useCreatePokerSession
  participant Server as createPokerSession (server)
  participant DB as Supabase
  participant Router as App Router

  User->>Form: Fill form & submit
  Form->>Hook: mutateAsync(input)
  Hook-->>Form: insert optimistic session in cache
  Hook->>Server: createPokerSession(input)
  Server->>DB: INSERT INTO poker_sessions
  DB-->>Server: { id, unique_url, title }
  Server->>Router: revalidatePath("/poker")
  Server-->>Hook: success
  Hook-->>Form: navigate to /poker/{unique_url}
Loading
sequenceDiagram
  autonumber
  actor User
  participant Page as PokerPage (client)
  participant Query as usePokerSessions
  participant Server as update/delete actions
  participant DB as Supabase
  participant RT as Realtime Channel

  Page->>Query: fetch sessions (cookie-based list)
  Query->>DB: SELECT sessions
  DB-->>Query: sessions[]
  Query-->>Page: render list

  User->>Page: End / Archive / Delete action
  Page->>Query: mutate (optimistic)
  Query->>Server: updatePokerSession / deletePokerSession
  Server->>DB: UPDATE / soft-delete
  DB-->>Server: ok
  Server->>RT: broadcast session change
  RT-->>Page: event (session/story/participant/vote)
  Page->>Page: apply realtime update
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

I hopped through rows of schema seeds,
I crafted tiny URLs and deeds.
Toggles blink and cards align,
Votes reveal like stars that shine.
Hop—session born, let’s estimate! 🐇🃏

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request includes unrelated changes to the retrospective feature by removing the description field from the RetrospectiveBoard, BoardCustomizationDialog, and corresponding Supabase type definitions, which are not part of the session management objectives outlined in linked issue #17. Please isolate or remove the retrospective description removal changes into a separate PR or provide justification for including these modifications alongside the Planning Poker session management work.
✅ 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 reflects the primary focus of the changeset by referencing the session management story for Planning Poker and aligns with the linked issue’s objectives without extraneous details.
Linked Issues Check ✅ Passed The pull request fully addresses the linked issue #17 by implementing session creation with unique URLs, a configurable settings UI and defaults, a session listing and archive history view, end and archive lifecycle actions, and a rejoin capability via the joinPokerSession action and participant tracking. Components, hooks, server actions, real-time channels, types, and database schema collectively fulfill each acceptance criterion in the issue.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements Story 1 of EPIC-003 Planning Poker Core, establishing the foundational session management system for planning poker estimation sessions. The implementation provides unique shareable URLs, configurable estimation sequences, session lifecycle management, and cookie-based anonymous user tracking with real-time infrastructure.

  • Database schema for poker sessions, stories, participants, and votes with comprehensive RLS policies
  • Session CRUD operations with cookie-based anonymous user support and rejoin capabilities
  • Frontend session listing, creation forms, and basic session detail views with archive/delete functionality

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
supabase/migrations/20250930000001_planning_poker_schema.sql Complete database schema for poker sessions with tables, indexes, RLS policies, and real-time triggers
src/lib/realtime/channels.ts Real-time channel functions and event constants for poker session subscriptions
src/lib/poker/utils.ts Core utilities for estimation sequences, session settings, voting statistics, and validation
src/lib/poker/types.ts TypeScript interfaces for poker sessions, stories, participants, votes, and input types
src/lib/poker/actions.ts Server actions for session CRUD, participant management, and cookie-based authentication
src/hooks/use-poker-session.ts React hooks with TanStack Query for session management with optimistic updates
src/components/poker/PokerSessionList.tsx Session listing component with status badges, settings display, and action menus
src/components/poker/PokerSessionForm.tsx Session creation form with estimation sequence selection and settings configuration
src/app/poker/page.tsx Main poker page with session listing, statistics cards, and archive filtering
src/app/poker/new/page.tsx New session creation page layout
src/app/poker/[sessionUrl]/page.tsx Individual session view page with settings and status display
CLAUDE.md Documentation updates for poker features and database schema
CHANGELOG.md Changelog entry for planning poker session management features

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

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.

Actionable comments posted: 9

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0f3048 and b278a47.

📒 Files selected for processing (13)
  • CHANGELOG.md (1 hunks)
  • CLAUDE.md (2 hunks)
  • src/app/poker/[sessionUrl]/page.tsx (1 hunks)
  • src/app/poker/new/page.tsx (1 hunks)
  • src/app/poker/page.tsx (1 hunks)
  • src/components/poker/PokerSessionForm.tsx (1 hunks)
  • src/components/poker/PokerSessionList.tsx (1 hunks)
  • src/hooks/use-poker-session.ts (1 hunks)
  • src/lib/poker/actions.ts (1 hunks)
  • src/lib/poker/types.ts (1 hunks)
  • src/lib/poker/utils.ts (1 hunks)
  • src/lib/realtime/channels.ts (3 hunks)
  • supabase/migrations/20250930000001_planning_poker_schema.sql (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use the @/* import alias for modules under src instead of relative paths

Files:

  • src/app/poker/new/page.tsx
  • src/lib/poker/utils.ts
  • src/lib/poker/types.ts
  • src/lib/realtime/channels.ts
  • src/hooks/use-poker-session.ts
  • src/components/poker/PokerSessionForm.tsx
  • src/components/poker/PokerSessionList.tsx
  • src/lib/poker/actions.ts
  • src/app/poker/[sessionUrl]/page.tsx
  • src/app/poker/page.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-28T15:22:35.016Z
Learnt from: CR
PR: TheEagleByte/scrumkit#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-28T15:22:35.016Z
Learning: Use React Hook Form with Zod for form handling and validation

Applied to files:

  • src/components/poker/PokerSessionForm.tsx
📚 Learning: 2025-09-28T15:22:35.016Z
Learnt from: CR
PR: TheEagleByte/scrumkit#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-28T15:22:35.016Z
Learning: Enable and consume Supabase real-time updates for retrospective_items, votes, retrospectives, and action_items where appropriate

Applied to files:

  • CLAUDE.md
🧬 Code graph analysis (9)
src/app/poker/new/page.tsx (1)
src/components/poker/PokerSessionForm.tsx (1)
  • PokerSessionForm (44-271)
src/lib/poker/utils.ts (1)
src/lib/poker/types.ts (3)
  • EstimationSequenceType (3-3)
  • EstimationSequence (9-14)
  • SessionSettings (16-22)
src/lib/realtime/channels.ts (1)
src/lib/logger.ts (1)
  • logger (104-104)
src/hooks/use-poker-session.ts (2)
src/lib/poker/types.ts (3)
  • PokerSession (24-50)
  • CreatePokerSessionInput (100-105)
  • UpdatePokerSessionInput (107-113)
src/lib/poker/actions.ts (6)
  • getUserPokerSessions (111-137)
  • createPokerSession (16-89)
  • updatePokerSession (140-214)
  • endPokerSession (217-219)
  • archivePokerSession (222-224)
  • deletePokerSession (227-279)
src/components/poker/PokerSessionForm.tsx (2)
src/hooks/use-poker-session.ts (1)
  • useCreatePokerSession (46-108)
src/lib/poker/utils.ts (1)
  • getAvailableSequences (49-51)
src/components/poker/PokerSessionList.tsx (3)
src/lib/poker/types.ts (1)
  • PokerSession (24-50)
src/hooks/use-poker-session.ts (3)
  • useDeletePokerSession (207-310)
  • useEndPokerSession (175-188)
  • useArchivePokerSession (191-204)
src/lib/poker/utils.ts (1)
  • getSequenceByType (38-46)
src/lib/poker/actions.ts (2)
src/lib/poker/types.ts (5)
  • CreatePokerSessionInput (100-105)
  • PokerSession (24-50)
  • UpdatePokerSessionInput (107-113)
  • CreatePokerParticipantInput (134-140)
  • PokerParticipant (66-77)
src/lib/poker/utils.ts (2)
  • generateSessionUrl (62-69)
  • DEFAULT_SESSION_SETTINGS (54-59)
src/app/poker/[sessionUrl]/page.tsx (2)
src/lib/poker/actions.ts (1)
  • getPokerSession (92-108)
src/lib/poker/utils.ts (1)
  • getSequenceByType (38-46)
src/app/poker/page.tsx (2)
src/hooks/use-poker-session.ts (1)
  • usePokerSessions (34-43)
src/components/poker/PokerSessionList.tsx (1)
  • PokerSessionList (45-273)
🔇 Additional comments (3)
src/lib/poker/utils.ts (1)

1-1: Switch to the @ alias for poker types import.

The coding guidelines require modules under src to use the @/* alias. Please update this import to keep paths consistent.
[ suggest_essential_refactor ]

-import type { EstimationSequence, EstimationSequenceType, SessionSettings } from "./types";
+import type { EstimationSequence, EstimationSequenceType, SessionSettings } from "@/lib/poker/types";

As per coding guidelines.

src/lib/realtime/channels.ts (1)

243-253: Filter poker vote changes to the current session.

This subscription listens to every row change in poker_votes, so a client connected to one session will receive events for all sessions—leaking data and burning realtime quota. Supabase recommends narrowing postgres_changes with a filter so only relevant rows are delivered.(supabase.com) Please scope this handler (for example, ensure poker_votes carries session_id and add filter: \session_id=eq.${sessionId}``) before shipping.
[ raise_critical_issue ]

src/lib/poker/actions.ts (1)

147-166: Server actions must refuse anonymous updates for other creators.

The migration’s policy already lets any anon with creator_cookie IS NOT NULL update rows; here you still check the cookie. That’s fine, but highlight: nothing flagged.

- Add trigger to set unique_url on poker_sessions using existing generate_unique_url function
- Fix missing space in PokerSessionForm render parameter
- Pass custom_sequence to getSequenceByType in session detail and list pages
- Allow archiving ended sessions by updating dropdown menu logic
- Fix optimistic defaults using nullish coalescing to preserve false values
- Add query invalidation to useEndPokerSession and useArchivePokerSession
- Fix undo delete by deferring server deletion until after 5-second window
- Secure RLS policies by requiring authenticated users only, handle anonymous via server-side validation
- Add session_id column to poker_votes table for realtime filtering
- Add realtime subscription filter for poker_votes by session_id
- Change relative import to @ alias in poker/utils.ts

All changes improve security, UX, and align with coding guidelines.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
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.

Actionable comments posted: 1

🧹 Nitpick comments (7)
src/lib/realtime/channels.ts (1)

191-258: Type the poker subscription handlers for better safety.

The retrospective subscriptions (lines 68–134) provide full Database["public"]["Tables"][...] row types in their handler signatures, but the new poker handlers use untyped RealtimePayload (lines 195–198). This loses type safety—consumers won't catch schema mismatches at compile time.

Apply this pattern to match the retrospective handlers:

 export function setupPokerSessionSubscriptions(
   channel: ReturnType<SupabaseClient<Database>["channel"]>,
   sessionId: string,
   handlers: {
-    onSessionChange?: (payload: RealtimePayload) => void;
-    onStoryChange?: (payload: RealtimePayload) => void;
-    onParticipantChange?: (payload: RealtimePayload) => void;
-    onVoteChange?: (payload: RealtimePayload) => void;
+    onSessionChange?: (payload: RealtimePayload<Database["public"]["Tables"]["poker_sessions"]["Row"]>) => void;
+    onStoryChange?: (payload: RealtimePayload<Database["public"]["Tables"]["poker_stories"]["Row"]>) => void;
+    onParticipantChange?: (payload: RealtimePayload<Database["public"]["Tables"]["poker_participants"]["Row"]>) => void;
+    onVoteChange?: (payload: RealtimePayload<Database["public"]["Tables"]["poker_votes"]["Row"]>) => void;
   }
 ) {
src/components/poker/PokerSessionForm.tsx (2)

44-84: Remove redundant loading state.

React Hook Form already tracks formState.isSubmitting, and the mutation exposes isPending/isLoading. The local isSubmitting state (lines 47, 64, 82) duplicates this and can desync if the mutation throws before reaching finally.

Replace the manual state with the built-in indicators:

 export function PokerSessionForm() {
   const router = useRouter();
   const createSession = useCreatePokerSession();
-  const [isSubmitting, setIsSubmitting] = useState(false);

   const sequences = getAvailableSequences();

   const form = useForm<FormValues>({
     resolver: zodResolver(formSchema),
     defaultValues: { /* ... */ },
   });

   async function onSubmit(values: FormValues) {
-    setIsSubmitting(true);
     try {
       const result = await createSession.mutateAsync({ /* ... */ });
       router.push(`/poker/${result.unique_url}`);
     } catch (error) {
       console.error("Error creating session:", error);
-    } finally {
-      setIsSubmitting(false);
     }
   }
+
+  const isSubmitting = form.formState.isSubmitting || createSession.isPending;

Then reference isSubmitting in the JSX as before.


79-80: Consider surfacing navigation errors to the user.

The catch block logs to console but doesn't inform the user if navigation fails. The mutation hook already shows a toast on mutation error, so this may be acceptable, but navigation failures are a separate concern.

If you want to handle navigation errors explicitly:

       router.push(`/poker/${result.unique_url}`);
     } catch (error) {
       console.error("Error creating session:", error);
+      // Navigation or other unexpected errors—mutation errors already toasted
     }

Or add a toast if the error isn't from the mutation itself.

src/components/poker/PokerSessionList.tsx (2)

53-66: Handle mutation errors in action handlers.

All three handlers (handleDelete, handleEndSession, handleArchiveSession) call mutateAsync without catching errors. If the mutation fails, you'll see an unhandled promise rejection in the console. The mutation hooks already toast errors, but explicit error handling is safer.

Wrap each in try/catch or append .catch():

   const handleDelete = async () => {
     if (!sessionToDelete) return;
-    await deleteSession.mutateAsync(sessionToDelete);
+    try {
+      await deleteSession.mutateAsync(sessionToDelete);
+    } catch {
+      // Mutation hook already toasts error
+    }
     setDeleteDialogOpen(false);
     setSessionToDelete(null);
   };

Apply the same pattern to handleEndSession and handleArchiveSession.


253-260: Update AlertDialog copy to reflect undo capability.

The dialog claims "This action cannot be undone," but useDeletePokerSession provides a 5‑second undo window. Users who read the message might not realize they can undo, defeating the UX benefit.

Revise the description to match the actual behavior:

           <AlertDialogDescription>
-            This action cannot be undone. The session and all its stories and votes
-            will be permanently deleted.
+            The session and all its stories and votes will be deleted after 5 seconds.
+            You can undo this action from the notification.
           </AlertDialogDescription>
src/lib/poker/utils.ts (2)

62-69: URL collision risk is low but not zero.

generateSessionUrl uses Math.random() with a 36^12 space (≈4.7×10^18), which makes collisions unlikely. However, there's no uniqueness check, so a duplicate URL would silently overwrite or fail constraint checks at the DB layer.

If you want deterministic uniqueness, consider a server-side helper that retries on collision or use a UUID-based approach. For now, the collision probability is acceptable for most use cases, but document the assumption or add a unique constraint + retry loop in the server action if you expect high session volume.


82-128: Tied votes produce non-deterministic consensus values.

When multiple estimates share the highest count (line 99–103), consensusValue is the last one encountered during Map iteration. JavaScript Map iteration order is insertion order, but this still depends on the order votes were cast, so ties are resolved arbitrarily.

If deterministic tie-breaking matters (e.g., for reproducible analytics), collect all tied values and either return an array or pick the lowest/highest by a stable sort:

   let maxCount = 0;
-  let consensusValue: string | undefined;
+  const consensusValues: string[] = [];
   distribution.forEach((count, value) => {
-    if (count > maxCount) {
+    if (count > maxCount) {
       maxCount = count;
-      consensusValue = value;
+      consensusValues.length = 0;
+      consensusValues.push(value);
+    } else if (count === maxCount) {
+      consensusValues.push(value);
     }
   });
+  const consensusValue = consensusValues.length === 1 ? consensusValues[0] : undefined;

Then update the return type and downstream consumers if you want to expose ties explicitly.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b278a47 and ab2ce66.

📒 Files selected for processing (7)
  • src/app/poker/[sessionUrl]/page.tsx (1 hunks)
  • src/components/poker/PokerSessionForm.tsx (1 hunks)
  • src/components/poker/PokerSessionList.tsx (1 hunks)
  • src/hooks/use-poker-session.ts (1 hunks)
  • src/lib/poker/utils.ts (1 hunks)
  • src/lib/realtime/channels.ts (3 hunks)
  • supabase/migrations/20250930000001_planning_poker_schema.sql (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/poker/[sessionUrl]/page.tsx
  • supabase/migrations/20250930000001_planning_poker_schema.sql
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use the @/* import alias for modules under src instead of relative paths

Files:

  • src/hooks/use-poker-session.ts
  • src/lib/realtime/channels.ts
  • src/components/poker/PokerSessionList.tsx
  • src/lib/poker/utils.ts
  • src/components/poker/PokerSessionForm.tsx
🧠 Learnings (1)
📚 Learning: 2025-09-28T15:22:35.016Z
Learnt from: CR
PR: TheEagleByte/scrumkit#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-28T15:22:35.016Z
Learning: Use React Hook Form with Zod for form handling and validation

Applied to files:

  • src/components/poker/PokerSessionForm.tsx
🧬 Code graph analysis (5)
src/hooks/use-poker-session.ts (2)
src/lib/poker/types.ts (3)
  • PokerSession (24-50)
  • CreatePokerSessionInput (100-105)
  • UpdatePokerSessionInput (107-113)
src/lib/poker/actions.ts (6)
  • getUserPokerSessions (111-137)
  • createPokerSession (16-89)
  • updatePokerSession (140-214)
  • endPokerSession (217-219)
  • archivePokerSession (222-224)
  • deletePokerSession (227-279)
src/lib/realtime/channels.ts (1)
src/lib/logger.ts (1)
  • logger (104-104)
src/components/poker/PokerSessionList.tsx (3)
src/lib/poker/types.ts (1)
  • PokerSession (24-50)
src/hooks/use-poker-session.ts (3)
  • useDeletePokerSession (207-324)
  • useEndPokerSession (175-188)
  • useArchivePokerSession (191-204)
src/lib/poker/utils.ts (1)
  • getSequenceByType (38-46)
src/lib/poker/utils.ts (1)
src/lib/poker/types.ts (3)
  • EstimationSequenceType (3-3)
  • EstimationSequence (9-14)
  • SessionSettings (16-22)
src/components/poker/PokerSessionForm.tsx (2)
src/hooks/use-poker-session.ts (1)
  • useCreatePokerSession (46-108)
src/lib/poker/utils.ts (1)
  • getAvailableSequences (49-51)
🔇 Additional comments (8)
src/hooks/use-poker-session.ts (4)

71-73: LGTM—optimistic defaults now preserve explicit false.

The switch from || true to ?? false/true correctly handles user input, so explicit false settings are no longer coerced to true.


178-187: LGTM—end mutation now invalidates cached lists.

The addition of queryClient.invalidateQueries in onSuccess ensures the session list refreshes immediately after ending a session.


190-203: LGTM—archive mutation now invalidates cached lists.

The addition of queryClient.invalidateQueries in onSuccess ensures the session list refreshes immediately after archiving a session.


206-323: Delete-with-undo flow is now correctly implemented.

The deferred server deletion pattern properly coordinates the timeout, localStorage, and cache restoration. The closure captures the deletionTimeoutId variable reference, so the undo action can clear the timeout even though it's assigned after the toast is shown.

src/components/poker/PokerSessionList.tsx (2)

104-107: LGTM—custom sequences now render correctly.

The call to getSequenceByType now passes session.custom_sequence, so custom estimation sequences display the configured values instead of falling back to defaults.


140-156: LGTM—ended sessions can now be archived.

The canArchive guard enables the Archive action for both active and ended sessions, supporting the full lifecycle described in the acceptance criteria.

src/lib/poker/utils.ts (2)

4-59: LGTM—estimation sequences and defaults are well-structured.

The predefined sequences, type-safe lookup, and default settings provide a clean foundation for the poker feature. The special values (?, ☕) add useful flexibility.


143-166: LGTM—validation and status guards are straightforward.

The helpers correctly validate vote values against sequences and check session/story status for join/vote/reveal actions.

Updated the generated TypeScript types to include the newly migrated
poker tables (poker_sessions, poker_stories, poker_participants,
poker_votes) while preserving all existing table definitions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fixed Vercel build failure by properly regenerating the types file
from the Supabase database schema. The types now include all four
poker tables (poker_sessions, poker_participants, poker_stories,
poker_votes) that were added in the migration.

This resolves the TypeScript error where poker_sessions was not
recognized as a valid table name.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/supabase/types.ts (1)

546-626: Add migration for dropping description column
Confirmed no code references to retrospectives.description, but I didn’t find a SQL migration removing that column—create and commit a migration in supabase/migrations to drop description from the retrospectives table.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db6e84f and 9b28836.

📒 Files selected for processing (1)
  • src/lib/supabase/types.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use the @/* import alias for modules under src instead of relative paths

Files:

  • src/lib/supabase/types.ts
src/lib/supabase/types.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain generated/centralized Supabase TypeScript types in src/lib/supabase/types.ts

Files:

  • src/lib/supabase/types.ts
🔇 Additional comments (1)
src/lib/supabase/types.ts (1)

913-913: Minor: End-of-file formatting adjustment.

The as const assertion formatting change is fine—maintains type safety for the Constants object.

The retrospectives table doesn't have a description column, but the
code was trying to access it. Removed all references to description
in:
- RetrospectiveBoard component (boardSettings memoization and JSX)
- BoardCustomizationDialog component (interface and form field)

Also fixed poker_sessions insert to include unique_url (required by
types, but will be set by trigger).

Build now passes successfully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
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.

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/lib/poker/actions.ts (3)

50-52: Remove unnecessary JSON serialization.

The JSON.parse(JSON.stringify()) is redundant here. Supabase client will handle JSON serialization for JSONB columns automatically. You can pass settings.customSequence directly or use null.

Apply this diff:

-      custom_sequence: settings.customSequence
-        ? JSON.parse(JSON.stringify(settings.customSequence))
-        : null,
+      custom_sequence: settings.customSequence ?? null,

189-190: Remove unnecessary JSON serialization.

Same issue as in createPokerSession: the JSON.parse(JSON.stringify()) pattern is unnecessary. Supabase handles JSONB serialization automatically.

Apply this diff:

     if (updates.settings.customSequence !== undefined) {
-      updateData.custom_sequence = JSON.parse(JSON.stringify(updates.settings.customSequence));
+      updateData.custom_sequence = updates.settings.customSequence;
     }

351-366: LGTM!

Participant retrieval is straightforward and correct. The ordering by joined_at makes sense for displaying participants in join order.

For very large sessions (unlikely but possible), consider adding pagination support in the future.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b28836 and 54049ea.

📒 Files selected for processing (3)
  • src/components/RetrospectiveBoard.tsx (0 hunks)
  • src/components/retro/BoardCustomizationDialog.tsx (0 hunks)
  • src/lib/poker/actions.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • src/components/retro/BoardCustomizationDialog.tsx
  • src/components/RetrospectiveBoard.tsx
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use the @/* import alias for modules under src instead of relative paths

Files:

  • src/lib/poker/actions.ts
🧬 Code graph analysis (1)
src/lib/poker/actions.ts (2)
src/lib/poker/types.ts (5)
  • CreatePokerSessionInput (100-105)
  • PokerSession (24-50)
  • UpdatePokerSessionInput (107-113)
  • CreatePokerParticipantInput (134-140)
  • PokerParticipant (66-77)
src/lib/poker/utils.ts (2)
  • generateSessionUrl (62-69)
  • DEFAULT_SESSION_SETTINGS (54-59)
🔇 Additional comments (10)
src/lib/poker/actions.ts (10)

1-14: LGTM!

Imports are well-organized and correctly use the @/* alias as per coding guidelines.


21-31: Cookie generation looks secure.

The creator cookie uses a combination of generateSessionUrl() and timestamp. The cookie configuration is correct with httpOnly, secure in production, and sameSite strict.


93-109: LGTM!

Session retrieval logic is straightforward and correct. Public read access by URL is appropriate for the planning poker use case.


162-167: Permission model is correct for current scope.

The permission check correctly uses creator_cookie for anonymous sessions and explicitly rejects team session updates. This is appropriate for the current implementation.

Verify that authenticated team session updates will be implemented in a future story.


176-180: Status transition logic looks correct.

Setting ended_at only when status becomes 'ended' is the right approach.


218-220: LGTM!

Clean wrapper function that correctly delegates to updatePokerSession.


223-225: LGTM!

Clean wrapper function that correctly delegates to updatePokerSession.


291-301: Participant cookie generation is secure.

Cookie configuration and generation pattern matches the creator cookie approach and is appropriately secure.


303-326: Rejoin logic is well-implemented.

The function correctly handles existing participants by updating last_seen_at, enabling seamless reconnection after disconnects.


328-347: New participant creation looks correct.

The insert logic properly handles optional fields with sensible defaults for facilitator and observer flags.

Added try-catch blocks around all JSON.parse calls when reading the
scrumkit_poker_sessions cookie to prevent crashes from corrupted
cookie data. If parsing fails, the cookie is reset to an empty array.

This addresses CodeRabbit's security concern about unhandled JSON
parsing errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@TheEagleByte TheEagleByte merged commit f26773a into main Oct 1, 2025
4 checks passed
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.

[EPIC-003] Story 1: Session Management

2 participants