feat: wake-triggered derivation + RR-from-minute HRV + TTL cache + D1-hot/R2-sealed storage#9
Merged
Merged
Conversation
…inute, D1-only loadDayRr) + TTL read-cache + v11/v12 migrations
…alytics), cron isUserAwake ladder (wake_cron), close_day queue job (processUser + HRV-from-minute + cache invalidate), demote nightly to maintenance + retry-net, v13 cursor migration
… per-nap hypnograms on /day/v2/sleep (on-read, no migration)
…) — all minute-reading day endpoints now cached
…acks days >3d into gzipped R2 objects (put-before-delete), day-detail reads fall back to R2; cuts D1 storage + per-row prune-deletes + dodges the 10GB cap. Ingest/processUser paths unchanged (hot window).
…gzipped blob, RMW merge mirroring the old ON CONFLICT) replaces row-per-minute as the hot store; all readers (analytics/daydetail/query/workouts/biometrics/seed) + seal + prune go through minute_store. The D1 write-cost lever (~1,440→~1 write/day). v14 migration.
…ging loadMinutes/stageNight now carry MinuteRec.rr through to stageSleep so the autonomic deep/REM split (RMSSD) runs on /day/sleep and /day/v2/sleep. Pairs with the analytics stageSleep RR change. ops/ + wrangler.v*.toml stay local.
…ake-only detectSessions and autoCloseStaleWorkouts ran only inside the once-a-day close_day / every */10 tick, so an auto-detected workout didn't surface until the next wake and the frequent cron wasn't purely wake-detection. - new workouts.ensureTodayWorkouts(db,user): closes this user's forgotten LIVE workouts + (re)detects TODAY's auto sessions, mirroring processUser Pass-2's session block EXACTLY (delete auto-non-deleted, insert done/auto; manual/ edited/deleted untouched). THROTTLED via read_cache (~120s) so a burst of reads costs one detect, and only for users actively opening the app. - called on-read from listWorkouts, getSessions, and getDayStrain(today). - autoCloseStaleWorkouts now takes an optional userId scope (on-read = one user). - scheduled(): removed autoCloseStaleWorkouts from the every-*/10-tick path; the */10 cron now does ONLY runWakeLadder. Kept autoCloseStaleWorkouts on the nightly 30:3 tick as a safety net for users who never open the app. Validated on v2: /workouts + /sessions 200, detection runs + throttles (wkscan read_cache sentinel set), 0 false workouts on a non-workout night.
… drop steps_imu Steps had three code paths: a per-record r10Motion heuristic (-> minute.steps, fed the chart), the full AN-2554 (calcSteps in ingest_signals -> computed then DISCARDED), and an R2 recompute (steps_imu -> daily.steps at close, lagged all day). Collapse to one: AN-2554, counted once at ingest, live on read. - minute_store.writeBatch: minute.steps += ingest_signals.steps (AN-2554), additive (idempotent via edge hex-dedup). rollup no longer sums the r10Motion s.steps_inc. - processUser folds daily.steps = SUM(this day's minute.steps) into the close (no R2), so past days keep a permanent total after minutes prune. - reads serve TODAY live: getToday + getDayStrain(today) = SUM(minute.steps) from the hot day blob (zero R2); past days use the stored daily.steps. - REMOVED steps_imu.ts entirely + the 'steps_full' queue job + runStepsIncremental from close_day/sweep + the /admin/run-steps endpoint. No step work in any cron now. Validated on v2: /today + /day/strain return live steps (104) from minute.steps; tsc clean.
Ingest aggregates per-minute red/IR/temp ADCs (wrist-on) into the minute blob; biometrics_minute derives a RELATIVE red/IR SpO2 index (confidence- gated) + skin-temp index vs rolling EWMA baselines, feeds temp into illness. Zero extra R2 (reads the D1 minute blob). admin/enqueue forwards onset/wake for close_day verification. Validated end-to-end on v2.
0db11a3 to
91b31f6
Compare
…er + cache /day/sleep Drive the hypnogram, stage breakdown, duration, awake and efficiency from ONE source (analytics stageHypnogram — v1 Cole-Kripke method) so the graph and breakdown can't disagree; pass per-minute RR so the REM tiebreaker reclaims REM mislabeled as awake. Add the fractal sleep cycles (detectSleepCycles) to the response. Wrap getDaySleep in the TTL read-cache: compute the night ONCE, serve from read_cache (past day immutable, today <=60s, close clears it) — no more recomputing the hypnogram on every read.
…creen The RR REM-tiebreaker lived only in the on-read /day/sleep path, so the STORED sleep.duration_min (what /today reads) stayed HR-only — Today showed 4.9h while the Sleep screen showed 5h43m for the same night. processUser now runs the same stageHypnogram (RR tiebreaker) at close and stores its duration/efficiency/stages, so both screens read one consistent value. Verified: /today and /day/sleep both 343min.
…ways-on irregular - Cycle tracking: cycle_log table (migrate_v15) + opt-in users.track_cycle (migrate_v16); GET/POST/DELETE /cycle gated on explicit consent; biometric overlay. - Live HRV spot-check: POST /spotcheck decodes RR from posted live frames (realtimeRr) + timeDomainHrv (stateless). - Skin-temp + SpO2 registered in /trend; skin_temp added to /day/heart. - Irregular-rhythm served for the always-on on-screen card. - Profile GET/PATCH carry track_cycle.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat/wake-trigger → main
Wake-triggered architecture (v2)
isUserAwakeladder (wake_cron.ts) → firesclose_dayonce per physiological day at wake.minute.rrat close (biometrics_minute.ts) — zero R2 re-decode (RR un-banned).read_cache(no watermark); D1-hot / R2-sealed minute storage (minute_store.ts); naps-as-sleep + per-nap hypnograms.minute.rr(v11),read_cache(v12), cursor wake-state (v13),minute_day(v14).New in this update
cycle_log(v15) + opt-inusers.track_cycle(v16);GET/POST/DELETE /cyclegated on explicit consent; biometric overlay; profile carriestrack_cycle.POST /spotcheckdecodes RR from posted live frames (realtimeRr) +timeDomainHrv(stateless)./trend;skin_tempadded to/day/heart.🤖 Generated with Claude Code