Skip to content

exercise architecture, migrate interval classifier

jedmund requested to merge jedmund/exercise-architecture into main

Created by: jedmund

Summary

Lays down a shared architecture for scored exercises and migrates the first two off the old ad-hoc pattern (Interval Classifier, Interval Trainer). Adds a new app icon along the way.

Shared exercise architecture

  • ExerciseState tracks progress and results; ScoringOverlay wraps content with progress dots and a completion screen; PickerWidget handles multiple-choice answers with adaptive grid columns (2 / 3 / 4 based on choice count).
  • ExerciseLayoutContext environment switches exercises between standalone (centered glass card) and lesson-page (context text above card) presentations without duplicating views.
  • ExerciseHost, InteractionWidget, and ToolBridge are scaffolding for future exercises that drive a tool. Not yet wired — we intentionally deferred ToolBridge until a keyboard-driven exercise forces its design.
  • ProgressDotsView reworked: filled circles with design tokens (.accentColor / .incorrectWarm), visible on glass.
  • ActivityStateRegistry.scoredExerciseState(for:) + progressStore integration so progress survives navigation and gets persisted across sessions.

Interval Classifier

  • Rebuilt on the shared stack. Static-prompt JSON, categorical answers.
  • New design baseline: glass card, .thinMaterial picker buttons, centered feedback with inline bold answer, filled-circle feedback icon matching progress dots, small capsule Next button, progress dots hidden during feedback state.
  • Old IntervalClassifierView and its ToolViewFactory case removed.

Interval Trainer

  • Same visual baseline as Interval Classifier.
  • First exercise with procedural prompt generation (random root in range + random interval from active pool). ExerciseState handled it without changes, confirming the model is generation-agnostic.
  • Streak-based interval unlocks preserved (silent, no toast). Streak/unlock bookkeeping stays local to the exercise view — it's domain-specific, not universal.
  • IntervalTrainerConfig gained a context field (the lesson JSON already had one that was being silently dropped).
  • Old IntervalTrainerView and its ToolViewFactory case removed.

App icon

  • New waveform glyph on cream. Dark/tinted slots registered but unfilled for now (fall back to default). Build number bumped to 3.

Test plan

  • Standalone Interval Classifier: Exercises grid → tap → listen, pick, advance, complete. Retry from completion resets dots.
  • Standalone Interval Trainer: Exercises grid → tap → same loop. After 3 correct, new intervals silently appear in the picker; grid stays readable.
  • Lesson with each exercise: context text renders above card, progress persists across navigation away and back.
  • Exercise completion records an attempt via progressStore and allows scrolling past the gate.
  • App icon renders in Springboard and reflects correctly on TestFlight / App Store Connect upload.

Merge request reports

Loading