4. add counter (recitation) mode as a separate meditation mode
status: accepted
deciders: phase 6 implementation
date: 2026-01-11
context
phase 6 adds a "recitation mode" to the existing timer-based meditation engine. meditations now fall into one of two runtime behaviors:
timer: the existing time-based player with segment durations.counter(recitation): a count-up player where the user repeats an action (tapping a button or pressing hardware keys) until a target count is reached.
this required changes to the database schema, the domain model, the player, stat aggregation, and the editor.
superseded terminology: the runtime values described here are now
MeditationMode.meditation(wastimer) andMeditationMode.recitation(wascounter);TimerSegmentis nowSegmentandEditorScreenisMeditationEditorScreen. the stored valuestimer/counterare unchanged — seedocs/dev-guide/terminology.md.
decision
we decided to model recitation mode as a first-class mode field on Meditation
rather than a separate Recitation entity.
MeditationsTablehas amodecolumn (timerorcounter).TimerSegmentis reused with a newtarget_countcolumn;duration_secondsis still present but set to zero for counter segments.RecitationPlayerScreenis a separate screen for the counter runtime, launched whenmeditation.mode == MeditationMode.counter.RecitationInputServiceabstracts all input sources (on-screen buttons, keyboard, and Android hardware keys) so the player only seesincrement/decrementevents.
consequences
positive:
- editor, library, stats, and easy mode can all work with both modes with minimal conditional logic.
- stats aggregation naturally mixes timer and counter sessions;
SessionStatsnow has parallel time and recitation metrics. - counter meditations can use the same bell/background/prompt infrastructure as timer meditations.
negative:
TimerSegmentnow has two mutually exclusive fields (duration_secondsandtarget_count). validation inEditorScreenandMeditationsRepositorymust ensure a segment is meaningful for the meditation's mode.- the migration to schema version 14 adds a
modecolumn to an existing table, which required idempotent migration helpers (_addColumnIfMissing) to keep v9→v10 and v13→v14 paths safe.