CREATE TYPE "public"."release_gate_category" AS ENUM('product', 'quality', 'retention', 'economy');--> statement-breakpoint CREATE TYPE "public"."release_gate_comparison" AS ENUM('gte', 'lte', 'eq');--> statement-breakpoint CREATE TYPE "public"."release_gate_status" AS ENUM('pending', 'passed', 'failed', 'blocked', 'not_measurable');--> statement-breakpoint CREATE TABLE "release_gates" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "gate_key" varchar(64) NOT NULL, "category" "release_gate_category" NOT NULL, "name_sv" text NOT NULL, "name_en" text NOT NULL, "description_sv" text, "description_en" text, "target_value" double precision NOT NULL, "comparison" "release_gate_comparison" NOT NULL, "evaluation_window_days" integer DEFAULT 7 NOT NULL, "last_value" double precision, "last_evaluated_at" timestamp with time zone, "status" "release_gate_status" DEFAULT 'pending' NOT NULL, "blocking" boolean DEFAULT false NOT NULL, "notes" text, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "release_gates_gate_key_unique" UNIQUE("gate_key") ); --> statement-breakpoint CREATE INDEX "release_gates_category_idx" ON "release_gates" USING btree ("category");--> statement-breakpoint CREATE INDEX "release_gates_status_idx" ON "release_gates" USING btree ("status"); --> Seed built-in release gates from spec §17 (idempotent) INSERT INTO "release_gates" ("gate_key", "category", "name_sv", "name_en", "description_sv", "description_en", "target_value", "comparison", "evaluation_window_days", "blocking", "status") VALUES ('first_scan_rate', 'product', 'Första skanning genomförd', 'First scan completed', 'Andel användare som genomför minst en skanning efter kontoregistrering.', 'Share of users who complete at least one scan after account creation.', 0.7, 'gte', 7, true, 'pending'), ('recipe_view_rate', 'product', 'Rekommendation visad', 'Recommendation viewed', 'Andel användare som ser minst ett relevant recept.', 'Share of users who view at least one relevant recipe.', 0.5, 'gte', 7, true, 'pending'), ('recipe_save_or_cook_day0', 'product', 'Recept sparat eller tillagat dag 0', 'Recipe saved or cooked on day 0', 'Andel användare som sparar eller startar ett recept samma dag som registrering.', 'Share of users who save or start cooking a recipe on registration day.', 0.35, 'gte', 1, true, 'pending'), ('second_inventory_event_week1', 'product', 'Andra lagerhändelsen inom 7 dagar', 'Second inventory event within 7 days', 'Andel användare som lägger till, konsumerar eller kasserar en vara inom en vecka.', 'Share of users who add, consume, or discard an item within one week.', 0.25, 'gte', 7, true, 'pending'), ('household_collaboration_rate', 'product', 'Hushållssamarbete', 'Household collaboration', 'Andel hushåll där en andra medlem bjuds in eller är aktiv.', 'Share of households where a second member is invited or active.', 0.2, 'gte', 14, false, 'pending'), ('weekly_trusted_meal_week2', 'product', 'Weekly Trusted Meal vecka 2', 'Weekly trusted meal week 2', 'Andel aktiverade hushåll som lagar minst en måltid under vecka två.', 'Share of activated households that cook at least one meal in week two.', 0.3, 'gte', 14, false, 'pending'), ('critical_allergy_errors', 'quality', 'Kritiska allergifel', 'Critical allergy errors', 'Accepterade kritiska allergifel ska vara noll.', 'Accepted critical allergy errors must be zero.', 0, 'lte', 7, true, 'pending'), ('correction_rate', 'quality', 'Korrigeringsgrad skanning', 'Scan correction rate', 'Andel skannade varor som korrigeras av användaren.', 'Share of scanned items that the user corrects.', 0.15, 'lte', 7, false, 'pending'), ('sync_conflict_resolution_rate', 'quality', 'Sync-konflikter lösta', 'Sync conflicts resolved', 'Andel sync-konflikter som löses utan dataförlust.', 'Share of sync conflicts resolved without data loss.', 0.95, 'gte', 7, false, 'pending'), ('retention_d1', 'retention', 'D1-retention', 'D1 retention', 'Andel användare aktiva dagen efter registrering.', 'Share of users active one day after registration.', 0.4, 'gte', 7, true, 'pending'), ('retention_d7', 'retention', 'D7-retention', 'D7 retention', 'Andel användare aktiva sju dagar efter registrering.', 'Share of users active seven days after registration.', 0.2, 'gte', 14, true, 'pending'), ('retention_d30', 'retention', 'D30-retention', 'D30 retention', 'Andel användare aktiva 30 dagar efter registrering.', 'Share of users active 30 days after registration.', 0.1, 'gte', 60, true, 'pending'), ('retention_week4_household', 'retention', 'Hushållsretention vecka 4', 'Week 4 household retention', 'Andel hushåll aktiva under vecka fyra.', 'Share of households active in week four.', 0.15, 'gte', 35, false, 'pending'), ('paid_month2_retention', 'retention', 'Betald månad 2-retention', 'Paid month 2 retention', 'Andel betalande hushåll kvar vid månad två.', 'Share of paying households retained at month two.', 0.5, 'gte', 90, true, 'pending'), ('trial_to_paid_rate', 'economy', 'Trial till betalande', 'Trial to paid', 'Andel trial-användare som blir betalande.', 'Share of trial users who convert to paid.', 0.25, 'gte', 30, true, 'pending'), ('refund_rate', 'economy', 'Återbetalningsgrad', 'Refund rate', 'Andel betalningar som återbetalas.', 'Share of payments refunded.', 0.05, 'lte', 30, false, 'pending'), ('monthly_churn', 'economy', 'Månatlig churn', 'Monthly churn', 'Andel betalande hushåll som avslutar per månad.', 'Share of paying households that churn per month.', 0.1, 'lte', 60, false, 'pending') ON CONFLICT ("gate_key") DO NOTHING;