- Update pnpm-lock.yaml with new semantic-release dependencies
- Fix Makefile paths to use packages/core/src/ instead of src/
- All Python scripts now reference correct monorepo structure
Fixes:
- ERR_PNPM_OUTDATED_LOCKFILE in CI workflows
- Missing generate_examples.py in verify-examples workflow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Refactor db/index.ts to use lazy initialization via Proxy pattern.
This prevents the database from being accessed at module import time,
which was causing Next.js build failures in CI/CD environments where
no database file exists.
The database connection is now created only when first accessed at
runtime, allowing static site generation to complete successfully.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement foundational infrastructure for multi-room arcade system:
Database:
- Add arcade_rooms table for room metadata and lifecycle
- Add room_members table for membership tracking
- Add nullable roomId field to arcade_sessions for room association
- Create migration 0003_naive_reptil.sql
Managers:
- Implement room-manager.ts with full CRUD operations
- Implement room-membership.ts for member management
- Add room-code.ts utility for unique room code generation
- Include TTL-based room cleanup functionality
Documentation:
- Add arcade-rooms-technical-plan.md with complete system design
- Add arcade-rooms-implementation-tasks.md with 62-task breakdown
This establishes the foundation for public multiplayer rooms with:
- URL-addressable rooms with unique codes
- Guest user support
- Configurable TTL for automatic cleanup
- Room creator moderation controls
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove duplicate New Game button from GamePhase and update ResultsPhase
to use proper arcade navigation now that controls are in the nav bar.
- Remove New Game button from GamePhase (now in nav)
- Change ResultsPhase "Back to Games" to "Back to Arcade"
- Add proper session exit in ResultsPhase arcade navigation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Connect game control buttons to actual game actions using context
functions for Setup, New Game, and Quit functionality.
- Setup: exit session and navigate to /arcade/matching (returns to setup)
- New Game: call resetGame() from context
- Quit: exit session and navigate to /arcade
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Wire up GameControlButtons through PageWithNav and GameContextNav to
enable game control buttons in the navigation bar during gameplay.
- Add onSetup, onNewGame props to PageWithNav and GameContextNav
- Show GameControlButtons when !showFullscreenSelection && !canModifyPlayers
- Pass callbacks through component hierarchy
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add reusable GameControlButtons component with Setup, New Game, and Quit
buttons for arcade game navigation. Includes comprehensive unit tests.
- Create GameControlButtons component with optional callbacks
- Add flexWrap: nowrap and whiteSpace: nowrap to prevent wrapping
- Write 10 unit tests covering all button behaviors
- All tests passing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added detailed logging to help debug player ID vs user ID flow:
- useArcadeSocket: Log full move payload with JSON stringification
- session-manager: Log player ID, game state players, and phase info
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
START_GAME moves must explicitly provide activePlayers array containing
database player IDs. Removed fallback to [data.userId] which incorrectly
used guest ID as player ID. Server now rejects START_GAME moves that are
missing activePlayers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
All game moves now explicitly include the database player ID (avatar ID)
in the playerId field. This fixes multiplayer turn validation which was
failing because it was comparing database player IDs with guest IDs.
- FLIP_CARD: Use state.currentPlayer (database player ID)
- START_GAME/resetGame: Use first active player ID, with validation
- CLEAR_MISMATCH: Use state.currentPlayer
Removed fallback to viewerId which incorrectly conflated user/guest IDs
with player IDs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Player IDs (database avatar IDs) must never be conflated with or fall back
to user/guest IDs. This commit makes playerId a required field in all game
moves and throws an error if missing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed CreateSessionOptions.activePlayers from number[] to string[]
- Updated socket-server.ts fallback from [1] to [data.userId]
- Added debug logging to validateFlipCard to diagnose turn validation issues
This ensures that when a session is created without explicit activePlayers,
it uses the actual UUID of the requesting player instead of the numeric value 1.
Changed Player type from number to string (UUID) throughout the
matching game to properly identify players by their unique IDs
rather than array positions. This fixes the "Not your turn"
validation errors that were occurring because server-side
validation was comparing UUIDs (move.playerId) with numeric
indices (state.currentPlayer).
Changes:
- Updated Player type from number to string in both arcade and
games matching context types
- Changed all player tracking to use UUID strings instead of
numeric indices (1, 2, 3)
- Updated turn validation in MatchingGameValidator to compare
string IDs correctly
- Fixed all UI components (GameCard, PlayerStatusBar, etc.) to
use player.findIndex() for array positions when needed
- Updated MatchingStartGameMove type to expect string[] for
activePlayers
- Re-enabled turn validation (previously disabled as workaround)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive Playwright tests for arcade modal session system:
- Session redirects and persistence
- Player modification blocking during games
- "Return to Arcade" button functionality
- Session lifecycle from creation to end
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed turn validation check that was causing "Not your turn" errors.
The validator was comparing player numbers (1, 2, 3) with viewer UUIDs,
which never matched. In arcade mode, a single user controls all players,
so turn validation is not applicable.
- Removed comparison of state.currentPlayer (number) vs playerId (UUID)
- Allows single user to flip cards for any player in arcade mode
- Fixes card flipping functionality in matching game
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added session-state event emission after creating new arcade session
during START_GAME. This ensures connected clients (like useArcadeRedirect)
are immediately notified of the new session, triggering proper UI updates.
- Fetches newly created session after START_GAME
- Emits session-state to all clients in user's arcade room
- Enables "Return to Arcade" button to appear immediately
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Modified GameContextNav to only display "Return to Arcade" button when
user cannot modify players (i.e., during active game session). This
prevents the button from appearing during game setup phase.
- Button now conditional on !canModifyPlayers
- Removed incorrect "Setup" button display during setup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Modified useArcadeRedirect to not redirect users away from game pages
when they have no active session. Users can now navigate to game setup
pages to start new sessions.
- Removed redirect logic from onNoActiveSession callback
- Updated canModifyPlayers to allow modification whenever no active session
- Only redirect when user has active session for DIFFERENT game
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Prevents users from changing isActive status of players while they have
an active arcade session in progress. Returns 403 error with game info
when blocked.
- Added arcade session check in PATCH /api/players/[id] endpoint
- Enhanced error handling to surface server validation errors to users
- Added comprehensive E2E tests for validation behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add game components (GameCard, GamePhase, SetupPhase, MemoryGrid)
- Add player status bar with multiplayer support
- Add emoji picker for player customization
- Add card generation and validation utilities
- Add game scoring system with combo multipliers
- Add page route for arcade matching game
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use flexbox with space-between for non-scrollable layout
- Reduce all spacing and font sizes for compactness
- Remove performance analysis section
- Add responsive breakpoints for mobile/desktop
- Ensures Play Again button is always visible
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add onExitSession prop to PageWithNav and GameContextNav
- Display Setup button (⚙️) in nav bar during games
- Call exitSession() and reload page to return to setup
- Provides consistent exit UI across all arcade games
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add exitSession method to context type definition
- Enables arcade session cleanup and return to setup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add MatchingClearMismatchMove type to arcade validation types
- Implement CLEAR_MISMATCH validation in MatchingGameValidator
- Keep mismatched cards visible briefly (1.5s) so player can see them
- Auto-dismiss mismatch feedback toast after timeout
- Essential for memory gameplay where seeing wrong cards builds memory
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Moved apps/web/auth.ts to apps/web/src/auth.ts
- Updated import in src/lib/viewer.ts to use @/auth alias
- Fixes 'Module not found: Can't resolve @/auth' error
- This error was not caught in dev due to typescript.ignoreBuildErrors
but caused production build failures and 500 errors on /api/auth/* endpoints
The @/* path alias resolves to src/*, so auth.ts must be in src/ directory.
Previously, /api/auth/csrf and other NextAuth endpoints returned 500 errors.
- Add python3, py3-setuptools, make, g++ to Alpine base image
- Required for better-sqlite3 native module compilation in Docker build
- Fixes 'ModuleNotFoundError: No module named distutils' error
Security improvements:
- Add comprehensive e2e tests for userId injection attacks
- Explicitly strip userId from abacus-settings PATCH request body
- Add security comments to player update routes
- Tests verify foreign key and unique constraints prevent attacks
- Document that API layer security is critical (DB constraints insufficient)
Test coverage:
- 12 tests for abacus-settings API (including 3 security tests)
- 11 tests for players API (including 3 security tests)
- All 23 tests passing
Key findings documented in tests:
- Database foreign keys prevent invalid userId references
- Primary key constraints prevent duplicate userIds (abacus_settings)
- For players, userId CAN be changed to another valid userId at DB level
- API layer MUST filter userId from request body and use session-derived userId
- WHERE clauses scope all queries to current user's data
Defense in depth:
1. Session-derived userId (JWT cookie)
2. Explicit userId filtering from request body
3. WHERE clauses limiting scope to user's own data
4. Foreign key constraints (fallback)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add abacus_settings table with all display configuration fields
- Create API routes (GET/PATCH) for abacus settings
- Add React Query hooks with optimistic updates
- Create AbacusSettingsSync component to bridge localStorage and API
- Settings now persist server-side per guest/user session
- Maintains backward compatibility with existing localStorage pattern
Migration includes:
- Database schema for 12 abacus display settings
- Automatic migration generation and application
- API-driven persistence with guest session support
- Sync component loads from API on mount and saves changes automatically
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 4 & 5: Cleanup + Optimistic Updates
- Removed dead localStorage types from player.ts
- Added optimistic updates to all player mutations
- Added optimistic updates to stats mutations
- Instant UI feedback with automatic rollback on error
Breaking changes: None
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 3: React Query Integration
- Created useUserPlayers hook with CRUD mutations
- Created useUserStats hook with update mutations
- Rewrote GameModeContext to use API instead of localStorage
- Rewrote UserProfileContext to use API instead of localStorage
- Removed playerMigration.ts (localStorage utilities)
- Maintained backward-compatible interfaces
Technical details:
- All data now persists to SQLite via API
- React Query handles caching, invalidation, and optimistic updates
- Contexts still provide same interface for existing components
- No localStorage dependencies remaining (except sound settings)
Breaking changes:
- None - interfaces remain compatible
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 2.2: API Routes
- POST /api/players - Create player
- GET /api/players - List user's players
- PATCH /api/players/[id] - Update player
- DELETE /api/players/[id] - Delete player
- GET /api/user-stats - Get user statistics
- PATCH /api/user-stats - Update user statistics
Technical details:
- Middleware passes guest ID via x-guest-id header for same-request access
- API routes use getViewerId() to identify guest/user sessions
- Automatic user record creation on first API access
- Full test coverage (16 tests passing)
- Manual API testing verified with curl
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive plan for migrating from localStorage to server-side
database with NextAuth guest sessions.
Key features:
- SQLite + Drizzle ORM with type-safe schema definitions
- NextAuth v5 with JWT strategy for stateless guest sessions
- React Query for client-side data fetching and caching
- Comprehensive testing strategy (unit, e2e, manual)
- Fast-failure approach with no backwards compatibility
- Detailed Drizzle migration setup and workflow
- 5 phases with 10 checkpoints, each with specific tests
Strategy: greenfield approach with hard cutover at each checkpoint,
no localStorage fallbacks, no gradual migration.
- Install @tanstack/react-query
- Create QueryClientProvider in ClientProviders with stable client instance
- Add queryClient.ts with createQueryClient() and api() helper
- Add api() helper that wraps fetch with automatic /api prefix
- Add example.ts with complete CRUD hook examples
- Configure sensible defaults (5min staleTime, retry once)
All API routes are now prefixed with /api automatically via api() helper.
Remove unused multiplayer/player code and abandoned e2e tests.
Add gitignore patterns for playwright reports and database files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Refactor to use server component composition pattern where DeploymentInfoContent (server component) imports build info JSON directly and is rendered as a child of DeploymentInfoModal (client component). Eliminates unnecessary API fetch.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add modal component to view deployment information (version, git commit, build time, etc.) accessible via Cmd/Ctrl+Shift+I keyboard shortcut throughout the app.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add API route to serve deployment information and TypeScript definitions for type safety.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add script to capture deployment metadata (git commit, branch, timestamp, version) at build time and integrate it into the build process.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- GamePhase: Convert Map to array, map numeric IDs to players
- GameCard: Use active players array for emoji lookup
- PlayerStatusBar: Fix Map.filter, fix duplicate className, fix type comparison
- ResultsPhase: Convert to array-based player lookup, add numericId mapping
- MemoryPairsContext: Create compatibility layer for numeric player IDs
- EmojiPicker: Update import path for PLAYER_EMOJIS
- EmojiPicker test: Update import path
Maintains backward compatibility with internal numeric player tracking
while using UUID-based players from GameModeContext
CircularTrack, LinearTrack, SteamTrainJourney:
- Convert Map to array for active player lookup
- Use player.emoji directly instead of profile.player1Emoji
- Remove hardcoded player ID switch statements
- Render player cards from Map instead of hardcoded 1-4
- Support arbitrary number of players
- Use player.color for theming
- Add React import for Fragment usage
- Update all player ID types from number to string
- Remove switch statements for player lookups
- Use Map/Set operations instead of array methods
- Support arbitrary number of players
- PlayerConfigDialog now accepts string IDs
GameModeContext:
- Use Map<string, Player> instead of array
- Use Set<string> for active player tracking
- Add migration support on initialization
- Remove hardcoded 1-4 player limit
UserProfileContext:
- Remove player1-4 fields (moved to GameModeContext)
- Keep only game statistics
- Add stats migration from V1
- Add Player interface with UUID-based id field
- Add PlayerStorageV2 format for new storage schema
- Add migration utilities to convert from V1 (indexed) to V2 (UUID)
- Add validation and rollback support
- Move PLAYER_EMOJIS to shared constants
Adds detailed console logging to capture all state during passenger
boarding and delivery in Steam Sprint mode. When passengers are left
behind, the entire log can be copied and pasted into a new Claude Code
session for immediate debugging.
Debug log includes:
- Train position, speed, momentum, and configuration
- All station positions and details
- Complete passenger states (waiting, boarded, delivered)
- Car positions and occupancy
- Passengers scheduled for delivery in current frame
- Detailed boarding attempt analysis for each waiting passenger
- Distance calculations and eligibility checks per car
- Actual boarding and delivery events
Enable by setting DEBUG_PASSENGER_BOARDING = true in useSteamJourney.ts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The bug occurred when a car reached a station where both:
- A passenger needed to be delivered
- A new passenger was waiting to board
The car appeared occupied during boarding check, then the passenger was delivered,
leaving the new passenger behind.
Fix: Identify passengers to be delivered BEFORE building the occupiedCars map,
and exclude them from the map. This makes cars that are about to become empty
immediately available for new passengers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>