Skip to content

Schedule by SM-2 or FSRS-7, keeping both up to date - #989

Draft
ixuz wants to merge 4 commits into
mainfrom
fsrs-7
Draft

ixuz wants to merge 4 commits into
mainfrom
fsrs-7

Conversation

@ixuz

@ixuz ixuz commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Before this can merge: FSRS-7 upstream readiness

This PR schedules with a port of FSRS-7 checked against ts-fsrs@6.0.0-beta.13 (pinned exactly, used only to generate test vectors). FSRS-7 is not yet in a stable release, so this stays a draft until the items below are done.

Blocking

  • ts-fsrs v6 stable is released: open-spaced-repetition/ts-fsrs#373 (TS-FSRS v6 Roadmap). Feature-complete as of 2026-09-30; the stable release is planned for 2026-10-28. FSRS-7 itself landed in ts-fsrs#520.
  • fsrs-rs publishes a release with FSRS-7: fsrs-rs releases. FSRS-7 is only on main; the latest release, v6.6.2, has none of it. fsrs-rs is the reference ts-fsrs's FSRS-7 is checked against. How FSRS model versions will map to crate versions is open in fsrs-rs#446.

Watch (does not affect the default weights this PR ships with)

  • fsrs-rs#445: parameter clipping drops the 0.5 lower bound on w[26]. Matters only once we fit personal weights (planned follow-up).

Already resolved upstream (for context)

  • fsrs-rs#459: FSRS-7 SM-2 stability conversion fixed; our memoryFromSm2 ports the fixed version.
  • JSchoreels/anki#6 (closed): FSRS-7 must be given fractional elapsed time; this PR does so from exact review timestamps.

Then, in this PR

  • Bump ts-fsrs in the root package.json to the stable release, run node scripts/generateFsrsVectors.ts, and review any change in packages/domain/src/testing/fsrsVectors.ts. A change means the formulas moved after the beta: update packages/domain/src/fsrs.ts, and decide whether review-state format 3 still describes the stored memory.
  • Re-run npm run check and the blocking pod suite (npm run test:pod).

ixuz added 4 commits October 3, 2026 17:50
node-solid-server keeps a decimal such as "1.0"^^xsd:decimal as "1", but
serves it back as the Turtle shorthand 1.0. A later PATCH that deletes
the value as read, "1.0", finds nothing to delete there and fails with
409. Deletions now spell a whole-number decimal as the integer it is
("1"), which every server tested deletes as stored, and which is how the
app writes it itself. Insertions are sent as they are.

No decimal the app wrote so far was a whole number; FSRS's difficulty,
clamped to 1 and 10, will be.
fsrs.ts is FSRS-7, the Free Spaced Repetition Scheduler's dual-trace
model, ported from ts-fsrs (itself a port of fsrs-rs): the memory after
an answer given any fractional number of days after the last, the
probability of recall, the interval for a desired retention (Newton
steps, then bisection), and a memory estimated from an SM-2 interval for
a prompt FSRS has not seen (fsrs-rs's memory_state_from_sm2, which for
FSRS-7 uses no ease factor). The domain imports no vendor code, so the
formulas are ported rather than imported.

scripts/generateFsrsVectors.ts writes test vectors with ts-fsrs, pinned
exactly as a root dev dependency (6.0.0-beta.13, the first releases with
FSRS-7): 36 sequences of answers over the default weights and two
perturbed sets, at elapsed times from seconds to years. The port matches
every memory and retrievability to nine significant digits, and every
interval to six. Nothing uses the port yet.
Every answer now moves both algorithms on (nextReviewState): SM-2's ease
and repetitions, and FSRS-7's memory, given the exact time since the
prompt's last answer. The instance's scheduler alone decides the
interval, so switching takes effect at the next review, either way. A
prompt FSRS has not seen gets a memory estimated from its SM-2 interval
first. FSRS's interval is the time until recall falls to the desired
retention, fuzzed, kept in order across ratings, and at least a day: a
card forgotten today comes back within the session, not on another day.

Review-state format 3 adds the memory (sm:stability, sm:stabilityFast,
sm:difficulty) and the same in the undo snapshot, each all or nothing;
the step from format 2 changes nothing. Preferences format 4 adds
sm:scheduler, a concept of the new sm:Schedulers scheme, and
sm:desiredRetention (0.70 to 0.97); the step from format 3 keeps SM-2.
A new instance's preferences are written at creation: FSRS, with the
Again/Hard/Good/Easy scale. An instance without preferences still
studies by SM-2, so nobody is rescheduled unasked.

The preferences screen chooses the scheduler and the desired retention,
and, once FSRS is saved, offers to reschedule every reviewed prompt's
due day as FSRS would have set it (rescheduleWithFsrs), after a
confirmation, saying how many moved sooner and later. Resetting a study
day restores the morning's memory. The repair of a half-written snapshot
also covers a half-written memory, which format 3 reports through
sh:and.

An end-to-end test runs FSRS reviews, a reset and a reschedule on every
blocking server.
# Conflicts:
#	docs/boundaries.md
#	packages/application/src/useCases.ts
@ixuz
ixuz marked this pull request as draft October 3, 2026 20:19

This branch has not been deployed

No deployments
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