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>
- 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>
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>
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>
- 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>
- 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>
- 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>
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>
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>
- 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>
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>
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>
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>
- Banner projects from content area to nav when scrolled under sticky header
- Uses IntersectionObserver to detect when content banner scrolls out of view
- FLIP animation smoothly transitions between slots with spring physics
- Preserves content slot space when projecting to prevent layout jump
- Uses react-use-measure for accurate dimension tracking
- Animates both position and size (width/height) between variants
- Nav slot collapses to height:0 when content slot is active
- Animation overlay uses higher z-index to appear above sticky nav
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace fixed-position ProjectingBanner with FLIP-based BannerSlots:
- Banners now render in document flow (relative positioning)
- Content flows naturally around banners
- FLIP animation only uses fixed positioning during ~400ms transition
- After animation, banner returns to document flow at target slot
Architecture:
- ContentBannerSlot/NavBannerSlot: Render content in-flow when active
- ProjectingBanner: Orchestrates FLIP animation during slot transitions
- Uses framer-motion springs for smooth position/size interpolation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Consolidates two competing CTAs (resume button in dashboard vs practice
button in banner) into a single unified experience in the session banner.
When an active session exists, the projecting banner now shows:
- Session progress (X/Y problems completed)
- When started / last activity timestamps
- Focus description and skill changes since session start
- "Resume Session" button to continue where left off
- "Start Fresh" link to open modal (session only abandoned when new one starts)
Changes:
- Add ActiveSessionBanner component with dashboard and nav variants
- Add computeSkillChanges utility for tracking skill state changes
- Extend SessionModeBannerContext with activeSession state and handlers
- Modify ProjectingBanner to conditionally render ActiveSessionBanner
- Remove resume button and activeSession prop from ProgressDashboard
- Wire up active session data in DashboardClient
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove all "tall phone" media queries (@media max-width: 480px and min-height: 700px)
- Remove useEffect that auto-expanded settings on tall phones
- Use CSS order: 99 on session-config to always show CTAs first, settings last
- Remove redundant header section (🎯 Ready to practice?) - CTAs provide context
- Keep Dialog.Title/Description as sr-only for accessibility
- Simplify to just 2 layouts: default and landscape mode
- Format SessionProgressIndicator.stories.tsx
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Settings now expand automatically on tall phones (height >= 700px)
- Increased gap between sections (1rem -> 1.5rem)
- Restored normal padding and gaps inside expanded settings
- Larger labels and button gaps in settings on tall phones
This fills the empty space by showing all settings expanded,
making the layout feel intentional rather than sparse.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
On tall phones (width ≤ 480px AND height ≥ 700px):
- Reorder content: hero CTA banner first, settings second (CSS order)
- Hero CTA (tutorial/remediation/generic) is larger, centered, more prominent
- 2.5rem emoji, 1.125rem title, generous padding
- Centered vertical layout with skill badges
- Session settings appear below as secondary option
- Title and icon restored to full size
- Generous spacing creates intentional, designed appearance
- Content vertically centered with balanced whitespace
Fixes the "empty middle gap" issue where settings were at top
and CTA was pushed to bottom with marginTop: auto.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The modal was only going full-screen based on max-height: 700px,
which didn't trigger on iPhone 14 (844px height). Now also triggers
on max-width: 480px to cover all phone-sized screens.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Superseded by CelebrationProgressionBanner which is integrated
into SessionModeBanner and handles skill unlock celebrations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add bottomOffset/rightOffset to MyAbacusContext for virtual keyboard avoidance
- NumericKeypad sets offsets when mounted (48px bottom, 100px right)
- Floating abacus repositions above/beside keyboard in portrait/landscape
- PracticeSubNav: fix horizontal overflow with minWidth: 0 on flex children
- SessionProgressIndicator: allow proper flex shrinking
- ActiveSession: reduce padding/gaps, use flex layout to fill available space
- PracticeClient: use fixed positioning with proper insets for all orientations
- Portrait: bottom 48px for keypad
- Landscape: right 100px for keypad
- Desktop: no offsets needed
- Prevent viewport scrolling during practice sessions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements a unified banner that seamlessly animates between positions:
- Full banner in content area (Dashboard, Summary pages)
- Compact banner in nav slot (other practice pages)
- Smooth React Spring animation when navigating between pages
New files:
- SessionModeBannerContext: manages slot registration and bounds tracking
- ProjectingBanner: animated portal banner using React Spring
- CompactBanner: condensed single-line variant for nav slot
- PracticeLayout: wrapper component with provider
- ProjectingBanner.stories: interactive demos showcasing transitions
Modified:
- PracticeSubNav: removed Start Practice button, added NavBannerSlot
- DashboardClient/SummaryClient: wrapped with provider, use ContentBannerSlot
- zIndex constants: added SESSION_MODE_BANNER layer
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit includes accumulated work from the SessionMode system:
- Add SkillUnlockBanner component for celebrating skill mastery
- Improve SessionSummary to show skill unlock celebrations
- Add session detail page at /practice/[studentId]/session/[sessionId]
- Update seedTestStudents script with more realistic test data
- Extend skill-tutorial-config with more skill mappings
- Improve BKT compute with better uncertainty handling
- Update progress-manager with skill completion tracking
- Remove legacy sessions API routes (replaced by session-plans)
- Add migration 0037 for practice_sessions schema cleanup
- Add plan documents for celebration wind-down and SessionMode
- Update gitignore to exclude db backups
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add @media (max-width: 400px) breakpoints to all three banner components:
- RemediationBanner: smaller padding, icon size, font sizes, button
- ProgressionBanner: smaller padding, icon size, font sizes, button
- MaintenanceBanner: smaller padding, icon size, font sizes, button
Also adds minWidth: 0 to text containers to prevent overflow on small screens.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Now that the Remediation CTA prominently displays weak skills,
the duplicate "Targeting:" and "Focusing on weak skills:" sections
in the config panel are no longer needed.
Removed:
- Target skills summary in collapsed view
- Target skills info in expanded config panel
- Unused targetSkillsInfo useMemo
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a student is in remediation mode (has weak skills to strengthen),
the StartPracticeModal now shows a special amber-themed CTA similar to
the tutorial CTA:
- 💪 "Time to build strength!" heading
- Lists weak skills with pKnown percentages
- "Start Focus Practice →" amber button
- Shows up to 4 skills with "+N more" overflow
Includes Storybook stories for:
- Single weak skill
- Multiple weak skills (2)
- Many weak skills (6, with overflow)
- Dark theme variant
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The session planner now accepts an optional sessionMode parameter that:
- Uses pre-computed weak skills from SessionMode (remediation mode)
- Eliminates duplicate BKT computation between UI and problem generation
- Ensures "no rug-pulling" - what the modal shows is what configures problems
Changes:
- session-planner.ts: Accept sessionMode, use getWeakSkillIds() when provided
- useSessionPlan.ts: Accept sessionMode in generateSessionPlan function
- plans/route.ts: Pass sessionMode from request body to planner
- StartPracticeModal.tsx: Pass sessionMode when generating plan
- index.ts: Export SessionMode types from curriculum module
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Creates a single source of truth for practice session decisions:
- SessionMode types: remediation, progression, maintenance
- getSessionMode() centralizes BKT computation
- SessionModeBanner component displays context-aware messaging
- useSessionMode() hook for React Query integration
Updates Dashboard, Summary, and StartPracticeModal to use SessionMode,
eliminating the "three-way messaging" problem where different parts of
the UI showed conflicting skill information.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds interpolation utilities and a celebration banner component that
smoothly morphs between celebration and normal states over 60 seconds.
🤫🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Full-screen mode at ≤700px height for iPhone SE support
- Two-column grid layout for settings in landscape mode
- Integrated tutorial CTA: combines unlock banner + start button
- Fixed collapsed mode clipping of target skills section
- Made "focusing on weak skills" visible on all screen sizes
- Fixed duplicate CSS media query breakpoints
- BKT: changed computeBktFromHistory to accept Partial<BktComputeExtendedOptions>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Documents the critical requirement for --> statement-breakpoint markers
between multiple SQL statements in drizzle migrations. References the
2025-12-18 production outage caused by missing breakpoint.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Drizzle's better-sqlite3 driver requires --> statement-breakpoint
markers between SQL statements in migration files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Tabs stack vertically on mobile (icon above label) instead of hiding labels
- Summary cards use 2x2 grid on mobile, 4x1 on tablet+
- Skill card grids use smaller min-width on mobile (120px vs 140px)
- Reduced padding throughout on mobile screens
- Section headers and buttons stack vertically on mobile
- History and Notes tabs use responsive padding and font sizes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Student Notes Feature:
- Add notes column to players table with migration
- Create NotesModal component with zoom animation from student tile
- Add notes button on each student card in StudentSelector
- Support viewing and editing notes directly in modal
- Fix modal reopening bug with pointerEvents during animation
- Fix spring animation to start from clicked tile position
BKT & Curriculum Improvements:
- Add configurable BKT thresholds via admin settings
- Add skill anomaly detection API endpoint
- Add next-skill recommendation API endpoint
- Add problem history API endpoint
- Improve skills page with BKT classifications display
- Add skill tutorial integration infrastructure
Dashboard & Session Improvements:
- Enhanced dashboard with notes tab
- Improved session summary display
- Add StartPracticeModal stories
Test Infrastructure:
- Add seedTestStudents.ts script for BKT manual testing
- Add generateTrajectoryData.ts for simulation data
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add smooth spring-animated accordion expand/collapse using react-spring
- Add dynamic scroll indicators that show when content is scrolled
- Auto-scroll to show expanded category content optimally
- Replace ambiguous arrows with "Show/Hide" + rotating chevron
- Make modal full-screen on mobile, centered on desktop
- Add sticky category headers within scroll container
- Fix z-index layering using shared constants
- Add optimistic updates for skill mutations (instant UI feedback)
- Fix React Query cache sync for live skill updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace manual useLayoutEffect + resize listener with react-use-measure
hook for cleaner reactive measurement of takeover container bounds.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
DashboardClient had 3 direct fetch() calls that bypassed React Query:
- handleStartOver (abandon session)
- handleSaveManualSkills (set mastered skills)
- handleRefreshSkill (refresh skill recency)
These used router.refresh() to update data, which didn't reliably
update the React Query cache, causing stale UI state.
Fix:
- Add useSetMasteredSkills and useRefreshSkillRecency hooks
- Use useActiveSessionPlan with server props as initial data
- Replace direct fetch with mutation hooks
- Remove router.refresh() calls - React Query handles cache invalidation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add session 0 data to show initial mastery state before practice
- Single Skill tab: line thickness based on skill tier, category average toggles
- All Skills tab: session 0 for ghost lines and averages
- Fix broken GitHub link (was placeholder "...")
- Add source code links to test files that generate chart data
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New chart design with multiple visual encodings:
- Ghost lines (40% opacity) show individual skill trajectories
- Green spectrum for Adaptive, gray spectrum for Classic
- Darker shades = later in pedagogical sequence
- Line thickness encodes skill difficulty:
- 1px: basic skills
- 1.5px: five-complements
- 2px: ten-complements (friends of 10)
- 2.5px: cascading/multi-place regrouping
- 4px: average line (full opacity, on top)
- Clear legend showing Adaptive (avg) vs Classic (avg)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaced cluttered 12-line chart with clean 2-line comparison:
- Green line: Average mastery across all skills (Adaptive mode)
- Gray line: Average mastery across all skills (Classic mode)
- Clear legend with Adaptive/Classic labels
- Area fill for visual distinction
- Tooltip shows both values plus advantage in pp
Much easier to see that Adaptive consistently outpaces Classic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed from heading-based injection to explicit marker comments:
- Markers like <!-- CHART: ValidationResults --> in markdown
- Charts now appear after explanatory text, not directly under headings
- Gives explicit control over chart placement in the document flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed "All Skills" tab to display both modes simultaneously:
- Solid lines = Adaptive mode (with circle markers)
- Dashed lines = Classic mode (no markers)
- Same color = same skill
- Tooltip shows both values with diff highlighted
This makes comparison much easier than toggling between modes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The JSON was stale - regenerated from the correct 6-skill A/B test
snapshot showing adaptive wins 4-0 at 50% and 6-0 at 80%.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Explain the three classification zones (Struggling, Learning, Automated)
and their P(known) thresholds before showing the visualization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>