feat: lay down the shared v1.42 contract

Land every file the v1.42 work shares before any of it is built, so the
parts built side by side never edit the same file.

Schema and migrations 0379-0386: the HEALTH_CONNECT measurement source,
ImportJob.kind, the air-quality columns and the per-user switch on the
environment tables, sealed environment locations with nullable plaintext,
managed-profile handover links, the per-type HealthKit arrival ledger, lab
unit-conversion provenance, and user-defined vaccines. Every new column is
annotated for the column-reader sweep.

HEALTH_CONNECT reaches every exhaustive list: the source labels, the
read enum (not the write allowlist), the source-priority ladder cap, now
the number of sources rather than a fixed eight, and the write-path smoke
test. RMSSD gets its own priority class and a 300 ms ceiling. The batch
syncTrigger gains `manual`, with its label on the Apple Health card.

Queues for the Health Connect import, the compaction-tombstone purge and
the measurement-table maintenance are registered with stub handlers;
host-metric sampling moves to every five minutes. Environment and work_mem
variables reach the compose whitelist, the production example and the
manifest. The proxy admits the handover link and redirects it at the edge,
and the Health Connect upload bypasses the proxy body limit.

The new routes answer 501 behind their final authentication and are
published in the contract; backup, wipe and key rotation classify the new
models and columns; the seven message bundles carry the base copy. Guards
that list release-pending keys and columns fail once an entry is wired, so
those lists empty themselves before the tag.

Also re-anchor a login-throttle fixture to the wall clock, where its
expiry is compared, and re-base the aggregate bundle cap on a measured
12 KB of new locale strings.
This commit is contained in:
Marc-André Bombeck committed 2026-10-08 07:56:59 +02:00
1 parent 125b15bfb7
commit e63be67b5a
82 files changed
+5099 -727

No files matched your search

+34
View File
@@ -186,6 +186,40 @@
}
]
},
{
"name": "Environmental context (Open-Meteo)",
"description": "Endpoints and switches for the opt-in Environment module. Every variable is optional: unset uses the hosted, keyless Open-Meteo services. Point the URLs at a self-hosted Open-Meteo instance to keep coordinate lookups off a third party.",
"required": false,
"variables": [
{
"name": "OPENMETEO_BASE_URL",
"purpose": "Historical weather (archive) API base URL. Default https://archive-api.open-meteo.com."
},
{
"name": "OPENMETEO_GEOCODING_URL",
"purpose": "Geocoding API base URL for the home and travel location search. Default https://geocoding-api.open-meteo.com."
},
{
"name": "OPENMETEO_AIR_QUALITY_URL",
"purpose": "Air-quality API base URL (CAMS air quality, pollen, UV). Default https://air-quality-api.open-meteo.com. CAMS data is licensed by the Copernicus Atmosphere Monitoring Service; the attribution is shown in the app."
},
{
"name": "ENVIRONMENT_AIR_QUALITY_DISABLED",
"purpose": "Set to 1, true or yes to stop the air-quality requests for the whole instance. Weather and daylight keep working; the per-user switch cannot turn air quality back on."
}
]
},
{
"name": "Database session tuning",
"description": "Per-session Postgres settings the app applies to every pooled connection.",
"required": false,
"variables": [
{
"name": "DATABASE_WORK_MEM",
"purpose": "Postgres work_mem for every app session, as a Postgres size string. Default 16MB. Raise it when large sorts and aggregates spill to disk; each sort step of each connection may use this much."
}
]
},
{
"name": "APNs (iOS push)",
"description": "All four core APNS_* vars together. Missing any disables APNs silently \u2014 the v1.4.40 AP-2 gap that motivated this manifest. allOrNone trips the synthetic <all-or-none> required row whenever 1-3 of the 4 are set.",
+24
View File
@@ -815,6 +815,30 @@ async function main() {
} as unknown as CorpusClient),
);
// ───── v1.42 environment locations (Bytes, binary2, batched) ─────
// The sealed home, each dated location period and each day's resolved
// location, all under the environment-location label. NULL on a row the
// encryption backfill has not reached yet, which the walk skips.
results.push(
await rotateRegistryColumn("User", "homeLocationEncrypted", {
user: prisma.user,
} as unknown as CorpusClient),
);
results.push(
await rotateRegistryColumn(
"EnvironmentTravelLocation",
"locationEncrypted",
{
environmentTravelLocation: prisma.environmentTravelLocation,
} as unknown as CorpusClient,
),
);
results.push(
await rotateRegistryColumn("EnvironmentContext", "locationEncrypted", {
environmentContext: prisma.environmentContext,
} as unknown as CorpusClient),
);
// ───── v1.39.3 custom-metric reading note (Bytes column) ─────
// NULL on a reading without a note, and on a row the free-text backfill has
// not reached yet; `rotateBytesColumn` skips both.