Skip to content

feat: FSRS scheduler behind an adapter with prioritized daily review queue — closes #5 - #24

Merged
mikim merged 2 commits into
mainfrom
feat/fsrs-scheduler
Jul 14, 2026
Merged

feat: FSRS scheduler behind an adapter with prioritized daily review queue — closes #5#24
mikim merged 2 commits into
mainfrom
feat/fsrs-scheduler

Conversation

@mikim

@mikim mikim commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the fixed-interval stub (24h/72h/7d/14d forever) with real FSRS-based spaced repetition (#5), wired end to end: adapter → raw ReviewEvent log → prioritized daily queue → API.

Closes #5

Acceptance criteria → done

Criterion How
FSRS behind an adapter (no hand-rolling) ts-fsrs@5.4 fully isolated in adapter.ts; callers see plain ratings + opaque JSON state; deterministic (fuzz off, tested)
Raw ReviewEvents preserved (rating/latency/algorithm) → recomputable POST /api/review/events appends rating, latencyMs, ts-fsrs v5.4.1 using FSRS-6.0 version string, and verbatim before/after state; integration test proves event N+1's before-state === event N's after-state
Daily queue: due + overdue, prioritized by weak topics / recurring causes buildDailyQueue: priority = recurrenceCount × 10 000 + overdueMinutes — a recurring error (failed transfer) outranks ~7 days of overdue time; deterministic tiebreak
Validation for invalid dates/counts SchedulerValidationError on invalid dates, corrupted state, negative/non-integer counts (422 at the API for corrupt stored state)
Tests for interval progression and due detection Interval growth past the old 14-day cap, lapse-vs-easy behavior, JSON round-trip, determinism, queue filtering/ordering

Also

Verified

14 scheduler unit tests + 5 CI Postgres integration tests; lint/typecheck/build/smoke green locally.

🤖 Generated with Claude Code

mikim and others added 2 commits July 14, 2026 17:26
…queue

Replaces the fixed-interval stub (24h/72h/7d/14d, capped forever at 14
days) with real spaced repetition (#5).

@study-os/scheduler rework:
- ts-fsrs 5.4 isolated behind adapter.ts — no hand-rolled algorithm;
  callers see only plain ratings (again/hard/good/easy) and opaque
  JSON-serializable card-state snapshots (ISO dates)
- SCHEDULER_ALGORITHM version string stamped on every application so
  schedules are recomputable from raw events when the algorithm changes
- Deterministic: fuzz off; identical (state, rating, now) -> identical
  output (tested)
- applyReview() covers first-review (null state) and chained state;
  validation rejects invalid dates, corrupted state, unknown ratings
- buildDailyQueue(): due/overdue detection with documented priority
  recurrenceCount * 10_000 + overdueMinutes — a recurring error
  (failed transfer attempt) outranks ~7 days of overdue time; ties
  break deterministically
- Legacy exports removed (defaultReviewSchedule, getNextReviewDate,
  buildReviewTask, getDueReviewTasks); the deprecated
  ErrorNotebookEntry / ReviewTask / ErrorType / ReviewStatus core types
  are deleted as promised in #2

API wiring:
- POST /api/review/events: applies FSRS and appends the raw
  ReviewEvent — rating, latency, algorithm version, before/after state,
  scheduledAt; 404 unknown episode, 400 bad rating, 422 corrupt state
- GET /api/review/queue: loads active episodes with latest event state
  (or an immediately-due initial card) + failed-transfer counts and
  returns the prioritized queue
- Demo route now exercises the FSRS adapter at runtime; smoke updated

Tests: 14 scheduler unit tests (interval growth past the old 14-day
cap, lapse behavior, serialization round-trip, determinism, queue
priority/validation) + 5 CI integration tests (queue ordering with a
recurring episode, raw-event chain where event N+1's before-state
equals event N's after-state, 404/400 paths).

Closes #5

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- before-state on a first review is the explicit initial card (reps 0),
  not null — better for recomputation
- FSRS-6 does not count a lapse for 'again' on a learning-phase card

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mikim
mikim merged commit 786df00 into main Jul 14, 2026
1 check passed
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.

Build daily review queue generation

1 participant