Skip to content

Completion-moment stats: longest streak and the Module Reward count, once derivable #149

Description

@maximsan

Corrected 2 Sep 2026 against main: both inputs now exist, so this ticket is unblocked. #141 landed (closed) — the active-day set is stored and the streak derives from it. And the card model now does distinguish the five module collectibles: CollectibleUnlock carries exactly one of lessonId / moduleId, and the bank holds 32 lesson-unlocked and 5 module-unlocked cards — the Module Reward count is a pure derivation, with no content-model change owed. The screen still shows the substitutions ('Cards collected' / 'Day streak', course_completion_screen.dart), so the ask stands.

Parent

Follow-up owed by #120's build notes: the two completion-moment stats #128 shipped as substitutions, upgraded to what the spec asked for once their inputs exist.

What to build

The Foundations completion moment shows three stats. Two shipped as flagged substitutions (#120 build notes) because their spec form was not derivable at the time:

  1. "Day streak" → longest streak. The spec asks for the learner's longest streak. Once the active-day set is wired (Tracer bullet: a completed lesson records its day and the streak everywhere derives from the day set #141 and the §10 fold), longest streak is a pure derivation over stored days — compute it, show it, and label it honestly ("Longest streak"). No new field: deriving is the point, and a stored copy is exactly the Sync scope and the persisted data model #14-shaped decision the build notes refused to make silently.
  2. "Cards collected" → the Module Reward count. The spec names "every Field Guide" — the five module collectibles — but the card model does not distinguish them from lesson cards. Settle the distinction by derivation (e.g. the module-reward ids are already known content-side), not by a new stored marker; if it needs a content-model change, that goes back to the extractor's owner as a question first.

Both changes are label + derivation swaps on the existing screen; the moment's structure, firing rule and hand-off are untouched.

Acceptance criteria

  • The completion moment shows the longest streak, derived purely from the active-day set — verified against a day set holding a broken streak (e.g. 5 then gap then 3 shows 5)
  • The Module Reward stat counts exactly the module collectibles, derived, with the derivation's source named in the code
  • No new stored field anywhere; a reset leaves nothing behind
  • The screen's stat labels match what they actually show
  • Unit tests cover the longest-streak derivation (gap cases) and the Module Reward derivation; the existing widget tests are updated to the new labels
  • The two Keep Sharp on Today, and the Foundations completion moment #120 build notes are answered with a pointer to this ticket's PR

Blocked by


Vocabulary note (22 Aug 2026). This ticket was written when the five module collectibles were called Field Guides. CONTEXT.md now rules them Module Rewards and lists Field Guide under Avoid (#106, swept in #222). The title and criteria above are updated; the quoted phrase "every Field Guide" is left as-is, because it quotes the design spec as it was written. Nothing this ticket asks for changes — only the name of the thing being counted.


Built — PR #557. What verified each box: the longest-streak cases (5-gap-3 reads 5, three-then-five reads 5, an abandoned history keeps its best, a freeze-covered miss joins one run rather than two) are in streak_engine_test.dart, and the property test's day-by-day oracle now derives the longest run independently and agrees. The Module Reward count is checked against the real bank in collected_module_rewards_test.dart — 5 of 37, and 5 for a full collection. Nothing is stored: both come off the day set and the owned-collectible set a reset already clears. The screen was driven on a simulator with a nine-day run behind a gap and a live three-day one, and showed 9.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfeature-buildA screen or mechanic the app does not have yetready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions