chore(release): v1.28.34 — nutrient intake lands as quiet context (server side)

Supplement-style intake written to Apple Health by nutrition apps —
vitamins, minerals, water, caffeine — can now sync as daily totals into a
dedicated NutrientIntakeDay store (migration 0241): composite (user, day,
nutrient) identity, re-post replaces, no soft delete, no rollup coupling.
A code-side catalog carries the 26 nutrient codes with canonical units,
plausibility caps, and EFSA dietary reference values (direction-aware;
chromium cites NIH ODS where EFSA declined a DRV).

Deliberately not a nutrition feature: no diary, no calories, no dashboard
surface. POST /api/nutrients/batch is gated behind the new opt-in
nutrients module (default off — with the module off the server refuses
ingest entirely), rate-limited, idempotency-wrapped, with per-entry
unit-mismatch and out-of-range skips. GET /api/nutrients serves a
catalog-ordered summary; the only visible surface is a read-only card
under Settings → Sources. Coach/correlation access is a later slice; the
iOS reading side is coordinated in the companion-app tracker.
This commit is contained in:
Marc-André Bombeck committed 2026-07-14 13:10:47 +02:00
1 parent 14a9a08a6e
commit 5445fc67ea
34 files changed
+2766 -12

No files matched your search

+6 -2
View File
@@ -357,14 +357,18 @@ describe("registry — opt-in marker", () => {
// are highly sensitive: `mcp` (remote external-assistant endpoint),
// `environment` (outbound weather fetch tied to a coarse location),
// `inboundDocuments` (sends an uploaded clinical document to the OCR/vision
// provider), and `mentalHealth` (a depression / anxiety self-assessment — at
// least as sensitive as mood, surfaced only on explicit opt-in). Every other
// provider), `mentalHealth` (a depression / anxiety self-assessment — at
// least as sensitive as mood, surfaced only on explicit opt-in), and
// `nutrients` (supplement-pattern intake synced from the phone's health
// store — the device-side HealthKit permission is not visible consent to
// server/AI-side storage, so ingest refuses while off). Every other
// module is the default-on disabled-allowlist.
const OPT_IN_KEYS = new Set([
"mcp",
"environment",
"inboundDocuments",
"mentalHealth",
"nutrients",
]);
it("marks only the egress-surface modules opt-in (every other is default-on)", () => {
+20
View File
@@ -147,6 +147,15 @@ export const MODULE_KEYS = [
// assessment routes refuse server-side. Item answers are always encrypted and
// always excluded from the AI Coach / MCP regardless of this toggle.
"mentalHealth",
// v1.28 — micronutrient-intake sync (vitamins, minerals, water,
// caffeine as day totals from Apple Health). OPT-IN (off by default):
// the HealthKit read permission is consent to read on the DEVICE, not
// visible consent to "a server — and later an AI assistant — holds my
// supplement pattern"; the project already answered this for less
// sensitive data (`environment`, weather, is opt-in). With it off the
// ingest route refuses (403 `module.disabled`) and the settings card
// does not render. The `optIn` marker inverts the per-user default.
"nutrients",
] as const;
export type ModuleKey = (typeof MODULE_KEYS)[number];
@@ -323,6 +332,17 @@ export const MODULE_REGISTRY: Readonly<Record<ModuleKey, ModuleDefinition>> =
// off the AI Coach / MCP regardless.
optIn: true,
},
nutrients: {
key: "nutrients",
labelKey: "modules.nutrients.label",
descriptionKey: "modules.nutrients.description",
category: "device",
// Off by default: supplement-pattern intake synced from the phone's
// health store is server/AI-consent-sensitive even though the
// HealthKit read permission was granted on the device. Ships dark;
// ingest refuses while off (refuse-ingest posture, not surface-only).
optIn: true,
},
});
const MODULE_KEY_SET: ReadonlySet<string> = new Set(MODULE_KEYS);