Commit Graph

3492 Commits

Author SHA1 Message Date
Thomas Hallock adc7d8fa0e Enhance working problem display with full history ledger
Replace the simple breadcrumb display with a proper ledger showing:
- Step numbers (1, 2, 3...)
- The math expression at each step with proper formatting
- Labels describing what happened (e.g., "Remove the constant")
- Tooltip showing which flowchart node caused each change

The latest step is highlighted with a more prominent border and
background, while previous steps are shown with reduced opacity
to maintain focus on current work while preserving full context.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 20:00:04 -06:00
Thomas Hallock b8c24303df Add interactive flowchart walker for step-by-step math procedures
Implements a generic flowchart execution engine that walks kids through
multi-step math procedures interactively. Features:

- Expression evaluator with support for arithmetic, comparisons, and functions
  (lcm, gcd, min, max, abs, floor, mod)
- Flowchart state management with problem input, computed variables, and
  user-entered values
- Working problem display that evolves as the student progresses
  (e.g., 2x - 9 = 3 → 2x = 12 → x = 6)
- MathDisplay component for rendering fractions, mixed numbers, and
  algebraic expressions with proper formatting
- Decision nodes with validation and feedback
- Checkpoint nodes with number input and answer validation
- Three initial flowchart definitions:
  - Fraction addition/subtraction with LCD finding and borrowing
  - Linear equation solving (one-step and two-step)
  - Subtraction with regrouping

The system uses companion .flow.json files alongside Mermaid .mmd diagrams,
keeping the visual flowcharts valid while adding interactivity metadata.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 19:39:28 -06:00
Thomas Hallock 1483de3acd fix: Redis client initialization at runtime vs build time
- Add `dynamic = 'force-dynamic'` to /api/build-info route to prevent
  static caching of runtime values (hostname, REDIS_URL, etc.)
- Fix Redis client singleton to retry initialization if REDIS_URL
  wasn't set at build time but is set at runtime
- Update Traefik health check from `/` to `/api/health` for better
  health checking (verifies database connectivity)

The issue was that Next.js was evaluating redis.ts at build time
(when REDIS_URL isn't set in Docker buildkit), caching `null` as the
client, and never re-checking at runtime. Now it retries if the env
var becomes available.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 19:17:46 -06:00
Thomas Hallock c9899eddb7 chore: formatting cleanup for MCP and API files
Auto-formatting changes from Biome linter.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 18:32:55 -06:00
Thomas Hallock 0bc55530ac fix(vision): detect orphaned recordings at read time
When VisionRecorder's in-memory state is lost (server restart, socket
reconnection, browser refresh), recordings can get stuck in 'recording'
status forever. Instead of complex cleanup logic on writes, detect
orphaned recordings at read time using a simple rule:

Any recording with status='recording' that has an older startedAt than
another recording in the same session is orphaned and treated as
'no_video'. Only the most recently started recording can legitimately
be "in progress".

This handles all scenarios: moving to next problem, manual redo, retry
epochs - all result in a newer recording starting, marking older ones
as orphaned.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 17:51:03 -06:00
Thomas Hallock 57781a9ecc feat: enhance deployment info with health checks and refactor keypad
- Add useDeploymentInfo hook with live health/build info fetching
- Refactor DeploymentInfoContent with server health status, WebSocket
  connectivity, and database status displays
- Add Storybook stories and tests for DeploymentInfoContent
- Extract NumericKeypad styles to CSS file and config to separate module
- Add debug page index
- Update NAS deployment configs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 17:22:52 -06:00
Thomas Hallock 31b9f9dac1 feat(docs): add auto-generated API documentation with Scalar UI
- Install next-openapi-gen for automatic OpenAPI spec generation from routes
- Add /api-docs page with Scalar UI for interactive API documentation
- Add generate step to build script (runs on every deploy)
- Configure to scan Zod schemas and App Router API routes
- Fix migration 0071 to use IF NOT EXISTS for idempotency
- Add public/openapi.json to .gitignore (generated file)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 15:56:27 -06:00
Thomas Hallock 633c789338 feat(mcp): add worksheet tools and resources
Add MCP tools for worksheet generation:
- generate_worksheet: Create worksheets with configurable difficulty
- get_worksheet_info: Get info about existing worksheets
- list_difficulty_profiles: List available difficulty presets

Add MCP Resources for worksheet documentation:
- docs://worksheet/regrouping - Carrying/borrowing pedagogy
- docs://worksheet/scaffolding - Visual aids configuration
- docs://worksheet/difficulty-profiles - The 6 preset profiles
- docs://worksheet/digit-range - Min/max digit settings
- docs://worksheet/operators - Addition/subtraction/mixed

Includes 47 unit tests for tools, resources, and download endpoint.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 15:38:09 -06:00
Thomas Hallock 9f483b142e feat(settings): add settings page with live abacus preview
- Create /settings page with tabs for General, Abacus Style, and MCP Keys
- Extract AbacusStylePanel as shared component for nav dropdown and settings
- Add AbacusDock integration with auto-dock/undock on tab mount/unmount
- Add hideUndock prop to prevent manual undocking in settings preview
- Add updateDockConfig to update dock props without re-registration
- Fix requestDock stability using ref to prevent animation on config changes
- Add ThemeToggle back to AppNavBar while keeping it in settings
- Add navSlot prop to PageWithNav for vision training contextual nav
- Create VisionTrainingNavSlot for model selection in vision training pages

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 15:23:37 -06:00
Thomas Hallock bf5de17e22 feat(mcp): add session management tools
Add 5 new MCP tools for creating and managing practice sessions:
- start_practice_session: Create/start sessions with full config
- get_active_session: Get current session with URLs and progress
- control_session: Approve, start, end, or abandon sessions
- create_observation_link: Generate shareable observation URLs
- list_observation_links: List active share links for a session

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 14:05:57 -06:00
Thomas Hallock 4fbdb3fe50 feat(debug): add debugging tools for cross-instance issues
1. Enhanced /api/build-info endpoint:
   - Shows instance hostname and container ID
   - Shows Redis connection status
   - Shows Socket.IO adapter type (redis/memory)

2. Instance-specific subdomain routes:
   - blue.abaci.one routes to blue container only
   - green.abaci.one routes to green container only
   - Useful for testing cross-instance communication

3. Socket.IO debug page (/debug/socket):
   - Shows connection status and socket ID
   - Join/leave rooms (remote-camera, arcade, game)
   - Send custom events with JSON data
   - Real-time event log with direction arrows

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 11:42:54 -06:00
Thomas Hallock 2e77b46ca1 fix(deploy): remove depends_on from blue/green compose files
compose-updater can't resolve depends_on references to services
defined only in the main docker-compose.yaml. Remove depends_on
and rely on REDIS_URL environment variable instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 11:39:32 -06:00
Thomas Hallock 3286fc56b0 fix: Add missing journal entry and snapshot for mcp_api_keys migration
The migration SQL file was committed but the drizzle journal and snapshot
were not updated, causing the migration to be skipped on prod.
2026-01-15 11:39:10 -06:00
Thomas Hallock 16f67bf305 Add error details to MCP key creation response for debugging 2026-01-15 11:18:38 -06:00
Thomas Hallock 0346455b3e feat(remote-camera): add Redis for cross-instance session sharing
Production blue/green deployment caused remote camera to fail because
desktop and phone could hit different instances with separate in-memory
session storage and Socket.IO rooms.

Changes:
- Add Redis service to docker-compose (production only)
- Create Redis client utility with optional connection
- Update session manager to use Redis when REDIS_URL is set
- Add Socket.IO Redis adapter for cross-instance room broadcasts
- Convert session manager functions to async
- Update tests for async functions

In development (no REDIS_URL), falls back to in-memory storage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 11:18:22 -06:00
Thomas Hallock 8e4338bbbe fix(deploy): add sticky sessions for Socket.IO and remote camera
Remote camera sessions are stored in-memory per instance. Without sticky
sessions, Traefik could route desktop to Blue and phone to Green, causing
"session expired" errors and failed connections.

Sticky sessions ensure the same client always hits the same backend instance,
which is required for:
- Socket.IO connections (rooms are per-instance)
- Remote camera session state (in-memory Map)
- Any stateful WebSocket communication

Note: Sessions will still be lost on container restart/deployment. For full
robustness, sessions should be persisted to database and Socket.IO should
use Redis adapter. This is a workaround for the immediate issue.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 11:08:32 -06:00
Thomas Hallock 26f90f5428 fix(vision): restart rectified view loop when switching to local camera
The rectified view effect checked `rectifiedCanvasRef.current` but refs
don't trigger re-renders. When switching back to local camera, the effect
ran before the canvas was mounted and returned early, showing a black image.

Added `canvasMounted` state that gets updated via the canvas ref callback,
ensuring the effect re-runs when the canvas becomes available.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 10:59:07 -06:00
Thomas Hallock 4399ce9cef fix(vision): separate local/remote calibration preview state
The local camera preview was using a shared `calibrationCorners` state
that got polluted by remote camera calibration. Now each camera source
uses its own editing corners state:
- Local camera preview uses `localEditingCorners`
- Remote camera preview uses `remoteEditingCorners`

Removed the unused shared `calibrationCorners` state entirely.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 10:55:47 -06:00
Thomas Hallock e2816ae88b feat(vision): improve remote camera calibration UX
- Add dual-stream calibration: phone sends both raw and cropped preview
  frames during calibration so users can see what practice will look like
- Add "Adjust" button to modify existing manual calibration without
  resetting to auto-detection first
- Hide calibration quad editor overlay when not in calibration mode
- Fix rotation buttons to update cropped preview immediately
- Add rate limiting (10fps) for cropped preview frames during calibration
- Fix multiple bugs preventing dual-stream mode from working:
  - Don't mark calibration as complete during preview mode
  - Don't stop detection loop when receiving preview calibration
  - Sync refs properly in frame mode change effects

Also includes accumulated formatting and cleanup changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 10:51:59 -06:00
Thomas Hallock 5977cca2b5 feat: Add MCP API keys settings UI
Adds a user-facing settings page at /settings/mcp-keys for managing
MCP API keys. Features:
- Generate new keys with custom names
- Copy key to clipboard (only shown once on creation)
- List active keys with last-used timestamps
- Revoke keys with confirmation
- Shows collapsed list of revoked keys
- Usage instructions with copy-paste config

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 10:21:22 -06:00
Thomas Hallock 3d643d3cc5 feat: Add MCP (Model Context Protocol) integration
Adds MCP support for external tools like Claude Code to access student
skill data via JSON-RPC 2.0 over HTTP.

Features:
- API key authentication with user-scoped access
- 6 MCP tools: list_students, get_student_overview, get_skills,
  get_recent_errors, get_practice_sessions, get_recommended_focus
- API key management endpoints (create, list, revoke)
- Full documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 10:04:52 -06:00
Thomas Hallock bb385b1699 chore: upgrade @pandacss/dev from 0.20 to 1.8.1
- Upgrade Panda CSS for better watch mode stability
- Add --clean flag at dev startup to prevent stale files
- Add --poll flag to watch mode for more reliable file watching
- Move @pandacss/dev to devDependencies (was incorrectly in dependencies)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 09:01:49 -06:00
Thomas Hallock eaa1d11c65 chore: formatting and training data updates
- Apply code formatting across codebase
- Add new vision training boundary frames
- Update model configurations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 06:38:57 -06:00
Thomas Hallock 5281e384ad fix(vision): hide crop settings when phone camera not connected
Don't show the crop settings section when phone camera source is
selected but no phone is connected yet - the user needs to connect
first before crop options are relevant.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 06:37:51 -06:00
Thomas Hallock ba3d204b11 feat(vision): move calibration controls out of video overlay
- Move Cancel, Done, and Rotate buttons from CalibrationOverlay to
  the calibration-preview section in AbacusVisionBridge
- Add animated rotation with smooth easeOutCubic interpolation (300ms)
- CalibrationOverlay now exposes imperative handle (rotate, complete)
- Controls no longer block drag handles on the calibration quad
- Preview canvas animates in sync with corner rotation
- Support both local and remote camera calibration UIs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 06:32:31 -06:00
Thomas Hallock c35b02bbaf fix(vision): fix QR code clipping in phone camera modal
- Remove redundant overflow:hidden from phone-camera-feed element
- Use compact QR code mode to reduce content height
- Use proper centering that fits within container constraints
- Increase QR size to 150px to show branded abacus logo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 05:51:20 -06:00
Thomas Hallock c7f14b11de fix(vision): improve modal UX on small screens and touch devices
- Fix camera feed container to maintain fixed 4:3 aspect ratio regardless
  of cropped image dimensions (prevents button being pushed off screen)
- Add touch-action: none to calibration handles for proper touch dragging
- Use drag controls to allow modal repositioning from header during calibration
- Reduce modal heights and padding for better small screen support
- Remove overflow:hidden from crop settings to prevent content clipping

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 19:17:28 -06:00
Thomas Hallock e703e90875 chore: cleanup unused imports and apply formatting
- Remove unused `and` import from VisionRecorder.ts
- Remove unused `IncomingMessage` and `ws` imports from socket-server.ts
- Add `muted` attribute to video element in ProblemVideoPlayer
- Apply code formatting across vision and practice components
- Update documentation formatting in DEPLOYMENT.md and README

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 18:49:31 -06:00
Thomas Hallock cecd1e93e2 fix(practice): handle loading phase in activeProblem effect
The useInteractionPhase effect now handles two cases:
1. activeProblem.key changes (redo mode, navigation, session advances)
2. Phase becomes 'loading' (after incorrect answer or part transition)

Previously, only key changes triggered problem loading, which caused
"Loading next problem..." to persist after incorrect answers since
clearToLoading() sets phase to 'loading' without changing the key.

Also adds debug logging for part transitions and game breaks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 18:32:39 -06:00
Thomas Hallock 4733149497 chore: update local Claude settings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:50:00 -06:00
Thomas Hallock 95b209e471 style: apply formatting to vision and practice components
Minor whitespace/line-wrapping adjustments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:49:55 -06:00
Thomas Hallock 2f06ed3bbf test(players): add unit tests for useUserPlayers optimistic updates
Tests cover:
- useCreatePlayer optimistic updates to list() and listWithSkillData()
- Graceful degradation when listWithSkillData isn't cached
- Rollback on server errors and network failures
- Cache invalidation on success/error
- API integration (correct endpoint, request body)
- useUpdatePlayer optimistic updates and rollback
- useDeletePlayer optimistic delete and rollback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:43:38 -06:00
Thomas Hallock 72c4825333 feat(players): add optimistic update for listWithSkillData
When creating a new player, optimistically update both the player list
and the listWithSkillData query (used by /practice page). This makes
new players appear immediately without requiring a page reload.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:35:22 -06:00
Thomas Hallock 71e9345d2a fix(players): invalidate all player queries on create/delete
useCreatePlayer and useDeletePlayer were only invalidating
playerKeys.lists() which doesn't include listWithSkillData().

The practice page uses usePlayersWithSkillData() so newly created
players weren't appearing until page reload.

Now invalidates playerKeys.all to catch all player-related queries.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:20:09 -06:00
Thomas Hallock eefa4e8d71 fix(vision): show disabled Enable Vision button with explanation
Instead of hiding the "Enable Vision" button when setup isn't complete,
show it disabled with an explanation of what's needed:
- "Waiting for camera access..." for local camera
- "Connect your phone to enable vision" for phone camera

This provides better UX feedback about what the user needs to do.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:18:24 -06:00
Thomas Hallock 4cace475b9 fix(vision): infer activeCameraSource from existing config
When loading vision config from localStorage, infer activeCameraSource
if it's null but cameraDeviceId or remoteCameraSessionId is set.

This fixes a bug where the "Enable Vision" button wouldn't appear
until the user clicked "This Device" or "Phone Camera", even if
they had previously configured vision before activeCameraSource
was added to the config schema.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:17:20 -06:00
Thomas Hallock b47992f770 feat(deploy): add blue-green deployment with health endpoint
- Add /api/health endpoint that checks database connectivity
- Set up blue-green deployment with two containers (abaci-blue, abaci-green)
- Add docker-compose.yaml with YAML anchors for DRY config
- Add generate-compose.sh to create blue/green compose files from main
- Update deploy.sh with NAS-specific fixes (scp -O, PATH for docker)
- Fix deploy.sh to not overwrite production .env by default

The blue-green setup allows zero-downtime deployments via compose-updater,
which watches separate compose files and restarts containers independently.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:04:01 -06:00
Thomas Hallock b08a796058 fix(observe): show banner with link instead of auto-redirect
- Change from automatic redirect to persistent banner with link
- Authenticated observation: show "session ended" banner with link to report
- Public observation: show banner with link if user has view access
- Remove auto-redirect behavior per user feedback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 12:38:55 -06:00
Thomas Hallock 66acd0c7c9 feat(observe): redirect to session report when session is over
- Authenticated observation page: redirect to session report if completed
- Public observation page: redirect to session report if user has view access
- Show friendly "session ended" page for unauthenticated users on public links
- Prevents 404 for ended sessions when user has access to view the report

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 12:36:17 -06:00
Thomas Hallock a607fce977 fix(vision): add ffmpeg for video encoding and lazy encoding support
- Add ffmpeg to Docker runtime dependencies for encoding vision frames to MP4
- Fix VisionRecorder to mark videos as 'failed' (not 'ready') when ffmpeg unavailable
- Add lazy encoding: when video requested but MP4 missing, encode frames on-demand
- Returns 202 with retryAfterMs so client knows to poll for completion
- Add link to vision markers page from /create hub
- Various vision recording improvements from plan mode work

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 12:30:15 -06:00
Thomas Hallock b5c83b89eb refactor(practice): derive active problem from single source of truth
- 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>
2026-01-14 11:52:38 -06:00
Thomas Hallock ce8c15f6cf fix: add missing vision recording files
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>
2026-01-14 09:34:23 -06:00
Thomas Hallock e368a66b5f fix: add missing vision-recordings schema file
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>
2026-01-14 09:27:15 -06:00
Thomas Hallock a5f5179a57 feat(debug): add socket debug panels for practice and observer pages
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>
2026-01-14 09:10:33 -06:00
Thomas Hallock 5ee18d6f74 fix(socket): handle already-connected socket in session hooks
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>
2026-01-14 08:40:38 -06:00
Thomas Hallock 9b86739b46 feat(vision): add multi-attempt recording support for epoch retries and manual redos
- 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>
2026-01-14 07:55:24 -06:00
Thomas Hallock fbbbf9f50b fix(vision): fix problem video recording when camera starts mid-session
- 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>
2026-01-13 18:56:21 -06:00
Thomas Hallock 371b449d15 feat(vision): implement per-problem video recording for teacher playback
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>
2026-01-13 18:21:54 -06:00
Thomas Hallock 87112c0235 feat(vision): add per-problem DVR scrubbing for teacher observation
- 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>
2026-01-13 17:33:47 -06:00
Thomas Hallock 7d2756e3d7 feat(scoreboard): add skills metrics with classroom leaderboard
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>
2026-01-13 13:13:21 -06:00