- Add activeProblem computed value that derives from redoState + currentProblemInfo
- Modify useInteractionPhase to accept activeProblem prop and react to key changes
- Remove synchronization effects that caused redo mode bugs
- Simplify SessionProgressIndicator to compute attempt counts from results directly
- Remove plan prop dependency - both student and observer views now use same code path
This eliminates the dual source of truth (redoState vs phase.attempt.problem) that
was causing synchronization bugs when entering/switching/exiting redo mode.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
These files were created but never committed:
- VisionDvrControls.tsx - DVR-style playback controls
- VisionRecordingPlayer.tsx - video player component
- useSessionRecording.ts - recording data fetching hook
- cleanupVisionRecordings.ts - cleanup script
- ObserverVisionFeed.stories.tsx - storybook stories
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The TypeScript schema file was never committed, causing build failures.
The migration (0067) that creates the table was already committed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add visual debug panels that show socket connection state when the
"visual debug" toggle is enabled in the app nav:
- BroadcastDebugPanel: Shows on student practice page with socket
connection status, broadcast state, recording status, and current
problem info
- ObserverDebugPanel: Shows on observer page with connection status,
observed state, vision frame info, DVR buffer info, and error messages
Both panels are fixed at bottom-left, collapsible, and include
JSON copy buttons for easy bug reporting.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When socket.io-client reuses an existing Manager that's already connected,
the 'connect' event doesn't fire for new Socket instances. This caused
session broadcasting and observation to silently fail until the old
connection timed out (~45 seconds).
Fix: After attaching the 'connect' handler, check if socket.connected is
already true and manually trigger the connect logic.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add epoch/attempt tracking to vision_problem_videos schema (epochNumber, attemptNumber, isRetry, isManualRedo)
- Update VisionRecorder filename pattern to include epoch/attempt: problem_NNN_eX_aY.mp4
- Fix getRetryContext to use correct slot indices during manual redos
- Add answer-submitted marker for redo problems via handleRecordRedo wrapper
- Create attempt-tracking.ts utility for video attempt grouping and labeling
- Add attempt selector dropdown to ProblemVideoPlayer when multiple attempts exist
- Update API routes to accept epoch/attempt query params for video and metadata
- Add comprehensive documentation for vision recording system (README.md)
Multiple attempts at the same problem (from epoch retry rounds or manual redo clicks)
now save separate recordings instead of overwriting each other.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add effect to emit problem-shown marker when recording starts, ensuring
the current problem gets a video entry even if camera was enabled late
- Improve DockedVisionFeed UX: show session ID, action buttons immediately
for remote camera connections (not just after 15s timeout)
- Add .prettierignore to prevent prettier from corrupting Panda CSS
generated styled-system directory
- Document styled-system fix procedure in CLAUDE.md
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add server-side per-problem video recording that allows teachers to click
on completed problems in the observer progress indicator to watch recorded
videos of each problem attempt.
Key changes:
- New vision_problem_videos schema and migration for per-problem videos
- VisionRecorder refactored for per-problem frame management
- Socket server triggers ffmpeg encoding on problem transitions
- API routes for streaming videos and listing available recordings
- SessionObserverModal enables browse mode on progress indicator
- ProblemVideoPlayer component for viewing past problem recordings
Each problem gets its own MP4 video encoded incrementally as it completes,
making playback immediately available for recently completed problems.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add 'problem-shown' marker emission when new problems appear in practice
- Track currentProblemStartMs in VisionRecorder for each session
- Update vision-buffer-info socket event to include problem context
- Constrain DVR scrubber to current problem range only
- Auto-reset to live view when student moves to next problem
- Move DVR controls below video for better usability
- Fix QR code being cut off by removing overflow:hidden from containers
- Add QueryClientProvider to Storybook for React Query components
- Add comprehensive stories for ObserverVisionFeed with DVR states
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive skill metrics to the scoreboard that allow comparing
students across a classroom with "fun ways to compare across ability levels":
Skills Progress Section (individual view):
- Overall mastery % (weighted by BKT confidence)
- Category breakdown (basic, fiveComplements, tenComplements, etc.)
- Speed metric (normalized seconds per term) with trend arrows
- Accuracy with trend indicators
- Weekly/streak/total problem counts
Classroom Achievements Section (fair comparisons):
- Practice Warriors: most problems this week
- Streak Masters: longest practice streak
- Rising Stars: highest improvement rate (pKnown delta/week)
- Speed Champions: fastest per category (only mastered students compete)
Also includes:
- Remote camera session validation to prevent stale sessions
- Comprehensive Storybook stories for all skill metrics components
New files:
- src/lib/curriculum/skill-metrics.ts (core computation)
- src/app/api/curriculum/[playerId]/skills/metrics/route.ts
- src/app/api/classroom/[classroomId]/skills/leaderboard/route.ts
- src/hooks/useSkillMetrics.ts (React Query hooks)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The barrel export from @/lib/classroom pulls in all manager modules,
some of which have transitive dependencies on React components with
styled-system imports. Import directly from the specific manager files
(classroom-manager.ts and enrollment-manager.ts) to avoid the
dependency chain that breaks esbuild bundling for the seed script.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The scoreboard was incorrectly using the teacher's classroom instead of
the student's enrolled classroom. Now uses useEnrolledClassrooms hook
to get the student's classroom membership for the leaderboard display.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create classroom for current user if they don't have one
- Enroll all seeded test students in the teacher's classroom
- Enables testing of classroom leaderboard features
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Dates serialized through JSON API come back as ISO strings, not Date objects.
Updated formatRelativeTime to handle string | number | Date inputs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update scanner settings schema
- Update remote camera hooks
- Update socket server and vision session API
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add gameHistory configs to all 21 test student profiles
- Implement generateGameResults function to create scoreboard data
- Each profile has appropriate game history matching their characteristics:
- Struggling students get low scores (35-45)
- Developing students get medium scores (55-75)
- Strong students get high scores (78-95)
- Add accuracyMultiplier to TuningAdjustment interface (fixes TS error)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
gray.850 is not a valid Panda CSS token, causing the value to be
output as a literal string instead of resolving to a hex color.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add complete game results tracking and scoreboard system:
- Database schema: Add game_results table for storing game outcomes
- API endpoints: Add routes for saving results, player history, and
classroom leaderboards
- React Query hooks: Add usePlayerGameHistory, usePlayerClassroomRank,
useSaveGameResult
- ScoreboardTab: New dashboard tab showing personal bests, recent games,
and classroom rankings
- GameBreakResultsScreen: Interstitial showing results after game breaks
- Integration: Save game results when matching game completes, display
results before returning to practice
Includes Storybook stories and unit tests for both components.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add AttemptHistoryPanel component for browse mode that displays all
attempts (original + retries) for each problem slot. Teachers/parents
can now:
- Mark incorrect attempts as correct (for typo fixes)
- Exclude attempts from progress tracking
- Re-include previously excluded attempts
Technical changes:
- New ResultWithGlobalIndex type for tracking result indices
- Extended SlotResultSource with 'teacher-corrected' and 'teacher-excluded'
- New PATCH API endpoint for editing individual results
- updateSessionPlanResults() function in session-planner
- Query invalidation on result edits
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When abacus is docked, handleSubmit was using indexOf() to check if the
user's answer matched a prefix sum. This failed when an intermediate
prefix sum equaled the final answer (e.g., 65-34-15+33-18=31 where
prefixSums=[65,31,16,49,31]). indexOf(31) returns 1, triggering help
mode instead of accepting the correct answer.
Now uses findMatchedPrefixIndex() which correctly checks if the answer
equals the final answer FIRST before looking for intermediate matches.
Adds regression tests for this edge case.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1 - Foundation:
- Extended PracticeBreakConfig type with suggestedConfig, lockedFields, difficultyPresets
- Added PracticeBreakOptions and PracticeBreakGameConfig types
- Extended GameBreakSettings with gameConfig field
Phase 2 - Game Integration:
- Added practiceBreakConfig to matching, memory-quiz, card-sorting manifests
- Implemented getInitialStateForPracticeBreak in game validators
- Set practiceBreakReady: false until full integration complete
Phase 3 - Teacher Configuration UI:
- Added GameBreakDifficultyPresets component (Easy/Medium/Hard presets)
- Added GameBreakCustomConfig component (per-field customization)
- Extended StartPracticeModalContext with game config state
- Integrated presets and customize into GameBreakSettings
Test Coverage:
- 23 context tests for game config state
- 8 component tests for GameBreakDifficultyPresets
- 12 component tests for GameBreakCustomConfig
- 7 Storybook stories for manual testing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The boundary detector saves images as either PNG or JPG depending on the
camera source format, but the sync system only looked for PNG files. This
caused 3000+ JPG boundary frames on production to be invisible to sync.
Changes:
- SSH find pattern now includes *.png and *.jpg for boundary-detector
- countLocalData counts both PNG and JPG for boundary-detector
- listLocalFiles lists both extensions for boundary-detector
- rsync progress regex matches both extensions
Column classifier remains PNG-only as intended.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add practiceBreakReady: false to all game manifests that were missing it.
This field is required by the GameManifest type after it was added for
practice session game break support.
Affected games:
- card-sorting
- complement-race
- know-your-world
- memory-quiz
- rithmomachia
- yjs-demo
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add GameLayoutContext to control StandardGameLayout behavior
- Container mode uses 100% height (no viewport sizing) for practice breaks
- SetupPhase detects compact mode and hides verbose descriptions/hints
- Add data attributes to all SetupPhase elements for debugging
- Add Storybook stories for matching game in practice context
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add fill indicator track that animates to selected position
- Use dramatic typography progression (font-size and weight increase)
- Position track at bottom as baseline, not through text
- Export GameInfo type from context for consistent typing
- Combine label and toggle into single clickable element
- Add gaming-inspired gradient backgrounds and glow effects
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add unit tests for single-game scenario (hasSingleGame, singleGame, auto-selection)
- Add GameBreakSettings tests for single-game UI mode
- Add initialExpanded and practiceApprovedGamesOverride props for Storybook
- Update stories to properly demonstrate both single-game and multi-game UI
- Increase expanded panel maxHeight from 520px to 620px for multi-game UI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace hacky .replace(' Battle', '').replace(' Lightning', '') calls
with a proper shortName field that games can define for compact UI spaces.
- Add optional shortName to GameManifestSchema
- Update matching game with shortName: 'Matching Pairs'
- Use shortName || displayName in GameBreakSettings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Game Break Progress Indicators:
- Add game break countdown badge to practice nav (shows "3 until 🎮")
- Display specific game icon when a game is selected (not random)
- Show game icon and name in game break HUD header
Practice-Approved Games:
- Add practiceBreakReady flag to game manifest schema
- Games must opt-in via manifest AND be whitelisted
- Currently only Matching Pairs is practice-break ready
StartPracticeModal Refactoring:
- Extract state management to StartPracticeModalContext
- Create sub-components: DurationSelector, PracticeModesSelector,
GameBreakSettings, MaxTermsSelector, TutorialCTA, RemediationCTA,
StartButton, ErrorDisplay, SessionConfigSummary
- Add comprehensive unit tests for context and sub-components
Single-Game Mode UI:
- Simplified GameBreakSettings when only one game available
- Shows game icon + name inline with compact duration selector
- Removes unnecessary selection mode toggle and dropdown
- Adds "More games coming soon!" teaser
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add torch/flashlight toggle (when available)
- Add camera flip button (when multiple cameras)
- Add lighting preset selector with auto-detection based on frame analysis
- Add experimental finger occlusion mode for better edge detection
- Persist scanner settings per-user in database
- Add responsive ScannerControlsDrawer with compact mobile layout
- Hide sublabels on small screens, show on tablets+
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Auto-generated fresh SVG examples and unified gallery from latest templates.
Includes comprehensive crop mark demonstrations with before/after comparisons.
Files updated:
- packages/templates/gallery-unified.html
🤖 Generated with GitHub Actions
Co-Authored-By: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Add NavSyncIndicator component for compact sync status in nav bar
- Shows sync availability, new items count, syncing progress
- Dropdown with sync actions and history
- States: loading, unavailable, in-sync, new-available, syncing, error
- Add useSyncStatus hook for managing sync state
- Fetches sync status from API
- Handles sync execution with SSE progress
- Tracks sync history
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add multi-selection support with click to toggle, shift+click for range
- Add bulk delete for selected items (both model types)
- Add bulk reclassify for column-classifier (move images to different digit)
- Show bulk selection panel when 2+ items selected with action buttons
- Single click now also opens detail panel for the clicked item
- Add selection indicators on grid items (checkmark badge)
- Add hover actions on grid items (view details, delete buttons)
- Update grid items to show focused vs selected states with different colors
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix sync status fetch URL from non-existent /api/vision-training/sync/status
to the correct /api/vision-training/sync endpoint
- Add modelType query parameter to both GET (status) and POST (sync) requests
- Enable sync feature for both boundary-detector and column-classifier
(was previously restricted to column-classifier only)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create unified data panel architecture replacing separate BoundaryDataPanel and ColumnClassifierDataPanel
- Add MiniBoundaryTester with SVG overlay showing ground truth vs predicted corners
- Add MiniColumnTester with prediction badge overlay showing digit and correct/wrong status
- Support preloading images in full test page via ?image= query param
- Fix overflow on detail panel to allow scrolling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Track all training data sync operations with detailed metrics:
- Add vision_training_sync_history table with SQLite schema
- Record sync start/end times, file counts, tombstone stats
- Prune tombstone entries for files that no longer exist on remote
- Show sync history indicator in ColumnClassifierDataPanel
New files:
- vision-training-sync-history.ts - DB schema for sync history
- sync/history/route.ts - API to query sync history
- SyncHistoryIndicator.tsx - UI component showing recent syncs
The tombstone pruning ensures local tombstone files don't grow unbounded -
after each sync, entries for files deleted on remote are removed since
there's no longer a risk of re-syncing them.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All training data deletions now go through shared utility functions that
ensure deletions are recorded to tombstone files (preventing re-sync from
production).
- Add trainingDataDeletion.ts with deleteColumnClassifierSample() and
deleteBoundaryDetectorSample() functions
- Update bulk delete endpoint to use shared utility (was missing tombstone)
- Update single-file delete endpoint to use shared utility
- Update boundary-samples delete to use shared utility (was missing tombstone)
- Update sync route to use shared readTombstone() function
- Add comprehensive unit tests (17 tests covering validation, deletion,
tombstone recording, and edge cases)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Image route now tries both .png and .jpg extensions (passive captures are JPEG)
- Session detail page guards against undefined result/config values
- Prevents "Cannot read properties of undefined" errors
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Major changes:
- Add passive boundary capture during practice sessions via DockedVisionFeed
- Create BoundaryFrameFilters component with capture type, session, player,
and time range filtering using TimelineRangeSelector
- Add sessionId/playerId metadata to boundary frame annotations
- Update boundary-samples API to store and return session/player context
- Improve boundary detector training pipeline with marker masking
- Add preprocessing pipeline preview in training data browser
- Update model sharding (2 shards instead of 1)
Files added:
- BoundaryFrameFilters.tsx - Filter UI component
- usePassiveBoundaryCapture.ts - Hook for passive training data collection
- saveBoundarySample.ts - Shared utility for saving boundary samples
- preview-augmentation/route.ts - API for preprocessing pipeline preview
- preview-masked/route.ts - API for marker masking preview
- marker_masking.py, pipeline_preview.py - Python training utilities
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement shared nav bar and path-based model selection for vision training pages.
Changes:
- Add useModelType() hook to get model type from URL path
- Add VisionTrainingNav component with model selector dropdown and tab links
- Add [model]/layout.tsx with fixed nav and --nav-height CSS variable
- Move pages under [model]/ directory structure:
- /vision-training/[model] - Data hub
- /vision-training/[model]/train - Training wizard
- /vision-training/[model]/test - Model tester
- /vision-training/[model]/sessions - Sessions list
- /vision-training/[model]/sessions/[id] - Session detail
- Remove Model Card from training wizard (model selection now via URL/nav)
- Update TrainingWizard to use modelType from URL (always defined, not null)
- Remove localStorage restoration of model type
- Add registry.tsx for model metadata
URL is now single source of truth for model type. Browser history and
deep linking work correctly.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>