mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
Largest feature delta in the v1.4.x line. Insights expands into seven dedicated metric sub-pages; GLP-1 medication tracking lands end-to-end (drug-level chart with Research Mode acknowledgment, EMA titration ladder, cadence + compliance, side-effect taxonomy, pen/vial inventory); cross-source priority becomes a two-axis resolver; per-user timezone threads through ten analytics surfaces; Withings activity + sleep v2 sync; onboarding rebuilt as a nested-route wizard; Personal Records ship end-to-end; Health Score provenance accordion; Coach runs native first-party prompts across six locales with a 1800-assertion refusal-probe matrix; OpenAPI drift gate hard-fails; multi-arch GHCR image. Migrations 0043 through 0060 are additive and forward-only. PROMPT_VERSION 4.24.0 → 4.25.0 with GROUND RULE 9 (refuses dose recommendations) and GROUND RULE 15 (refuses drug-level estimates with MDR + MDCG 2021-24 cites). See CHANGELOG.md for the full per-section delta.
51 lines
2.7 KiB
SQL
51 lines
2.7 KiB
SQL
-- v1.4.25 W8d — PersonalRecord schema (no detection worker yet).
|
|
--
|
|
-- Schema lands now so the v1.5 iOS-Swift app can build its query path
|
|
-- against a real GET /api/personal-records route from day one. The
|
|
-- worker that populates the rows (sweep on insert / nightly job) is
|
|
-- explicitly out of v1.4.25 scope — see the deferred section in the
|
|
-- W8d phase report. v1.4.25 ships the bare list endpoint that returns
|
|
-- whatever rows the DB carries (initially empty for every user).
|
|
--
|
|
-- Direction enum (MAX | MIN) is stored on the row rather than derived
|
|
-- at read time so the future worker doesn't re-resolve a per-metric
|
|
-- lookup on every query. Some metric types deliberately carry NULL
|
|
-- direction in the helper (BP, glucose, weight, ambiguous-direction
|
|
-- metrics) — those never produce a PersonalRecord row at all, so the
|
|
-- column stays NOT NULL for the metrics where a record is actually
|
|
-- defined.
|
|
--
|
|
-- Composite uniqueness on (userId, metricType, metricSlot, achievedAt)
|
|
-- ensures the detection worker is idempotent — re-running it never
|
|
-- inserts a duplicate. A multi-source tie (e.g. iPhone + Apple Watch
|
|
-- both report 12_348 steps for the same day) is broken by including
|
|
-- `source` so each provenance retains its row; the display layer
|
|
-- runs the existing pickCanonicalSourceRows() helper to surface one.
|
|
|
|
CREATE TYPE "personal_record_direction" AS ENUM ('MAX', 'MIN');
|
|
|
|
CREATE TABLE "personal_records" (
|
|
"id" TEXT PRIMARY KEY,
|
|
"user_id" TEXT NOT NULL REFERENCES "users"("id") ON DELETE CASCADE,
|
|
"metric_type" "measurement_type" NOT NULL,
|
|
-- Sport-specific slot for workout-driven PRs (e.g. "running_5km_time").
|
|
-- NULL for measurement-driven PRs that don't need a sport dimension.
|
|
"metric_slot" TEXT,
|
|
"direction" "personal_record_direction" NOT NULL,
|
|
"value" DOUBLE PRECISION NOT NULL,
|
|
"unit" TEXT NOT NULL,
|
|
"achieved_at" TIMESTAMP(3) NOT NULL,
|
|
-- FK to the Measurement row that achieved the record. SET NULL on
|
|
-- delete so a PR survives the deletion of the originating
|
|
-- measurement (the PR is the historical fact, not a derived view).
|
|
"source_measurement_id" TEXT REFERENCES "measurements"("id") ON DELETE SET NULL,
|
|
"source" "measurement_source" NOT NULL DEFAULT 'MANUAL',
|
|
"external_id" TEXT,
|
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE UNIQUE INDEX "personal_records_dedup_key"
|
|
ON "personal_records" ("user_id", "metric_type", "metric_slot", "achieved_at");
|
|
CREATE INDEX "personal_records_user_metric_idx"
|
|
ON "personal_records" ("user_id", "metric_type", "value");
|