Skip to content

feat(07): Spaced repetition study system (SM-2) - #49

Open
tpaulshippy wants to merge 9 commits into
mainfrom
feature/roadmap-07-spaced-repetition
Open

feat(07): Spaced repetition study system (SM-2)#49
tpaulshippy wants to merge 9 commits into
mainfrom
feature/roadmap-07-spaced-repetition

Conversation

@tpaulshippy

@tpaulshippy tpaulshippy commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Roadmap 07: Spaced Repetition Study

Implements #59.

What changed

  • Backend: SM-2 scheduling algorithm (srs.py), due_at/interval/ease/reps/lapses fields on Flashcard, FlashcardReview log model, POST .../review/ endpoint, GET .../study_queue/ with due filtering, deck annotations (due_count, last_studied_at)
  • Frontend: Study mode with card flip animation, Again/Hard/Good/Easy ratings with <1d/1d/3d/7d hints, progress tracking, completion summary, due badges on deck list
  • Scheduling: Again→relearn (4h), Hard→interval×1.2, Good→1d→6d→interval×ease, Easy→×1.3 bonus; ease floored at 1.3
  • Tests: 126 backend (44 new incl. table-driven SM-2 tests + timezone-aware due filtering), 55 frontend, e2e walkthrough

Demo

  1. Seed: manage.py seed_e2e_spaced_repetition → 8 Cell Bio cards (6 due)
  2. Open deck → Study (6) → flip cards → rate Again/Hard/Good/Easy
  3. Completion: summary shows reviewed count, next due time
  4. Re-open: badge vanishes, study says 'Nothing due 🎉'

Evidence

Spaced Repetition screenshot

https://github.com/tpaulshippy/bots/raw/feature/roadmap-07-spaced-repetition/evidence/pr49-spaced.mp4

…endpoints, deck annotations

Implements docs/roadmap/07-spaced-repetition-study.md backend sections:
- Flashcard scheduling fields (due_at, interval_days, ease, reps, lapses,
  last_reviewed_at) with due_at=now default so new cards are studyable
  immediately, including agent-created ones
- Pure apply_sm2(card, rating, now) in bots/services/srs.py (Again/Hard/
  Good/Easy), unit-tested and swappable; no math in the viewset
- POST /api/decks/{deck_id}/flashcards/{id}/review/ reschedules via srs.py
  and writes a FlashcardReview log row
- GET /api/decks/{deck_id}/study_queue/?mode=due|all&limit=N ordered by
  due_at asc (nulls last)
- Deck list/detail serializers gain due_count + last_studied_at annotations
- FlashcardViewSet now scopes lookups to the requesting user's decks and
  requires IsAuthenticated, so foreign decks 404 like every other resource
- seed_e2e_spaced_repetition management command (idempotent) for Detox runs
…ngs, due badges

Implements docs/roadmap/07-spaced-repetition-study.md frontend sections:
- flashcards/study.tsx: study queue via GET study_queue?mode=due with
  'Study all anyway' fallback, rotateY flip animation, rating row
  (Again/Hard/Good/Easy with interval hints) revealed after the flip,
  review POST per rating, haptics (warning on Again, light on others),
  session summary (reviewed count, Again count, 'Next due in X'), testIDs
  on all new interactive elements
- flashcards.tsx list rows: red 'N due' badge + 'Last studied X ago' line
- flashcards/deck.tsx: Study button label 'Study (N)' from deck.due_count,
  passes mode=due; study-button testID
- api/flashcards.ts: scheduling field types, fetchStudyQueue,
  reviewFlashcard
- drawer/menu button testIDs for e2e navigation
- typecheck fixes for pre-existing errors in __mocks__/handlers.ts and
  __tests__/api/{apiClient,bots,profiles,aiModels}.test.ts so tsc is clean;
  new fetchStudyQueue/reviewFlashcard tests in flashcards.test.ts
- package.json: run jest with --watchman=false (local watchman daemon broken)
…ngs, due badges

Implements docs/roadmap/07-spaced-repetition-study.md frontend sections:
- flashcards/study.tsx: study queue via GET study_queue?mode=due with
  'Study all anyway' fallback, rotateY flip animation, rating row
  (Again/Hard/Good/Easy with interval hints) revealed after the flip,
  review POST per rating, haptics (warning on Again, light on others),
  session summary (reviewed count, Again count, 'Next due in X'), testIDs
  on all new interactive elements
- flashcards.tsx list rows: red 'N due' badge + 'Last studied X ago' line
  (deck-row-* / deck-due-badge-* testIDs)
- flashcards/deck.tsx: 'Study (N)' button from deck.due_count, mode=due
  param, study-button testID
- api/flashcards.ts: scheduling field types, fetchStudyQueue,
  reviewFlashcard, Deck.due_count/last_studied_at
- drawer-item-flashcards + drawer-menu-button testIDs for e2e navigation
- new fetchStudyQueue/reviewFlashcard tests in flashcards.test.ts

Note: these changes were previously stashed by an external checkpoint tool
(stash@{0}, commit e64f5ad) mid-session; restored from that stash.
front/e2e/07-spaced-repetition.e2e.js models chatImageUpload.e2e.js:
JWT auth against API_BASE/token/, profile/bot/deck fetched from the seed
data, AsyncStorage injected via simctl, then drawer -> Flashcards ->
'Cell Bio' deck -> Study -> six cards rated Again/Hard/Good/Easy ->
session-complete summary -> Done. Verifies next-due state afterwards:
study_queue?mode=due returns [], the deck shows the nothing-due state with
'Study all anyway', and the list row's red due badge disappears.
Header comment documents the idempotent
seed_e2e_spaced_repetition management command and env vars.
back-button testID added to shared BackButton for navigation.
@tpaulshippy tpaulshippy changed the title feat(07): Spaced repetition study system feat(07): Spaced repetition study system (SM-2) Aug 25, 2026
opencode added 3 commits August 26, 2026 03:56
- Backend: remove unused studied variable, fix import ordering
- Frontend: replace useRef with useState for Animated.Value and Date.now()
- Frontend: add jest globals to eslint config
tpaulshippy pushed a commit that referenced this pull request Aug 26, 2026
Screenshot shows the flashcards deck list with Biology 101 (12 cards) and World History (8 cards).
Screenshot shows the flashcards deck list with Biology 101 (12 cards) and World History (8 cards).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant