In one sentence
As a learner moves through a lesson the app tells micro-learning, which keeps the authoritative progress record, mirrors it to assignment-service so the admin roster is accurate, issues a certificate on completion, and maintains the streak and points that make people come back tomorrow.
The app reports
The mobile app calls micro-learning's progress endpoints as the learner starts, advances through and finishes a lesson. micro-learning stores this and emits per-lesson events: learning.lesson.started, .progressed, .completed, .abandoned (micro-learning-service-v2/app/routes/progress_mobile_routes.py:783, :900, :1264, :1514). These are telemetry — they are stored on the stream and, today, read by nobody.
Quiz answers follow the same path with scoring on top; the quiz graph decides what the learner sees next based on how they answered.
Mirroring to assignments
sequenceDiagram autonumber participant APP as Mobile app participant ML as micro-learning participant AS as assignment-service APP->>ML: lesson progress ML-->>AS: training.progress.assignment_progress Note over AS: mirror onto assignment_progress rows APP->>ML: last lesson finished ML-->>AS: training.module.completed AS-->>AS: drive assignment to 100%
Two different signals, both live, and the distinction matters:
- Partial progress. After each lesson micro-learning publishes
training.progress.assignment_progress(app/events/events.py:125, called from progress_mobile_routes.py:1344). assignment-service mirrors it onto its own rows. This is why an admin roster shows "3 of 8 lessons" rather than a flat zero, and why the reminder engine stops nagging somebody who is visibly making progress. - Completion. When the last lesson is done, micro-learning publishes
training.module.completed(app/events/events.py:80). assignment-service resolves the assignments for that person and module, drives each to 100%, and fires the completion notification.
Completion & certificates
Certificates are issued inside micro-learning — there is no certificate service any more, despite the leftover cluster objects. On completion it emits training.certificates.module_completed (app/events/events.py:257), and the learner can fetch their certificate from the app.
Streaks, points and leaderboards
A scheduler scans daily for learners whose streak is about to lapse — evaluated in their local timezone, so a Tokyo learner is not warned on London's clock — and publishes learning.streak.at_risk.ten.<tenant> (app/services/streak_reminder_scheduler.py:183). notification-worker delivers it, respecting quiet hours: losing a streak is not worth waking somebody up.
Leaderboard movements are published as learning.leaderboard.*.ten.* (app/services/leaderboard_diff_service.py:664) and consumed on learning.leaderboard.>.
ARCHITECTURE.md §4.1(3) is out of date on this point. It records that streak notifications can never fire because the publisher emits a flat learning.streak.at_risk while the consumer filters …ten.*. The current publisher appends .ten.<tenant>, so both sides are five tokens and the path works. Verified from both ends — see the publisher and the consumer.
Where it breaks today
| Issue | Detail |
|---|---|
| Per-lesson telemetry is written and never read | The four learning.lesson.* events are stored on the MICRO_LEARNING stream via learning.> but no consumer filter matches them. If the intent was analytics, it was never built. See the micro-learning verdicts. |
| Two dead branches in the progress router | assignment-service's progress consumer routes three branches; only assignment_progress has a producer. The lesson-completion and completion branches are unreachable. The consumer itself is live — see the assignment verdicts. |
| Stale comments and reports | The worker's own comment claims the training.progress.> subscription has no producer; bug-hunt #8 and the earlier review say the same. All three are now out of date. Flagged for a later code-comment fix; no code was changed by these docs. |
| Module lifecycle events go nowhere | training.module.created, .archived, .assigned and user.notification.training have no consumer, and three of the four have emitters with no call site at all. |