@@ -30,10 +30,37 @@ CREATE TABLE IF NOT EXISTS refresh_tokens(
3030);
3131CREATE INDEX IF NOT EXISTS idx_refresh_user ON refresh_tokens(user_id);
3232
33- -- ── TIMESERIES ROLLUP (≤1440 rows/day/user; pruned to R2 after 90 days) ───────
34- -- hr_sum / act_sum / act_n are running aggregates kept so the ingest upsert can
35- -- merge new samples into the stored minute EXACTLY (deterministic idempotency).
36- -- hr_avg / activity are the derived display values (kept in sync on every write).
33+ -- ── TIMESERIES (day-packed) ───────────────────────────────────────────────────
34+ -- [feat/wake-trigger] minute_day is the HOT minute store: ONE row per (user, ymd),
35+ -- value = gzipped JSON MinuteRec[] (one entry per touched minute; RR rides the blob
36+ -- as number[]). Ingest read-merge-writes ~1 row/day instead of ~1,440. Days older
37+ -- than the hot window are sealed to gzipped R2 objects and the D1 row dropped; the
38+ -- 10-day prune drops ~1 row/day. See minute_store.ts. (migrate_v14)
39+ CREATE TABLE IF NOT EXISTS minute_day(
40+ user_id TEXT NOT NULL ,
41+ ymd TEXT NOT NULL , -- 'YYYY-MM-DD' (UTC day of the minute)
42+ blob BLOB NOT NULL , -- gzipped JSON MinuteRec[] (bound param; << 2MB cap)
43+ updated_at INTEGER NOT NULL ,
44+ PRIMARY KEY (user_id, ymd)
45+ ) WITHOUT ROWID;
46+ -- seal/prune scan by day across all users (WHERE ymd < cutoff) — index the bare ymd.
47+ CREATE INDEX IF NOT EXISTS idx_minute_day_ymd ON minute_day(ymd);
48+
49+ -- TTL read-cache for Tier 1/2 on-read metrics (no watermark; time-based only).
50+ -- key e.g. 'today:strain:2026-06-19'; today→60s, past days immutable-until-prune.
51+ -- See cache.ts. (migrate_v12)
52+ CREATE TABLE IF NOT EXISTS read_cache(
53+ user_id TEXT NOT NULL ,
54+ key TEXT NOT NULL ,
55+ payload TEXT NOT NULL , -- JSON
56+ computed_at INTEGER NOT NULL ,
57+ PRIMARY KEY (user_id, key)
58+ ) WITHOUT ROWID;
59+
60+ -- ── LEGACY ROLLUP (deprecated; empty on fresh DBs) ────────────────────────────
61+ -- The pre-v14 row-per-minute store. Superseded by minute_day above; left in place so
62+ -- old deployments keep working, but ingest no longer writes here. Safe to drop once
63+ -- no instance predates v14.
3764CREATE TABLE IF NOT EXISTS minute(
3865 user_id TEXT NOT NULL ,
3966 ts_min INTEGER NOT NULL , -- unix sec floored to minute
@@ -177,7 +204,13 @@ CREATE TABLE IF NOT EXISTS analytics_cursor(
177204 last_run INTEGER DEFAULT 0 ,
178205 steps_cursor_ts INTEGER , -- incremental steps: last settled minute counted (unix s)
179206 steps_cursor_day TEXT , -- UTC day the steps accumulator is for
180- bio_last_date TEXT -- event-driven biometrics: last sleep-date already triggered
207+ bio_last_date TEXT , -- event-driven biometrics: last sleep-date already triggered
208+ -- [feat/wake-trigger] incremental sleep/wake state machine (migrate_v13). The */N
209+ -- cron reads these: skip awake-and-closed users, peek the asleep ones, fire close_day
210+ -- once per physiological day.
211+ sleep_phase TEXT , -- 'awake' | 'asleep' | NULL
212+ phase_since INTEGER , -- unix s of last transition
213+ last_close_date TEXT -- YYYY-MM-DD of last day-close
181214);
182215
183216-- Per-user ingest rate-limit token bucket (RESILIENCE §7).
0 commit comments