Commit Graph

50 Commits

Author SHA1 Message Date
Thomas Hallock 005140a1e7 feat(vision): add CV-based bead detection and fix remote camera connection
- 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>
2026-01-01 11:29:02 -06:00
Thomas Hallock b206eb3071 feat(vision): add physical abacus column setting and fix remote flash toggle
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>
2025-12-31 22:33:58 -06:00
Thomas Hallock 957ff71cf1 chore: polish vision and practice components
- CalibrationOverlay: formatting fixes
- useDeskViewCamera: add SSR guard and focusMode setting
- ActiveSession: integrate vision mode changes
- DocumentAdjuster/PhotoViewerEditor: component updates
- SummaryClient: updates for practice summary
- Drizzle meta: formatting consistency

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 19:07:49 -06:00
Thomas Hallock 156a0dfe96 feat(practice): add photo editing with rotation persistence and auto-detect
- 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>
2025-12-31 13:34:19 -06:00
Thomas Hallock 9b853116ec feat(practice): add photo attachments for practice sessions
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>
2025-12-30 20:33:18 -06:00
Thomas Hallock 474c4da05a feat(practice): implement retry wrong problems system
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>
2025-12-29 21:56:48 -06:00
Thomas Hallock de39ab52cc feat(classroom): implement entry prompts system
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>
2025-12-29 16:41:47 -06:00
Thomas Hallock 3ac7b460ec feat(observer): implement shareable session observation links
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>
2025-12-28 17:46:34 -06:00
Thomas Hallock 07484fdfac feat(practice): parent session observation + relationship UI + error boundaries
- 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>
2025-12-28 10:29:17 -06:00
Thomas Hallock d6e369f9dc feat: API authorization audit + teacher enrollment UI + share codes
Security:
- Add authorization checks to curriculum API endpoints (session plans, skills, record-game)
- Add e2e tests for API authorization (positive and negative cases)
- Fix missing player_stats table migration

Classroom:
- Add TeacherEnrollmentSection for teachers to approve parent enrollment requests
- Add share code system with ShareCodePanel component and useShareCode hook
- Add /join/classroom/[code] and /join/family/[code] pages
- Remove dead code: ClassroomDashboard, ClassroomTab, StudentManagerTab

UI:
- Update StudentFilterBar and StudentSelector styling
- Fix PageTransitionOverlay z-index
- Minor chart and banner improvements

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 19:27:09 -06:00
Thomas Hallock 576abcb89e refactor(practice): unify student actions with shared useStudentActions hook
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>
2025-12-26 19:00:36 -06:00
Thomas Hallock 9d8b5e1148 fix(db): add missing is_paused column to session_plans
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>
2025-12-26 12:28:08 -06:00
Thomas Hallock ccea0f86ac feat(classroom): implement teacher-initiated pause and fix manual pause
- Add teacher pause/resume functionality to SessionObserverModal
- Extend PauseInfo interface with 'teacher' reason and teacherMessage
- Handle session-paused/session-resumed WebSocket events in student client
- Update SessionPausedModal UI for teacher-initiated pauses:
  - Show teacher emoji (👩‍🏫) and custom message
  - Disable resume button (only teacher can resume)
- Fix manual pause not showing modal when clicking HUD dropdown
- Add pause columns to session_plans schema (isPaused, pausedAt, pausedBy, pauseReason)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 18:43:15 -06:00
Thomas Hallock 398603c75a fix(db): add missing journal entries for migrations 0041-0042
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>
2025-12-22 13:19:05 -06:00
Thomas Hallock c5a0586094 refactor(practice): remove per-skill accuracy in favor of BKT classification
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>
2025-12-21 09:28:50 -06:00
Thomas Hallock c522620e46 refactor(help): rename helpLevel terminology to hadHelp boolean
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>
2025-12-21 07:04:52 -06:00
Thomas Hallock 538718a814 feat(practice): add student organization with filtering and archiving
Implements comprehensive student organization system for /practice page:

Database:
- Add isArchived column to players table (migration 0039)
- Drop unused skill stat columns from playerSkillMastery (migration 0038)

New features:
- Search bar for filtering by student name or skill
- Skill filter pills with AND logic
- Hierarchical grouping: recency buckets → skill categories
- Archive/unarchive from notes modal
- Edit mode with bulk checkbox selection
- Bulk archive for selected students
- Show/hide archived students toggle

New files:
- src/constants/skillCategories.ts - shared skill category definitions
- src/utils/studentGrouping.ts - grouping/filtering logic
- src/utils/skillSearch.ts - skill search utilities
- src/components/practice/StudentFilterBar.tsx - filter bar component

Updated:
- PracticeClient.tsx - filter state management, grouped display
- StudentSelector.tsx - edit mode, archived badges
- NotesModal.tsx - archive button in footer
- ManualSkillSelector.tsx - uses shared skill categories
- server.ts - getPlayersWithSkillData() for enhanced queries

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 10:37:34 -06:00
Thomas Hallock 4daf7b7433 feat(practice): add SkillUnlockBanner + session summary improvements
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>
2025-12-18 15:16:34 -06:00
Thomas Hallock 2702ec585f feat(practice): add student notes with animated modal + BKT improvements
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>
2025-12-18 06:39:04 -06:00
Thomas Hallock 354ada596d feat(bkt): implement adaptive skill targeting with validated convergence
BKT (Bayesian Knowledge Tracing) integration for intelligent practice:

Architecture:
- Separate cost calculation (fluency-based) from skill targeting (BKT-based)
- Cost controls difficulty via complexity budgets
- BKT identifies weak skills (pKnown < 0.5, confidence >= 0.3) for targeting
- Weak skills added to targetSkills in focus slots

New modules:
- src/lib/curriculum/bkt/ - Core BKT implementation
  - conjunctive-bkt.ts - Multi-skill blame distribution
  - evidence-quality.ts - Help level and response time weighting
  - confidence.ts - Data-based confidence calculation
  - skill-priors.ts - Initial P(known) estimates by skill type
- src/lib/curriculum/config/bkt-integration.ts - Targeting thresholds

Validation (journey simulator):
- Hill function learning model: P(correct) = exposure^n / (K^n + exposure^n)
- Per-skill assessment without learning pollution
- Convergence results: Adaptive reaches 80% mastery faster in 9/9 scenarios
- Adaptive reaches 50% mastery faster in 8/9 scenarios

Key changes:
- session-planner.ts: identifyWeakSkills() and addWeakSkillsToTargets()
- skillComplexity.ts: Always use fluency multiplier for cost (not BKT)
- comprehensive-ab-test.test.ts: Convergence speed comparison tests
- Updated learner profiles with realistic learning rates (K=25-60)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 17:16:18 -06:00
Thomas Hallock aae53aa426 debug: add comprehensive logging to trace REINFORCEMENT_CONFIG import issue
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>
2025-12-13 10:29:11 -06:00
Thomas Hallock 9f07bd6df6 refactor(practice): remove deprecated masteryLevel column
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>
2025-12-12 21:03:20 -06:00
Thomas Hallock b2e7268e7a feat(practice): migrate mastery model to isPracticing + computed fluency
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>
2025-12-12 20:10:46 -06:00
Thomas Hallock 18ce1f41af feat(practice): add response time tracking and live timing display
- 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>
2025-12-11 18:23:03 -06:00
Thomas Hallock 5d61de4bf6 feat(practice): add complexity budget system and toggleable session parts
- 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>
2025-12-10 20:18:20 -06:00
Thomas Hallock c40543ac64 feat(practice): unify dashboard with session-aware progress display
- 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>
2025-12-09 20:59:53 -06:00
Thomas Hallock 41c46038d8 feat(help-system): add schema for progressive help and feedback loop
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>
2025-12-06 15:16:52 -06:00
Thomas Hallock 585543809a feat(practice): add three-part daily practice session system
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>
2025-12-06 12:23:53 -06:00
Thomas Hallock 7fbc743c4c feat: add skill configuration system with interactive 2D difficulty plot
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>
2025-11-11 15:04:28 -06:00
Thomas Hallock 906fa63f24 feat: add database schema for custom skills and skill customizations
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>
2025-11-11 13:30:36 -06:00
Thomas Hallock 1c10a82c78 feat: improve shared worksheet viewer UX and multi-page support
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>
2025-11-11 13:10:34 -06:00
Thomas Hallock 7b4c7c3fb6 feat: add worksheet sharing infrastructure with database persistence
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>
2025-11-11 11:17:44 -06:00
Thomas Hallock b36df3a40c fix(worksheets): ten-frames not rendering in mastery mode
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>
2025-11-10 10:06:27 -06:00
Thomas Hallock 2d33f35c4d fix: correct GPT-5 API parameters and surface actual grading errors
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>
2025-11-10 06:12:10 -06:00
Thomas Hallock 6e9573288f feat: add AI-powered worksheet grading with GPT-5 vision
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>
2025-11-10 04:33:22 -06:00
Thomas Hallock e6e9ec3e4f fix(worksheets): remove foreign key constraint to support guest users
- 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>
2025-11-06 08:26:09 -06:00
Thomas Hallock 8231d24f8c chore(db): add migration for worksheet_settings table
- 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>
2025-11-06 08:26:09 -06:00
Thomas Hallock 79f7347d48 feat(abacus): add nativeAbacusNumbers setting to schema and UI
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>
2025-10-31 20:44:16 -05:00
Thomas Hallock 3bae00b9a9 feat: add drizzle migration for room_game_configs table
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>
2025-10-15 13:40:40 -05:00
Thomas Hallock a9a6cefafc refactor: make game_name nullable to support in-room game selection
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>
2025-10-14 11:30:27 -05:00
Thomas Hallock 040d7495a0 fix(migrations): add migration 0009 for display_password column
- 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>
2025-10-14 09:56:26 -05:00
Thomas Hallock dcbb5072d8 feat: improve room creation UX and add password support for share links
- 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>
2025-10-14 07:09:22 -05:00
Thomas Hallock 6ff21c4f1d feat: add room access modes and ownership transfer
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>
2025-10-13 19:19:49 -05:00
Thomas Hallock 97d16041df feat: add database schema for room moderation and invitations
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>
2025-10-13 11:22:02 -05:00
Thomas Hallock f005fbbb77 feat: implement modal room enforcement (one room per user)
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>
2025-10-08 08:41:39 -05:00
Thomas Hallock 60d70cd2f2 style: apply Biome formatting to entire codebase
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>
2025-10-07 12:48:26 -05:00
Thomas Hallock a9175a050c feat: add arcade room system database schema and managers (Phase 1)
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>
2025-10-06 15:11:24 -05:00
Thomas Hallock 22541df99f fix: use UUID player IDs in session creation fallback
- 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.
2025-10-06 13:04:33 -05:00
Thomas Hallock 92ef1360a4 feat: migrate abacus display settings to database
- 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>
2025-10-05 19:34:33 -05:00
Thomas Hallock 5d5afd4e68 feat: set up Drizzle ORM with SQLite database
Phase 1.1 Complete: Database & Auth Infrastructure

- Configure Drizzle with SQLite and better-sqlite3
- Create schema for users, players, and user_stats tables
- Set up database client with foreign keys and WAL mode enabled
- Add migration runner and package.json scripts
- Generate initial migration (0000_third_carnage.sql)

Database Features:
- Users table with guestId for guest sessions
- Players table with userId FK (cascade delete)
- UserStats table with userId FK (cascade delete)
- Indexes on foreign keys for performance
- Type-safe schema with Drizzle ORM

Testing:
- 20 unit + e2e tests all passing
- Schema validation tests
- Migration idempotency tests
- Foreign key constraint tests
- Cascade delete tests

Scripts added:
- pnpm db:generate - Generate migration from schema
- pnpm db:migrate - Run pending migrations
- pnpm db:push - Push schema directly (dev)
- pnpm db:studio - Visual DB browser
- pnpm db:drop - Drop migration (dev)

User tests verified:
 Migration runs successfully
 Database tables created with correct schema
 Migration is idempotent (can run multiple times)
2025-10-05 17:01:27 -05:00