diff --git a/src/lib/measurements/__tests__/cumulative-day-sum.test.ts b/src/lib/measurements/__tests__/cumulative-day-sum.test.ts index 9ad9f52db..62f4c8c6b 100644 --- a/src/lib/measurements/__tests__/cumulative-day-sum.test.ts +++ b/src/lib/measurements/__tests__/cumulative-day-sum.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; +import { CUMULATIVE_HK_TYPES } from "../apple-health-mapping"; import { CUMULATIVE_DAY_SUM_TYPES, isCumulativeDaySumType, @@ -105,3 +106,16 @@ describe("isCumulativeDaySumType", () => { expect(isCumulativeDaySumType("MOOD")).toBe(false); }); }); + +describe("CUMULATIVE_DAY_SUM_TYPES / CUMULATIVE_HK_TYPES parity", () => { + // v1.4.37 W10 — single source of truth. The day-sum array is derived + // from `CUMULATIVE_HK_TYPES` so adding a sixth cumulative HK type to + // the mapping module automatically flows through every downstream + // consumer (analytics route, exports, chart). The set equivalence + // guard pins the contract — divergence is impossible at runtime, but + // a future refactor that flips the derivation back to a literal + // would trip this assertion before shipping. + it("matches CUMULATIVE_HK_TYPES membership exactly", () => { + expect(new Set(CUMULATIVE_DAY_SUM_TYPES)).toEqual(CUMULATIVE_HK_TYPES); + }); +}); diff --git a/src/lib/measurements/cumulative-day-sum.ts b/src/lib/measurements/cumulative-day-sum.ts index 3648d3a9f..f09ac657e 100644 --- a/src/lib/measurements/cumulative-day-sum.ts +++ b/src/lib/measurements/cumulative-day-sum.ts @@ -62,25 +62,32 @@ export function pickCumulativeDaySum( return out; } +import type { MeasurementType } from "@/generated/prisma/client"; +import { CUMULATIVE_HK_TYPES } from "./apple-health-mapping"; + /** - * v1.4.36 W4c — Canonical set of cumulative metric types whose - * dashboard tile should read the *day's sum* and not the latest - * slice. Kept here so the analytics route and any other consumer - * (Coach snapshot, exports) share the same source of truth. + * v1.4.36 W4c / v1.4.37 W10 — Canonical list of cumulative metric + * types whose dashboard tile should read the *day's sum* and not the + * latest slice. + * + * The set lives in `apple-health-mapping.ts` as `CUMULATIVE_HK_TYPES` + * (used by the measurements route + nightly drain + chart). This + * array is the same membership in stable alphabetical order so + * downstream consumers (analytics route, exports) can iterate + * deterministically. The W10 reconcile pass collapses the previously + * duplicated literal to a single derivation so adding a sixth type + * in `apple-health-mapping.ts` automatically flows through every + * cumulative-day-sum consumer. * * Note: SLEEP_DURATION is also cumulative but the route handles * it inline because the picker is the canonical sleep source ladder * ("sleep" metricKey vs "default"). Once Apple-Health passthrough * lands and the sleep ladder needs the same shared treatment it can - * fold into this list. + * fold into `CUMULATIVE_HK_TYPES`. */ -export const CUMULATIVE_DAY_SUM_TYPES = [ - "ACTIVITY_STEPS", - "ACTIVE_ENERGY_BURNED", - "WALKING_RUNNING_DISTANCE", - "FLIGHTS_CLIMBED", - "TIME_IN_DAYLIGHT", -] as const; +export const CUMULATIVE_DAY_SUM_TYPES = Array.from( + CUMULATIVE_HK_TYPES, +).sort() as readonly MeasurementType[]; export type CumulativeDaySumType = (typeof CUMULATIVE_DAY_SUM_TYPES)[number];