mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
chore(release): v1.4.40 — comprehensive Critical+High architecture closure plus iOS PB30 enablement
Eleven touch-disjoint waves landed on develop plus a six-axis QA reconcile round before tagging. See CHANGELOG.md for the user-facing prose; this commit message documents the wave + report inventory for release-management traceability. Waves shipped (each with a phase report under .planning/): - phase-W-POOL-v1440-report.md — p-limit(4) wrapper around the analytics 15-way fan-out + pg.Pool max raised 10 -> 20. Closes the v1.4.39 empirical-trace finding #1 (Prisma pool starvation). - phase-W-DELETED-v1440-report.md — 11 reader-tier helpers + 3 integration contracts close audit Critical Finding #3 (soft-delete invisibility) across dashboard, analytics, summaries, rollup tiers, and the Coach snapshot path. - phase-W-INSIGHTS-v1440-report.md — mood-rollup swap on the three /api/insights/{features,targets,cards} routes plus the compliance-rollup hook gap closure for bulk-projection mints; the post-QA reconcile patch closes the six remaining measurement.findMany sites in the cluster that still leaked tombstoned rows. - phase-W-INFRA-v1440-report.md — rollup helper umbrella consolidation under src/lib/rollups/* with zero orphan re-exports; lint, typecheck, knip, and 93/93 rollup tests green post-restructure. - phase-W-RSC-v1440-report.md — per-tile Suspense boundaries plus the queryKey factory-bypass walker test (extended to comparison/ in the reconcile pass). - phase-W-PRIVACY-v1440-report.md — bilingual /privacy page with 17 paired-section tests pinning the 9 SB-3 disclosure requirements. - phase-W-AASA-v1440-report.md — /.well-known/apple-app-site-association endpoint pinned to S8WDX4W5KX.dev.healthlog.app with app-ID parity assertions guarding against split rotation. - phase-W-APNS-NOTIFY-v1440-report.md — MEDICATION_REMINDER time-sensitive APNs payload (priority 10 + interruption-level) scoped via a parameterised test that pins all 6 other event-types do not bypass Focus. AP-2 .p8 install gates real delivery. - phase-W-CONSENT-v1440-report.md — AI consent receipts CRUD with append-only invariant pinned in test; revoke-then-regrant leaves both rows; idempotent DELETE returns 200 + receipt: null. - phase-W-WMY-WIRE-v1440-report.md — three previously-dead WMY readers (week/month/year) wired through summaries-slice and health-score-fast-path with a linear-composability parity test pinning the cross-consumer routing contract. - phase-W-GHOSTS-v1440-report.md — ~1 177 lines of dead code removed across 9 atomic commits; startOfUtcDay + wallClockInTz consolidation eliminates a sibling-drift risk; knip whitelist trimmed to three files pending follow-up. QA reconcile patches applied per the v1.4.40 QA round (six reports under .planning/round-v1440-QA-*-findings.md): - senior C1 — dashboard-suspense-boundaries regex aligned with the post-W-INFRA Thread 2 userTimezone-local lift. - senior H1 — soft-delete filter added to the six remaining insights measurement.findMany sites. - specialist H1 — Promise.allSettled around bulk-projection compliance recompute on both /api/medications/intake and /api/dashboard/summary. - ux H1 — compare-toggle migrated to queryKeys.dashboardWidgets factory; walker test extended to src/components/comparison/. - security M1 — consent artefact 64 KB cap enforced via Buffer.byteLength (UTF-8 bytes, not UTF-16 code units). Quality gates green at tag: pnpm typecheck, pnpm lint, pnpm knip --include files,dependencies,binaries,unlisted, pnpm test --run (4726 passing / 1 skipped). Tests 4525 -> 4726 unit + 1 skipped over the v1.4.39 -> v1.4.40 span. User-facing prose: see CHANGELOG.md.
This commit is contained in:
1 parent
f9996552ed
commit
1114cf7e63
146 files changed
+8207
-2279
No files matched your search
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import {
|
||||
ResponsiveContainer,
|
||||
ComposedChart,
|
||||
@@ -541,8 +542,12 @@ export function HealthChart({
|
||||
}, [rangePoints, effectiveCompareBaseline]);
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: [
|
||||
"chart-data",
|
||||
// v1.4.40 W-RSC — route the chart-data key through `queryKeys.chartData`
|
||||
// so the `["chart-data"]` prefix lands in `measurementDependentKeys`
|
||||
// and a fresh measurement evicts every per-chart daily cache in one
|
||||
// pass (audit C2). The factory-packed tuple is byte-identical with
|
||||
// the pre-v1.4.40 inline literal so the cache layout stays stable.
|
||||
queryKey: queryKeys.chartData(
|
||||
types.join(","),
|
||||
valueMode,
|
||||
bmiDivisor ?? "no-bmi",
|
||||
@@ -555,7 +560,7 @@ export function HealthChart({
|
||||
// re-running the unbounded walk.
|
||||
fetchWindow.from,
|
||||
fetchWindow.to,
|
||||
],
|
||||
),
|
||||
// v1.4.28 FB-D2 — cache the bounded window for a minute so tab
|
||||
// navigation between insights sub-pages does not re-fire every
|
||||
// chart's fetch. `gcTime` keeps the inactive cache for five
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
*/
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useMemo, useState } from "react";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import {
|
||||
ResponsiveContainer,
|
||||
ComposedChart,
|
||||
@@ -226,8 +227,12 @@ export function MedicationComplianceChart({
|
||||
// room of the weight / BMI charts.
|
||||
const viewportWidth = useViewportWidth();
|
||||
|
||||
// v1.4.40 W-RSC — factory-routed key so the prefix
|
||||
// `["dashboard-medication-compliance"]` lands in
|
||||
// `medicationDependentKeys` and an intake POST refreshes the chart
|
||||
// immediately rather than waiting for `staleTime` (audit L4).
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ["medication-compliance-chart", days],
|
||||
queryKey: queryKeys.dashboardMedicationCompliance(days),
|
||||
queryFn: async (): Promise<DailyCompliancePoint[]> => {
|
||||
const res = await fetch(
|
||||
`/api/medications/intake?scope=compliance&days=${days}`,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import {
|
||||
ResponsiveContainer,
|
||||
ComposedChart,
|
||||
@@ -312,8 +313,22 @@ export function MoodChart({
|
||||
|
||||
const displayTitle = title ?? t("charts.mood");
|
||||
|
||||
// v1.4.40 W-RSC — align the chart's queryKey with the dashboard's
|
||||
// `useQuery({ queryKey: queryKeys.moodAnalytics() })` call against the
|
||||
// same `/api/mood/analytics` endpoint. Pre-fix the chart fetched its
|
||||
// own copy under `["mood-chart-data"]` (audit C1) — TanStack treated
|
||||
// the two keys as separate cache slots and the cold-mount dashboard
|
||||
// fired the request twice (once at `app/page.tsx:291`, once here on
|
||||
// the chart's first render). The dedup eliminates a ~22 KB round-trip
|
||||
// and frees one pg pool slot per cold mount.
|
||||
//
|
||||
// `staleTime: 60_000` matches the dashboard's `DASHBOARD_QUERY_OPTS`
|
||||
// so a route swing back to the dashboard within a minute keeps the
|
||||
// cache fresh; the chart's own subscription becomes the cache writer
|
||||
// for any insights / mood sub-page that mounts the chart without the
|
||||
// dashboard parent having fetched first.
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ["mood-chart-data"],
|
||||
queryKey: queryKeys.moodAnalytics(),
|
||||
queryFn: async (): Promise<MoodAnalyticsData> => {
|
||||
const res = await fetch("/api/mood/analytics");
|
||||
if (!res.ok) throw new Error("Failed to fetch mood analytics");
|
||||
@@ -321,6 +336,8 @@ export function MoodChart({
|
||||
return json.data as MoodAnalyticsData;
|
||||
},
|
||||
enabled: isAuthenticated,
|
||||
staleTime: 60_000,
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
const chartData = useMemo((): ChartDataPoint[] | undefined => {
|
||||
|
||||
Reference in new issue
Block a user