Status: Proposed — deferred until after the next release (the FSRS part is an architectural change worth landing on its own).
Proposal doc in-repo: docs-src/developer/term-status-fsrs.md.
Problem
The word-status model (1-5 learning, 98 ignored, 99 well-known) is the spine of both the reading UI (word colouring) and the review system, but it is modelled ad-hoc:
- Duplicated: the literal
[1,2,3,4,5,98,99] and checks like $status === 5 || $status === 99 recur across 11+ PHP files; label/colour/order/CSS tables are re-defined in ~6 TS files. A TermStatus value object exists (src/Modules/Vocabulary/Domain/ValueObject/TermStatus.php) but isn't the single source of truth.
- Scheduling is a hand-tuned Leitner curve:
TermStatusService::SCORE_FORMULA_TODAY/TOMORROW is a per-status linear decay stored in WoTodayScore/WoTomorrowScore, shuffled by WoRandom; a review just nudges status ±1. No memory model, no per-term difficulty, no retention target, no review history.
One integer conflates two concerns: how familiar a word is (reading view) and when it should next be reviewed (scheduling).
Phase 1 — status as a single source of truth (independent, low-risk)
Promote TermStatus to authoritative (value, label, abbr, cssClass, colour, order, isKnown()/isIgnored()/isLearning()); fold in TermStatusService/StatusHelper; replace the ~11 PHP literals; expose once to the frontend (bootstrap or GET /api/v1/settings/status-definitions) and collapse the ~6 duplicated TS tables into one store.
Phase 2 — FSRS-aligned scheduling
Split display familiarity from memory state:
| Concern |
Today |
Proposed |
| Display familiarity (colours) |
WoStatus 1–5 |
keep 1–5, derived from stability |
| Scheduling |
per-status decay score |
FSRS memory state per term |
FSRS models each term with Stability (S), Difficulty (D), Retrievability (R) (power forgetting curve; due when R hits target retention, default 0.9). Reviews become 4 grades (Again/Hard/Good/Easy), each updating S/D and the next due date.
Changes: schedule columns / term_schedule + review_log tables (retire WoTodayScore/WoTomorrowScore/WoRandom + formulas); a swappable Scheduler service (FSRS impl, interface-backed); SubmitAnswer calls the scheduler; 4-grade review UI; derive 1–5 colours by bucketing S; 98/99 stay manual flags (≈ suspended/known). Existing terms seed FSRS state from WoStatus + WoStatusChanged.
Decisions to make
- Display status derived from stability (recommended, with manual override) vs. fully manual/orthogonal.
- 4-grade vs. a 2-button (Again/Good) mode for existing users.
- Vendor a maintained PHP FSRS port vs. hand-port
open-spaced-repetition (licence check before vendoring).
See the proposal doc for migration, scope, and verification detail.
Status: Proposed — deferred until after the next release (the FSRS part is an architectural change worth landing on its own).
Proposal doc in-repo:
docs-src/developer/term-status-fsrs.md.Problem
The word-status model (
1-5learning,98ignored,99well-known) is the spine of both the reading UI (word colouring) and the review system, but it is modelled ad-hoc:[1,2,3,4,5,98,99]and checks like$status === 5 || $status === 99recur across 11+ PHP files; label/colour/order/CSS tables are re-defined in ~6 TS files. ATermStatusvalue object exists (src/Modules/Vocabulary/Domain/ValueObject/TermStatus.php) but isn't the single source of truth.TermStatusService::SCORE_FORMULA_TODAY/TOMORROWis a per-status linear decay stored inWoTodayScore/WoTomorrowScore, shuffled byWoRandom; a review just nudges status ±1. No memory model, no per-term difficulty, no retention target, no review history.One integer conflates two concerns: how familiar a word is (reading view) and when it should next be reviewed (scheduling).
Phase 1 — status as a single source of truth (independent, low-risk)
Promote
TermStatusto authoritative (value, label, abbr, cssClass, colour, order,isKnown()/isIgnored()/isLearning()); fold inTermStatusService/StatusHelper; replace the ~11 PHP literals; expose once to the frontend (bootstrap orGET /api/v1/settings/status-definitions) and collapse the ~6 duplicated TS tables into one store.Phase 2 — FSRS-aligned scheduling
Split display familiarity from memory state:
WoStatus1–5FSRS models each term with Stability (S), Difficulty (D), Retrievability (R) (power forgetting curve; due when R hits target retention, default 0.9). Reviews become 4 grades (Again/Hard/Good/Easy), each updating S/D and the next due date.
Changes: schedule columns /
term_schedule+review_logtables (retireWoTodayScore/WoTomorrowScore/WoRandom+ formulas); a swappableSchedulerservice (FSRS impl, interface-backed);SubmitAnswercalls the scheduler; 4-grade review UI; derive 1–5 colours by bucketing S;98/99stay manual flags (≈ suspended/known). Existing terms seed FSRS state fromWoStatus+WoStatusChanged.Decisions to make
open-spaced-repetition(licence check before vendoring).See the proposal doc for migration, scope, and verification detail.