From 14d79ab4531452e2b47b83bf05c7af0174c51fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 2 Jun 2026 21:25:58 +0200 Subject: [PATCH] fix(insights): reconcile the derived vitals + composite waves Resolve the overlap where the vitals tier and the composite tier both extend the derived-metric registry, dispatch, query-key factory, i18n bundles and the OpenAPI contract: every derived metric (vitals baseline, fitness/vascular age, HRV balance, BMI, sleep score, readiness, coincident-deviation) is now wired and implemented, the dispatch test asserts the full set, and the locale bundles carry both the vitals and the score-anatomy/composite strings. --- src/lib/insights/derived/__tests__/dispatch.test.ts | 13 +++---------- src/lib/insights/derived/dispatch.ts | 3 +++ src/lib/query-keys.ts | 1 + 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/lib/insights/derived/__tests__/dispatch.test.ts b/src/lib/insights/derived/__tests__/dispatch.test.ts index 6babe8d0a..2f595b115 100644 --- a/src/lib/insights/derived/__tests__/dispatch.test.ts +++ b/src/lib/insights/derived/__tests__/dispatch.test.ts @@ -27,22 +27,15 @@ const NOW = new Date("2026-06-02T07:00:00Z"); beforeEach(() => vi.clearAllMocks()); describe("registry", () => { - it("exposes the implemented vitals-tier metrics and the composite stubs", () => { + it("exposes every derived metric as implemented", () => { expect(getDerivedMetricMeta("VITALS_BASELINE")?.implemented).toBe(true); expect(getDerivedMetricMeta("FITNESS_AGE")?.implemented).toBe(true); expect(getDerivedMetricMeta("VASCULAR_AGE_DELTA")?.implemented).toBe(true); expect(getDerivedMetricMeta("HRV_BALANCE")?.implemented).toBe(true); expect(getDerivedMetricMeta("BMI")?.implemented).toBe(true); - // Composites still land in W3. - expect(getDerivedMetricMeta("READINESS")?.implemented).toBe(false); - expect(getDerivedMetricMeta("COINCIDENT_DEVIATION")?.implemented).toBe(false); - it("exposes the flagship + W3 composites as implemented; the passthroughs stay stubs", () => { - expect(getDerivedMetricMeta("VITALS_BASELINE")?.implemented).toBe(true); expect(getDerivedMetricMeta("SLEEP_SCORE")?.implemented).toBe(true); expect(getDerivedMetricMeta("READINESS")?.implemented).toBe(true); expect(getDerivedMetricMeta("COINCIDENT_DEVIATION")?.implemented).toBe(true); - // FITNESS_AGE + VASCULAR_AGE_DELTA land in a sibling wave. - expect(getDerivedMetricMeta("FITNESS_AGE")?.implemented).toBe(false); }); it("isDerivedMetricId rejects unknown ids", () => { @@ -76,7 +69,7 @@ describe("computeDerivedMetric dispatch", () => { expect(result.status).toBe("insufficient"); }); - it("returns not_implemented for a stubbed metric (never a 500)", async () => { + it("routes FITNESS_AGE to its engine (no data → insufficient, not not_implemented)", async () => { const result = await computeDerivedMetric({ metric: "FITNESS_AGE", userId: "u1", @@ -85,7 +78,7 @@ describe("computeDerivedMetric dispatch", () => { }); expect(result.status).toBe("insufficient"); if (result.status === "insufficient") { - expect(result.reason).toBe("not_implemented"); + expect(result.reason).not.toBe("not_implemented"); } }); diff --git a/src/lib/insights/derived/dispatch.ts b/src/lib/insights/derived/dispatch.ts index c393f7535..d09346df2 100644 --- a/src/lib/insights/derived/dispatch.ts +++ b/src/lib/insights/derived/dispatch.ts @@ -124,6 +124,9 @@ export async function computeDerivedMetric( }) as Promise>; case "BMI": return computeBmi(args.userId, args.profile, { + windowDays: args.windowDays, + now, + }) as Promise>; case "SLEEP_SCORE": return computeSleepScore(args.userId, args.profile, { windowDays: args.windowDays, diff --git a/src/lib/query-keys.ts b/src/lib/query-keys.ts index 2f3e9652a..f9101c8c7 100644 --- a/src/lib/query-keys.ts +++ b/src/lib/query-keys.ts @@ -143,6 +143,7 @@ export const queryKeys = { */ insightsDerived: (metric: string, type?: string | null) => ["insights", "derived", metric, type ?? null] as const, + /** * v1.10.0 — a derived wellness metric (`/api/insights/derived?metric=…`). * One generic route backs every derived composite (sleep score, * readiness, coincident-deviation, vitals baseline, …) so the key is