release/v1.37.20 - #796
Merged
Merged
Conversation
…ustom-metric tombstone Migration 0334, all additive. MeasurementReminder gains snoozedUntil, lastSkippedAt and skipCount; the new MeasurementReminderEvent table is the append-only completion ledger (kind SATISFIED|SKIPPED, occurredAt, write-time onTime, source). CustomMetricEntry gains the deletedAt tombstone for undo parity with every other entry surface. Consumers land in the sibling commits of this branch. Refs #223
…rt endpoint Ten operations the spec owed: the seven mood-entries routes (list, create, read, edit, delete, restore, bulk-delete) whose /bulk twin was already published, GET /api/mood/linked-context (a cross-section read through the mood URL that grant-scope reviewers need visible), and POST /api/import (the spec documented polling an import job with no way to create one). Every schema mirrors the handler's own Zod source; the envelope, refusal and error semantics are read off the routes, not invented. Request-body ids are bound instances now — a bare .meta() call clones in Zod 4 and never reaches components.schemas.
Refs #611. The nap separation itself shipped in v1.34.4; what remained of the report was the tooltip footer labelling a single night's total with the orphaned 'Average per night' key (the headline card that owned it was dropped in v1.22.0, so hovering one column claimed an average that was that night's sum). The footer now names the main session's sleep, and on a day with an inferred nap adds the whole-day total beside it — the Main sleep / Naps / Total triple the report asked for. Percentages keep dividing by the night alone.
Refs #223. A due Vorsorge item could only be done, deleted or ignored; Telegram's postpone moved the date with no trace. Both decisions are first-class now, and both are honest: - POST /{id}/skip (no body): the interval restarts from the skip instant through the recurrence engine, lastSkippedAt is stamped, skipCount increments, any snooze clears. lastSatisfiedAt is NEVER touched — done and skipped stay distinguishable forever, enforced in the primitive and pinned by tests. - POST /{id}/snooze ({until: YYYY-MM-DD}): the server resolves the day to the notifyHour in the profile timezone and moves snoozedUntil and nextDueAt to the same instant, so every due-state consumer follows without knowing snooze exists. At least tomorrow, at most five years. - GET /{id}/history: the new append-only completion ledger (MeasurementReminderEvent), one row per satisfy from every engine path and per skip, onTime derived at write time against the due instant that was current when the event landed. Two anchor fixes close the backdated-event gaps: the auto-resolve floor is now the later of last satisfy and last skip (an old reading synced late no longer resolves the cycle the user just skipped), and a backdated real result re-anchors the next due at the later of its own instant and the skip decision. Screenings stay skippable and snoozable — the satisfy-side 409 guards claimed fulfilment; a skip claims the opposite. Appointment reminders 404 on all three routes, in the lookup, like every by-id sibling. /checkups gains the postpone sheet (quick chips, date field, the skip zone with its own confirm) in both view branches, with muted meta lines and no card colour change. The MCP preventive-care tool carries the resolved fields; the OpenAPI contract publishes all three paths and the widened DTO.
The documents view already round-trips its filters through the query string; the measurements and mood lists kept theirs in memory, so any navigation dropped them. The documents pattern is extracted into a shared hook (use-url-filter-sync) and both lists adopt it: type/source/day-bounds and the measurement value range survive reload, back and sharing, defaults stay out of the URL, invalid facets drop leniently. The insights readings subpage keeps its locked type in memory — that URL belongs to the insights route. /measurements no longer strips ?type=, so the deep links the Vorsorge cards mint now persist as filter state.
Refs A3-11. Deleting a logged custom-metric value was the one hard delete left on an entry surface — everywhere else a deletion tombstones and the toast offers Undo. The DELETE verb now stamps deletedAt, every read (list, latest-value, counts, revive check) filters the tombstone, and the new entries/restore endpoint clears it scoped to owner and parent metric — a foreign, live or mismatched id is a silent no-op, never an existence leak. The history table's delete toast carries the Undo action.
Document search does whole-token equality over a blind index, so 'Blutdruck' never found the discharge letter that says 'blood pressure' and 'hypertension' missed every German Arztbrief. A curated German/English medical synonym list (vitals, common labs, imaging, specialties, document words — static, no vendor names) now expands the QUERY tokens before they are hashed: the stored index, its tokenizer version and the schema are untouched, the expansion happens ahead of the one-way hash, and recall simply widens. Typed tokens always survive the expansion cap.
The reminder cadences were the oldest entry on the coverage-pending register: carried in name, restored never — an account came back silent until every reminder was rebuilt by hand. Both models are two-ended now, in the house shape (both ends in one file, reminders-backup.ts): every column travels, nextDueAt verbatim rather than recomputed, the completion ledger beside it, tombstones in disaster recovery only, and a portable export omits a tombstoned reminder together with its ledger rows. Encounter.reminderId and VaccinationRecord.reminderId stop being dropped on restore: both remap against the restored reminders and fall to NULL with a skip report only when the file genuinely lacks the row. The wipe plan names the ledger child-before-parent; the classification guard, completeness guards and the integration round trip all demand the new coverage (proven failing with the section removed). Refs #223
Refs #778. Two honesty gaps from the first-run report: the web onboarding presented Apple Health like a web connector although the connection only completes in the iPhone app, and the initial backfill gave no sign of progress, so a working first sync looked broken. The onboarding source step now says plainly that Apple Health is set up in the iPhone app after onboarding, without dead-ending the flow. The integration card gains a server-authoritative progress summary — rows accepted, oldest reading reached, and an honest flowing/waiting line derived from the last accepted batch. Nothing the server does not know is shown: no percentage, no ETA, no invented throttle state; a quiet stretch reads as waiting for the phone, not as an error.
The hero card composition was fixed: score panel first, the worth-a-look
rail beneath. A reminder-driven person asked for the reverse. The stored
dashboard layout gains an optional hero field ('score' default,
'reminders' promotes the rail into the hero slot), validated as a closed
enum, preserve-when-absent on write, omitted from the blob at the default
so untouched accounts serialize byte-identically. The choice sits in
Settings beside the other hero controls; the promoted rail degrades to
the calm all-clear line when nothing is due.
Refs A3-8. The per-item PHQ-9 / GAD-7 / WHO-5 / SCI answers have been stored encrypted since the assessments shipped, and nothing ever read them back — the clinical detail (item 9 included) was invisible. A new by-id read decrypts them for whoever holds the mind read grant (the same grant that already serves the mood diary's prose and the item-9 flag), degrades to an honest items-unavailable state on any decrypt or shape problem, and the history rows gain an expandable breakdown rendering the official instrument texts the check-in flow already carries — nothing retranslated, no alarm styling on item 9 beyond what the house already does. Rides along: the guard inventories learn the branch's new routes (frozen counts moved with reasons), the reminder-engine test harnesses carry the ledger delegate, and the custom-metric restore toast keys sit in their right namespace in all six locales.
The restore dialog asked for its typed confirmation on nothing but a filename and a date; the counts only surfaced in the audit row after the rows were already replaced. A read-only summary endpoint now decrypts and schema-validates the stored file through the same helpers the restore itself runs and answers the summarizeBackup counts, and the dialog shows them — per section, with an other-records line so a future summary key can never silently vanish from the preview. A failed preview states its own absence and never blocks the restore.
…eeze the no-FK pair, document the pin bump Four small debts from the audit tail. The FHIR bundle's missing Immunization resource gets the same written-decision paragraph the visits exclusion carries — deliberate follow-up, not oversight. The service worker's offline allowlist gains /api/daily/digest (both copies, policy module and sw.js, plus the test) so the Today rail renders on an offline reopen. The two Telegram plumbing tables that link to an account by a bare userId string are frozen as a closed NO_FK_BY_DESIGN set in the wipe guard — a third such table now fails the test instead of passing rule 1 silently. And deploy.md documents the manual digest-pin bump end to end: imagetools inspect, the env update, and the three-probe /api/version verification with the too-fast-deploy warning.
The entries gained a soft-delete this release; both backup ends follow in the same one. A portable export omits tombstoned readings so a restore cannot resurrect them; a disaster-recovery payload carries the tombstone and the restore writes it back, so the account returns exactly as it stood — undo affordance included.
…ire the tokenizer version The knip blind spot (ignoreExportsUsedInFile) left eight symbols carrying an export nothing outside their file consumes — the modifier goes, the symbols stay. The score series module goes whole: its only importer was its own guard test, whose composite- and config-guarding suites survive in place. DocumentContentIndex.tokenizerVersion gains the consumer that was the point of storing it: the content-index job now re-tokenises rows whose version trails CONTENT_TOKENIZER_VERSION, from the decrypted index text along the same path the rotation script uses, keyset-paginated and fault-isolated per row — a future tokenizer bump migrates the index instead of silently splitting it. ImportJob.uploadSha256 turned out to already have its dedupe consumer (both kick-off routes short-circuit on same-bytes non-failed jobs); its comment now names that instead of pretending to be write-only. The remaining write-only columns carry their honest annotations in the shape the audit sweep parses.
…irms, tokened micro-text The German bundle's 109 spaced en-dash pauses join the em-dash norm (the two date-range placeholders stay ranges); the three straight-quote stragglers and three mixed-quote strings take German quotes; the eleven Bitte-Infinitiv validation errors speak du-imperative like the rest of the app; the two wirklich confirms become bare questions; the notification-status description trades its semicolon telegram style for two sentences in every locale; the Withings setup action shortens to the sibling single word. Twelve arbitrary text-[10px]/[11px] sites move onto the text-2xs/text-xs tokens; chart tooltips and the vendor calendar stay as they are.
The rewritten notification-status description ran to two sentences and 130 characters — the card-description guard is right, the slot holds one meta sentence. Folded to one em-dash sentence in every locale. The new documents_retokenised job fact joins the outcome allowlist with its reason.
Apple Health exports write ECG CSVs in two shapes. The importer only understood the paired Lead,Voltage form; the single-column form (Lead and Unit as metadata rows, a blank separator, then one bare reading per line) failed every row as malformed and the whole recording was skipped. The parser now treats a blank line after Lead and Unit metadata as the start of the waveform, reads one bounded numeric sample per row, and converts the declared unit to integer microvolts before the existing amplitude and safe-integer bounds. The paired layout parses exactly as before, and an unknown unit fails with a fixed message. Refs #794
The two new measurement-reminder routes were admitted in the frozen fixture but never got their integration drivers, so the matrix count stayed at 75 and the executable proof was missing. Both routes now carry a full manageContract driver: the skip driver asserts the stamped column, exactly one SKIPPED ledger row, and that lastSatisfiedAt stays untouched; the snooze driver asserts the snoozed-until write under the same invariant. All three inventory pins move to 77.
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.
Preventive-care snooze and skip with a completion ledger, the configurable dashboard hero, first-run Apple Health progress, the single-column ECG import fix, restore preview, per-item screening breakdown, document-search synonyms, filter URL sync, and the backup carry for all of it. CHANGELOG carries the details, including the repaired v1.37.18 heading.