Commit Graph

3053 Commits

Author SHA1 Message Date
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
github-actions[bot] d8dee1d746 🎨 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-14 02:41:33 +00: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
Thomas Hallock 9f07bd6df6 refactor(practice): remove deprecated masteryLevel column
Complete Phase 9 of isPracticing migration by removing all traces of the
deprecated 3-state masteryLevel system:

Schema changes:
- Remove masteryLevel column from player_skill_mastery table
- Remove MasteryLevel type export
- Remove MASTERY_CONFIG constant
- Remove calculateMasteryLevel function

Code cleanup:
- Remove masteryLevel from all insert/update operations in progress-manager
- Remove getSkillsByMasteryLevel function and export
- Remove masteryLevel from SkillPerformance interface
- Remove masteryLevel from SkillMasteryData interface in usePlayerCurriculum
- Remove deprecated dbMasteryToState and buildStudentSkillHistory functions
- Remove deprecated tests for removed functions

The system now uses:
- isPracticing: boolean - Set by teacher via checkbox
- FluencyState - Computed from practice history (practicing/effortless/fluent/rusty)
- MasteryState - For cost calculation (adds not_practicing for non-practicing skills)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 21:03:20 -06:00
Thomas Hallock 49d3a8c2d6 chore: remove unused LEGACY_PART_BUDGETS
Dead code that was never used - marked deprecated but had no consumers.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 20:16:51 -06:00
Thomas Hallock b2e7268e7a feat(practice): migrate mastery model to isPracticing + computed fluency
Major refactor of skill mastery tracking system:

**Schema Changes:**
- Add `isPracticing` boolean column to player_skill_mastery
- Fluency state (effortless/fluent/rusty/practicing) now computed from
  practice history instead of stored
- Keep masteryLevel for backwards compatibility (to be removed later)

**Session Planning:**
- Update session-planner to use new isPracticing model
- Add part-type-specific challenge ratios (abacus: 25%, visualization: 15%, linear: 20%)
- Skip challenge slots for students with only basic skills (cost 0)
- Add NoSkillsEnabledError for clear feedback when no skills enabled

**Complexity Budget System:**
- First term exempt from min budget (basic skills always have cost 0)
- Update ProblemDebugPanel to show min/max budgets and term costs
- Create centralized config directory for tuning parameters

**UI Improvements:**
- ManualSkillSelector now uses isPracticing checkboxes
- StartPracticeModal shows specific error for "no skills enabled"
- Better error messages throughout

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 20:10:46 -06:00
Thomas Hallock 59f574c178 fix(practice): improve dark mode contrast for sub-nav buttons
- Adjust pause/play button colors for better dark mode contrast
- Restyle stop button with proper dark/light mode variants
- Use conditional colors based on isDark for all button states

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 07:41:45 -06:00
Thomas Hallock 2fca17a58b feat(practice): integrate timing display into sub-nav with mobile support
- Move timing display from ActiveSession to PracticeSubNav
- Add per-part-type timing stats (abacus/visualization/linear calculated separately)
- Pass timing data from PracticeClient through sessionHud
- Add responsive mobile styles:
  - Smaller padding and gaps on mobile
  - Hide student name during session on small screens
  - Hide part type text label (keep emoji)
  - Compact timing display with hidden SpeedMeter on very small screens
  - Hide health indicator on small screens
- Add comprehensive Storybook stories for PracticeSubNav covering:
  - Dashboard states, session part types, progress states
  - Timing display states, health indicators
  - Dark mode, mobile/tablet viewports, edge cases

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 07:07:14 -06:00
Thomas Hallock 18ce1f41af feat(practice): add response time tracking and live timing display
- Fix response time bug: exclude pause duration from calculations
- Add global per-kid stats tracking with new DB columns
- Create SkillPerformanceReports component for dashboard
- Add PracticeTimingDisplay with live problem timer and speed meter
- Extract SpeedMeter to shared component
- Add defensive handling for empty JSON in abacus-settings API

New features:
- Live timer showing elapsed time on current problem
- Speed visualization bar showing position vs average
- Per-part-type timing breakdown (abacus/visualize/linear)
- Skill performance analysis on dashboard (fast/slow/weak skills)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 18:23:03 -06:00
Thomas Hallock 0c40dd5c42 fix(practice): ensure speed meter bar is always visible
Add minimum width (8%) for the variation bar so it's always visible,
even for very fast students with small absolute values. Clamp mean
position between 5-95% for edge cases.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 17:07:51 -06:00
Thomas Hallock f45428ed82 fix(practice): use inline styles for progress bar
Fix progress bar not rendering by using inline styles instead of
Panda CSS, similar to the resume button fix.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 17:06:37 -06:00
Thomas Hallock cc5bb479c6 fix(practice): correct pause phrase attribution
Auto-pause (system pressed): "We pressed paws!", "This one's a thinker!"
Manual pause (kid pressed): "You pressed paws!", "Good call!", "Smart break!"

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 17:04:47 -06:00
Thomas Hallock 652519f219 feat(practice): separate phrase sets for manual vs auto pause
Auto-pause phrases (thinking-themed):
- "This one's a thinker!", "Brain at work!", "Processing...", etc.

Manual pause phrases (break-themed):
- "We pressed paws! 🙏", "Break time!", "Taking five!", etc.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 17:03:45 -06:00
Thomas Hallock 4800a48128 fix(practice): update pun to "We pressed paws!"
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 17:02:16 -06:00
Thomas Hallock 8405f64486 feat(practice): add "Press paws!" pun to auto-pause phrases
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 17:01:16 -06:00
Thomas Hallock c13feddfbb feat(practice): inline emoji with random pause phrases
- Put emoji (🤔/) and phrase on same line in header
- Add random phrases for auto-pause: "This one's a thinker!", "Brain at
  work!", "Deep thoughts happening...", etc.
- Horizontal layout with avatar on left, title/timer on right
- Smaller avatar (56px) for more compact layout

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 17:01:00 -06:00
Thomas Hallock 0ee14a71b6 refactor(practice): simplify paused modal header layout
Consolidate the stacked header elements into a cleaner layout:
- Single title: "This one's a thinker!" or "Break Time!"
- Timer integrated inline below title instead of separate pill
- Removed redundant name repetition and "Taking a Thinking Break!"

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:58:21 -06:00
Thomas Hallock 80a33bcae2 feat(practice): add play emoji to Keep Going button
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:55:53 -06:00
Thomas Hallock 366a1f4b83 refactor(practice): remove manual pause encouragement message
Remove the "Smart thinking to take a break" element to declutter the
paused modal layout.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:55:38 -06:00
Thomas Hallock dd3dd4507c fix(practice): fix invisible resume button by using inline styles
Switch from Panda CSS to inline styles for the resume button to ensure
the green background and white text are properly applied.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:55:10 -06:00
Thomas Hallock 883b683463 refactor(practice): replace "hide" text with close button on stats panel
Add an × close button in the top-right corner of the stats visualization
panel instead of toggling "hide" text in the main paragraph.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:54:11 -06:00
Thomas Hallock a892902e8a refactor(practice): improve paused modal UX based on feedback
- Remove "We Know Your Rhythm!" heading (creepy)
- Combine explanation text above the bar: "Usually you take X. This one
  took longer, so we paused to check in."
- Hide stats visualization behind low-key "really?" toggle
- Make resume button much more prominent with deeper green, border,
  larger padding, and stronger shadow
- Make "end session" button less prominent with smaller text, muted
  color, and stop emoji

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:53:25 -06:00
Thomas Hallock 0d17809330 refactor(practice): remove stats panel for default timeout case
Only show the rhythm/stats panel when we have enough data to display
meaningful statistics. For the default 5-minute timeout case (before
we've collected enough samples), just show the standard pause UI.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:49:35 -06:00
Thomas Hallock 3f61dbc0b5 refactor(practice): use Intl.NumberFormat for time duration formatting
Replace hand-rolled duration formatting with Intl.NumberFormat using
the 'unit' style. This leverages browser-native localization for
pluralization (1 second vs 2 seconds) and proper formatting.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:47:18 -06:00
Thomas Hallock 11ecb385ad feat(practice): redesign paused modal with kid-friendly statistics UX
Redesign SessionPausedModal to be approachable for children while
maintaining high-fidelity statistical information:

- New visual components:
  - SpeedMeter: shows average response time vs variation range
  - SampleDots: visualizes progress toward learning user's rhythm (5 samples)

- Educational framing:
  - "We Know Your Rhythm!" when we have enough samples
  - "Learning Your Rhythm..." when collecting data
  - "Taking a Thinking Break!" instead of clinical "paused" language

- Friendly UI improvements:
  - Contextual emoji thought bubbles (🤔 for auto-pause,  for manual)
  - Encouraging messages ("Smart thinking to take a break!")
  - "Keep Going!" button instead of "Resume"
  - Progress bar with gradient styling

- Statistical transparency:
  - Shows "Usually you take about X seconds" for mean
  - Visual representation of standard deviation as "wiggle room"
  - Explains why the pause happened in child-friendly terms

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:46:06 -06:00
Thomas Hallock 826c8490ba feat(practice): add pause info with response time statistics to paused modal
- Add auto-pause when response time exceeds mean + 2σ (or 5min default)
- Track pause reason (manual vs auto-timeout) and timing info
- Display live-updating pause duration counter
- Show statistical details: sample count, mean, std dev, threshold
- For insufficient data, show "need X more problems for personalized timing"
- Add comprehensive Storybook stories for all pause scenarios

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 16:38:17 -06:00
Thomas Hallock 9c1fd85ed5 feat(practice): add auto-pause and improve docked abacus sizing
- Add auto-pause when user takes too long on a problem
  - Uses mean + 2 standard deviations of response times when ≥5 problems
  - Falls back to 5 minute timeout otherwise
  - Clamped between 30 seconds and 5 minutes

- Fix docked abacus to auto-scale to match problem dimensions
  - AbacusDock now uses width: 100% and measured problem height
  - MyAbacus calculates effectiveScaleFactor based on container size
  - Animations use consistent scale calculations

- Fix bug: session paused modal no longer shows on page reload
- Fix bug: help mode now exits when both overlay and panel are dismissed

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-11 15:01:45 -06:00