A cluster is just related words kept side by side — food, travel, feelings — a reference the agent can lean on, never a checklist it has to clear. When the FSRS backbone marks an item due, the agent resurfaces it where the chat opens a door, and the cluster is just the nearby words it may also reach for if the moment allows. This is V1's teaching loop, riding on the Anki backbone.
6 minA plain SRS queue hands you whatever's due, in scheduler order: 久しぶり (long time no see), then 醤油 (soy sauce), then 締め切り (deadline). No single conversation can carry all three — they have nothing to do with each other — so there's no natural way to resurface them in a real chat. You're forced back to flashcards.
Cluster by meaning and the problem dissolves. Group the food words together, and when one of them comes due, one ordinary conversation — "what's for lunch?" — has room for it, plus any neighbors that happen to fit. The theme is what makes conversation-native review possible: a thread can hold a topic, so a due word has somewhere natural to land — and related words sit right there as reference, not as a list to get through.
A cluster is a handful of tracked items that hang together by topic — drawn from the user's own life, the same words the backbone already logged. Examples after a few weeks of chatting:
Each item still lives in the backbone with its own status and its own FSRS due date — the cluster owns neither. It's just a theme tag grouping related words so the agent has them on hand as reference. A word can carry more than one theme; the cluster is a lens for review, not a folder that owns the word, and never a set the agent has to use in full.
FSRS is what says an item is due. When a due item belongs to a theme the user actually talks about, the agent can open a proactive message on that theme — and the job is simply: resurface the due item where the conversation gives it a natural home. The cluster around it is reference — related words the agent may reach for if a door opens, never a list it has to clear.
醤油) is the point — soy vs miso gives it a natural opening. Cluster neighbors like 注文 or おすすめ are reference: if the thread happens to open a door, reach for one; if not, leave it. Gloss only if they'd be lost.醤油 cleanly → it grades well and FSRS pushes its next review out; they blank on おすすめ → it grades down and comes back sooner. Same inferred grading as always — the conversation is the grade button.注文 gets no natural opening, it simply isn't used — the agent does not shoehorn it in, and nothing is owed. Even the due item waits if the thread won't carry it; FSRS will surface it again. This is the backbone-advises, agent-decides rule: FSRS sets the table, the agent still reads the room.Anna opens unprompted. The due item is 醤油; the rest — 注文 · おすすめ · 辛い — are food-cluster neighbors she reaches for only because the thread opens a door to each.
One due word resurfaced, three neighbors riding along because the chat happened to make room — the user produced 注文 and 辛い unprompted by the end (both grade well, FSRS pushes them out), got a quick gloss on おすすめ (graded down, comes back sooner), and never once felt reviewed. Had the chat gone elsewhere, only 醤油 would've surfaced — and that'd be fine. That's the whole loop.
Cheap and emergent, not a hand-built taxonomy. As the backbone captures each item, the agent also tags it with a theme — a short semantic label (food, travel, work, feelings). Items sharing a theme are a cluster.
In V1, FSRS already answers what's due — per item, with real timing. What's left to the agent's judgment is which theme to open on and when:
One theme per session. Mixing clusters re-creates the random-queue problem inside a single chat.
Mechanically this reuses what onboarding already does. V0 gives the agent a live objective block for the current stage (currentObjective() in core/agent.mjs). A themed open is just another objective: a transient block naming the FSRS-due item(s) to resurface, with the cluster's other words listed underneath as reference — use the due ones where the conversation allows, lean on the neighbors only if they fit, force nothing.
# Right now → themed open: Food & eating
Open naturally on food (they often grab ramen).
DUE (FSRS) — resurface where it fits, don't force:
醤油 (learning)
REFERENCE — related words; reach for one only if a door opens:
注文 · おすすめ · 辛い
Gloss only if they'd be lost. Grade from how they reply
(remember → FSRS). Nothing here is owed — if the thread won't
carry the due item, leave it; it'll come due again.
No new machinery — same objective-injection pattern, same remember tool writing grades back to FSRS. FSRS picks what's due; the cluster just supplies the related words sitting next to it.
A small extension to the backbone's item store: each item gains a theme (or a list of themes).
"醤油": {
surface: "醤油", reading: "しょうゆ", gloss: "soy sauce",
status: "learning",
fsrs: { due: "2026-06-25", stability: 4.2 }, // from the backbone
theme: "food", // the cluster's only addition
lastSession: "2026-06-23" // when it last got a themed pass
}
On the read-only web page, themes become the natural grouping — vocab shown in clusters (Food, Travel, Work…) instead of one long list. Same read-only rule: view, don't edit; you grow a cluster by talking about it, not by filing words into it.
辛い = food and feelings/"painful") — pick one per session, or let it ride either?