--- phase: 06-preset-data-first-run-detection-and-db-safety verified: 2026-04-20T19:15:00Z status: human_needed score: 5/5 overrides_applied: 0 human_verification: - test: "Attempt duplicate budget INSERT for same (user_id, start_date) via Supabase SQL editor" expected: "INSERT fails with unique constraint violation error 23505" why_human: "Requires a running Supabase instance with live DB to test constraint enforcement" - test: "Attempt duplicate category INSERT for same (user_id, name) via Supabase SQL editor" expected: "INSERT fails with unique constraint violation error 23505" why_human: "Requires a running Supabase instance with live DB to test constraint enforcement" - test: "Check profiles table for existing v1.0 user rows — confirm setup_completed = true" expected: "All users who had categories or template items before migration show setup_completed = true" why_human: "Requires inspecting live database state after backfill" --- # Phase 6: Preset Data, First-Run Detection, and DB Safety Verification Report **Phase Goal:** The data layer is safe and ready -- duplicate budget/category writes are impossible at the DB level, and the app correctly identifies first-run users **Verified:** 2026-04-20T19:15:00Z **Status:** human_needed **Re-verification:** No -- initial verification ## Goal Achievement ### Observable Truths | # | Truth | Status | Evidence | |---|-------|--------|----------| | 1 | Attempting to create two budgets for the same user and month is rejected at the DB level | VERIFIED | `006_uniqueness_constraints.sql` contains `ADD CONSTRAINT budgets_user_month_unique UNIQUE (user_id, start_date)` wrapped in BEGIN/COMMIT. User confirmed DB push completed successfully. | | 2 | Attempting to create two categories with the same name for the same user is rejected at the DB level | VERIFIED | `006_uniqueness_constraints.sql` contains `ADD CONSTRAINT categories_user_name_unique UNIQUE (user_id, name)` with safe deduplication DELETE. User confirmed DB push completed. | | 3 | All existing v1.0 users have `profiles.setup_completed = true` after backfill | VERIFIED | `007_setup_completed.sql` adds column `boolean NOT NULL DEFAULT false` then runs UPDATE with UNION covering both `categories` and `template_items` tables. User confirmed DB push applied. | | 4 | `useFirstRunState` hook returns `true` only for users with zero categories or zero template items | VERIFIED | `src/hooks/useFirstRunState.ts` exports function returning `{ isFirstRun: categories.length === 0 \|\| items.length === 0, loading: catLoading \|\| tmplLoading }`. Derives from `useCategories()` and `useTemplate()` caches. `tsc --noEmit` passes. | | 5 | `src/data/presets.ts` contains ~15-20 curated budget items with i18n translation keys | VERIFIED | 19 items (4 income, 4 bill, 5 variable_expense, 2 debt, 2 saving, 2 investment). `en.json` and `de.json` both have `presets` key with 19 slugs across 6 type categories. Both JSON files parse cleanly. | **Score:** 5/5 truths verified ### Deferred Items Items not yet met but explicitly addressed in later milestone phases. | # | Item | Addressed In | Evidence | |---|------|-------------|----------| | 1 | useFirstRunState not yet consumed by any component | Phase 7 | Phase 7 SC 1: "A new user is automatically redirected to /setup on their first login" -- requires useFirstRunState | | 2 | PRESETS/PresetItem not yet imported by any component | Phase 7 | Phase 7 SC 2: "recurring items step shows ~15-20 pre-filled common items" -- consumes PRESETS array | ### Required Artifacts | Artifact | Expected | Status | Details | |----------|----------|--------|---------| | `supabase/migrations/006_uniqueness_constraints.sql` | Atomic deduplication + unique constraint DDL | VERIFIED | 29 lines. BEGIN/COMMIT transaction. 2 DISTINCT ON dedup DELETEs. 2 ADD CONSTRAINT statements. | | `supabase/migrations/007_setup_completed.sql` | ALTER TABLE + backfill UPDATE | VERIFIED | 18 lines. ADD COLUMN setup_completed boolean NOT NULL DEFAULT false. UPDATE with UNION backfill. | | `src/lib/types.ts` | Profile interface with setup_completed | VERIFIED | Line 16: `setup_completed: boolean` present in Profile interface. | | `src/data/presets.ts` | PresetItem interface + PRESETS array | VERIFIED | Exports PresetItem interface and PRESETS array with 19 items. No one_off tier values. Pure static module. | | `src/i18n/en.json` | English preset translations | VERIFIED | Top-level `presets` key with 6 type groups, 19 total slugs. Valid JSON. | | `src/i18n/de.json` | German preset translations | VERIFIED | Top-level `presets` key with 6 type groups, 19 total slugs. Valid JSON. | | `src/hooks/useFirstRunState.ts` | Derived first-run state hook | VERIFIED | 28 lines. Exports `useFirstRunState()` returning `{ isFirstRun, loading }`. No direct Supabase calls. | ### Key Link Verification | From | To | Via | Status | Details | |------|----|-----|--------|---------| | `007_setup_completed.sql` | profiles table | ALTER TABLE ADD COLUMN | WIRED | `setup_completed boolean NOT NULL DEFAULT false` present | | `src/lib/types.ts` | Profile interface | TypeScript field | WIRED | `setup_completed: boolean` on line 16 | | `src/data/presets.ts` | `src/lib/types.ts` | `import type { CategoryType }` | WIRED | Line 1 imports CategoryType from types | | `src/i18n/en.json` | presets.{type}.{slug} | react-i18next dot-path | WIRED | `"presets"` key present with nested type/slug structure | | `src/hooks/useFirstRunState.ts` | `src/hooks/useCategories.ts` | useCategories() call | WIRED | Line 1 import + line 21 invocation | | `src/hooks/useFirstRunState.ts` | `src/hooks/useTemplate.ts` | useTemplate() call | WIRED | Line 2 import + line 22 invocation | ### Data-Flow Trace (Level 4) | Artifact | Data Variable | Source | Produces Real Data | Status | |----------|--------------|--------|-------------------|--------| | `src/hooks/useFirstRunState.ts` | categories, items | useCategories() cache, useTemplate() cache | Yes -- upstream hooks query Supabase | FLOWING | | `src/data/presets.ts` | PRESETS | Static array literal | Yes -- 19 hardcoded items (intentionally static) | FLOWING | ### Behavioral Spot-Checks | Behavior | Command | Result | Status | |----------|---------|--------|--------| | TypeScript compiles cleanly | `npx tsc --noEmit` | Zero errors (no output) | PASS | | Migration 006 has 2 constraints | `grep -c "ADD CONSTRAINT" 006_uniqueness_constraints.sql` | 2 | PASS | | Migration 006 is transactional | `grep -c "BEGIN" 006_uniqueness_constraints.sql` | 1 | PASS | | Migration 007 has column add | `grep "ADD COLUMN setup_completed" 007_setup_completed.sql` | 1 match | PASS | | Migration 007 has backfill | `grep "UPDATE profiles" 007_setup_completed.sql` | 1 match | PASS | | Presets has 19 items | `grep -c '{ slug:' src/data/presets.ts` | 19 | PASS | | No one_off in presets | `grep "one_off" src/data/presets.ts` | 0 matches | PASS | | en.json has 19 preset slugs | node JSON parse + count | 19 | PASS | | de.json has 19 preset slugs | node JSON parse + count | 19 | PASS | | useFirstRunState exports function | `grep "export function useFirstRunState"` | 1 match | PASS | ### Requirements Coverage | Requirement | Source Plan | Description | Status | Evidence | |-------------|-----------|-------------|--------|----------| | AUTO-01 | 06-01, 06-03 | Auto-budget uses template on first month visit | SATISFIED (Phase 6 portion) | Budgets unique constraint prevents duplicates; useFirstRunState detects first-run. Full auto-creation in Phase 8. | | AUTO-03 | 06-01, 06-02 | Auto-creation uses user's configured currency | SATISFIED (Phase 6 portion) | Preset amounts are plain EUR numbers with no currency symbol. Profile has currency field. Full currency usage in Phase 8. | | SETUP-01 | 06-01, 06-03 | New user guided through wizard | SATISFIED (Phase 6 portion) | setup_completed column + backfill identifies existing users. useFirstRunState hook detects new users. Wizard UI in Phase 7. | | SETUP-02 | 06-02 | User sees pre-filled common budget items | SATISFIED (Phase 6 portion) | 19 preset items in PRESETS array with en/de translations. Wizard display in Phase 7. | No orphaned requirements found -- all 4 IDs (AUTO-01, AUTO-03, SETUP-01, SETUP-02) appear in plan frontmatter and are traced to REQUIREMENTS.md. ### Anti-Patterns Found | File | Line | Pattern | Severity | Impact | |------|------|---------|----------|--------| | (none) | - | - | - | No anti-patterns detected in any phase 6 artifact | ### Human Verification Required ### 1. Budget Unique Constraint Enforcement **Test:** In Supabase SQL editor, INSERT two budget rows with the same `user_id` and `start_date`. **Expected:** Second INSERT fails with error code 23505 (unique_violation). **Why human:** Requires running Supabase instance with live DB. Note: User already confirmed DB push succeeded, but runtime constraint rejection is a behavioral check. ### 2. Category Unique Constraint Enforcement **Test:** In Supabase SQL editor, INSERT two category rows with the same `user_id` and `name`. **Expected:** Second INSERT fails with error code 23505 (unique_violation). **Why human:** Requires running Supabase instance with live DB. ### 3. Backfill Correctness for Existing Users **Test:** In Supabase Table Editor, check `profiles` rows for users who existed before migration 007. **Expected:** Any user with existing categories or template items has `setup_completed = true`. New test users have `setup_completed = false`. **Why human:** Requires inspecting live database state. ### Gaps Summary No gaps found. All 5 roadmap success criteria are verified at the code/artifact level. Two artifacts (useFirstRunState hook and PRESETS array) are intentionally orphaned -- they are consumed by Phase 7 (Setup Wizard), which is the next phase. Three items require human verification against the live database: budget constraint enforcement, category constraint enforcement, and backfill correctness. These are behavioral checks that cannot be verified by static code analysis alone. --- _Verified: 2026-04-20T19:15:00Z_ _Verifier: Claude (gsd-verifier)_