--- phase: 06-preset-data-first-run-detection-and-db-safety plan: "01" subsystem: database tags: [migrations, schema, typescript, constraints, first-run] dependency_graph: requires: [] provides: [uniqueness-constraints, setup_completed-column, profile-type-update] affects: [profiles, budgets, categories] tech_stack: added: [] patterns: [safe-deduplication-before-unique-constraint, alter-table-with-backfill] key_files: created: - supabase/migrations/006_uniqueness_constraints.sql - supabase/migrations/007_setup_completed.sql modified: - src/lib/types.ts decisions: - "Used wider UNION backfill in 007 (categories OR template_items) per Pitfall 3 guidance — protects v1.0 users with templates but no categories" - "Migration 006 wraps deduplication DELETE and ADD CONSTRAINT in single BEGIN/COMMIT for atomicity" metrics: duration: "4 minutes" completed: "2026-04-20" tasks_completed: 3 tasks_total: 3 --- # Phase 06 Plan 01: DB Safety Constraints and First-Run Flag Summary **One-liner:** Two atomic SQL migrations adding UNIQUE constraints on budgets/categories with safe deduplication, plus a `setup_completed` boolean on profiles with UNION backfill, synced to the TypeScript Profile interface. ## Tasks Completed | Task | Name | Commit | Files | |------|------|--------|-------| | 1 | Migration 006: uniqueness constraints | 23fd3fa | supabase/migrations/006_uniqueness_constraints.sql | | 2 | Migration 007: setup_completed column + backfill | 0f441b6 | supabase/migrations/007_setup_completed.sql | | 3 | Update Profile TypeScript interface | 39840ca | src/lib/types.ts | ## What Was Built **Migration 006** (`006_uniqueness_constraints.sql`): A single `BEGIN/COMMIT` transaction that: 1. DELETEs duplicate budgets keeping the oldest per `(user_id, start_date)` using `DISTINCT ON` 2. ADDs `CONSTRAINT budgets_user_month_unique UNIQUE (user_id, start_date)` 3. DELETEs duplicate categories keeping the oldest per `(user_id, name)` using `DISTINCT ON` 4. ADDs `CONSTRAINT categories_user_name_unique UNIQUE (user_id, name)` **Migration 007** (`007_setup_completed.sql`): Two statements: 1. `ALTER TABLE profiles ADD COLUMN setup_completed boolean NOT NULL DEFAULT false` 2. `UPDATE profiles SET setup_completed = true WHERE id IN (SELECT DISTINCT user_id FROM categories UNION SELECT t.user_id FROM templates t INNER JOIN template_items ti ON ti.template_id = t.id)` **TypeScript** (`src/lib/types.ts`): Added `setup_completed: boolean` to the `Profile` interface between `currency` and `created_at`. `tsc --noEmit` passes cleanly. ## Deviations from Plan None — plan executed exactly as written. The UNION backfill in migration 007 was specified in the plan (per Pitfall 3 guidance from RESEARCH.md). ## Known Stubs None — this plan produces SQL DDL and a TypeScript type update; no UI or data-flow stubs. ## Threat Flags No new threat surface beyond what was documented in the plan's threat model. The UNIQUE constraints directly address T-06-01 and T-06-02. The `setup_completed` column is a UX routing flag with existing RLS (T-06-03 accepted). ## Self-Check - [x] `supabase/migrations/006_uniqueness_constraints.sql` exists — FOUND - [x] `supabase/migrations/007_setup_completed.sql` exists — FOUND - [x] `src/lib/types.ts` contains `setup_completed: boolean` — FOUND (line 16) - [x] `tsc --noEmit` passed with no errors - [x] Commits 23fd3fa, 0f441b6, 39840ca exist in git log ## Self-Check: PASSED