Daily missions architecture¶
Daily missions (EXP-010, Phase 56) add active, opt-in motivation on top of the passive XP/badge/streak rewards. They run in both storage modes and add exactly one tracking table.
Pieces¶
| Module | Role |
|---|---|
plugins/.../missions/templates.yaml |
Static mission catalog (22 templates, 5 categories), synced to the frontend by make sync-missions. |
MissionTemplate (Pydantic) |
Catalog entry shape (config, NOT a table). |
UserMission (model) |
The one new table: per-user/per-day assignment + progress + xp_awarded guard. Alembic 0021, Dexie v20, sync surface (MUTABLE). |
lib/missions/generator.ts + generator.py |
Deterministic (seeded PRNG) adaptive selection: one pick per difficulty slot, eligibility by history (new/active/veteran), no back-to-back repeats. |
lib/missions/checks.ts + SUPPORTED_CHECK_FUNCTIONS |
Only checks computable from existing data are assignable (the other 5 catalog entries stay un-assigned until tracking exists). |
lib/missions/progress.ts |
Pure check_function + stats snapshot -> {current, target, completed}. |
lib/missions/schedule.ts |
Local-midnight today (language->timezone) + streak-joker. |
storage/missions-dexie.ts |
Dexie: gather a "today" stats snapshot, assign idempotently, evaluate, award XP on completion. |
missions plugin service.py |
Same flow against SQLAlchemy for API mode (GET /today, POST /regenerate). |
Flow¶
- The dashboard widget (or lesson completion) calls
getStorage().missions.getDaily(userId, {todayIso}). - If no rows exist for that local day, the generator assigns a
fresh set (seeded by
userId + date, excluding yesterday's). - Progress is re-evaluated against existing data
(LessonProgress / ElementError / streak). A newly-completed
mission flips
completedand, once, awards itsxp_reward(idempotent viaxp_awarded). celebrateMissions(celebration bus) plays the mission sound + surfaces a praise phrase; an all-clear plays the bigger sound + a confetti burst.
Rules¶
- Deterministic per (userId, date); a user uses one storage backend, so cross-backend exact parity is not required.
- No tracking beyond
UserMission; un-trackable checks are not assigned. - Missions are supplementary - a failure never breaks the lesson flow (all reads are defensive). No penalty for missed days.