Files
HealthLog/prisma/migrations/0034_recommendation_feedback/migration.sql
T
Marc-André BombeckandClaude Opus 4.7 badc893853 feat(db): RecommendationFeedback model with GDPR cascade + provider attribution
v1.4.16 phase B5e: ship the table that backs per-recommendation
thumbs-up / thumbs-down feedback. Each row captures (userId, recId,
recText) with snapshotted severity + metric-source attributes plus the
provider + prompt-version that produced the rec — server-fills the
attribution from the user's most recent cached payload so the client
can't tamper.

Dedup key (userId, recommendationId, recommendationText) lets a
regeneration that rewrites the same id with new text re-rate cleanly
while blocking accidental double-submission of the same (id + text).

Indexes: (userId, createdAt) for the per-user timeline and
(severity, helpful, createdAt) for the daily aggregator's slice. Both
are needed by the v1.4.16 admin AI quality preview.

GDPR: ON DELETE CASCADE through users; cascade-delete integration
test now seeds and asserts a feedback row.

Same migration adds AppSettings.adminAiInsightsFeedbackSummary — the
JSON blob the daily aggregator (next commit) writes a per-(severity x
provider x prompt) helpful-rate to. Null until the aggregator runs
or until there is feedback in the 30-day window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 01:25:57 +02:00

57 lines
2.4 KiB
SQL

-- v1.4.16 phase B5e: per-recommendation thumbs-up/thumbs-down feedback.
--
-- Each row captures one rating event from one user against one
-- recommendation. The dedup key (userId, recommendationId,
-- recommendationText) lets a regeneration that rewrites the same id
-- with new text re-rate cleanly while preventing accidental double-
-- submission of the same (id + text) pair.
--
-- Provider attribution (providerType + promptVersion) is filled
-- server-side from the user's most recent cached insight payload, so
-- the client can't tamper with which provider / prompt version a
-- thumbs-down is attributed to. The aggregator slices on these to
-- power the admin AI quality dashboard.
--
-- Indexes:
-- (userId, createdAt) — per-user timeline lookups
-- (severity, helpful, createdAt) — aggregator's cross-user slice
-- The unique key doubles as a (userId, recommendationId, text) lookup.
--
-- GDPR: ON DELETE CASCADE through users → personal data follows the
-- account; covered in tests/integration/cascade-delete.test.ts.
CREATE TABLE "recommendation_feedback" (
"id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"recommendation_id" TEXT NOT NULL,
"recommendation_text" TEXT NOT NULL,
"recommendation_severity" TEXT NOT NULL,
"metric_source_type" TEXT NOT NULL,
"metric_source_time_range" TEXT NOT NULL,
"helpful" BOOLEAN NOT NULL,
"provider_type" TEXT NOT NULL,
"prompt_version" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "recommendation_feedback_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "rec_feedback_user_rec_text_key"
ON "recommendation_feedback" ("user_id", "recommendation_id", "recommendation_text");
CREATE INDEX "recommendation_feedback_user_id_created_at_idx"
ON "recommendation_feedback" ("user_id", "created_at");
CREATE INDEX "recommendation_feedback_recommendation_severity_helpful_cre_idx"
ON "recommendation_feedback" ("recommendation_severity", "helpful", "created_at");
ALTER TABLE "recommendation_feedback"
ADD CONSTRAINT "recommendation_feedback_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
-- v1.4.16 phase B5e: daily-aggregated feedback summary written by the
-- pg-boss `feedback-aggregator` job. Read by /admin/ai-quality.
ALTER TABLE "app_settings"
ADD COLUMN "admin_ai_insights_feedback_summary" JSONB;