[EPIC-003] Story 1: Planning Poker Session Management#48
Conversation
- 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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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 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. 📒 Files selected for processing (1)
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughAdds 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
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}
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Actionable comments posted: 9
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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.tsxsrc/lib/poker/utils.tssrc/lib/poker/types.tssrc/lib/realtime/channels.tssrc/hooks/use-poker-session.tssrc/components/poker/PokerSessionForm.tsxsrc/components/poker/PokerSessionList.tsxsrc/lib/poker/actions.tssrc/app/poker/[sessionUrl]/page.tsxsrc/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
srcto 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 narrowingpostgres_changeswith afilterso only relevant rows are delivered.(supabase.com) Please scope this handler (for example, ensurepoker_votescarriessession_idand addfilter: \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 NULLupdate 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>
There was a problem hiding this comment.
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 untypedRealtimePayload(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 exposesisPending/isLoading. The localisSubmittingstate (lines 47, 64, 82) duplicates this and can desync if the mutation throws before reachingfinally.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
isSubmittingin 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) callmutateAsyncwithout 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
handleEndSessionandhandleArchiveSession.
253-260: Update AlertDialog copy to reflect undo capability.The dialog claims "This action cannot be undone," but
useDeletePokerSessionprovides 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.
generateSessionUrlusesMath.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),
consensusValueis 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
📒 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.tssrc/lib/realtime/channels.tssrc/components/poker/PokerSessionList.tsxsrc/lib/poker/utils.tssrc/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
|| trueto?? false/truecorrectly handles user input, so explicitfalsesettings are no longer coerced totrue.
178-187: LGTM—end mutation now invalidates cached lists.The addition of
queryClient.invalidateQueriesinonSuccessensures the session list refreshes immediately after ending a session.
190-203: LGTM—archive mutation now invalidates cached lists.The addition of
queryClient.invalidateQueriesinonSuccessensures 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
deletionTimeoutIdvariable 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
getSequenceByTypenow passessession.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
canArchiveguard 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>
There was a problem hiding this comment.
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 droppingdescriptioncolumn
Confirmed no code references toretrospectives.description, but I didn’t find a SQL migration removing that column—create and commit a migration insupabase/migrationsto dropdescriptionfrom theretrospectivestable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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 constassertion 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>
There was a problem hiding this comment.
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 passsettings.customSequencedirectly or usenull.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: theJSON.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_atmakes 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
📒 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_atonly 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>
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
poker_sessionstable with unique URLs and settingspoker_storiestable for stories to estimatepoker_participantstable for session participantspoker_votestable for estimate votesBackend
Frontend
/pokerroute for session listing/poker/newroute for creating sessions/poker/[sessionUrl]route for viewing sessionsFeatures
Documentation
Testing Notes
Important: This PR includes a database migration that needs to be applied before the build will pass. After merging:
supabase/migrations/20250930000001_planning_poker_schema.sqlThe linter passes with only existing warnings from other files.
Test Plan
Future Work
Remaining stories in EPIC-003:
Related Issues
Closes #17
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
UI
Realtime
Client-side
Documentation
Chores