Commit Graph

3188 Commits

Author SHA1 Message Date
Thomas Hallock 2f1b9df9d9 feat(classroom): integrate Enter Classroom into StudentActionMenu
- Add classroom data (enrolled, current presence) to useStudentActions hook
- Add enterSpecificClassroom handler for entering a specific classroom
- Replace simple enter/leave actions with smart classroom section:
  - In classroom: shows presence indicator with leave option
  - 1 enrollment: direct enter action
  - Multiple enrollments: Radix submenu with classroom list
  - Always shows enroll option
- Remove EnterClassroomButton from dashboard (now handled by StudentActionMenu)
- Fix EnrollChildModal z-index to use same value as overlay

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 12:14:30 -06:00
Thomas Hallock bf262e7d53 feat(practice): add StudentActionMenu to dashboard + fix z-index layering
- Add StudentActionMenu to student dashboard with inline variant
- Build proper StudentActionData with relationship/activity info
- Use useEnrolledClassrooms and useMyClassroom hooks for context
- Fix z-index layering for sub-modals (FamilyCodeDisplay, EnrollChildModal, SessionObserverModal)
- Use Z_INDEX.TOOLTIP (15000) for nested modals to appear above parent modals
- Remove unnecessary portal from FamilyCodeDisplay
- Add variant prop to StudentActionMenu: 'card' (absolute) vs 'inline' (normal flow)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 10:02:29 -06:00
Thomas Hallock fd1df93a8f perf: reduce practice page dev bundle from 47MB to 115KB
- Dynamic import echarts-for-react in SkillProgressChart, ValidationCharts,
  and SkillDifficultyCharts to avoid bundling 58MB echarts library
- Lazy load game-registry in CreateRoomModal to prevent all arcade games
  from being bundled into every page using PageWithNav

The practice page was bundling echarts (58MB) and all arcade game code
because of static imports in shared components. These changes ensure:
- echarts only loads when charts are actually rendered
- game-registry only loads when CreateRoomModal is opened

Bundle size: 47MB → 115KB (99.8% reduction)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 08:02:08 -06:00
Thomas Hallock d1176da9aa feat(practice): add mini start practice banner to QuickLook modal
- Create MiniStartPracticeBanner component showing session mode or active session status
- Shows "Start" for idle students (navigates to dashboard with startPractice=true)
- Shows "Resume" for practicing students (parent view)
- Shows "Watch" for practicing students (teacher view, opens SessionObserverModal)
- Add session observer integration for teachers to watch active sessions
- Mode-specific styling: amber for remediation, green for progression, blue for maintenance

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 22:58:07 -06:00
Thomas Hallock cb8b0dff67 feat(practice): add smooth fullscreen transition from QuickLook to dashboard
- Add PageTransitionContext to manage transition state across navigation
- Add PageTransitionOverlay component with react-spring animations
- Add fullscreen ⛶ button to NotesModal header
- Implement cross-fade transitions:
  - Modal fades out while overlay fades in at modal position
  - Overlay expands to fullscreen during navigation
  - Overlay fades out while dashboard content fades in
- Use sessionStorage to persist transition state across page navigation
- Add pulse keyframe animation for loading indicator

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 22:29:36 -06:00
Thomas Hallock 0ba1551fea feat(practice): polish unified student list with keyboard nav and mobile UX
Completes Steps 14-18 of the unified student list plan:

Step 14: Move edit mode to secondary menu
- Replace prominent Edit button with ⋮ dropdown menu
- Add "Select Multiple" option, "Done" button in edit mode

Step 15-16: Clean up deprecated components
- Remove ClassroomDashboard, ClassroomTab, StudentManagerTab exports
- Add StudentQuickLook alias for NotesModal

Step 17: Real-time updates
- Add useClassroomSocket to PracticeClient for live updates
- Teachers now see presence/session changes in real-time

Step 18: Mobile responsive polish
- ViewSelector: horizontal scroll on mobile, wrap on desktop
- NotesModal: responsive height for virtual keyboard

Bonus: Arrow key navigation in QuickLook modal
- Left/Right/Up/Down arrows navigate between students
- Uses bounding rects to find nearest card in direction
- Disabled when editing notes (textarea focused)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 21:31:52 -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 1b3dcbe14f docs: add rule about never directly modifying database schema
This rule documents the failure pattern from December 2025 where
columns were added directly to the local dev database, causing
migration 0043 to be committed as a no-op. Production never got
the columns and crashed.

The rule requires all schema changes to go through the Drizzle
migration system.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 12:41:39 -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 2f7002e575 feat(classroom): implement two-way abacus sync for session observation (Step 5)
Teacher observing a student's practice session can now:
- Dock their MyAbacus alongside the observed problem (AbacusDock pattern)
- Manipulate their abacus to sync the student's docked abacus
- See their abacus sync when the student manipulates theirs

Key changes:
- SessionObserverModal: Uses same AbacusDock pattern as ActiveSession
  with ResizeObserver for height matching
- MyAbacus: Fixed docked mode to use setDockedValue instead of
  setAbacusValue, ensuring read/write use same state source
- MyAbacusContext: Added contextAbacusValue for external abacus control
- useSessionObserver: Sends abacus-control events to student
- useSessionBroadcast: Receives abacus-control events from teacher

The two-way sync flow:
1. Teacher→Student: Teacher moves beads → sendControl(set-abacus-value)
   → student's abacus syncs via onAbacusControl callback
2. Student→Teacher: Student moves beads → broadcasts studentAnswer
   → observer receives → setDockedValue syncs teacher's abacus

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 18:03:04 -06:00
Thomas Hallock 962a52d756 fix(classroom): auto-transition tutorial→session observation + fix NaN display
- Auto-transition: When teacher observes a tutorial that completes,
  automatically switch to observing the student's practice session
  - Added polling mechanism while waiting for session to start
  - Shows "Waiting for practice to start" overlay with cancel button
  - 30-second timeout to prevent indefinite waiting

- Fixed NaN display in complexity tooltip "Per term (avg)":
  - Added !Number.isNaN() checks in PurposeBadge and ActiveSession
  - Fixed problemGenerator reduce to explicitly filter NaN values
    (the ?? operator only catches null/undefined, not NaN)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 16:42:25 -06:00
Thomas Hallock 4b7387905d feat(classroom): implement real-time skill tutorial observation
Teachers can now observe students' skill tutorials in real-time:

- Added TutorialObserverModal that reuses SkillTutorialLauncher component
- Student broadcasts tutorial state via WebSocket (useSkillTutorialBroadcast)
- Teacher receives live updates via useClassroomTutorialStates
- Teacher controls work: Start, Skip, Prev, Next, Complete buttons
- Socket server forwards skill-tutorial-state and skill-tutorial-control events
- Added onControl prop to SkillTutorialLauncher and TutorialPlayer
- In observation mode, buttons send control events instead of changing local state

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 15:55:35 -06:00
Thomas Hallock fb73e85f2d fix(classroom): broadcast digit-by-digit answer and correct phase indicator
- Add onBroadcastStateChange callback to ActiveSession for real-time state
- Change studentAnswer from number to string for digit-by-digit display
- Map internal phases correctly: inputting→problem, showingFeedback→feedback, helpMode→tutorial
- Update SessionObserverModal to display string answers directly
- Teachers can now see each digit as students type their answers

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 13:41:10 -06:00
Thomas Hallock 2feb6844a4 feat(classroom): implement real-time session observation (Step 3)
Teachers can now observe students' practice sessions in real-time:
- Added SessionObserverModal with live problem display
- Created useSessionObserver hook for receiving broadcasts
- Refactored useSessionBroadcast to re-broadcast on observer join
- Added join-session socket event for session channel subscription
- Created shared PurposeBadge component with tooltip
- Created shared PracticeFeedback component
- Fixed tooltip z-index to work inside modals (15000)
- Used Z_INDEX constants throughout modal components

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 13:29:59 -06:00
Thomas Hallock 9636f7f44a feat(classroom): add session broadcast and active session indicators
Step 1: Student broadcasts practice state via WebSocket
- Create useSessionBroadcast hook that emits practice-state events
- Only broadcasts when student is present in a classroom
- Wired into PracticeClient to broadcast during active sessions

Step 2: Teacher sees active sessions indicator
- Add useActiveSessionsInClassroom to ClassroomTab
- Show "Practicing" badge with problem progress for active students
- Blue styling for practicing students vs green for idle

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 08:24:15 -06:00
Thomas Hallock 07f6bb7f9c feat(classroom): add active sessions API endpoint
Provides API for useActiveSessionsInClassroom hook to fetch
which students in a classroom are currently practicing.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 07:57:43 -06:00
Thomas Hallock 2015494c0e feat(classroom): complete reactivity fixes (Steps 7-11)
- Fix parent enrollment creation cache invalidation (Step 7)
- Add usePlayerEnrollmentSocket for student-side enrollment updates (Step 8)
- Update parent approval route to use socket-emitter helper (Step 8)
- Fix enter/leave classroom to use centralized playerKeys (Step 9)
- Add session started/ended socket events for active sessions (Step 11)
- Teacher sees real-time session updates when students in classroom

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 07:54:57 -06:00
Thomas Hallock a0693e9084 feat(classroom): add real-time enrollment/unenrollment reactivity
- Add studentUnenrolled event to socket system for real-time updates
- Emit socket events when teacher or parent unenrolls a student
- Handle enrollment-approved events in usePlayerPresenceSocket
- Add EnrollChildModal with reactive success state and auto-close
  - Detects when teacher approves while modal is open
  - Shows countdown progress bar in Done button
  - Smooth fade-out transition on close

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 07:30:11 -06:00
Thomas Hallock 77336bea5b feat(classroom): improve enrollment reactivity and UX
Contextual Enrollment Flow:
- Add "Enroll in Classroom" option to EnterClassroomButton dropdown
- Add "Enroll" button to NotesModal for child-specific enrollment
- Remove global enrollment button from PracticeClient
- Show EnterClassroomButton even without enrollments

Reactivity Improvements (Steps 0-6):
- Create query invalidation map for consistent cache updates
- Create type-safe socket emission helper
- Add EnrollmentRequestApprovedEvent and EnrollmentRequestDeniedEvent types
- Teacher approval/denial now emits socket events to parents
- Parent denial now emits socket events to classroom (teacher)
- Refactor useParentSocket and useClassroomSocket to use invalidateForEvent
- Add enrollment-request-approved and enrollment-request-denied listeners

Both teacher and parent dashboards now update in real-time without reloading.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-24 08:14:56 -06:00
Thomas Hallock bbe0500fe9 feat(classroom): implement real-time enrollment updates
- Add socket events when parent approves enrollment request
  - Emits enrollment-request-approved and enrollment-approved to classroom channel
  - Teacher's view updates immediately without page reload

- Add socket events to notify parents of new enrollment requests
  - Teacher adding student by family code now emits to parent's user channel
  - Parent sees new pending approvals in real-time

- Create useParentSocket hook for parent real-time notifications
  - Connects to user:${userId} channel
  - Listens for enrollment-request-created events
  - Invalidates pendingParentApprovals query

- Add "Awaiting Parent Approval" section in StudentManagerTab
  - Shows teacher-initiated requests waiting for parent response
  - Styled in blue to differentiate from pending requests

- Add useAwaitingParentApproval hook and query key
- Add AddStudentByFamilyCodeModal for teachers to add students
- Auto-approve requester's side when creating enrollment requests

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 08:26:54 -06:00
Thomas Hallock 629bfcfc03 feat(classroom): implement real-time presence with WebSocket (Phase 6)
- Add classroom presence management (enter/leave classroom)
- Implement WebSocket events for real-time presence updates
- Add useClassroomSocket hook for teachers to receive student enter/leave events
- Add usePlayerPresenceSocket hook for students to receive removal notifications
- Create EnterClassroomButton component for students to join classroom
- Add PendingApprovalsSection for parents to approve enrollment requests
- Fix React Query cache invalidation for enter/leave mutations
- Move socket subscription to ClassroomDashboard for cross-tab persistence
- Add getDbUserId() helper to fix viewerId vs user.id bug pattern
- Delete legacy configure/resume pages (consolidated into dashboard)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 22:14:24 -06:00
Thomas Hallock 1952a412ed feat(classroom): implement enrollment system (Phase 4)
- Add EnrollChildFlow component for parents to enroll children
- Add enrollment hooks (useEnrolledStudents, useCreateEnrollmentRequest, etc.)
- Update StudentManagerTab with enrolled students and pending requests
- Add "Enroll in another classroom" option for teachers who are parents
- Fix critical auth bug: convert guestId to user.id in all classroom API routes

The API routes were incorrectly using viewerId (guestId from cookie) when
they should have been using users.id. This caused 403 errors for all
classroom operations. Fixed by adding getOrCreateUser() to convert
guestId to proper user.id before calling business logic functions.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 13:31:04 -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 2202716f56 feat(classroom): implement teacher classroom dashboard (Phase 3)
Add teacher classroom functionality allowing users to create a classroom
and manage students:

- Add classroom query keys for React Query cache management
- Create useClassroom hooks (useMyClassroom, useClassroomByCode,
  useCreateClassroom, useIsTeacher)
- Add classroom UI components:
  - ClassroomCodeShare: Display join code with copy button
  - CreateClassroomForm: Form to create a classroom
  - ClassroomTab: Live classroom view (empty state for Phase 6)
  - StudentManagerTab: Enrolled students list (empty state for Phase 4)
  - ClassroomDashboard: Main teacher dashboard with tabs
- Integrate into PracticeClient with conditional routing:
  - Teachers see ClassroomDashboard with own children shown separately
  - Parents see normal student list with "Become a Teacher" option
- Fix API route to remove non-existent 'image' field from User type

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 13:07:40 -06:00
Thomas Hallock 02842270c9 feat(family): implement parent-to-parent family code sharing (Phase 2)
Add family code system allowing multiple parents to access the same child's
practice data. This is the foundation for the Suzuki Triangle model where
both parents can supervise daily practice.

Key features:
- Family codes (FAM-XXXXXX) per student for sharing access
- FamilyCodeDisplay modal for viewing/copying/regenerating codes
- LinkChildForm modal for linking to existing child via code
- parent_child junction table for many-to-many relationships
- React Query mutations with proper cache invalidation

API routes:
- GET/POST /api/family/children/[playerId]/code - manage family codes
- POST /api/family/link - link to child via family code
- GET /api/family/children - list linked children

Also includes classroom system foundation (Phase 1):
- Classroom, enrollment, and presence schemas
- Teacher classroom management APIs
- Student enrollment request workflow
- Real-time presence tracking infrastructure

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 12:41:20 -06:00
Thomas Hallock dfc2627ccb refactor(bkt): unify skill classification logic into shared BktContext
- Add ExtendedSkillClassification type with 5 categories (strong, stale, developing, weak, unassessed)
- Add getExtendedClassification() as single source of truth for classification
- Add SkillDistribution interface for consistent counts across components
- Update ProgressDashboard to display grouped skill categories with visual hierarchy
- Update DashboardClient to compute full 5-category distribution
- Update SkillProgressChart to use shared types from BktContext

This ensures Overview and Skills tabs use identical classification logic.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 16:30:34 -06:00
Thomas Hallock 594e22c428 feat(chart): add grouped structure to chart hover tooltip
- Tooltip now shows MASTERED, IN PROGRESS, NOT STARTED group headers
- Categories organized under their respective groups (Strong/Stale, Developing/Weak, Unassessed)
- Styled tooltip background for light/dark mode
- Skip empty categories (0 count) from tooltip display
- Consistent visual hierarchy with legend cards

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 16:10:38 -06:00
Thomas Hallock c9518a6b99 feat(chart): improve skill classification visual hierarchy with colors and patterns
- Add diagonal stripe pattern to Stale and Weak categories to indicate "needs attention"
- Update color scheme: Strong/Stale share green family, Developing stays blue, Weak is coral
- Make Unassessed nearly transparent with dashed border in legend
- Legend cards now have colored backgrounds matching chart colors
- Add group headers (Mastered, In Progress, Not Started) with vertical dividers
- Ensure text contrast works in both light and dark modes
- Add "(7+ days ago)" descriptor to Stale card for clarity

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 16:08:18 -06:00
Thomas Hallock bf5b99afe9 feat(practice): add intervention system and improve skill chart hierarchy
- Add "Needs Attention" section to /practice page surfacing struggling students
- Implement intervention detection (struggling, declining, stale, absent, plateau)
- Group skill chart legend into Mastered/In Progress/Not Started hierarchy
- Use color families: green for mastered (strong+stale), patterns for attention states
- Add diagonal stripe pattern to Stale and Weak categories in chart and legend
- Update tooltip to show same grouped structure with tree branches
- Fix encouragement message to not celebrate when weak skills are growing
- Make Unassessed transparent/dashed to fade into background

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 16:00:39 -06:00
Thomas Hallock 1fc8949b06 feat(dashboard): add skill progress chart with trend analysis and timing awareness
Major changes:

1. Sentinel approach for skill recency refresh
   - Added 'recency-refresh' session status and SlotResult source field
   - BKT skips pKnown updates for sentinel records (zero-weight)
   - refreshSkillRecency() now inserts sentinel records instead of updating DB field
   - Single source of truth: all lastPracticedAt comes from problem history

2. Skill progress chart (new component)
   - 100% stacked area chart showing skill distribution over time
   - Clickable legend cards for filtering by classification
   - Adaptive time window presets (Recent, Last N, All)
   - Synthetic "current" snapshot ensures chart matches legend staleness

3. Trend-aware encouragement messages
   - Linear regression to detect improving/declining/stable trends
   - Session timing analysis (gap detection, frequency patterns)
   - Window-aware scope text ("over the past 2 weeks", "since Nov 15")
   - Priority-based message selection (urgent issues first)
   - Consistent "stale" terminology (not "rusty")

4. Dashboard improvements
   - Virtualized session history list for performance
   - Paginated session history API with cursor-based pagination
   - BKT-computed lastPracticedAt used throughout (single source of truth)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 14:37:15 -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 d5e4c858db fix(seed): accurate BKT simulation for developing classifications
Fixed the seed script to reliably produce "developing" skill classifications
(pKnown 0.5-0.8) by correcting several issues:

- Fix BKT simulation to only apply learning transition after CORRECT answers
  (matching actual updateOnCorrect vs updateOnIncorrect behavior)
- Remove result shuffling to preserve designed correct/incorrect sequence order
- Force single-skill problem annotations to avoid multi-skill blame distribution
- Add multiple pattern generators for finding developing-range sequences

The simulation now accurately predicts actual BKT outcomes, enabling 10
developing classifications across 6 test profiles.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 09:27:59 -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 446678799c refactor(help): simplify to binary help system + add seed script CLI
Help System Cleanup:
- Simplify HelpLevel from 0|1|2|3 to binary 0|1 (matching actual usage)
- Change BKT help weight from 0.8 to 0.5 for helped answers
- Delete unused PracticeHelpPanel.tsx (~540 lines)
- Delete unused usePracticeHelp.ts (~400 lines)
- Delete unused reinforcement-config.ts
- Remove reinforcement tracking (BKT handles skill weakness detection)
- Update all journey simulator profiles to use binary help arrays
- Update documentation (BKT_DESIGN_SPEC.md, decomposition README)

Seed Script Improvements:
- Add CLI argument parsing with node:util parseArgs
- Add --help, --list, --name, --category, --dry-run flags
- Add new "Forgotten Weaknesses" test profile (weak + stale skill mix)
- Enable seeding individual students or categories

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 06:28:27 -06:00
github-actions[bot] b9d4bc552a 🎨 Update template examples and crop mark gallery
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>
2025-12-21 02:41:19 +00:00
Thomas Hallock 7d03d8c69b test(seed): update NaN stress test to cover missing helpLevelUsed root cause
- Add simulateLegacyData option to SkillConfig interface
- When set, generateSlotResults omits helpLevelUsed field to simulate legacy data
- Update NaN Stress Test profile to test 3 skills with legacy data format
- This tests the actual production issue where old sessions lack helpLevelUsed

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 14:44:15 -06:00
Thomas Hallock f883fbfe23 feat(seed): add category field to all mock student profiles
All 15 profiles now have correct category assignments:
- 5 BKT profiles (category: 'bkt')
- 4 Session profiles (category: 'session')
- 6 Edge case profiles (category: 'edge')

This enables CLI filtering when seeding specific profile types.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 14:36:56 -06:00
Thomas Hallock b300ed9f5c fix(bkt): handle missing helpLevelUsed in legacy data causing NaN
Root cause: Old production data (before Dec 2024) is missing the
`helpLevelUsed` field. The `helpLevelWeight` function had no default
case in its switch statement, returning `undefined` when called with
undefined, which caused `undefined * rtWeight = NaN` to propagate
through BKT calculations.

Changes:
- evidence-quality.ts: Add default case returning 1.0 for undefined/null
  and add NaN guard to responseTimeWeight
- bkt-core.ts: Add NaN guards that surface invalid data with console.warn
- conjunctive-bkt.ts: Add NaN guards for multi-skill BKT updates
- compute-bkt.ts: Skip problem results that would produce NaN, preserving
  prior state rather than corrupting skill estimates
- bkt-integration.ts: Add NaN guard to calculateBktMultiplier with
  conservative fallback
- DashboardClient.tsx: Add UI error state for NaN pKnown values showing
  "⚠️ Data Error" instead of displaying "~NaN%"

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 14:31:47 -06:00
Thomas Hallock f760ec130e refactor(summary): replace compact/detailed toggle with tap-for-details popover
- Remove view mode toggle (Compact/Detailed) - confusing UX
- Add problem numbers (#1, #2...) to compact view for easy reference
- Make each problem tappable to open detail popover
- Popover shows: equation, student answer if wrong, response time,
  pause threshold, help level, abacus usage, skills exercised
- Close popover with tap outside, X button, or Escape key

Users can now see all problems at a glance with numbers, and tap any
specific problem to get the full details without losing their place.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 13:55:57 -06:00
Thomas Hallock d6c8e582a7 refactor(skills): unify skill presentation with SkillCard grid
- Replace horizontal rows in stale skills section with SkillCard grid
- Move "Mark Current" action into SkillDetailDrawer for stale skills
- Add staleness alert section in drawer with explanation and action
- Consistent visual presentation between practicing and stale skills
- User can now tap any stale skill to see details and mark as current

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 13:16:35 -06:00
Thomas Hallock 0e0356113d feat(practice): add fixed filter bar, sticky headers, and shared EmojiPicker
- Make StudentFilterBar fixed below nav with proper z-index layering
- Add sticky bucket headers (Today, This Week, etc.) and category headers
- Move bulk actions into filter bar (shown in edit mode in place of search)
- Create shared EmojiPicker component with emojibase-data integration
- Simplify AddStudentModal to use shared EmojiPicker (single way to pick emoji)
- Add z-index constants for filter bar and sticky headers

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 12:17:12 -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 11d48465d7 fix(dashboard): compute skill stats from session results in curriculum API
The dashboard fetches skills from /api/curriculum/[playerId] which was
returning raw playerSkillMastery records with persisted attempts/correct
values (always 0 for seeded data).

Updated the API to compute skill stats from session results, consistent
with the recent single-source-of-truth refactor.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 09:39:38 -06:00
Thomas Hallock f804d24a29 refactor(skills): compute skill stats from session results (single source of truth)
Previously, skill stats (attempts, correct, accuracy) were stored as
persisted aggregates in playerSkillMastery and updated incrementally.
This caused issues with seeded test data showing "0 correct".

Now:
- analyzeSkillPerformance() computes stats from session results on-the-fly
- findStrugglingSkills() computes accuracy from session results
- Seeder no longer needs to update aggregate columns

Benefits:
- Single source of truth (session results)
- No drift between aggregates and actual data
- Seeded data automatically works correctly

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 09:31:16 -06:00
Thomas Hallock 35720820f3 refactor(seeder): reframe problematic profiles for realistic data generation
- Rename "⚖️ Balanced Mix" → "⚖️ Multi-Weak Remediation" to match actual output
- Update intention notes to reflect BKT's natural tendency to push skills to extremes
- Remove tuning criteria that fought against realistic behavior
- Focus profiles on app feature coverage rather than perfect BKT states

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 09:10:34 -06:00
Thomas Hallock 430c46adb9 feat(session-summary): redesign ProblemToReview with BKT integration and animations
ProblemToReview component improvements:
- Use AnnotatedProblem for unified collapsed/expanded problem display
- Integrate BKT mastery data for weak skill detection and ordering
- Add smooth CSS Grid animations for expand/collapse transitions
- Make header row clickable (toggle moved from footer to header)
- Add purpose explanations and timing info in expanded mode
- Show up to 3 weak skills in collapsed mode with "+N more" indicator

React Query cache fix:
- Fix "Mark Current" button not updating UI (stale cache issue)
- Replace plain fetch + router.refresh() with useRefreshSkillRecency mutation
- Mutation properly invalidates curriculumKeys.detail(playerId)

Documentation:
- Add CLAUDE.md section on React Query mutation patterns
- Document the relationship between mutations and query cache invalidation
- Include checklist to prevent future cache invalidation bugs

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 07:31:43 -06:00
semantic-release-bot 05aa87ffd2 chore(abacus-react): release v2.17.0 [skip ci]
# [2.17.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v2.16.0...abacus-react-v2.17.0) (2025-12-20)

### Bug Fixes

* **modal:** expand settings by default on tall phones to fill space ([2ffb71a](2ffb71ab28))
* **modal:** make StartPracticeModal full-screen on mobile phones ([1383db8](1383db8185))

### Features

* **banner:** add scroll-based projection with FLIP animation ([d405038](d405038711))
* **modal:** redesign StartPracticeModal layout for tall phones ([0e2fcee](0e2fcee0ae))
* **practice:** unify session resume/start into projecting banner ([d128e80](d128e808db))
* **session-summary:** unify summary and debug views with progressive disclosure ([2977bd5](2977bd57df))
2025-12-20 03:16:36 +00:00
Thomas Hallock 2977bd57df feat(session-summary): unify summary and debug views with progressive disclosure
Major changes:
- Merge SessionSummary and SessionOverview into unified experience
- Add "Problems Worth Attention" section with expandable problem details
- Add "All Problems" collapsible section with compact/detailed toggle
- Keep auto-pause timing info in unified view
- Remove debug view toggle from SummaryClient
- Delete SessionOverview.tsx (replaced by new components)

New components:
- AllProblemsSection.tsx - collapsible all-problems view
- ProblemToReview.tsx - expandable problem row with reason badges
- sessionSummaryUtils.ts - filtering utilities for attention-worthy problems

Bug fix:
- Fix ROTATION_MULTIPLIERS import in DashboardClient (was undefined due to re-export chain)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-19 21:15:14 -06:00
Thomas Hallock 4b291b304b refactor(practice): remove stub features and add stale skills refresh
Remove unimplemented features that didn't actually save data:
- Placement test: UI worked but results were never persisted
- Offline session recording: form collected data but just logged it
- View Progress button: was an empty callback
- Worksheet button: redirected but didn't integrate with practice

Add functional stale skills management:
- Add "Mark Current" refresh buttons to Skills tab stale skills section
- Show BKT status badges and staleness warnings
- Wire up refresh API to update skill recency

Clean up dead code:
- Delete SkillsClient.tsx (was unused, /skills redirects to dashboard)
- Remove onRefreshSkill from ManualSkillSelector modal
- Add BKT badges to ManualSkillSelector for skill status visibility

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-19 20:30:02 -06:00