mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
Add a calm "your week / month in review" summary to the Insights overview, generated from the period-narrative context (deltas, personal bands, FDR-controlled associations, coincident days). - New insight_narratives table (migration 0113): one durable row per (user, period, locale); generated prose AES-256-GCM at rest following the coach-message precedent; labels-only provenance stays plaintext. - generatePeriodNarrative feeds the context into the provider chain with a tight, descriptive-never-causal prompt; an insufficient context yields no narrative, a missing provider no-ops, neither fabricates. - Nightly warm cron on week (Mon) / month (1st) boundaries, budget-gated per user; the read-only GET enqueues a single-user warm on a cold read. - Read route is stale-while-revalidate and never blocks on the provider; the card renders plain text (no markdown), a single provenance disclosure, and a fixed footprint (no CLS). i18n in all six locales.
44 lines
1.8 KiB
SQL
44 lines
1.8 KiB
SQL
-- v1.11.0 — period-narrative cache (Pillar P1).
|
|
--
|
|
-- One durable, typed row per (user, period, locale) holding the latest
|
|
-- generated period summary ("how your week/month went"). Delete +
|
|
-- regenerate clean: the unique index on (user_id, period, locale) enforces
|
|
-- a single row per slot, so a regeneration upserts in place and there is
|
|
-- never a stale duplicate to disambiguate.
|
|
--
|
|
-- The generated prose is held AES-256-GCM at rest in `encrypted_content`
|
|
-- (BYTEA), following the `coach_messages.encrypted_content` precedent. The
|
|
-- `provenance_json` envelope is labels-only (metric names, the read window,
|
|
-- the FDR footer) and carries no PII, so it stays plaintext for a stable,
|
|
-- queryable shape — matching `coach_messages.metric_source_json`.
|
|
--
|
|
-- Purely additive: one new table, no enum changes, no backfill. Deleting the
|
|
-- user cascades every narrative away (clean GDPR-erasure).
|
|
|
|
CREATE TABLE IF NOT EXISTS "insight_narratives" (
|
|
"id" TEXT NOT NULL,
|
|
"user_id" TEXT NOT NULL,
|
|
"period" TEXT NOT NULL,
|
|
"locale" TEXT NOT NULL,
|
|
"date_key" TEXT NOT NULL,
|
|
"encrypted_content" BYTEA NOT NULL,
|
|
"provenance_json" TEXT,
|
|
"provider_type" TEXT,
|
|
"prompt_version" TEXT,
|
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "insight_narratives_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "insight_narratives_user_id_period_locale_key"
|
|
ON "insight_narratives" ("user_id", "period", "locale");
|
|
|
|
CREATE INDEX IF NOT EXISTS "insight_narratives_user_id_updated_at_idx"
|
|
ON "insight_narratives" ("user_id", "updated_at" DESC);
|
|
|
|
ALTER TABLE "insight_narratives"
|
|
ADD CONSTRAINT "insight_narratives_user_id_fkey"
|
|
FOREIGN KEY ("user_id") REFERENCES "users" ("id")
|
|
ON DELETE CASCADE ON UPDATE CASCADE;
|