The Today hero stayed on its all-clear line while a dose was actually due:
the dose-window item only fired once the slot was already overdue, so a
dose the user could take right now produced no rail item at all. Emit the
item from the moment the dose becomes takeable — the on-time window's lead
plus the early grace, the same instant logging it starts crediting the
slot — and give the two faces distinct wording and status: a due dose reads
calm and informational, an overdue one keeps the warning. A settled day
still emits nothing, and a cached block whose anchor has since passed keeps
rendering neither face. Push reminders are untouched; they still fire only
on overdue.
The briefing grounding gate discarded the whole block over one number it
could not verify, and it could not verify plenty of legitimate ones: the
allow-set was a hand-maintained list of feature blocks that had already
drifted from the prompt three times, and still omitted medication
compliance, the context counters, the correlation coefficients, every
coverage object and the comparison snapshot — all of which the prompt hands
the model verbatim. Derive the allow-set by walking the prompt payload
instead, so grounded means exactly what it says: a figure the server gave
the model. A fabricated number still matches nothing.
Fix the disposal to match. A content-failed retry left a hole while a
transport-failed one fell back to the previous cached briefing, though in
both cases the rejected text is discarded and the previous briefing passed
this same gate when it was written. Fall back on both paths, and only
report the briefing as withheld when there is genuinely nothing to show.
The on-demand route had no fallback at all; it now mirrors the background
generator that writes the same cached payload.
Fold the ECG silo into the insights narrative without crossing the
non-diagnostic line — the app reports the recording device's own verdict
and the existence/count of recordings, never an interpretation of the trace.
- Feature snapshot: a bounded, device-verdict-only ECG descriptor (recording
count, device-verdict distribution, latest recorded-days-ago + average HR)
flows into the nightly briefing prompt. The waveform never enters the
payload; the grounding gate admits the pre-computed counts so a
device-attributed sentence stays grounded. A prompt rule (EN + DE)
constrains any mention to the device's attribution and forbids reading
morphology, measuring intervals, or emitting a verdict of the app's own.
- Pulse cross-link: a device-attributed pointer from the resting-HR / pulse
page into the ECG viewer, un-mounting when no recordings exist.
- Daily rail: an ecg_new_recording item surfaces once when a recording landed
within the last day — calm, info-level, deep-linking the viewer. Gated on
the insights module; the DTO carries no waveform.
- i18n across all six locales; non-diagnostic disclaimer copy per language.
The daily-briefing number-grounding gate strips the whole dailyBriefing block
to null when it finds a restated figure it cannot trace to a pre-computed
server value. Its allow-set covered signalsOfDay plus the glucose / labs /
preventive-care / workout / grip / waist / pain aggregates, but omitted the
core vitals blocks — weight, blood pressure, pulse, body fat, mood, sleep,
activity, rate-pressure product, seasonal variation, historical comparison —
even though the briefing prompt is fed all of them.
A verdict-first briefing that restated a blood-pressure average or a resting
pulse (figures every BP-tracking user has) therefore tripped the gate on every
run: one ungrounded number strips the entire briefing while the rest of the
insight refreshes, so the surface reads as "every other text updated, only the
briefing is gone".
Admit every figure the server pre-computes for those blocks into the allow-set.
The anti-fabrication guarantee is unchanged — only server-computed numbers are
ever added, so a genuinely invented value is still flagged and stripped.
The insights alert cards computed medication compliance with the naive
`schedules.length × days` denominator, so a fully-adherent weekly
injectable read ~14% and fired a false low-compliance alert (and a paused
medication mis-fired the same way). Route the alert path through the
canonical `calculateCompliance` engine like every other compliance
surface — pause eras, schedule revisions and dose windows honoured.
Document confirm: committing a numeric observation against an existing
biomarker silently replaced the stated unit with the catalog's, with no
conversion — a stated "6.1 mmol/L" against a mg/dL marker committed
"6.1 mg/dL". Reject on a unit mismatch (fail closed) so the user
reconciles on the review screen, the same posture as the unit-required
guard.
Wire grip strength, waist (with waist-to-height) and pain into the
briefing feature set and the grounding allow-set so the clinical-depth
signals reach the daily narrative; the mental-health screeners stay
excluded by construction.
Biomarker detail: fall back to a generic description for catalog-less
markers, and keep the heading + description with a retry on the error
state. Collapse the medication External-API and Lifecycle groups by
default. Re-enable the insights scroll-to-top regression and assert the
shipped main-content reset.
Align the EN haemoglobin/haematocrit display names to British spelling.
Drop the redundant `(userId, deletedAt)` index on inbound documents — a
strict prefix of the new three-column index. Trim the dead
MedicalDisclaimer variants to the one live `dataPosture`. Move the
crisis-resources card off the retired amber tint to a calm
destructive-subtle border. Add a registry invariant asserting every
biomarker signal key resolves to a real catalog slug.
Replace the fixed three-beat assessment skeleton with a menu of moves the
model orders freely, and reframe the daily briefing from per-signal
enumeration to a verdict-first read that weaves the few salient signals into
one associative story. Score cards now name both the helped and the hurt
contributors, translate the band into a forward read for the day, and close
on one grounded nudge; the deterministic fallback gains the same band-meaning
sentence so provider-less and demo accounts read as well.
Add a hash-seeded opener-archetype rotation (deterministic, testable) so
consecutive cards and days no longer open the same way, a day-rotated sampling
seed for the period narrative in place of the fixed reference seed, and a
shared paragraph formatting contract. Add a score-number grounding gate and
extend the briefing grounding allow-set to the glucose, labs, preventive-care
and workout aggregate blocks so real figures can be cited without opening the
gate. Gate the daily score warm on an input fingerprint so a quiet day skips
the model call.
The daily-briefing block (paragraph / signalsOfDay / keyFindings) was
schema-shaped but never number-checked the way recommendations[] are: a
restated figure could drift from the snapshot with no server rejection.
Add a grounding cross-check that asserts every number the briefing prose
states traces to a features.signalsOfDay figure (latest / avg / delta)
within a rounding tolerance. Wire it into both generation sites with one
corrective retry, then strip the briefing rather than persist a
fabricated number. Also gate the higher-temperature paragraph reroll on
the same check so a phrasing reroll cannot drift a figure.