Repository navigation
Flash cards with SM-2 spaced repetition, built on automations #262
Description
I want flash cards with spaced repetition, but I don't want a bespoke flash card feature. This should be built entirely by dogfooding automations / widgets, and anything that can't be expressed yet is a gap in those systems, not a reason to hand-code it. So this issue is mostly a tracker for those gaps.
Using SM-2 (the algorithm Anki grew out of) rather than Leitner boxes. Leitner would almost work with today's verbs since it's fixed intervals per box, but SM-2 is the more proven one and the gaps it exposes are ones automations should close anyway.
The card shape:
What does SM-2 stand for? #card
ease:: 2.5
reps:: 2
interval:: 6
SuperMemo 2
The block tagged #card is the question, its children are the answer. Card state lives in ease:: / reps:: / interval:: properties and due_at decides when it shows up, so due cards land on the daily page for free. Review queue is a saved view like tag:card and due <= today.
Grading is a tag (#again / #hard / #good / #easy) that a set of automations consume, one automation per grade × reps bucket so nothing needs conditionals. SM-2 per grade q:
- q < 3 (again): reps = 0, interval = 1
- otherwise: interval = 1 if reps = 0, 6 if reps = 1, else round(interval × ease); reps + 1
- ease = max(1.3, ease + 0.1 - (5 - q)(0.08 + (5 - q)(0.02)))
With q fixed per automation the ease delta is a constant (easy +0.10, good +0.00, hard -0.14), which keeps the expressions small. A good-review automation would look roughly like:
Card good, mature #automation
trigger:: schedule every 5m
query:: tag:card and tag:good and not prop:reps=0 and not prop:reps=1
action:: set_property interval {{block.prop:interval|mul:block.prop:ease|round}} then set_property reps {{block.prop:reps|add:1}} then set_due +{{block.prop:interval|mul:block.prop:ease|round}}d then untag good
Plus an init automation (tag:card and not has:ease) that seeds ease:: 2.5, reps:: 0, interval:: 0 and set_due today.
What's missing to make that real:
- Automations: chain verbs in one run (
action:: A then B) #225 verb chaining (then). Without it each grade is several automations and the untag has to land last or the card gets re-graded - Automations: {{block.prop:<key>}} — read arbitrary block properties in action args #244
{{block.prop:<key>}}and|default: - Arithmetic filters on tokens:
add,mul(taking another prop as its arg),round,max. Doesn't exist yet, probably its own issue - Verify
set_duehandles a token-built offset like+{{...}}dper matched block. Should already work since it's a grouped verb, but untested - A way to grade without typing a tag. In the app that's a button on a block that runs a manual automation against that block - manual runs don't take a target block today. On Discord it's Customizable Discord reminder actions — user-defined buttons that can trigger automations #224 with grade buttons that add the tag
- Hiding the answer and the card state. Collapsing the parent works but it's persisted, and the
key:: valuelines render in the question. Aflashcardwidget type under Widgets: a declarative render layer (activity grid, streaks, countdowns, charts) #168 (or a reveal render) is the dogfooded version - Nice to have: Automations: reactive slice, prompt action, event triggers, webhooks #206 event triggers so grading fires on the tag instead of polling every 5m
Here are some links for SM-2 itself:
https://super-memory.com/english/ol/sm2.htm
https://en.wikipedia.org/wiki/SuperMemo#Description_of_SM-2_algorithm
Activity