release/v1.12.2#259
Merged
Merged
Conversation
…spine, consolidate status cards The per-metric AI assessment was named four ways across the bundle (Einschätzung / Auswertung / Analyse / Bewertung in German, with parallel drift in the other locales). Sweep every assessment-state value — the no-provider, no-result, start / refresh / re-run, regenerate and warm strings — onto the one noun the card title already carries, in all six locales. Keys are unchanged; only the human strings unify. Restore the canonical metric-detail spine on the two bespoke pages that had drifted: the mood page rendered the assessment directly under the first chart with the target summary and breakdown sections below it, and the medications page rendered the therapy timeline after the assessment. Both now end on the assessment block, matching weight / bmi / pulse / blood-pressure and the generic scaffold. A source-level guard test asserts the assessment card is the last child of every bespoke page's shell so the order cannot silently regress again. Consolidate the repeated eight-prop assessment wiring: the five useInsightStatus-backed pages now mount a shared SlugInsightStatusCard seam (the sibling of MetricStatusCard for the generic route) instead of hand-wiring the card and the status?.x defaults inline. The medications page keeps its inline card because its route carries a richer summary-shaped envelope than the standard text-only generators. The assessment footer's "updated" caption now uses the shared relative-time formatter the briefing, hero, last-measurement and coach-history captions use, so adjacent cards no longer read one relative and one absolute timestamp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 2cd73ce5d6cdca63fea0829990fac2a7e24afebf)
…tatus tokens Lift the take / skip intake flow — the optimistic spinner, the failure toast, the soft-delete Undo, and the dependent-key invalidation — into a shared `useMedicationIntake` hook consumed by both the generic medication card and the GLP-1 card. The GLP-1 card previously inlined its own copy that never gained the failure toast or the Undo affordance, so a failed POST was swallowed silently and a misclicked dose had no quick reversal. Both cards now behave identically by construction; each keeps only its post-success injection-site prompt. Converge the medication status-colour surface onto the semantic feedback vocabulary. The status pill reads as a success → warning → destructive urgency ramp (dropping the lone `text-dracula-yellow` middle tier), the streak flame moves off the raw `text-dracula-orange` palette token onto `text-warning`, the detail status dot swaps its raw `hsl(var(--…))` form for `bg-success` / `bg-warning`, and the import result line drops `text-dracula-green` for `text-success`. Each semantic token aliases the same colour the raw form resolved to, so the hue is unchanged in dark mode and lands AA-safe on the light card. Route the compliance percentages through the locale number formatter with rounding so a non-integer rate never leaks raw, and give the medications Add button the same `min-h-11 sm:min-h-9` tap-target floor the dashboard Add button carries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit a4415d706154b063a8783390e35eded5dbc57910)
The native client SPKI-pins the server's TLS leaf certificate. When the leaf auto-renews (GTS re-issues it roughly every 90 days) the leaf keypair — and so the SPKI pin — changes, and a client shipped only the old pin refuses to connect on the next renewal: a silent outage. Nothing server-side signalled that a client pins the served leaf. Add a pg-boss monitor that probes the served leaf every 6 hours, computes its iOS SPKI pin (base64(sha256(DER subjectPublicKeyInfo)) via a raw TLS socket + X509Certificate), and compares it against the operator's known-good set in TLS_LEAF_SPKI_PINS (comma-separated for the dual-pin renewal window). On a served pin outside the set it fails loud: a tls.pin.leaf_changed wide-event annotation, a system.tls.pin_changed audit row, and a high-priority SYSTEM_ALERT fanned out to admins through the existing dispatcher idiom. Transient TLS/network failures annotate tls.pin.probe_failed and never alarm. The baseline is an env var, not a persisted last-seen row — a self-learning baseline would silently adopt the first rotation and suppress the alarm the pinned client needs; an unset baseline logs the served pin and warns rather than auto-adopting. The runbook documents extraction, the re-pin + dual-pin procedure (≥11 days before expiry, ship to TestFlight), and the assumption that the probed host is the host the client connects to. Wires the queue into the worker (allQueues + cron + worker), adds the optional env to the manifest, compose whitelist, and .env example, and covers the SPKI computation + change detection with a fixture-cert unit test plus a queue-registration guard. (cherry picked from commit cdb88cd77b88aec71ad2f21caf2b15c185dfc01e)
Two connect-in-app enhancements for the native client. return_scheme: GET /api/whoop/connect accepts an optional custom scheme, threaded through the server-side OAuth-state row so it survives the round trip without ever riding in the URL or cookie. When a valid scheme is present, the callback's final redirect targets <scheme>://whoop?whoop= connected|error&reason=... so an in-app web session auto-completes on its custom-scheme match; absent or invalid, the web settings redirect is unchanged. Schemes validate against a strict allowlist (custom app scheme only, ^[a-z][a-z0-9.+-]*$, http/https/javascript/data/file/vbscript rejected, pinned to dev.healthlog.app). A rejected scheme falls back to the web redirect and never reflects an arbitrary value. Bearer connect ticket: a purely Bearer-authenticated client holds no web session cookie, so it cannot start the handshake through the cookie-gated connect route. POST /api/whoop/connect/ticket (Bearer) mints a one-time, ~60s, opaque ticket bound to the user; only its HMAC-SHA256 hash is stored (the raw value never persists). GET /api/whoop/connect?ticket=<opaque> accepts the ticket in lieu of a cookie, consumes it atomically (single-use, reuse rejected), sets the nonce cookie, and 302s to WHOOP exactly as the cookie path. Expired, consumed, or invalid tickets return a typed 401. The two combine. The daily WHOOP OAuth-state cleanup also sweeps expired ticket rows. The ticket query param is added to the egress redaction denylist so the opaque value never reaches logs or error reports. Migration 0124 adds whoop_connect_tickets (id, user_id FK cascade, token_hash unique, expires_at, consumed_at, created_at) and a nullable return_scheme column on whoop_oauth_states. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 221b362011d52dda314139e0be7cc195c47d016e)
…ssments and medications
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.
v1.12.2 — WHOOP connect-from-app, consistent assessments/medications, cert-pin alarm.
iOS parity (WHOOP connect-in-app)
POST /api/whoop/connect/ticket(Bearer) → opaque single-use 60s HMAC-hashed ticket;GET /api/whoop/connect?ticket=accepts it in lieu of a session cookie (atomic consume, typed 401 on reuse/expiry, no IDOR, fails closed).?return_scheme=custom-scheme final redirect threaded through the signed state, strict allowlist (no http/js/data/file, pinned to dev.healthlog.app). Migration 0124 (WhoopConnectTicket + returnScheme).Insights polish
Medications polish
Operations / security
Gate
typecheck · lint (one documented allowed warning) · knip · openapi in sync · 7390 unit · build · 358 integration. Pre-ship adversarial security re-verify of the ticket/scheme/cert-pin surfaces: 0 Critical/High/Med.