v1.4.35 — persistent measurement rollups + partial read-swap

Foundation release for the persistent measurement_rollups cache tier.
Adds the additive table + enum, populates it from every measurement
write hook, lands the Apple-Health-import end-of-job backfill, and
ships the parity-checked read-swap on two reader surfaces.

The comprehensive insights aggregator and the slim analytics
summaries slice now source count / min / max / mean per type from the
DAY rollup buckets via aggregateBuckets, plus the comprehensive
dailyByType correlation feed straight from the bucket means. Slope /
R² / standard deviation / anomaly counts stay on live SQL — they do
not compose across DAY buckets. A defensive parity check against the
live aggregate's COUNT(*) falls back to live SQL when divergence is
detected.

Includes the new scripts/backfill-rollups.ts CLI for the one-shot
fold of historical measurements into all four granularities.

Tests: 4280 unit + 228 integration. tsc + lint clean. Migration
0067_v1434_measurement_rollups is idempotent under re-run.
This commit is contained in:
Marc-André Bombeck committed 2026-05-17 09:55:33 +02:00
1 parent 9715831f82
commit 8dc9edb65e
26 files changed
+2650 -65

No files matched your search

@@ -25,6 +25,14 @@ vi.mock("@/lib/db", () => ({
medication: { findMany: vi.fn() },
// v1.4.33 C1 — slim summaries slice runs through `$queryRaw`.
$queryRaw: vi.fn(),
// v1.4.35 — slim slice also reads DAY buckets; the freshness
// watermark inside `ensureUserRollupsFresh` pokes `findFirst`.
// Default both to empty so the parity check falls back to live
// SQL and the slim slice assertions stay byte-shape stable.
measurementRollup: {
findMany: vi.fn().mockResolvedValue([]),
findFirst: vi.fn().mockResolvedValue(null),
},
},
}));
@@ -100,6 +108,17 @@ beforeEach(() => {
[] as never,
);
vi.mocked(prisma.medication.findMany).mockResolvedValue([] as never);
// v1.4.35 — rollup table defaults. `resetAllMocks` clears the
// module-level implementations, so we re-seed both per test. Empty
// findMany + null findFirst means the slim slice's parity check
// diverges and falls back to live SQL (preserves the pre-v1.4.35
// assertions in this file).
vi.mocked(prisma.measurementRollup.findMany).mockResolvedValue(
[] as never,
);
vi.mocked(prisma.measurementRollup.findFirst).mockResolvedValue(
null as never,
);
});
afterEach(() => {