Deleting a measurement or mood entry can be undone from the toast via
new restore endpoints, dialogs submit on enter with busy states, silent
mutations gain error toasts, and the settings area finishes its
consolidation: provider and integration cards live in focused
subcomponents, every setting persists server-side at exactly one home,
and the save conventions are written down. The remaining raw fetches in
settings move onto the shared client.
A slot-less "taken now" write (no client scheduledFor) defaulted its
instant to now/takenAt and snapped across the wide ±halfGap window onto
the nearest scheduled slot. For a 07:00 / 19:00 med a midday write
captured the 07:00 morning slot and stamped it taken — a phantom morning
dose. The web fix only threaded the displayed slot on the card; any
client posting takenAt-only (iOS bulk/sync) still tripped it server-side.
Thread an instantIsExplicit flag through resolveSlotInstantForWrite into
the canonical resolver. An explicit scheduledFor keeps the full ±halfGap
snap unchanged. A defaulted-now write snaps only inside the tight
dose-grace window and otherwise resolves to null (PRN), so it records a
standalone taken-now row instead of back-filling a far slot. Both intake
call sites pass scheduledFor !== undefined; takenAt alone is not explicit.
Add resolver unit coverage for midday defaulted-now writes (07:00/19:00 →
null at 11:00/12:00/13:00, explicit 07:00 still snaps) and a DB-backed
regression that a takenAt-only midday write leaves the 07:00 slot untaken.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A daily oral with multiple timesOfDay and a wide schedule window had its
dose-slot capture zones overlap, so a second same-day dose could snap onto
an already-actioned slot and be dropped by the no-downgrade guard — the
intake never landed as its own row. A twice-daily 08:00 / 20:00 schedule
with an 08:00-22:00 window yielded a +/-7h half-span tolerance that
exceeded half the 12h inter-slot gap, so the evening write collapsed onto
the morning slot. Weekly and single-dose schedules (one slot/day) were
immune.
snapToleranceMs now caps the tolerance at half the minimum gap between
adjacent sorted timesOfDay entries (the midnight wrap counted as one of
those gaps) whenever a schedule has more than one slot, keeping the two
nearest slots' capture zones disjoint. Single-slot schedules keep the
full half-window-span behaviour, and the >=1-minute drift floor is
preserved, so weekly injectables and once-daily orals are unchanged.
Adds unit coverage for the wide-window two-dose case (distinct slots, no
collapse) plus an explicit weekly single-dose regression, and an
integration test driving both same-day writes through the DB-backed slot
resolver and canonical-slot upsert to assert two distinct live rows.
The write-path convergence stops new duplicate slot rows, but accounts
that already accrued a pending + taken pair for one dose keep an inflated
expected count, a phantom taken marker, and a suppressed "due now" prompt
until the bad rows are cleared.
Add a boot-time, idempotent cleanup queue (mirroring the existing
backfill queues): discover only the users holding a same-slot duplicate,
then per medication snap every live intake to its canonical slot, keep
the most-actioned row per slot (taken over skipped over pending),
normalise its instant, soft-delete the losers, and recompute the
affected compliance rollups. Rows that resolve to no scheduled slot
(PRN / as-needed) are never collapsed. A second pass is a no-op.
Registered in the worker's queue list with a static guard test, after a
prior incident where a queue was built but never registered and silently
never ran.