feat: FSRS scheduler behind an adapter with prioritized daily review queue — closes #5 - #24
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Replaces the fixed-interval stub (24h/72h/7d/14d forever) with real FSRS-based spaced repetition (#5), wired end to end: adapter → raw
ReviewEventlog → prioritized daily queue → API.Closes #5
Acceptance criteria → done
ts-fsrs@5.4fully isolated inadapter.ts; callers see plain ratings + opaque JSON state; deterministic (fuzz off, tested)POST /api/review/eventsappends rating, latencyMs,ts-fsrs v5.4.1 using FSRS-6.0version string, and verbatim before/after state; integration test proves event N+1's before-state === event N's after-statebuildDailyQueue: priority =recurrenceCount × 10 000 + overdueMinutes— a recurring error (failed transfer) outranks ~7 days of overdue time; deterministic tiebreakSchedulerValidationErroron invalid dates, corrupted state, negative/non-integer counts (422 at the API for corrupt stored state)Also
ErrorNotebookEntry/ReviewTaskcore types are deleted (promised in Design the ErrorEpisode remediation data model (replaces error-notebook model) #2).Verified
14 scheduler unit tests + 5 CI Postgres integration tests; lint/typecheck/build/smoke green locally.
🤖 Generated with Claude Code