Shipped per-day reflections for mirror-habit: a note, effort, and mood attached to each habit’s daily cell, plus effort trend charts.
What shipped
- Reflection editor (
CheckInNoteEditor) on every check-in surface — dashboards, habit detail, private habits, and the daily briefing. - A note/effort/mood is attached to the
(habit_id, user_id, date)cell, not individual entries rows — so counted habits (many rows/day) get exactly one reflection per day. - Effort trend charts on habit detail pages, and reflections surfaced in the yearly heatmap.
supabase/migrations/20260913_entry_notes.sql: PK-enforced table, RLS (read-all / write-your-own), indexes, and realtime replication alongside entries.
DB design notes
effort/moodareCHECK-constrained to1..5so the bar charts never get nonsense.- PK
(habit_id, user_id, date)is what keeps “one reflection per cell” true even when a counted habit logs several entries in a day. - Reflections are in the realtime publication like entries, so guild-mate notes stream across devices without a reload.
What got tricky
- Store churn: every writer path (fetch, migration push, realtime channel) had to validate reflections the same way or stale
0%rows leak through the reducer — the same class of bug the challenge tiers had. Fix: validate at every ingest point. - Moving the reflection data through
store.tsxmeant keeping streak math and counts untouched — the store diff is mostly additive.
A good “small feature, careful data model” push.