Blog ยท release
Latch 0.5.0
The health module โ what your body did, moving the game. Apple Health and SparkyFitness readings arrive once an hour and become praise, points, time on or off the clock, and a Handler who eases off on the days you are running on empty. Closes issue #10.
- ๐ด UNKNOWN IS A THIRD STATE, AND IT IS THE COMMON ONE. Roughly a quarter of days carry no
reading at all, so every rule resolves to fired / not-fired / unknown, and an unknown fires
nothing in either direction. A missing reading is stored as no row at all โ never a 0, which
would tell your Handler you took no steps on a day nobody counted them and trigger the
sedentary penalty for it. A measured zero (
meals_logged = 0, written after SparkyFitness answered) is legal and different; the sync decides which it is and omits what it could not read. - ๐ด Penalties are held back on a recovery day โ short sleep or low mood โ because those are
exactly the days the accommodation exists for, and a game that adds time for being sedentary on
four hours of sleep is punishing you for being unwell. The mercy writes its own
penalty_withheldevent: a silent skip would be indistinguishable from a broken rule. Rewards are never suppressed โ moving at all on a day like that is worth more, not less. Recovery is derived at read time, never stored, so it cannot outlive the reading behind it. - ๐ด The export lands a day late, so rules run over a WINDOW. Health Auto Export delivers day
D at ~01:15 on D+1; an engine that only ever evaluated "today" would find nothing but unknown
and fire literally never, looking exactly like a feature that was merely waiting. Events are
stamped on the day they describe, not when they were credited โ stamping at
nowwould put Tuesday's workout in Wednesday's calendar cell and shift every streak the activity lens derives. A late credit is markedbackdatedand the timeline printsโถ logged later. - ๐ด Two independent kinds of stale, and checking only the first is the trap. The sync can be
perfectly alive โ running hourly, exiting 0, updating
synced_atโ while the source behind it has gone dry, so freshness of the plumbing is not freshness of the data. Stale if the sync stopped or the newest reading stops advancing. Nothing fires on stale data. - ๐ด Thresholds are measured, not guessed. Baselined against 90 days of real readings: steps
p10 3,508 / p25 12,510 / p50 19,208, exercise p25 39 / p50 121, workouts on 18 of 90 days.
The obvious 5,000-step bar would have fired every single day and meant nothing.
no_workoutships disabled โ as a daily rule it would fire four days in five. - Push, not pull. Latch can reach the databases; it deliberately does not. The fetch lives in
tools/sync/sync-health-to-latch.pyon the apps pod, so the image carries no database driver and "makes no outbound calls" stays checkable by reading the imports. - Three activities seeded free โ Moved, Workout, Slept well. Because an activity is a saved query, they light up the calendar and streaks from the whole history the moment they exist.
- Diet and mood absence is rendered, not hidden โ "nothing logged for 21 days" in words, because a blank tile reads as fine.
- ๐ด
/api/v1/handler/todayleaked the Handler's prose again, through theaccommodationfield added long after its prose-stripping was written. A route that strips prose has to strip it from fields that did not exist when the rule was made. Guard extended to both health routes and verified to actually go red without the fix. - New:
health_daily(migration 8),POST /api/v1/health/daily,GET /api/v1/health/today,/health,docs/health.md, 22 tests (314 total).