Commit Graph

2036 Commits

Author SHA1 Message Date
Thomas Hallock 3c9406afc5 fix(practice): improve mobile layout + floating abacus positioning
- 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>
2025-12-18 19:49:59 -06:00
Thomas Hallock 0f84edec0a feat(practice): add projecting SessionModeBanner with slot-based animation
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>
2025-12-18 17:02:31 -06:00
Thomas Hallock 4daf7b7433 feat(practice): add SkillUnlockBanner + session summary improvements
This commit includes accumulated work from the SessionMode system:

- Add SkillUnlockBanner component for celebrating skill mastery
- Improve SessionSummary to show skill unlock celebrations
- Add session detail page at /practice/[studentId]/session/[sessionId]
- Update seedTestStudents script with more realistic test data
- Extend skill-tutorial-config with more skill mappings
- Improve BKT compute with better uncertainty handling
- Update progress-manager with skill completion tracking
- Remove legacy sessions API routes (replaced by session-plans)
- Add migration 0037 for practice_sessions schema cleanup
- Add plan documents for celebration wind-down and SessionMode
- Update gitignore to exclude db backups

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 15:16:34 -06:00
Thomas Hallock be08efe06f fix(practice): add responsive styles to SessionModeBanner for small screens
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>
2025-12-18 14:58:46 -06:00
Thomas Hallock e9f9aaca16 refactor(practice): remove redundant targeting sections from modal
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>
2025-12-18 14:47:08 -06:00
Thomas Hallock 7d8bb2f525 feat(practice): add Remediation CTA for weak skill focus sessions
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>
2025-12-18 14:42:44 -06:00
Thomas Hallock 9851c01026 feat(session-planner): integrate SessionMode for single source of truth targeting
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>
2025-12-18 14:06:42 -06:00
Thomas Hallock b345baf3c4 feat(practice): add unified SessionMode system for consistent skill targeting
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>
2025-12-18 13:56:15 -06:00
Thomas Hallock bb9506b93e feat(practice): add celebration progression banner with smooth transitions
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>
2025-12-18 13:42:29 -06:00
Thomas Hallock 56742c511d fix(StartPracticeModal): responsive improvements + integrated tutorial CTA
- 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>
2025-12-18 10:49:49 -06:00
Thomas Hallock 5735ff0810 docs: add drizzle statement-breakpoint lesson to CLAUDE.md
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>
2025-12-18 07:37:00 -06:00
Thomas Hallock ba68cfc75d fix(migration): add statement-breakpoint between CREATE and INSERT
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>
2025-12-18 07:18:50 -06:00
Thomas Hallock 129907fcc6 fix(dashboard): make student dashboard responsive for small screens
- 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>
2025-12-18 06:45:26 -06:00
Thomas Hallock 2702ec585f feat(practice): add student notes with animated modal + BKT improvements
Student Notes Feature:
- Add notes column to players table with migration
- Create NotesModal component with zoom animation from student tile
- Add notes button on each student card in StudentSelector
- Support viewing and editing notes directly in modal
- Fix modal reopening bug with pointerEvents during animation
- Fix spring animation to start from clicked tile position

BKT & Curriculum Improvements:
- Add configurable BKT thresholds via admin settings
- Add skill anomaly detection API endpoint
- Add next-skill recommendation API endpoint
- Add problem history API endpoint
- Improve skills page with BKT classifications display
- Add skill tutorial integration infrastructure

Dashboard & Session Improvements:
- Enhanced dashboard with notes tab
- Improved session summary display
- Add StartPracticeModal stories

Test Infrastructure:
- Add seedTestStudents.ts script for BKT manual testing
- Add generateTrajectoryData.ts for simulation data

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 06:39:04 -06:00
Thomas Hallock b94f5338e5 feat(skills-modal): add spring animations and UX improvements
- 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>
2025-12-17 07:23:14 -06:00
Thomas Hallock fad386f216 refactor(know-your-world): use react-use-measure for safe scale calculation
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>
2025-12-17 07:22:21 -06:00
Thomas Hallock ceadd9de67 feat(blog): add Bayesian blame attribution validation and address reviewer feedback
- Add proper Bayesian inference implementation alongside heuristic approximation
- Create blame-attribution.test.ts with multi-seed validation (5 seeds × 3 profiles)
- Result: No significant difference (t=-0.41, p>0.05), heuristic wins 3/5

Blog post improvements addressing expert reviewer feedback:
- Add Limitations section (simulation-only validation, technique bypass, independence assumption)
- Add "Why We Built This" section explaining automatic proctoring context
- Soften claims: "validate" → "suggest...may...pending real-world confirmation"
- Commit to follow-up publication with real student data
- Add BlameAttribution interactive chart with comparison data

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 19:04:07 -06:00
Thomas Hallock ff7554b005 fix(dashboard): use React Query mutations instead of direct fetch
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>
2025-12-16 15:31:32 -06:00
Thomas Hallock c40baee43f feat(blog): add session 0, line thickness, and category averages to charts
- 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>
2025-12-16 14:05:50 -06:00
Thomas Hallock b227162da6 feat(blog): add layered skill trajectory visualization
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>
2025-12-16 13:58:25 -06:00
Thomas Hallock 6ef329dd60 feat(blog): simplify All Skills chart to show average comparison
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>
2025-12-16 13:36:48 -06:00
Thomas Hallock df9f23d2a3 refactor(blog): use marker comments for chart placement
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>
2025-12-16 13:34:30 -06:00
Thomas Hallock b0c0f5c2da feat(blog): show adaptive vs classic comparison on same chart
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>
2025-12-16 13:30:10 -06:00
Thomas Hallock ce85565f06 fix(blog): regenerate trajectory data from correct snapshot
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>
2025-12-16 13:26:52 -06:00
Thomas Hallock 84217a8bb6 docs(blog): add proper introduction before Automaticity Classification chart
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>
2025-12-16 13:21:59 -06:00
Thomas Hallock 335c385390 docs(blog): remove straw man comparison in uncertainty reporting section
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 13:20:46 -06:00
Thomas Hallock 6a4dd694a2 feat(blog): add interactive ECharts for BKT validation blog post
- Add ValidationCharts with tabbed interface for A/B trajectory data
  - "All Skills" tab: shows 6 skills at once, toggle Adaptive/Classic
  - "Single Skill" tab: interactive skill selector for individual comparison
  - "Convergence" tab: bar chart comparing sessions to 80% mastery
  - "Data Table" tab: summary with advantage calculations
- Add SkillDifficultyCharts for skill difficulty model visualization
- Create snapshot-based test infrastructure for trajectory data
  - skill-difficulty.test.ts generates A/B mastery trajectories
  - Snapshots capture session-by-session mastery for 6 deficient skills
- Add generator scripts to convert snapshots to JSON for blog charts
  - generateMasteryTrajectoryData.ts → ab-mastery-trajectories.json
  - generateSkillDifficultyData.ts → skill-difficulty-report.json
- Add skill-specific difficulty multipliers to SimulatedStudent
  - Basic skills: 0.8-0.9x (easier)
  - Five-complements: 1.2-1.3x (moderate)
  - Ten-complements: 1.6-2.0x (harder)
- Document SimulatedStudent model in SIMULATED_STUDENT_MODEL.md

Results show adaptive mode reaches 80% mastery faster for all 6 tested
skills (4-0 at 50% threshold, 6-0 at 80% threshold).

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 13:16:47 -06:00
Thomas Hallock 7085a4b3df feat(bkt): add adaptive-bkt mode with unified BKT architecture
- Add 'adaptive-bkt' mode using BKT for both skill targeting AND cost
  calculation (previously BKT was only used for targeting)
- Make adaptive-bkt the default problem generation mode
- Fix session-planner to include adaptive-bkt in BKT targeting logic
- Add fatigue tracking to journey simulator (sum of skill multipliers)
- Add 3-way comparison test (classic vs adaptive vs adaptive-bkt)

Validation results show both adaptive modes perform identically for
learning rate (25-33% faster than classic). The benefit comes from
BKT targeting, not the cost formula - using BKT for both simplifies
the architecture with no performance cost.

UI changes:
- Simplify Problem Selection to two user-friendly options:
  "Focus on weak spots" (recommended) and "Practice everything"
- Remove jargon like "BKT" and "fluency" from user-facing labels

Blog post updated with 3-way comparison findings and unified
BKT architecture documentation.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 19:24:14 -06:00
Thomas Hallock 354ada596d feat(bkt): implement adaptive skill targeting with validated convergence
BKT (Bayesian Knowledge Tracing) integration for intelligent practice:

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

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

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

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

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 17:16:18 -06:00
Thomas Hallock 22cd11e2c3 docs(blog): update BKT post with validation results and adaptive targeting
- Add validation section with convergence speed comparison results
- Document adaptive skill targeting architecture (separate from cost calculation)
- Add per-skill assessment methodology that doesn't pollute learning state
- Include test results: adaptive reaches 80% mastery faster in 9/9 scenarios
- Update abstract and summary with key findings

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 17:15:27 -06:00
Thomas Hallock 86cd518c39 feat(test): add journey simulator for BKT A/B testing
Comprehensive test harness for comparing BKT-driven adaptive mode vs
classic mode using simulated students with Hill function learning.

Key components:
- SimulatedStudent: Exposure-based learning model with conjunctive
  skill probability (P = product of individual skill probabilities)
- JourneyRunner: Runs multi-session practice journeys
- Per-skill deficiency profiles: 32 skills × 3 learner types (96 profiles)
- Per-skill assessment: Measures exposure and mastery on specific skills
  WITHOUT learning during assessment

Learner types calibrated for gradual learning over 12 sessions:
- Fast: K=25, n=2.0 (~75 exposures for 90% mastery)
- Average: K=40, n=2.5 (~120 exposures for 90% mastery)
- Slow: K=60, n=3.0 (~150 exposures for 90% mastery)

Key finding: When measuring the RIGHT metric (deficient skill exposure
and mastery), adaptive mode wins 10/15 comparisons vs classic:
- Avg deficient skill exposure: Adaptive=189.6 vs Classic=180.6
- Avg deficient skill mastery: Adaptive=94.9% vs Classic=90.8%

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 15:38:27 -06:00
Thomas Hallock 52a4a5cfda test(generator): add A/B test proving targetSkills works in problem generator
Validates that the problem generator correctly responds to targetSkills
constraints:

- With targeting: 100% of problems include the target skill
- Without targeting: ~20% include the skill (baseline random chance)

Tests multiple skills across categories (basic, fiveComplements,
tenComplements) with deterministic seeding for reproducibility.

This test helped identify that the issue with adaptive mode wasn't in
the problem generator itself, but in how targetSkills was being set
upstream in session-planner.ts.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 11:08:43 -06:00
Thomas Hallock 15b633f59a test(bkt): add comprehensive skill identification test for all 34 skills
Validates BKT's ability to identify weak skills using the real problem
generator with deterministic seeding:

- Generates 15,000+ problems covering all 34 skills (400+ per skill)
- Tests each skill by answering 200 correctly and 200 incorrectly
- Verifies BKT identifies each skill as weak (P(known) < 0.6)
- Achieves 100% identification accuracy with sufficient data

Key findings:
- With 100 problems per skill: 82.4% accuracy (6 skills escaped due to
  conjunctive model blame distribution)
- With 400 problems per skill: 100% accuracy (all skills correctly
  identified as weak)

This confirms BKT's conjunctive model works correctly - it just needs
sufficient data to overcome blame distribution effects when skills
co-occur.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 11:07:00 -06:00
Thomas Hallock 184cba0ec8 fix(blog): correct misleading claim about BKT feeding problem generation
BKT estimates power the Skills Dashboard display only. Problem generation
uses separate fluency states (effortless/fluent/rusty/practicing) from
PlayerSkillMastery records, not BKT P(known) estimates.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 13:56:38 -06:00
Thomas Hallock 9c313d5303 docs(blog): add conjunctive BKT skill tracing blog post
Describes the pattern tracing system for soroban practice:
- Soroban pedagogy as visual-motor patterns drilled to automaticity
- Simulation-based pattern tagging at problem-generation time
- Conjunctive BKT with probabilistic blame distribution
- Evidence quality weighting (help level, response time)
- Automaticity-aware problem complexity budgeting
- Honest uncertainty reporting with confidence intervals

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 13:54:03 -06:00
Thomas Hallock bf4334b281 feat(skills): add Skills Dashboard with honest skill assessment framing
Add new Skills Dashboard at /practice/[studentId]/skills that provides:
- Per-skill performance tracking with problem history drill-down
- Fluency state categorization (practicing, fluent, effortless, rusty)
- Skills grouped by category (basic, five/ten complements, etc.)

Use conservative presentation to honestly represent skill data:
- Show "Correct: N" and "In errors: M" instead of misleading "Accuracy: X%"
- Label sections "Appear Frequently in Errors" instead of "Need Intervention"
- Add disclaimer: errors may have been caused by other skills in the problem

This addresses the epistemological issue that incorrect answers only tell us
ONE OR MORE skills failed, not which specific skill(s) caused the error.

Files:
- New: SkillsClient.tsx, skills/page.tsx (Skills Dashboard)
- Updated: DashboardClient.tsx (link to skills page)
- Updated: SkillPerformanceReports.tsx (honest framing)
- Updated: session-planner.ts (getRecentSessionResults for problem history)
- Updated: server.ts (re-export new function)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 10:46:25 -06:00
Thomas Hallock 7a2390bd1b fix(practice): prevent stray "0" rendering in problem area
Fix JSX gotcha where `problemHeight && <Component />` would render "0"
when problemHeight is 0. Changed to `(problemHeight ?? 0) > 0 &&` which
always evaluates to a boolean.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 23:50:39 -06:00
Thomas Hallock 8851be5948 feat(practice): consolidate nav with transport dropdown and mood indicator
- Add SessionMoodIndicator with streak animations and touch-friendly popover
- Consolidate transport controls (pause/resume/browse/end) into dropdown menu
- Remove summary section from SessionProgressIndicator (moved to mood tooltip)
- Add DetailedProblemCard with skill annotations and complexity breakdown
- Add autoPauseCalculator for timing threshold calculations
- Add 0.75 opacity to zero-cost skill pills for reduced visual noise
- Clean up unused timing/estimate code from progress indicator

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 23:35:34 -06:00
Thomas Hallock c0764ccd85 feat(practice): add inline practice panel for browse mode debugging
Add PracticePreview component that allows practicing any problem while
in browse mode without affecting session state. The practice panel
displays inline below the problem card with a clear header indicating
it doesn't affect the session, preventing UX confusion.

- Add PracticePreview component with keyboard and numpad input support
- Add inline mode to PracticePreview for embedded display
- Update BrowseModeView to show practice panel below problem card
- Toggle button switches between "Practice This Problem" / "Close Practice Panel"

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 23:35:34 -06:00
Thomas Hallock 3c52e607b3 feat(practice): add browse mode navigation and improve SpeedMeter timing display
Browse Mode:
- Add SessionProgressIndicator with collapsible sections for practice/browse modes
- Add BrowseModeView for reviewing problems during practice
- Navigation via clicking progress indicator slots in browse mode
- "Practice This Problem" button to exit browse mode at current problem
- Collapse non-current sections in practice mode (shows ✓count or problem count)

SpeedMeter:
- Add actual time labels (0s, ~Xs avg, Xs pause) positioned under markers
- Extend scale to 120% of threshold so threshold marker isn't always at edge
- Kid-friendly time formatting (8s, 30s, 2m)
- Label overlap detection - combines labels when mean is close to threshold
- Remove unused averageLabel/fastLabel/slowLabel props

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 23:35:34 -06:00
Thomas Hallock 6c88dcfdc5 fix(practice): state-aware complexity selection with graceful fallback
The problem generator was failing with ProblemGenerationError when
minComplexityBudgetPerTerm was set (e.g., challenge slots requiring
complexity >= 1). The issue: skill complexity depends on abacus state,
not just the term value. Adding +4 from 0 is basic (cost 0), but adding
+4 from 7 triggers fiveComplements (cost 1).

The old algorithm filtered terms by minBudget during collection, causing
empty candidate lists when no term could meet the budget at that state.

Fix: Two-phase approach in collectValidTerms() that categorizes ALL
valid terms into meetsMinBudget and belowMinBudget arrays. The selection
prefers high-cost terms but gracefully falls back to lower-cost terms
when the budget is impossible to meet at the current abacus state.

Also adds 31 comprehensive tests covering state-dependent skill
detection, graceful fallback, edge cases, stress tests (1000 problems),
and performance verification.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 23:35:34 -06:00
Thomas Hallock 818fdb438d chore: remove debug logging from curriculum modules
Remove console.log statements added during production issue debugging.
The circular import issue causing REINFORCEMENT_CONFIG.creditMultipliers
to be undefined has been fixed. Retain console.error statements for
actual error conditions.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 14:25:47 -06:00
Thomas Hallock aae53aa426 debug: add comprehensive logging to trace REINFORCEMENT_CONFIG import issue
Adding logging at:
1. fluency-thresholds.ts module load time (before and after exports)
2. progress-manager.ts import time (to see what was imported)

This will help identify if:
- The module isn't loading at all
- The config object is partially defined
- There's a circular dependency timing issue

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 10:29:11 -06:00
Thomas Hallock 147974a9f0 fix(practice): fix circular import causing REINFORCEMENT_CONFIG.creditMultipliers to be undefined
The issue was that progress-manager.ts was importing REINFORCEMENT_CONFIG
from @/db/schema/player-skill-mastery, which re-exported it from
@/lib/curriculum/config. This created a circular dependency that caused
the config object's creditMultipliers property to be undefined in production.

Root cause found via logging:
```
REINFORCEMENT_CONFIG.creditMultipliers=undefined, helpLevel=0
```

Fix:
- Import REINFORCEMENT_CONFIG directly from @/lib/curriculum/config/fluency-thresholds
- Remove unused re-export from player-skill-mastery.ts

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 10:08:07 -06:00
Thomas Hallock 58192017c7 debug: add detailed logging to recordSkillAttemptWithHelp
This is the function that's actually failing according to the previous
logs. Adding logging around:
- getSkillMastery lookup
- REINFORCEMENT_CONFIG access
- Database update/insert operations

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 08:49:42 -06:00
Thomas Hallock f0a9608a6b fix(practice): move SessionPausedModal into ActiveSession for single pause state
- Move SessionPausedModal inside ActiveSession component (single source of truth)
- Replace studentName prop with student: StudentInfo for modal display
- Remove sessionRef imperative handle (no longer needed)
- Fix auto-pause re-triggering immediately after resume by resetting timer
- Update PracticeClient to remove duplicate modal and pause state
- Update stories to use new student prop

Previously, there were two pause states that could get out of sync:
1. PracticeClient's isPaused (controlled modal visibility)
2. ActiveSession's internal phase.phase === 'paused' (controlled input)

Now ActiveSession owns both the modal and the pause state, eliminating the sync issue.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 08:30:41 -06:00
Thomas Hallock 55e5c121f1 fix: sync pause state between modal and ActiveSession
When the auto-pause modal was dismissed via the Resume button, the modal
would hide but input would remain blocked. This was because:

1. PracticeClient has isPaused state (controls modal visibility)
2. ActiveSession has internal phase.phase='paused' (controls input acceptance)

When the modal's Resume button was clicked, only PracticeClient's state
was updated, leaving ActiveSession stuck in 'paused' phase.

Fix: Add sessionRef prop to ActiveSession that exposes resume/pause
handlers, allowing PracticeClient to trigger ActiveSession's internal
resume when the modal is dismissed.

Long-term: Consider moving the modal inside ActiveSession so there's
a single source of truth for pause state.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 08:07:10 -06:00
Thomas Hallock 8802418fe5 debug: add granular logging to recordSlotResult
Add detailed try-catch logging around each step in recordSlotResult:
- Before/after calculateSessionHealth
- Before/after Drizzle update
- Before/after recordSkillAttemptsWithHelp

This will pinpoint exactly where the "[0]" error occurs.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 07:58:15 -06:00
Thomas Hallock 85d36c80a2 fix(practice): add comprehensive logging and validation in recordSlotResult
Add detailed logging and more defensive checks to help debug production 500 errors:

- Log entry point with plan ID
- Wrap getSessionPlan in try-catch for better error messages
- Log plan state (status, partIndex, slotIndex, parts/results arrays)
- Validate slots array exists on current part
- Validate results array exists
- All checks provide descriptive error messages

This should help identify exactly where the "Cannot read properties of undefined (reading '0')" error is coming from.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 07:13:21 -06:00
Thomas Hallock a33e3e6d2b fix(practice): add defensive checks in recordSlotResult
Add robust error handling to recordSlotResult to prevent cryptic
"Cannot read properties of undefined" errors:

- Check that plan.parts exists and is a valid array before indexing
- Validate currentPartIndex is within valid bounds
- Ensure the database update returned a result

These checks provide clearer error messages when data is corrupted
or in an invalid state, helping diagnose production issues.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 06:48:39 -06:00