Commit Graph
546 Commits
Author SHA1 Message Date
Marc-André Bombeck d1ba01548f Surface sleep-debt and chronotype on the sleep page
Compute cumulative sleep debt and the MCTQ chronotype server-side off the
canonical night reconstruction the Sleep Score reads, and render them on
the sleep page: a sleep-debt headline with a calm partial state under the
night threshold, and a chronotype card whose social-jetlag detail and
corrected mid-sleep sit behind an advanced disclosure. Both hold a learning
state on thin data and never assert a total or a band.

Serve the same values to the dashboard summary and a new GET /api/sleep/rhythm
read so the native client renders identical figures. Free vs work nights
default to weekend in the user's timezone until day-type tracking exists.
2026-06-14 19:03:11 +02:00
Marc-André Bombeck 524626d2bd feat(sources): add Nightscout, Polar, Oura schema foundation
Extend MeasurementSource with NIGHTSCOUT, POLAR, OURA and add the
per-user encrypted credential columns each source needs: a Nightscout
base URL + token plus a private-host opt-in flag, and Polar/Oura OAuth
tokens mirroring the WHOOP columns. Migration 0160 is additive only —
new enum values, nullable columns, one boolean default false.

Register the three sources across the read enum, the integration-status
key set, and the source-priority ladders.
2026-06-14 18:47:14 +02:00
Marc-André Bombeck 04b09e2905 chore(release): v1.16.16 — one engine for sleep, recovery and glucose
Read sleep, recovery and blood glucose from a single source on every surface —
dashboard, coach, doctor-report, CSV and FHIR exports, and the companion app —
so the displayed figure agrees wherever it appears. Sleep nights reconstruct
once with source de-duplication; WHOOP-native recovery is canonical and resolves
to one row per night; glucose converts to the user's unit at the point it is
served. Hardens the consent receipt path and deepens WHOOP sync with per-resource
cursors, targeted refresh and full backfill. Several displayed numbers become
correct-but-different — flagged in the changelog.
2026-06-14 17:58:56 +02:00
Marc-André Bombeck 32d275b4aa Harden consent receipts and document iOS series + ai-provider contract
Document GET /api/measurements/series and GET /api/user/ai-provider in the
OpenAPI registry — both are consumed by the iOS client (charts and Coach
visibility gating) but were absent from the spec.

Break migration 0159's active-receipt dedup on (created_at, id) so an exact
created_at tie still leaves one survivor, keeping the unique index build from
aborting 23505 on affected tenants.

Catch P2002 in createReceipt: two concurrent first grants both reach create,
the loser re-reads the winning active receipt and returns it instead of a 500.
2026-06-14 17:42:00 +02:00
Marc-André Bombeck b4b9d8d9d7 Unify sleep-night reconstruction onto the canonical engine
Replace the divergent UTC-day night reconstructor in the Sleep Score with a
thin adapter over reconstructSleepNights, the shared engine the dashboard,
hypnogram, and doctor surfaces already read. The old path bucketed rows by UTC
calendar day and never deduplicated writers, so a multi-source night (WHOOP +
Apple Health for the same sleep) summed both writers and roughly doubled the
total. The adapter inherits session-clustering, local-wake-day keying, and
multi-source writer dedup, then derives the score-only fields the canonical
night does not carry: REM and Deep minutes from the stage map, the stage-
breakdown flag from any granular stage, and the midpoint from the asleep span.

Select source and deviceType in the Sleep Score read so the writer-dedup
ladder fires, and thread the user source priority through so the chosen writer
matches every other sleep surface. Round emitted asleep and in-bed minutes to
whole numbers, and round the per-stage map in the sleep-night DTO, matching the
integer contract the native client decodes.

A multi-source user's sleep score and the readiness score that folds it drop
once the double-count is removed, reflecting the true single-writer night.
2026-06-14 17:14:14 +02:00
Marc-André Bombeck 8511b0bed7 chore(release): v1.16.15 — an honest calendar that learns before it predicts 2026-06-14 15:53:01 +02:00
Marc-André Bombeck 609d248252 feat(cycle): choose cervix as the symptothermal secondary symptom
Let a user pick the Sensiplan double-check's second indicator — cervical
mucus (default) or cervix observation — mirroring the published NFP
standard where the temperature primary sign pairs with one chosen
secondary sign.

- `CycleProfile.secondarySymptom` (MUCUS default | CERVIX) drives which
  second sign the engine confirms against. Casual users keep temperature
  + mucus untouched; the choice lives behind the advanced cycle settings.
- `CycleDayLog` gains the three standard cervix signs — position
  (LOW/HIGH), firmness (FIRM/SOFT), opening/os (CLOSED/OPEN) — as nullable
  columns (migration 0157), wired through Zod, the day-log upsert, the
  DTO, the sync feed, backup/restore and OpenAPI. Additive and
  back-compatible: existing profiles backfill MUCUS, existing day-logs
  keep NULL cervix signs, iOS inherits the fields.
- The engine's `confirmSymptothermal` takes the secondary-symptom choice;
  on CERVIX it confirms against `detectCervixPeak` (last fertile-cervix
  day followed by three closed days — the cervix-closure rule) instead of
  the mucus peak. The double-check still requires BOTH the temperature
  shift AND the secondary peak: a single cervix sign never confirms, and
  the still-learning gate and conservative posture are unchanged.

Engine derived clean-room from the Arbeitsgruppe-NFP / Raith-Paula &
Frank-Herrmann standard.
2026-06-14 15:23:26 +02:00
Marc-André Bombeck 4bfe9f11d7 feat(cycle): exclude disturbed temperatures from the symptothermal engine
A fever, illness, alcohol or a late/short-sleep reading can fake or mask
a temperature rise. Add a per-day `temperatureExcluded` flag on
CycleDayLog (migration 0156): when set, the engine drops the reading from
the cover-line baseline AND the shift evaluation, so a spike neither
fabricates nor masks a rise (Sensiplan: the cover line is drawn over the
last six unbracketed values).

Wired end to end — schema + Prisma, the engine's `detectTempShift`
filter, the Zod input/patch schemas, the day-log upsert (partial merge +
change detection), the read DTO, the calendar/insights/coach selects, the
backup round-trip, and the OpenAPI contract (a new boolean on the day-log
and calendar-day DTOs for the iOS client). The log-day sheet gains a
"mark reading as disturbed" toggle that appears once a temperature is
entered, translated across all six locales.

Citation: de.wikipedia "Symptothermale Methode"; myNFP "Temperaturkurve
auswerten" — Störfaktor bracketing.
2026-06-14 15:04:25 +02:00
Marc-André Bombeck f5798e20d8 fix(cycle): gate the calendar grid to a learning state during cold start
The predictions panel suppressed its fertile-window and ovulation rows while
fewer than three cycles were observed, but the month calendar did not — so a
brand-new user who logged a single period saw the grid paint a confident
fertile-window shading, a predicted-ovulation dot, and full phase bands off a
population 28/14 prior at ~0.20 confidence. Calendar-based fertility estimation
is unreliable below several recorded cycles, so the grid was asserting days it
could not know.

Thread the same still-learning gate into the calendar build: while fewer than
three cycles are observed, suppress the fertile window, the ovulation dot, and
the phase band (the predicted-period bar stays, mirroring the panel). Surface a
top-level stillLearning flag on the calendar response so the client renders a
calm "learning your cycle" banner over the grid instead of a bare, falsely
confident calendar. The flag is additive and back-compatible; older clients
ignore it and degrade to the previous render.

Also pin the observed-cycle count to a single source — the engine's
post-exclusion value — so the response never carries two divergent counts.
2026-06-14 14:43:50 +02:00
Marc-André Bombeck ae1373637f chore(release): v1.16.14 — one number everywhere across score, coach, and doctor-report
Coherence release: blood-pressure in-target unified on a labeled, thin-data-gated
90-day window across tile, health-score pillar and coach; the dashboard ring and
insights card built from one shared health-score input builder; the coach and the
doctor-report PDF/FHIR adherence drawn from the same dose ledger as the detail page;
and a shared entry-timestamp plausibility guard on measurement and mood. Several
displayed figures become correct-but-different (BP headline window, adherence).
2026-06-14 14:02:45 +02:00
Marc-André Bombeck 5aefcd59eb chore(release): v1.16.13 — installable offline, era-correct dose edits, steadier headers
App-wide service-worker registration (installable + offline, authenticated
pages kept out of the cache, logout wipe), era-correct historical dose-edit
attribution, CSV intake stock consumption, WHOOP late-workout ingest, web AI
consent-receipt minting, the layout partial-PUT section merge, the curated
mood-tag catalog, and a sweep of mobile header fixes.
2026-06-14 11:06:10 +02:00
Marc-André Bombeck f9876f9492 feat(consent): mint AI consent receipt from the web client
The server-side gate requires an active ConsentReceipt before any health
snapshot egresses via the operator's server-managed key, but the web
client never minted one — so web users on a shared-key deployment hit the
fail-closed no-key fallback on every AI surface.

Add ensureWebAiConsentReceipt() and POST /api/consent/ai/web: an
idempotent ai_full grant mirroring the iOS master grant (a user with an
active receipt is a no-op). The AI-settings card calls it on mount — the
web equivalent of the iOS shell-mount heal — so existing accounts gain a
receipt without a re-consent step.

Surface an honest signal: resolveReadOnlyStatusMiss now returns a
consent-missing outcome when a provider is configured but the gate would
block egress, distinct from no-provider, and skips the wasted enqueue.

Document the receipt precondition + the consent surface in the OpenAPI
contract (it shipped without a bump): the /api/consent/ai family, a
Consent tag, and a 403 consent.ai.required response on the AI-egress
route.
2026-06-14 10:25:18 +02:00
Marc-André Bombeck f4dec8c28f feat(medications): fractional dosing via decimal units per dose (#316)
A dose can now consume a fraction of a unit — a half, third or quarter
tablet for a split pill. The wizard offers a curated fraction set beside
the whole numbers (the most error-resistant input); units-per-dose and the
per-container counts widen from Int to Decimal, so a 30-tablet pack dosed
at a half reads 29.5 after one dose and the days-left projection follows.
The consumption ledger stamps and refunds the exact fraction, and existing
whole-number doses are untouched. OpenAPI regenerated — the three unit
fields move from integer to number — and the units-per-dose hint is
updated across the six locales.

Also anchors a first-use date relative to now in an inventory route test
that had aged past its 30-day in-use window.
2026-06-13 16:07:50 +02:00
Marc-André Bombeck 3189848ace chore(release): v1.16.11 — the supply speaks up, tags find their groups, and nothing asks twice 2026-06-12 23:49:06 +02:00
Marc-André Bombeck 6fef2081d0 fix(medications): the audit findings on the new supply and dose surfaces
- The escalated card row keeps its time anchor: the red badge carries
  the matched window or the overdue-slot value beside it, and the
  inline row's range stays visible on mobile, where it was the only
  clock on the card.
- Take-all-due collects what the cards offer: a dose escalated past its
  catch-up band joins the set with exactly the slot the card's own
  button would record; as-needed medications never join.
- The table's stock warning follows the user's runway threshold instead
  of a leftover four-dose constant; threshold off means no amber, empty
  stays red.
- The card itself shows the remaining runway in the warning tone once
  it crosses the threshold — only then, the card stays calm above.
- The low-stock push deep-links straight into the supply tab and its
  APNs payload keeps url, runwayDays and thresholdDays; supply tab and
  threshold setting link to each other; the reminder-thresholds and
  notification-prefs endpoints enter the OpenAPI contract.
2026-06-12 23:17:20 +02:00
Marc-André Bombeck 3943dcfb72 feat(medications): as-needed medications log without a schedule
A medication can declare itself as-needed: no schedules, never due,
never reminded, excluded from every compliance rate — but intakes log
ad-hoc, consume inventory exactly like scheduled doses, and the
history stays. The wizard offers it as a cadence choice and round-
trips it on edit; card, table and detail show a calm marker where the
next dose normally sits, the take-all dialog never collects one, and
the supply runway stays unknowable by construction. (#316)
2026-06-12 22:56:03 +02:00
Marc-André Bombeck 058d1d7a69 feat(mood): tags get groups, icons and a home of their own
/settings/mood manages the whole tag vocabulary: own groups (encrypted
labels, twelve per account), custom tags with a searchable icon picker
over a 75-icon catalog, hide and unhide of catalogue tags, archive
semantics that never touch historical entries (hard delete only behind
an explicit second confirm), and drag-or-arrow ordering of groups and
tags shared by the picker. One GET serves the fully ordered tree —
groups, placements, hidden and archived state — so the entry form, the
settings section and a native client all read the same resolved layout.
The picker finally renders custom tag labels (they were ignored since
v1.13.0) and offers inline tag creation per group. The mood-tag API
enters the OpenAPI contract for the first time, and the key-rotation
script now covers both encrypted tag and group labels — custom tag
labels had been missing from it since v1.13.0.
2026-06-12 22:03:01 +02:00
Marc-André Bombeck 9df6799a04 chore(release): stamp 1.16.10 into the OpenAPI info block
The generator reads the package version; the bump landed after the
last regeneration, so CI's drift gate caught the stale stamp.
2026-06-12 19:56:21 +02:00
Marc-André Bombeck 9dc0ccd03a fix(medications): release-QA findings on the inventory overhaul
- The legacy GLP-1 ledger reads again: GET and the Coach snapshot fall
  back to ledger-derived pen/dose figures when a medication has no
  inventory items; items always win once they exist. The delta branch
  on the POST is marked deprecated in favour of the inventory endpoints.
- Expired containers count as available nowhere: one shared supply
  summary (ACTIVE/IN_USE with units) feeds the overview row, the
  Bestand summary, the list payload, the GLP-1 card, and the runway
  estimate; expired units surface as a muted suffix instead.
- The inventory endpoints speak one wire dialect: request fields renamed
  to unitsTotal/unitsRemaining to match the responses, OpenAPI regenerated.
- The reorder dialog mirrors the page's grouping: active and inactive
  sections, each sortable within itself, so a saved order can never
  interleave them.
- Polish strings avoid the 2-4 plural trap, sub-dose quantities render
  as below one dose instead of approximately zero, and the table's name
  sort collates by the active locale instead of hardcoded German.
2026-06-12 19:11:53 +02:00
Marc-André Bombeck e21dfd040e feat(medications): a sortable table view with an order you choose
The medications page gains a compact table beside the card view — one
row per medication with the same served-due status pill, next-intake
line, cadence-scaled adherence bars, dose-derived stock and the take
and skip actions the cards use, all from the payloads the cards
already read; the list response aggregates stock in the same round
trip. Columns sort tri-state with nulls last, the default order is
yours to arrange — the same drag dialog the dashboard and insights
use, applied to both views — and the chosen view and order persist
per user on their own settings column with field-scoped writes, so
toggling the view never touches the order. Inactive medications pin
to the end, the first column stays put under horizontal scroll, and
the loading skeleton follows the view you left.
2026-06-12 18:10:21 +02:00
Marc-André Bombeck 7d3557a3fd fix(medications): every taken dose moves the stock, in units that match the product
Stock only decremented on the web card's take button — the status
toggle, the phone sync, the Telegram buttons and the token API all
recorded the dose and left the inventory untouched, which is why a
fresh pack kept reading full. Consumption now stamps the intake row
itself: every path that turns a row taken consumes through one module
— open container first, then earliest expiry with auto-open, spilling
into the next pack and flooring at zero — and every path that takes it
back (undo, delete, a take turned skip, a converged duplicate)
refunds exactly what the stamp recorded. Replays and pending sync
echoes can never double-charge, and the nightly convergence pass
refunds the rows it tombstones. A medication can declare units per
dose, so two 2 mg tablets book as one 4 mg dose and stock reads in
doses with the unit count alongside; containers carry a named type
including tablet packs, hold up to a thousand units for inhalers, and
the GLP-1 card reads the same stock as the inventory tab instead of a
separate ledger. The inventory endpoints join the documented API.
2026-06-12 17:42:31 +02:00
Marc-André Bombeck 12fce6fc14 chore(release): v1.16.9 — the day opens with a verdict, and every dose path tells the truth 2026-06-12 11:23:00 +02:00
Marc-André Bombeck aaecd141ee fix(medications): every intake path converges on the slot, and caches cannot resurrect a pre-write body
The external ingest endpoint was the one intake writer that never
attributed to the minted band: it stored a bare row beside the pending
slot, so an early take through the API showed as the last intake while
the slot kept demanding a dose, the ledger recorded an extra plus a
miss, and the day tally counted both. It now resolves the band and
converges onto the pending row like every other path, the Telegram
confirm and skip buttons do the same preferring the reminder's own
slot, and the nightly convergence pass heals the pairs this has
already produced — one band match, one live anchor row, both days'
rollups recomputed. The cache layer fences in-flight builders on
invalidation so a read can neither join a pre-write build nor have a
background refresh re-cache a pre-write body as fresh. Ad-hoc rows no
longer resolve a different slot through the six-hour radius, a bounded
early grace credits a take up to an hour before the window without
crossing the previous band, day-scale cadences taken early downgrade
the slot-day prompt instead of inviting a double dose, the overdue
pill ignores the projector's pending mints, created intakes carry the
same plausibility bounds as edits, slot moves keep the recorded
injection site and dose, both affected days recompute on a moved
toggle, deliberate slot-less skips converge instead of orphaning, the
coach reads adherence from the ledger, the cards and quick-add run in
the user's timezone with the served next-due as the gate, and
schedule-revision, wipe and restore writes invalidate the caches they
feed.
2026-06-12 10:43:32 +02:00
Marc-André Bombeck ad33fa46ed feat(dashboard): snapshot carries the day's dose state, score and verdict inputs
The snapshot's fast phase gains a medsToday block — active count,
today's scheduled/taken/skipped tally after the idempotent projection,
and the earliest server-computed next due across active medications —
and the warm phase derives a compact health score from the coverage
map and graded blood-pressure windows the extras build already holds.
A pure verdict resolver ranks the day's single highlighted item over
nine rungs from an overdue dose down to a quiet-day fallback, with
fixed crisis floors for the blood-pressure rung and timezone-correct
day checks. The layout grows a heroVisible flag riding the stored
widgets JSON, the iOS summary route adopts the shared dose-state
builder with an unchanged wire shape, and the new sub-blocks report
their build time alongside the existing snapshot timings.
2026-06-12 09:02:58 +02:00
Marc-André Bombeck 8fdf8597a2 chore(release): v1.16.8 — texts that follow your data, charts that never strand 2026-06-11 23:21:40 +02:00
Marc-André Bombeck f790e14edd fix(api,lib): consent before reuse, tighter inputs and caches that tell on themselves
Per-status texts check AI consent before the unchanged-data refresh can
re-stamp them, the comprehensive fingerprint folds in the comparison
baseline and generation language so toggling either regenerates once,
and profile, threshold and source-priority writes evict the analytics,
targets and derived caches they feed. CSV export neutralises spreadsheet
formula prefixes on free-text cells while numeric columns stay typed,
the derived batch validates its type token against the measurement enum
and carries a per-user rate limit, and a dose synced from the phone app
counts as the interactive write it is. Background cache refresh
failures log a structured event instead of vanishing, the warm queue
runs visit-triggered jobs alongside the nightly walk, the formatter
memo evicts one entry instead of flushing wholesale with its option
objects frozen, the targets cold build bounds its fan-out, and time in
bed unions every writer's envelope while stages stay with the richest
recorder.
2026-06-11 23:20:38 +02:00
Marc-André Bombeck b346e78cc7 perf(medications): one compliance round trip and a memoised formatter hot path
Intl.DateTimeFormat instances are memoised per locale, timezone and
options signature — construction dominated the 365-day ledger build and
the hot path drops about fivefold. A batched GET /api/medications/compliance
serves every card from one request through the same per-medication cache
cells the detail page reads, replacing the per-card fan-out; the list
response stays untouched so the due line never waits on a ledger build.
The medications and compliance buckets serve stale-while-revalidate,
interactive writes evict while background bulk sync marks stale, the
per-id route defers ownership and schedule reads until a cache miss,
and the card body reserves constant slots for the status and cycle
lines so late data stops shifting the layout.
2026-06-11 21:17:17 +02:00
Marc-André Bombeck 11a061c4a1 chore(release): v1.16.7 — insights answer instantly, every evening 2026-06-11 19:46:09 +02:00
Marc-André Bombeck 03453aaed2 fix(insights,cache): surface revalidation to clients and evict on interactive writes
Stale-served insights responses now carry a revalidating flag; the
advisor and comprehensive queries poll on a bounded 25 s cadence until
the background warm lands, so an open session converges instead of
pinning yesterday's briefing for an hour. Interactive measurement
writes (single create, edit, delete, bulk-delete, restore) hard-evict
the analytics cache so a manually entered value is reflected on the
next read; background batch sync keeps the mark-stale path. The
visit-triggered force warm collapses to one job per user instead of
one per locale, and the umami script proxy failure now annotates a
named action with the failure reason instead of a bare flag.
2026-06-11 19:45:32 +02:00
Marc-André Bombeck 390a362d7a chore(release): v1.16.6 — corrected eras, an honest weekly status and a faster first paint 2026-06-11 16:15:30 +02:00
Marc-André Bombeck 40f7196c20 feat(medications,coach,admin,perf): era corrections, a truthful weekly status and one card language everywhere
The dashboard snapshot fires the moment login succeeds instead of
waiting for the page chunk (start ~670 to ~200 ms throttled). Guided
follow-ups are personalised from the user's snapshot and an answer gets
the coach's streamed reaction before the next question. Archived
schedule eras can be corrected through a superseding revision that
keeps the original as an audit trail, era writes serialise per
medication, and duplicate dose times collapse in validation. A rolling
medication whose next dose is tomorrow can no longer read as heavily
overdue — the card status derives strictly from the served next-due —
and the adherence bars keep a fixed geometry in every state. The
server-wide assistant key gains its admin card, the one duplicated
coach preference collapses onto a single owner, twenty-seven admin and
settings card headers move onto the shared pattern with sixty
containers normalised, and the remaining hydration warnings are gone.
2026-06-11 13:57:15 +02:00
Marc-André Bombeck a4b3d2fcd0 chore(release): v1.16.5 — a guided coach dialog and a schedule that shows its past 2026-06-11 12:08:46 +02:00
Marc-André Bombeck 5639da4333 feat(medications,admin): the schedule's history is visible and invites count their uses
The schedule tab shows its eras as a collapsible timeline — current
plan since its date, predecessors with their times and span — and a
past era can be added manually for old history, with validation against
overlaps and the live plan. Invite creation chooses one to fifty uses
and the redemption list scrolls.
2026-06-11 11:45:58 +02:00
Marc-André Bombeck 5778dd0be0 fix(release): ssr-safe add-param, noon-anchored compliance days, localized wizard assertion
The measurements page consumed its add parameter during render, which
crashed server rendering now that every route renders dynamically with
real search params. Compliance day buckets anchored to the current
wall-clock minus twelve hours, so before local noon the current day was
missing from every bucket series — they anchor on local noon now. The
wizard spec accepts the localized start date its summary deliberately
shows, and the generated contract picks up the version bump.
2026-06-11 11:01:22 +02:00
Marc-André Bombeck 3b480b9885 perf(i18n,shell): ship one locale and stop blocking the first paint
The client bundle carries only the active locale — resolved on the
server and handed to the provider with no hydration flash, halving the
dashboard route to under a megabyte parsed (288 KiB gzip). The auth
shell renders the app chrome immediately instead of a spinner while the
session resolves in parallel, the widgets request folds into the
snapshot it duplicated, and insight pills prefetch their sub-pages —
a first visit to a sub-page lands in about ninety milliseconds instead
of nearly a second.
2026-06-11 02:19:27 +02:00
Marc-André Bombeck 7e7e17c68c feat(medications,coach,insights): record the dose you actually took
An intake can carry the dose that was actually taken — editable in the
quick-add and log dialogs, shown as a quiet deviation line in the
history. The measurements and mood lists refresh by pull on touch, the
insight sub-pages gain the same customize entry as the overview, and an
answered coach follow-up offers to adopt the answer into the matching
self-context field.
2026-06-11 00:10:01 +02:00
Marc-André Bombeck bcbb05c770 fix(charts,medications): hold the overdue dose on the card and floor non-negative axes
A dose past its slot time but still inside its catch-up window now stays
on the medication card as overdue and takeable instead of yielding to
the next occurrence — the next-due field can therefore carry a past open
slot, flagged with nextDueOverdue. Y-axis padding no longer dips below
zero for series that cannot be negative, so a steps chart stops
suggesting minus nine hundred steps.
2026-06-10 22:41:28 +02:00
Marc-André Bombeck 6067d9176e chore(release): v1.16.1 — a chat worth using, honest sleep stages, stock you can record 2026-06-10 21:20:22 +02:00
Marc-André Bombeck 5b7c554b1e chore(release): v1.16.0 — a coach that knows you, invites, and a faster, calmer app 2026-06-10 17:36:43 +02:00
Marc-André Bombeck 24928dbb38 chore(openapi): regenerate for the coach self-context and invite endpoints 2026-06-10 16:18:51 +02:00
Marc-André Bombeck aabc7915cb feat(settings): a time-format preference and one home for insight pills
The clock format followed the UI language with no way out — an English
interface forced AM/PM everywhere. A new profile preference (automatic,
24-hour, 12-hour) feeds every formatter through the shared locale
context, persists on the user row and rides the profile endpoints. The
insight pill list in settings gains the same eye toggle the overview
tiles have, so ordering and visibility live in one place; the page-side
'manage detail pages' block folds into a link there. Includes the
locale bundles for both features and the regenerated API contract.
2026-06-10 13:34:46 +02:00
Marc-André Bombeck ba4421e464 chore(release): v1.15.19 — honest dose states, bounded bodies, new licence 2026-06-10 12:16:38 +02:00
Marc-André Bombeck eb1bbef295 chore(license): move to PolyForm Noncommercial 1.0.0
The project licence changes from AGPL-3.0 to the PolyForm Noncommercial
License 1.0.0 for this and future versions: free to use, modify and
self-host for noncommercial purposes; commercial use needs a separate
arrangement. Releases up to and including v1.15.18 were published under
AGPL-3.0 and remain available under that licence. LICENSE carries the
verbatim PolyForm text, and the SPDX identifier is updated in
package.json, the version endpoint, the OpenAPI info block, the image
labels and the README.
2026-06-10 11:18:12 +02:00
Marc-André Bombeck 82d4081ac5 chore(release): v1.15.18 — traceable medication dose history 2026-06-08 19:49:12 +02:00
Marc-André Bombeck e523a3ae00 feat(medications): validate per-dose dose windows on schedule writes
Accept an optional doseWindows array on the schedule create/update routes:
each entry pins an explicit on-time range [start,end] to one of the
schedule's times-of-day, defaulting to the symmetric grace band when
absent. Reject malformed ranges (bad HH:mm, a timeOfDay outside the
schedule, an inverted span) with a 422 so the persisted window can never
feed the band minter a degenerate range.

Regenerates the OpenAPI contract for the additive field (iOS-safe).
2026-06-08 18:34:37 +02:00
Marc-André Bombeck 15f648588b chore(openapi): regenerate for the intake forceSlotInstant field 2026-06-08 18:08:14 +02:00
Marc-André Bombeck 8efdac7c5a chore(release): v1.15.17 — large imports, demo chart toggles 2026-06-07 23:38:01 +02:00
Marc-André Bombeck bc2f510371 chore(release): v1.15.16 — wellness ring glow no longer clipped 2026-06-07 22:05:33 +02:00
Marc-André Bombeck d51e569c59 chore(release): v1.15.15 — one warm, motivating assistant voice 2026-06-07 21:49:27 +02:00
Marc-André Bombeck 1d4b470efa chore(release): v1.15.14 — manage every detail page, calmer mood charts 2026-06-07 21:02:51 +02:00