docs(planning): W-MED v1.4.39 phase report

This commit is contained in:
Marc-André Bombeck committed 2026-05-18 12:32:01 +02:00
1 parent 8e29c3ab54
commit 4deaa18fd5
1 file changed
+105
+105
View File
@@ -0,0 +1,105 @@
# Phase W-MED — v1.4.39 medication-compliance rollup tier
## Directive
Eliminate the 3.2 s cold mount on
`/api/medications/intake?scope=compliance` (`.planning/round-v1438-perf-analysis.md`
§2.5 + §5 P4) by materialising a persistent
`medication_compliance_rollups` ledger keyed
`(userId, medicationId, day)` where `day` is `YYYY-MM-DD` anchored to
the user's IANA timezone.
## Scope
Owned file set:
- `src/lib/medications/compliance-rollups.ts` (NEW)
- `src/lib/medications/__tests__/compliance-rollups.test.ts` (NEW)
- `src/app/api/medications/intake/route.ts`
- `src/app/api/medications/intake/__tests__/route.test.ts`
- `src/app/api/medications/intake/bulk/route.ts`
- `src/app/api/medications/[id]/intake/route.ts`
- `src/app/api/medications/[id]/intake/[eventId]/route.ts`
- `src/app/api/medications/[id]/intake/import/route.ts`
- `src/app/api/medications/[id]/intake/purge/route.ts`
- `src/app/api/ingest/medication/route.ts`
- `src/app/api/telegram/webhook/route.ts`
- `src/app/api/admin/backups/[id]/restore/route.ts`
- `src/lib/jobs/reminder-worker.ts`
`prisma/schema.prisma` + `prisma/migrations/0071_v1439_medication_compliance_rollups`
were read-only inputs from the prerequisite commit `0d33f91d`.
## Commits
| SHA | Commit |
|---|---|
| `83d96a98` | `feat(medication-compliance-rollups): writer and reader helpers` |
| `227eb69b` | `perf(medications-intake): consume compliance rollup tier` |
| `7657b848` | `test(medication-compliance-rollups): hook, reader, and tz parity` |
| `8e29c3ab` | `feat(medication-compliance-rollups): boot-time backfill queue` |
## Test delta
| Surface | Before | After |
|---|---|---|
| `src/lib/medications/__tests__/compliance-rollups.test.ts` | — | **+16 tests** (NEW) |
| `src/app/api/medications/intake/__tests__/route.test.ts` | 8 | **10** (+2 cases pinning the rollup read swap + live fallback) |
| `pnpm test src/lib src/app/api` aggregate | 3 530 | **3 547** (+17) |
## Self-review
- Re-read `compliance-rollups.ts` end-to-end after wiring: the writer
handles 23-/25-hour DST days via `startOfDayUtcInTz(nextDayKey, …)`
rather than a naive `+86_400_000`. Verified by inspection that
Berlin fall-back at 2025-10-26 produces a 25-hour window and Berlin
spring-forward at 2025-03-30 a 23-hour window.
- Empty-window case deletes the rollup row rather than leaving a
stale `(scheduled, taken, skipped)` tuple from a now-deleted event.
- Bulk + import paths collapse per-row hooks to one recompute per
distinct `(medicationId, dayKey)` pair so a 500-row batch costs at
most ~20-50 rollup folds rather than 500.
- Coverage-probe fallback fires the boot-backfill enqueue in the
background; current request still returns a byte-identical live
response so no front-end can observe a hiccup during the
cold-mount converge.
- Writer is best-effort wrapped in
`recomputeMedicationComplianceForEvent` so a populator hiccup never
rolls back the parent intake-event write.
- `git status` clean across all four commits; no W-MOOD / W-SUM /
W-WMY / W-SINCE files staged.
## Performance expectation
Per the audit's §2.5 + §5 P4 estimate:
- Cold mount on `/api/medications/intake?scope=compliance`:
**3.2 s → ~200 ms** on Marc's tenant once the boot backfill has
converged. The dominant cost was a pool-stall on the unbounded
event findMany; reading 7-30 indexed rows from the rollup tier
collapses to a single millisecond-class query.
- Warm-cache hit (15-minute LRU) unchanged at < 50 ms.
- First request post-deploy is the live-fallback path (~3.2 s) until
the worker boot's discovery enqueue folds the trailing 90 days;
subsequent requests land on the rollup tier.
## Deferred
- **`health-score-fast-path.ts` consumer integration** — out of
scope for W-MED per task directive (collision with W-WMY). The
per-active-medication × intake-event fan-out inside
`computeUserHealthScoreFastPath` (`health-score-fast-path.ts:267-303`)
still pays the live cost. Follow-up phase (v1.4.40 or W-MED-2)
should swap that branch onto `readMedicationCompliance` for the
same tier reuse. Estimated saving: 1-2 s on the analytics full-slice
cold path.
- **Per-user-tz bucketing for non-Berlin tenants** — `day` is
string-anchored to the user's tz, which already removes the
`isNearUtc(userTz)` blocker that gates the measurement-rollup
fast-paths. No additional v1.5 work needed on the medication
tier specifically.
- **WEEK / MONTH / YEAR compliance rollups** — Marc's audit
(§2 + §5 P6) flags that no consumer reads multi-granularity
buckets yet; the medication tier follows the same shape, daily
granularity only. Future quarter-window analytics could fold the
WEEK/MONTH/YEAR tier off the daily ledger.