- Add beadDetector.ts with intensity-profile-based bead detection (CV approach)
- Integrate CV pipeline for both local camera and remote phone camera feeds
- Add processImageFrame() to frameProcessor for remote camera image processing
- Fix React 18 Strict Mode duplicate session creation in RemoteCameraQRCode
- Add debug logging to remote camera hooks for connection troubleshooting
- Add VisionStatusIndicator for remote camera feed in AbacusVisionBridge
The duplicate session bug was caused by React 18 Strict Mode double-mounting
components and running effects twice with fresh state, which called
createSession() twice and created two different sessions - phone joined
one, desktop subscribed to the other.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Physical Abacus Columns Setting:
- Add physicalAbacusColumns to AbacusDisplayConfig (default: 4)
- Add database column with migration 0054
- Add slider UI in AbacusDisplayDropdown (range 1-21)
- Update AbacusVisionBridge to use setting instead of calculating from problem
Remote Camera Flash Toggle Fix:
- Add socket events for torch sync (set-torch, torch-state)
- Phone reports torch state to desktop on change/connection
- Desktop can control phone's torch remotely
- Add torch button in AbacusVisionBridge for phone camera mode
- Both local and remote flash toggles now work correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add PhotoViewerEditor component for unified viewing/editing of session photos
- Persist rotation (0/90/180/270) in database when saving edited photos
- Always show cropping UI even when no quad detected (with fallback corners)
- Add "Auto-detect edges" button to re-run document detection
- Button shows detection status: "Edges detected" / "Auto-detect edges" / "No edges found"
- Fix: clicking Done/Back in edit mode closes viewer when opened via pencil icon
- Fix: pressing Escape in edit mode closes viewer when opened via pencil icon
- Show loading screen instead of view mode flash when opening editor
- Add original file preservation for re-editing photos
- Add corners column to practice_attachments for crop coordinates
- Add rotation column to practice_attachments schema
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds ability to upload, view, and manage photos for practice sessions:
- New database schema for practice attachments with file metadata
- Photo upload zone component with drag & drop and camera capture
- Offline session modal for logging practice done away from the app
- Session photo gallery with lightbox viewer and upload capability
- Photo indicators on session list cards
- Inline photo thumbnails on session summary page
- APIs for creating offline sessions, uploading photos, and serving files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When students get problems wrong, they are now re-queued to retry:
- Problems retry in epochs (max 3 attempts per problem)
- Mastery weight decays: 100% → 50% → 25% → 0%
- Transition screen shows encouraging message between epochs
- Progress indicator shows retry attempt badges on slots
- BKT calculations respect mastery weight from retries
Also fixes entry prompts UNIQUE constraint error by marking
expired prompts before inserting new ones.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Teachers can now send entry prompts to parents requesting their child
enter the classroom. Features include:
- Entry prompts API with create, list, and respond endpoints
- Real-time notifications via WebSocket to parents
- Parent can accept (enters child) or decline prompts
- Configurable expiry time per classroom (default 30 min)
- Classroom name editing in settings popover
- Active sessions API returns sessions for all enrolled students
- E2E and unit tests for the complete feature
Also fixes bug where expired prompts could block creating new prompts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add time-limited shareable links that allow anyone to observe a student's
practice session without logging in. Links expire after 1 hour or 24 hours.
Key features:
- Parents can generate share links from the observation modal/page
- Teachers cannot create share links (API enforces parent-only)
- Shared observers are view-only (no pause/resume, no abacus control)
- Links are automatically invalidated when sessions complete
- QR code and copy buttons for easy sharing
- View count and expiration tracking for active shares
New files:
- Session observation shares DB schema and migration
- Token generation utilities (10-char base62, ~59 bits entropy)
- Share CRUD API routes
- Public observation page for token-based access
- SessionShareButton component with popover UI
Modified:
- Socket server to accept token-based observer auth
- useSessionObserver hook to support shareToken param
- Session planner to revoke shares on session end
- Observer modal/page to show share button (parents only)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix parent authorization for session observation (use userId not viewerId)
- Move SessionObserverModal from NotesModal to parent components to fix z-index
- Add session observation support to student dashboard
- Add PracticeErrorBoundary to dashboard for tighter error handling
- Add RelationshipBadge, RelationshipCard, RelationshipIndicator components
- Add stakeholders API endpoint and useStudentStakeholders hook
- Integrate relationship info into PracticeSubNav and StudentSelector
- Add hover cards for relationship details on student tiles
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract all student action logic into a single useStudentActions hook
used by both StudentActionMenu (on tiles) and NotesModal (in quicklook).
This ensures consistent action availability across the UI.
Key changes:
- Create useStudentActions hook with actions, handlers, and modal state
- Create StudentActionMenu as thin component using the hook
- Refactor NotesModal to use same hook (removes duplicated logic)
- Add studentActions.ts for shared types and getAvailableActions
- Clean up actionContext prop drilling from StudentSelector/PracticeClient
- Add useUnifiedStudents hook for consistent student data across views
- Add ViewSelector component for grid/list toggle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The is_paused column was missing from migration 0043, causing a production
error: "SqliteError: no such column 'is_paused'"
The other pause columns (paused_at, paused_by, paused_reason) were added
manually during development but is_paused was overlooked.
This commit:
- Reverts 0043 to a no-op (those columns already exist)
- Adds 0044 to add only the missing is_paused column
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The classroom system migration files (0041_classroom-system.sql and
0042_classroom-system-indexes.sql) were created manually without using
drizzle-kit generate, so they weren't registered in _journal.json.
This caused drizzle to skip these migrations, resulting in:
- No parent_child table
- No family_code column on players
- No classrooms/enrollments tables
Fixes production error on /practice page.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Complete removal of per-skill accuracy tracking from the practice system.
BKT-based mastery classification (weak/developing/strong) now serves as
the sole measure of skill proficiency.
Changes:
- Remove accuracy field from skill stats and related components
- Remove accuracyMultiplier from tuning adjustments
- Simplify session summary to use BKT classification only
- Update DashboardClient to remove accuracy-based displays
- Clean up unused accuracy utility functions and theme colors
- Update journey simulator to remove accuracy references
This aligns with the design principle that BKT pKnown is a more reliable
indicator of true mastery than raw accuracy percentages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The codebase previously used "help level" terminology (HelpLevel type,
helpLevelUsed field, helpLevelWeight function) which implied a graduated
scale. Since the system now only tracks whether help was used or not,
this renames everything to use proper boolean terminology.
Changes:
- Delete HelpLevel type, use boolean directly
- Rename helpLevelUsed → hadHelp in SlotResult
- Rename lastHelpLevel → lastHadHelp in PlayerSkillMastery schema
- Rename helpLevelWeight() → helpWeight() with boolean parameter
- Update all components, tests, stories, and documentation
- Add database migration for column rename
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit includes accumulated work from the SessionMode system:
- Add SkillUnlockBanner component for celebrating skill mastery
- Improve SessionSummary to show skill unlock celebrations
- Add session detail page at /practice/[studentId]/session/[sessionId]
- Update seedTestStudents script with more realistic test data
- Extend skill-tutorial-config with more skill mappings
- Improve BKT compute with better uncertainty handling
- Update progress-manager with skill completion tracking
- Remove legacy sessions API routes (replaced by session-plans)
- Add migration 0037 for practice_sessions schema cleanup
- Add plan documents for celebration wind-down and SessionMode
- Update gitignore to exclude db backups
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Student Notes Feature:
- Add notes column to players table with migration
- Create NotesModal component with zoom animation from student tile
- Add notes button on each student card in StudentSelector
- Support viewing and editing notes directly in modal
- Fix modal reopening bug with pointerEvents during animation
- Fix spring animation to start from clicked tile position
BKT & Curriculum Improvements:
- Add configurable BKT thresholds via admin settings
- Add skill anomaly detection API endpoint
- Add next-skill recommendation API endpoint
- Add problem history API endpoint
- Improve skills page with BKT classifications display
- Add skill tutorial integration infrastructure
Dashboard & Session Improvements:
- Enhanced dashboard with notes tab
- Improved session summary display
- Add StartPracticeModal stories
Test Infrastructure:
- Add seedTestStudents.ts script for BKT manual testing
- Add generateTrajectoryData.ts for simulation data
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adding logging at:
1. fluency-thresholds.ts module load time (before and after exports)
2. progress-manager.ts import time (to see what was imported)
This will help identify if:
- The module isn't loading at all
- The config object is partially defined
- There's a circular dependency timing issue
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete Phase 9 of isPracticing migration by removing all traces of the
deprecated 3-state masteryLevel system:
Schema changes:
- Remove masteryLevel column from player_skill_mastery table
- Remove MasteryLevel type export
- Remove MASTERY_CONFIG constant
- Remove calculateMasteryLevel function
Code cleanup:
- Remove masteryLevel from all insert/update operations in progress-manager
- Remove getSkillsByMasteryLevel function and export
- Remove masteryLevel from SkillPerformance interface
- Remove masteryLevel from SkillMasteryData interface in usePlayerCurriculum
- Remove deprecated dbMasteryToState and buildStudentSkillHistory functions
- Remove deprecated tests for removed functions
The system now uses:
- isPracticing: boolean - Set by teacher via checkbox
- FluencyState - Computed from practice history (practicing/effortless/fluent/rusty)
- MasteryState - For cost calculation (adds not_practicing for non-practicing skills)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major refactor of skill mastery tracking system:
**Schema Changes:**
- Add `isPracticing` boolean column to player_skill_mastery
- Fluency state (effortless/fluent/rusty/practicing) now computed from
practice history instead of stored
- Keep masteryLevel for backwards compatibility (to be removed later)
**Session Planning:**
- Update session-planner to use new isPracticing model
- Add part-type-specific challenge ratios (abacus: 25%, visualization: 15%, linear: 20%)
- Skip challenge slots for students with only basic skills (cost 0)
- Add NoSkillsEnabledError for clear feedback when no skills enabled
**Complexity Budget System:**
- First term exempt from min budget (basic skills always have cost 0)
- Update ProblemDebugPanel to show min/max budgets and term costs
- Create centralized config directory for tuning parameters
**UI Improvements:**
- ManualSkillSelector now uses isPracticing checkboxes
- StartPracticeModal shows specific error for "no skills enabled"
- Better error messages throughout
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix response time bug: exclude pause duration from calculations
- Add global per-kid stats tracking with new DB columns
- Create SkillPerformanceReports component for dashboard
- Add PracticeTimingDisplay with live problem timer and speed meter
- Extract SpeedMeter to shared component
- Add defensive handling for empty JSON in abacus-settings API
New features:
- Live timer showing elapsed time on current problem
- Speed visualization bar showing position vs average
- Per-part-type timing breakdown (abacus/visualize/linear)
- Skill performance analysis on dashboard (fast/slow/weak skills)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add skill complexity budget system with base costs per skill type:
- Basic skills: 0 (trivial bead movements)
- Five complements: 1 (single mental substitution)
- Ten complements: 2 (cross-column operations)
- Cascading operations: 3 (multi-column)
- Add per-term complexity debug overlay in VerticalProblem (toggle via visual debug mode)
- Shows total cost per term and individual skill costs
- Highlights over-budget terms in red
- Make session structure parts toggleable in configure page:
- Can enable/disable abacus, visualization, and linear parts
- Time estimates, problem counts adjust dynamically
- At least one part must remain enabled
- Fix max terms per problem not being respected:
- generateSingleProblem was hardcoding 3-5 terms
- Now properly uses minTerms/maxTerms from constraints
- Set visualization complexity budget to 3 (more restrictive)
- Hide complexity badges for zero-cost (basic) skills in ManualSkillSelector
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Make ProgressDashboard session-aware with single primary CTA
- No session: "Start Practice →" (blue)
- Active session: "Resume Practice →" (green) with progress count
- Single "Start over" link replaces redundant Abandon/Regenerate buttons
- Add skill mismatch warning inline in level card
- Add masteredSkillIds to session_plans for mismatch detection
- Fix getActiveSessionPlan to check completedAt IS NULL (fixes loop bug)
- Remove separate Active Session Card from dashboard (now integrated)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 1 of help system implementation:
Schema changes:
- Add HelpLevel type (0-3) to session-plans.ts
- Extend SlotResult with helpLevelUsed, incorrectAttempts, helpTrigger
- Add REINFORCEMENT_CONFIG constants for mastery credit multipliers
- Add reinforcement tracking columns to player_skill_mastery:
- needsReinforcement: flag for skills needing extra practice
- lastHelpLevel: track struggling patterns
- reinforcementStreak: track progress toward clearing reinforcement
- Add StudentHelpSettings interface and column to players:
- helpMode: 'auto' | 'manual' | 'teacher-approved'
- autoEscalationTimingMs: configurable help timing thresholds
- beginnerFreeHelp: unlimited L1-L2 help without penalty
- advancedRequiresApproval: require teacher auth for L2+ help
This closes the feedback loop between help usage and session planning:
- Help usage informs skill mastery scoring
- Reinforcement flags guide session planner to include extra practice
- Teacher has visibility into which skills need attention
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement complete daily practice session system with:
**Practice Components:**
- StudentSelector: Select which student is practicing
- ProgressDashboard: Show student's current level and progress
- PlanReview: Review and approve generated session plan
- ActiveSession: Main practice UI with three-part structure
- SessionSummary: Show results after session completion
- NumericKeypad: Touch-friendly number input for mobile
- VerticalProblem: Columnar problem display
**Session Structure:**
- Part 1 (Abacus): Physical abacus practice, vertical format
- Part 2 (Visualization): Mental math visualizing beads
- Part 3 (Linear): Mental math with sentence format
**Infrastructure:**
- Database schemas for curriculum, skills, sessions
- Session planner with skill-based problem generation
- React Query hooks for session management
- Consolidated device capability detection hooks
- API routes for curriculum and session management
**Problem Generation:**
- ActiveSession now uses actual skill-based algorithm
- Problems generated with appropriate skills constraints
- Storybook stories use real problem generation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement comprehensive skill customization system allowing teachers to:
- Configure existing default skills with custom difficulty settings
- Create entirely new custom skills from scratch
- Visualize skills in mastery progression context with directional edges
- Interact with difficulty space using 2D plot with hover tooltips
Database Schema:
- custom_skills table: Stores user-created skills
- skill_customizations table: Stores modifications to default skills
- Both tables track regrouping config, display rules, and metadata
API Endpoints:
- POST /api/worksheets/skills/custom - Create custom skill
- GET /api/worksheets/skills/custom - List custom skills
- PUT /api/worksheets/skills/custom/[id] - Update custom skill
- DELETE /api/worksheets/skills/custom/[id] - Delete custom skill
- POST /api/worksheets/skills/[skillId]/customize - Save customization
- GET /api/worksheets/skills/customizations - List customizations
Components:
- DifficultyPlot2D: Interactive 2D visualization of difficulty space
- Regrouping Intensity (x-axis) × Scaffolding Level (y-axis)
- Dual mode: Default presets vs Mastery progression skills
- Directional edges showing skill progression sequence
- Hover tooltips with skill details
- Click to select configuration
- Visual legend explaining elements
- SkillConfigurationModal: Modal for skill configuration
- Name and description fields
- Digit range slider
- 2D difficulty plot integration
- Shows mastery progression context when editing
- Real-time configuration summary
- MasteryModePanel Integration:
- "Configure Skill" button for existing skills
- "Create Custom Skill" button for new skills
- Passes mastery progression to modal for context
Visual Design:
- Purple theme (#9333ea) for mastery progression skills
- Green theme (#10b981) for current configuration
- Dashed arrows with triangular arrow heads
- Numbered skill circles with hover tooltips
- Compact legend in top-right corner
Technical Features:
- PlotPoint interface for custom skill plotting
- Conditional snapping to either presets or custom points
- Vector math for arrow head calculations
- Z-ordering: edges before points
- Event propagation control for hover interactions
- Storybook examples for both components
Bug Fixes:
- Fix page indicator stuck on page 1 in WorksheetPreview
- Changed from threshold-based to most-visible-page tracking
- Works correctly for both scroll directions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Database changes:
- Add custom_skills table for user-created skills
- Add skill_customizations table for modified default skills
- Both tables support per-user, per-operator configurations
- Include digit range, regrouping config, and display rules
Schema includes:
- Foreign keys to users table with cascade delete
- Composite primary key for skill_customizations
- Index on (user_id, operator) for efficient queries
This enables:
- Teachers to create custom skills for mastery progression
- Teachers to customize default skill configurations
- Per-user skill configurations (don't affect other users)
- Full reversi bility (reset to defaults)
Next steps: API endpoints, SkillConfigurationModal UI
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes to shared worksheet viewer (/worksheets/shared/[id]):
- Move Download, Share, Edit actions from banner to floating action menu
- Main button shows "Edit" in read-only mode (most common action)
- Download and Share available in dropdown menu
- Simplified banner to show only read-only indicator
- Made banner light/dark mode ready with theme-aware colors
Changes to PreviewCenter component:
- Added onShare and onEdit props for read-only mode
- Main button adapts: "Edit" for read-only, "Download" for edit mode
- Dropdown menu adapts based on mode (Download/Share vs Share/Upload)
- Floating action button now always visible
Changes to WorksheetPreview component:
- Fixed virtualization to show all pages when initialData provided
- Shared worksheets now display all pages immediately (no lazy loading)
- Interactive editor still uses virtualization for performance
- Added debug logging for page visibility and theme changes
Bug fixes:
- Fixed page virtualization preventing multiple pages from showing
- Made shouldVirtualize persistent across re-renders
- Added comprehensive logging for debugging theme and page issues
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add complete sharing system for worksheet configurations:
Database Schema:
- New worksheet_shares table with short share IDs (7-char base62)
- Stores worksheetType, config JSON, views, creator IP hash, optional title
- Migration 0021 creates the table
Share ID Generation:
- Cryptographically secure base62 IDs (3.5 trillion combinations)
- Collision detection with retry logic (max 5 attempts)
- Validation function for ID format checking
API Endpoints:
- POST /api/worksheets/share - Creates share, returns URL
- GET /api/worksheets/share/[id] - Retrieves config, increments views
- Uses serializeAdditionConfig() for consistent config formatting
Share Modal:
- Auto-generates share link on open (no button needed)
- Displays QR code for mobile sharing (theme-aware colors)
- Copy to clipboard functionality with visual feedback
- Loading states during generation
Dependencies:
- Added qrcode + @types/qrcode for QR code generation
Config Serialization:
- Share uses same serializeAdditionConfig() as database auto-save
- Ensures version field and structure consistency
- Shared configs match database-saved settings exactly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two critical bugs preventing ten-frames from rendering:
1. **Mastery mode not handled** (typstGenerator.ts:61)
- Code only checked for 'smart' | 'manual' modes
- Mastery mode fell into manual path, tried to use boolean flags that don't exist
- Resulted in all display options being `undefined`
- Fix: Check for both 'smart' OR 'mastery' modes (both use displayRules)
2. **Typst array membership syntax** (already fixed in previous commit)
- Used `(i in array)` which doesn't work in Typst
- Changed to `array.contains(i)`
Added comprehensive unit tests (tenFrames.test.ts):
- Problem analysis tests (regrouping detection)
- Display rule evaluation tests
- Full Typst template generation tests
- Mastery mode specific tests
- All 14 tests now passing
Added debug logging to trace display rules resolution:
- displayRules.ts: Shows rule evaluation per problem
- typstGenerator.ts: Shows enriched problems and Typst data
- Helps diagnose future issues
The issue was that mastery mode (which uses displayRules like smart mode)
was being treated as manual mode (which uses boolean flags), resulting in
undefined display options.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Two critical fixes for worksheet grading:
1. **Fix OpenAI Responses API parameters**
- Move `verbosity` from top-level to `text.verbosity`
- API was rejecting requests with 400 error
- Confirmed against GPT-5 Responses API documentation
2. **Surface actual grading errors in UI**
- Add `error_message` column to worksheet_attempts table
- Store actual API/grading errors in database
- Display real error messages instead of generic "too blurry" text
- Users now see OpenAI API errors, validation failures, etc.
Changes:
- Updated gradeWorksheet.ts API call structure
- Created migration 0020 for error_message column
- Updated processAttempt.ts to save error messages
- Updated API route to return errorMessage field
- Updated results page to display actual errors
Now when grading fails, users see helpful error messages like:
"Unsupported parameter: 'verbosity'..." instead of just "too blurry"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement complete worksheet grading system with AI analysis and mastery tracking:
Features:
- GPT-5 vision integration for single-pass grading (OCR + analysis)
- Three upload modes: file upload, desktop camera, QR code for smartphone
- Real-time batch upload workflow via QR code scanning
- Automatic mastery profile updates based on grading results
- AI feedback with error pattern detection and next step suggestions
- Guest user support for anonymous uploads
Technical Implementation:
- New database tables: worksheet_attempts, problem_attempts, worksheet_mastery
- Removed foreign key constraints to support guest users
- Session-based batch uploads for efficient multi-worksheet grading
- Validation and retry logic for robust AI responses
- Browser-compatible UUID generation (Web Crypto API)
Components:
- CameraCapture: Desktop/mobile camera capture with high resolution
- QRCodeDisplay: Real-time upload tracking with 2-second polling
- UploadWorksheetModal: Unified interface with three upload tabs
- AttemptResultsPage: Detailed grading results with AI analysis
API Endpoints:
- POST /api/worksheets/upload: Upload and trigger grading
- GET /api/worksheets/sessions/[sessionId]: Poll batch uploads
- GET /api/worksheets/attempts/[attemptId]: Get grading results
Cost: ~$0.04 per worksheet graded
Documentation:
- AI_MASTERY_ASSESSMENT_PLAN.md: Complete system architecture
- PROMPTING_STRATEGY.md: GPT-5 prompting and validation
- UX_EXECUTIVE_SUMMARY.md: Stakeholder-friendly overview
- UX_UI_PLAN.md: Complete interface design
- IMPLEMENTATION_STATUS.md: Testing checklist
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove FK constraint from worksheet_settings table via migration 0014
- Update schema definition to remove .references() call
- Remove user existence check from POST /api/worksheets/settings
- Guest users can now save worksheet preferences without user account
- Matches pattern used by room_members table for arcade games
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create worksheet_settings table with versioned JSON config storage
- Foreign key to users with cascade delete
- Index on (user_id, worksheet_type) for efficient lookups
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds the nativeAbacusNumbers setting that was being used by Rithmomachia
but was never committed, causing CI build failures.
Changes:
- Add nativeAbacusNumbers boolean field to abacus_settings schema
- Add database migration for the new field (default: false)
- Add UI toggle in AbacusDisplayDropdown
- Update AbacusSettingsSync to exclude nativeAbacusNumbers from
abacus-react sync (it's app-specific, not abacus component config)
This setting allows displaying numbers as abaci throughout the app
where practical, used by arcade games like Rithmomachia.
Fixes TypeScript build errors in:
- src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx
- src/arcade-games/rithmomachia/components/RithmomachiaGame.tsx
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Creates migration 0011 to:
- Create room_game_configs table with proper schema
- Add unique index on (room_id, game_name)
- Migrate existing game_config data from arcade_rooms table
Migration is idempotent and safe to run on any database state:
- Uses IF NOT EXISTS for table and index creation
- Uses INSERT OR IGNORE to avoid duplicate data
- Will work on both fresh databases and existing production
This ensures production will automatically get the new table structure
when the migration runs on deployment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 1: Database and API updates
- Create migration 0010 to make game_name and game_config nullable
- Update arcade_rooms schema to support rooms without games
- Update RoomData interface to make gameName optional
- Update CreateRoomParams to make gameName optional
- Update room creation API to allow null gameName
- Update all room data parsing to handle null gameName
This allows rooms to be created without a game selected, enabling
users to choose a game inside the room itself. The URL remains
/arcade/room regardless of selection, setup, or gameplay state.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create 0009_add_display_password.sql migration
- Add entry to drizzle journal
- This adds the display_password column that was missing in production
The plan is to nuke the production database and let all migrations
run from scratch on container restart.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update placeholder text in room creation forms to show auto-generated format
- Make room.name nullable in database schema (migration 0008)
- Add accessMode field to RoomData interface
- Implement password prompt UI for password-protected rooms via share links
- Add password support to room browser join flow
- Remove autoFocus attribute for accessibility compliance
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive access control system for arcade rooms with 6 modes:
- open: Anyone can join (default)
- locked: Only current members allowed
- retired: Room no longer functions
- password: Requires password to join
- restricted: Only users with pending invitations can join
- approval-only: Requires host approval via join request system
Database Changes:
- Add accessMode field to arcade_rooms (replaces isLocked boolean with enum)
- Add password field to arcade_rooms (hashed with bcrypt)
- Create room_join_requests table for approval-only mode
New API Endpoints:
- PATCH /api/arcade/rooms/:roomId/settings - Update room access mode and password (host only)
- POST /api/arcade/rooms/:roomId/transfer-ownership - Transfer ownership to another member (host only)
- POST /api/arcade/rooms/:roomId/join-request - Request to join approval-only room
- GET /api/arcade/rooms/:roomId/join-requests - Get pending join requests (host only)
- POST /api/arcade/rooms/:roomId/join-requests/:requestId/approve - Approve join request (host only)
- POST /api/arcade/rooms/:roomId/join-requests/:requestId/deny - Deny join request (host only)
Updated Endpoints:
- POST /api/arcade/rooms/:roomId/join - Now validates access modes before allowing join:
* locked: Rejects all joins
* retired: Rejects all joins (410 Gone)
* password: Requires password validation
* restricted: Requires valid pending invitation
* approval-only: Requires approved join request
* open: Allows anyone (existing behavior)
Libraries:
- Add room-join-requests.ts for managing join request lifecycle
- Ownership transfer updates room.createdBy and member.isCreator flags
- Socket.io events for join request notifications and ownership transfers
Migration: 0007_access_modes.sql
Next Steps (UI not included in this commit):
- RoomSettingsModal for configuring access mode and password
- Join request approval UI in ModerationPanel
- Ownership transfer UI in ModerationPanel
- Password input in join flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive database schema to support:
- Room bans: Track banned users with reasons and timestamps
- Room reports: Allow players to report others for misconduct
- Room invitations: Send and track room invitations
- Room member history: Track all users who have ever been in a room
This foundational schema enables the complete moderation system
including banning, kicking, reporting, and invitation features.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement hybrid database + application-level enforcement to ensure users
can only be in one room at a time, with graceful auto-leave behavior and
clear error messaging.
## Changes
### Database Layer
- Add unique index on `room_members.user_id` to enforce one room per user
- Migration includes cleanup of any existing duplicate memberships
- Constraint provides safety net if application logic fails
### Application Layer
- Auto-leave logic: when joining a room, automatically remove user from
all other rooms first
- Return `AutoLeaveResult` with metadata about rooms that were left
- Idempotent rejoining: rejoining the same room just updates status
### API Layer
- Join route returns auto-leave information in response
- Catches and handles constraint violations with 409 Conflict
- User-friendly error messages when conflicts occur
### Frontend
- Room list and detail pages handle ROOM_MEMBERSHIP_CONFLICT errors
- Show alerts when user needs to leave current room
- Refresh room list after conflicts to show current state
### Testing
- 7 integration tests for modal room behavior
- Tests cover: first join, auto-leave, rejoining, multi-user scenarios,
constraint enforcement, and metadata accuracy
- Updated existing unit tests for new return signature
## Technical Details
- `addRoomMember()` now returns `{ member, autoLeaveResult? }`
- Auto-leave happens before new room join, preventing race conditions
- Database unique constraint as ultimate safety net
- Socket events remain status-only (joining goes through API)
## Testing
- ✅ All modal room tests pass (7/7)
- ✅ All room API e2e tests pass (12/12)
- ✅ Format and lint checks pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Run Biome formatter on all files to ensure consistent code style:
- Single quotes for JS/TS
- Double quotes for JSX
- 2-space indentation
- 100 character line width
- Semicolons as needed
- ES5 trailing commas
This is the result of running: npx @biomejs/biome format . --write
🤖 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>
- 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.
- 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>