fix(recovery): publish why the recovery page is empty from the server

The page worked out its empty note on the client from the integrations
status, so every client had to repeat the rule. The analytics summaries
slice now carries `recoveryEmpty` (general, oura_permission or
oura_no_resilience, null when any recovery type has a reading), decided
once in resolveRecoveryEmpty from the gated summaries and the stored
Oura grant. The page renders that value only, and its block list is
typed to the server's recovery types so the two cannot drift.

Each reason now reads as one sentence in every locale, and every reason
offers the same way on, the integrations settings.
This commit is contained in:
Marc-André Bombeck committed 2026-10-20 05:01:18 +02:00
1 parent 0ea8880923
commit 862361a224
20 files changed
+243 -171

No files matched your search

+18 -2
View File
@@ -62980,14 +62980,30 @@ components:
- type: "null"
description: Non-null when two writer buckets reported clearly different asleep totals for the latest night's main
session. Observational only — the served summary stays the winning writer's totals.
recoveryEmpty:
anyOf:
- type: string
enum:
- general
- oura_permission
- oura_no_resilience
- type: "null"
description: "Why the recovery page has nothing to chart, resolved on the server; null when any recovery type
(ANS_CHARGE, DAY_STRAIN, WORKOUT_STRAIN, CARDIO_LOAD, AVERAGE_HEART_RATE, MAX_HEART_RATE,
ENERGY_EXPENDITURE_KJ, RESILIENCE) has a reading. `oura_permission`: Oura is connected and its grant leaves
out the scope the resilience level needs, one reconnect fixes it. `oura_no_resilience`: Oura is connected
and no level has arrived yet. `general`: anyone else. Clients render the empty note from this value and
never work it out from the connection status."
required:
- summaries
- bmi
- lastSeenByType
- sleepSourceDiscrepancy
- recoveryEmpty
additionalProperties: false
description: "What `?slice=summaries` answers: the per-type summaries and their freshness, and nothing thick. Four keys,
and the caller gets them from two SQL passes rather than the default slice's chain."
description: "What `?slice=summaries` answers: the per-type summaries and their freshness, the recovery page's empty
reason, and nothing thick. Five keys, and the caller gets them from two SQL passes rather than the default
slice's chain."
InsightsGenerateReadEnvelope:
type: object
properties: