Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c574ef7
Add the per-user health-score composition column
MBombeck Jul 31, 2026
cb75986
Add the health-score record table
MBombeck Jul 31, 2026
d92be0d
Lift the score breadth rule into one shared place
MBombeck Jul 31, 2026
860a1fa
Record each local day's score on the path that computes it
MBombeck Jul 31, 2026
4e649de
Add the health-score composition resolver
MBombeck Jul 31, 2026
d89b90d
Hash what the score actually depends on, and nothing that cannot move
MBombeck Jul 31, 2026
2f6f357
Add the route that chooses what counts toward the score
MBombeck Jul 31, 2026
ab8e9a1
Carry the recorded score days through wipe, backup and restore
MBombeck Jul 31, 2026
cd7c6f4
Teach the export test mocks about the recorded score days
MBombeck Jul 31, 2026
956746f
Keep one sanitiser for pillar ids
MBombeck Jul 31, 2026
5fd5447
Make the band a database type instead of a text check
MBombeck Jul 31, 2026
54fed25
Silence an unused-parameter warning in the record write test
MBombeck Jul 31, 2026
c66aafe
Read the deferred configuration columns in one line in the audit report
MBombeck Jul 31, 2026
76e68f0
Let the score's recipe decide what counts, not the module switches
MBombeck Jul 31, 2026
0a877bd
Pin what decides the score's composition
MBombeck Jul 31, 2026
0826bd8
Merge the persisted score record into the composition swap
MBombeck Jul 31, 2026
087f3f4
Stop a settings change from reading as a score drop
MBombeck Jul 31, 2026
5d13f0e
Prove the false drop cannot come back
MBombeck Jul 31, 2026
3406ed6
Drop a boundary condition no input could reach
MBombeck Jul 31, 2026
98eb609
Remove two refusals that could not refuse anything
MBombeck Jul 31, 2026
0a524c5
Write down what an upgrade does to an account that already exists
MBombeck Jul 31, 2026
5d7d009
Resolve the configured flag on the score composite
MBombeck Jul 31, 2026
c29055b
Carry the configured flag onto the snapshot and digest wires
MBombeck Jul 31, 2026
0276ca9
State an authored composition in the score's method footer
MBombeck Jul 31, 2026
46146f3
Test the configured flag over the real snapshot and digest assembly
MBombeck Jul 31, 2026
308908c
Stop the score from describing a recipe the person may have changed
MBombeck Jul 31, 2026
bd4a6d1
Let a person choose what counts toward their Health Score
MBombeck Jul 31, 2026
f8b51b7
Keep the insights end-to-end fixture faithful to the wire
MBombeck Jul 31, 2026
78fe4d8
Guard the configured flag's meaning without a container
MBombeck Jul 31, 2026
bf0a637
Keep the score's own coverage honest about what it counts
MBombeck Jul 31, 2026
2b95d7e
Merge work/score-configured-flag
MBombeck Jul 31, 2026
f42d423
Merge work/score-coverage-contract
MBombeck Jul 31, 2026
cdfe4ec
Merge work/score-settings-ui
MBombeck Jul 31, 2026
7787258
chore(release): v1.35.0 — the score you decide
MBombeck Jul 31, 2026
88162ed
Say whether each coverage case is an authored recipe or the default
MBombeck Jul 31, 2026
8470619
Let the score settings announce a save the way every other surface does
MBombeck Jul 31, 2026
b85a348
Give the type check the same memory ceiling the build already has
MBombeck Jul 31, 2026
5424ab0
Assert the settings heading that the width actually shows
MBombeck Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,54 @@

## [Unreleased]

## [1.35.0] — 2026-07-31

The Health Score becomes something you decide rather than something that happens
to you.

### Added

- You choose what counts toward your Health Score. A settings page of its own
lists the pillars, each one on or off. It starts with everything that can be
scored, so a narrower score is a decision you make rather than a default you
inherit.
- Each day's score is now recorded with the recipe that produced it: the value,
the method, which pillars took part, what each of them scored, and a
fingerprint of the inputs it depended on. Until now nothing was kept, and every
comparison was recomputed under today's rules.
- The score says when its composition was authored, so a number built from a
chosen set never looks like one built from everything available.

### Changed

- The module switches no longer decide what the score is made of. Turning off
Sleep or Labs used to remove that pillar from the score with nothing on screen
saying so. Those switches now govern what you see, and the score's own page
governs what it counts. An existing account keeps exactly the composition it
has today, so no number moves on upgrade.
- Changing what counts no longer reads as a change in your health. The comparison
stops at the point the recipe changed and says so, rather than drawing a line
across two different definitions. The per-pillar figures continue, because those
stay comparable.

### Fixed

- A settings change can no longer produce a message telling you your score
dropped.
- The score's own coverage figure now describes the pillars it actually counts
rather than every pillar that exists.

### Removed

- Two refusals that could not refuse anything, and a boundary condition no input
could ever reach.

### Operations

- Two migrations: one for the per-account composition, one for the recorded
scores. The recorded days travel through backup, restore and delete-all-data
like every other table.

## [1.34.5] — 2026-07-31

A release about the score explaining itself, and about sentences arriving in the
Expand Down
4 changes: 2 additions & 2 deletions bundle-budget.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"$comment": "Client-bundle budgets, enforced by scripts/check-bundle-budget.mjs (--check) after `next build` in CI. Values are KB gzip and include every emitted lazy route and locale chunk, while route budgets include the shared rootMainFiles baseline. Re-measured at v1.34.1: / 431, /insights 420, /measurements 417, /insights/mood 438, total 3238, baseline 130, lazy catalogs 792. The 108 KB aggregate rise from v1.34.0's 3130 is not one user load: the explicit assessment error/timeout/poll-exhaustion contract adds 2.7-4.4 KB to each of roughly forty mutually exclusive metric route chunks, which the whole-build sum counts independently. Catalogs account for only about 2 KB of the delta, no catalog is statically route-referenced, and Recharts remains exactly one fingerprinted chunk. A measured single next/dynamic assessment boundary was reverted because it raised the artifact further to 3243 KB while saving only about 1 KB per route. The aggregate cap therefore moves narrowly from 3140 to 3260 (22 KB headroom); per-route ceilings, the static-catalog guard, and the one-Recharts-chunk guard remain unchanged.",
"$comment": "Client-bundle budgets, enforced by scripts/check-bundle-budget.mjs (--check) after `next build` in CI. Values are KB gzip and include every emitted lazy route and locale chunk, while route budgets include the shared rootMainFiles baseline. Re-measured at v1.35.0: / 431, /insights 421, /measurements 417, /insights/mood 439, total 3286, baseline 130, lazy catalogs 804. Every per-route ceiling is unchanged and every measured route is within a kilobyte of its v1.34.1 value, so nothing a person actually downloads grew. The 48 KB aggregate rise from 3238 is the score-configuration settings route, which is a new mutually exclusive chunk the whole-build sum counts independently, plus about 12 KB of its strings across the six locale catalogs. No catalog is statically route-referenced and Recharts remains exactly one fingerprinted chunk. The aggregate cap therefore moves from 3260 to 3310 (24 KB headroom, matching the previous margin); per-route ceilings, the static-catalog guard and the one-Recharts-chunk guard remain unchanged.",
"routesKbGz": {
"/page": 460,
"/insights/page": 445,
"/measurements/page": 445,
"/insights/mood/page": 460
},
"totalClientKbGz": 3260,
"totalClientKbGz": 3310,
"maxRechartsChunks": 1
}
33 changes: 31 additions & 2 deletions docs/api/openapi.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: HealthLog API
version: 1.34.5
version: 1.35.0
description: >-
Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest.

Expand Down Expand Up @@ -28112,11 +28112,19 @@ components:
- WELLBEING
- FITNESS
- LIPIDS
configured:
description: "True when the account's own recipe narrows the score's composition below what its defaults would resolve
to today. Server-resolved, so a client never interprets a configuration blob: an account that kept
every pillar reads false, and so does one whose disabled modules alone narrow the set. The
configuration itself is never on this wire. Optional so older cached snapshots without the field
stay valid."
type: boolean
deltaReason:
anyOf:
- type: string
enum:
- algorithm_changed
- config_changed
- composition_changed
- first_eligibility_window
- below_noise_floor
Expand Down Expand Up @@ -35261,13 +35269,34 @@ components:
anyOf:
- type: number
- type: "null"
configured:
description: True when the account's own recipe narrows the score's composition below what its defaults would resolve to
today. Resolved on the server and carried straight from the dashboard snapshot; the configuration
itself never rides this wire. Optional so older cached digests without the field stay valid.
type: boolean
deltaReason:
description: Why a delta was suppressed; null when it was not.
anyOf:
- type: string
- type: "null"
scoreVersion:
description: Algorithm version that produced the value.
type: integer
exclusiveMinimum: 0
maximum: 9007199254740991
composition:
description: Registry-ordered pillar ids the value was composed of.
type: array
items:
type: string
required:
- value
- band
- delta
additionalProperties: false
- type: "null"
description: Health score + band + week-over-week delta; null when none.
description: Health score + band + week-over-week delta, plus whether the account authored the composition; null when
none.
topSignal:
anyOf:
- type: object
Expand Down
Loading
Loading