From 59c43188e575e85e058ef2da93f40d49c2ad1ca4 Mon Sep 17 00:00:00 2001 From: SGC Date: Sat, 4 Apr 2026 14:53:33 +0200 Subject: [PATCH 1/2] feat: add DBML schema files and relspecgo migration generation - Add schema/*.dbml covering all existing tables (001-019) - Wire relspecgo via make generate-migrations target - Add make check-schema-drift for CI drift detection - Add schema/README.md documenting the DBML-first workflow Closes #19 --- Makefile | 25 +- migrations/020_generated_schema.sql | 3996 +++++++++++++++++++++++++++ schema/README.md | 35 + schema/calendar.dbml | 44 + schema/core.dbml | 48 + schema/crm.dbml | 53 + schema/files.dbml | 21 + schema/household.dbml | 31 + schema/maintenance.dbml | 30 + schema/meals.dbml | 49 + schema/meta.dbml | 29 + schema/skills.dbml | 42 + 12 files changed, 4402 insertions(+), 1 deletion(-) create mode 100644 migrations/020_generated_schema.sql create mode 100644 schema/README.md create mode 100644 schema/calendar.dbml create mode 100644 schema/core.dbml create mode 100644 schema/crm.dbml create mode 100644 schema/files.dbml create mode 100644 schema/household.dbml create mode 100644 schema/maintenance.dbml create mode 100644 schema/meals.dbml create mode 100644 schema/meta.dbml create mode 100644 schema/skills.dbml diff --git a/Makefile b/Makefile index dc24f94..7d71d83 100644 --- a/Makefile +++ b/Makefile @@ -7,13 +7,16 @@ PATCH_INCREMENT ?= 1 VERSION_TAG ?= $(shell git describe --tags --exact-match 2>/dev/null || echo dev) COMMIT_SHA ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) BUILD_DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) +RELSPEC ?= $(shell command -v relspec 2>/dev/null || echo $(HOME)/go/bin/relspec) +SCHEMA_FILES := $(sort $(wildcard schema/*.dbml)) +GENERATED_SCHEMA_MIGRATION := migrations/020_generated_schema.sql LDFLAGS := -s -w \ -X $(BUILDINFO_PKG).Version=$(VERSION_TAG) \ -X $(BUILDINFO_PKG).TagName=$(VERSION_TAG) \ -X $(BUILDINFO_PKG).Commit=$(COMMIT_SHA) \ -X $(BUILDINFO_PKG).BuildDate=$(BUILD_DATE) -.PHONY: all build clean migrate release-version test +.PHONY: all build clean migrate release-version test generate-migrations check-schema-drift all: build @@ -50,3 +53,23 @@ migrate: clean: rm -rf $(BIN_DIR) + +generate-migrations: + @test -n "$(SCHEMA_FILES)" || (echo "No DBML schema files found in schema/" >&2; exit 1) + @command -v $(RELSPEC) >/dev/null 2>&1 || (echo "relspec not found; install git.warky.dev/wdevs/relspecgo/cmd/relspec@latest" >&2; exit 1) + @schema_list=$$(printf '%s\n' $(SCHEMA_FILES) | paste -sd, -); \ + $(RELSPEC) convert --from dbml --from-list "$$schema_list" --to pgsql --to-path $(GENERATED_SCHEMA_MIGRATION) + +check-schema-drift: + @test -f $(GENERATED_SCHEMA_MIGRATION) || (echo "$(GENERATED_SCHEMA_MIGRATION) is missing; run make generate-migrations" >&2; exit 1) + @command -v $(RELSPEC) >/dev/null 2>&1 || (echo "relspec not found; install git.warky.dev/wdevs/relspecgo/cmd/relspec@latest" >&2; exit 1) + @tmpfile=$$(mktemp); \ + schema_list=$$(printf '%s\n' $(SCHEMA_FILES) | paste -sd, -); \ + $(RELSPEC) convert --from dbml --from-list "$$schema_list" --to pgsql --to-path $$tmpfile; \ + if ! cmp -s $$tmpfile $(GENERATED_SCHEMA_MIGRATION); then \ + echo "Schema drift detected between schema/*.dbml and $(GENERATED_SCHEMA_MIGRATION)" >&2; \ + diff -u $(GENERATED_SCHEMA_MIGRATION) $$tmpfile || true; \ + rm -f $$tmpfile; \ + exit 1; \ + fi; \ + rm -f $$tmpfile diff --git a/migrations/020_generated_schema.sql b/migrations/020_generated_schema.sql new file mode 100644 index 0000000..34a1e4f --- /dev/null +++ b/migrations/020_generated_schema.sql @@ -0,0 +1,3996 @@ +-- PostgreSQL Database Schema +-- Database: database +-- Generated by RelSpec + +-- Sequences for schema: public +CREATE SEQUENCE IF NOT EXISTS public.identity_thoughts_id + INCREMENT 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + START 1 + CACHE 1; + +CREATE SEQUENCE IF NOT EXISTS public.identity_projects_id + INCREMENT 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + START 1 + CACHE 1; + +CREATE SEQUENCE IF NOT EXISTS public.identity_embeddings_id + INCREMENT 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + START 1 + CACHE 1; + +CREATE SEQUENCE IF NOT EXISTS public.identity_stored_files_id + INCREMENT 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + START 1 + CACHE 1; + +CREATE SEQUENCE IF NOT EXISTS public.identity_tool_annotations_id + INCREMENT 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + START 1 + CACHE 1; + +-- Tables for schema: public +CREATE TABLE IF NOT EXISTS public.family_members ( + birth_date date, + created_at timestamptz NOT NULL DEFAULT now(), + id uuid NOT NULL DEFAULT gen_random_uuid(), + name text NOT NULL, + notes text, + relationship text +); + +CREATE TABLE IF NOT EXISTS public.activities ( + activity_type text, + created_at timestamptz NOT NULL DEFAULT now(), + day_of_week text, + end_date date, + end_time time, + family_member_id uuid, + id uuid NOT NULL DEFAULT gen_random_uuid(), + location text, + notes text, + start_date date, + start_time time, + title text NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.important_dates ( + created_at timestamptz NOT NULL DEFAULT now(), + date_value date NOT NULL, + family_member_id uuid, + id uuid NOT NULL DEFAULT gen_random_uuid(), + notes text, + recurring_yearly boolean NOT NULL DEFAULT false, + reminder_days_before integer NOT NULL DEFAULT '7', + title text NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.thoughts ( + archived_at timestamptz, + content text NOT NULL, + created_at timestamptz DEFAULT now(), + guid uuid NOT NULL DEFAULT gen_random_uuid(), + id bigserial NOT NULL, + metadata jsonb DEFAULT '''{}''::jsonb', + project_id uuid, + updated_at timestamptz DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.projects ( + created_at timestamptz DEFAULT now(), + description text, + guid uuid NOT NULL DEFAULT gen_random_uuid(), + id bigserial NOT NULL, + last_active_at timestamptz DEFAULT now(), + name text NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.thought_links ( + created_at timestamptz DEFAULT now(), + from_id bigint NOT NULL, + relation text NOT NULL, + to_id bigint NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.embeddings ( + created_at timestamptz DEFAULT now(), + dim integer NOT NULL, + embedding vector NOT NULL, + guid uuid NOT NULL DEFAULT gen_random_uuid(), + id bigserial NOT NULL, + model text NOT NULL, + thought_id uuid NOT NULL, + updated_at timestamptz DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.professional_contacts ( + company text, + created_at timestamptz NOT NULL DEFAULT now(), + email text, + follow_up_date date, + how_we_met text, + id uuid NOT NULL DEFAULT gen_random_uuid(), + last_contacted timestamptz, + linkedin_url text, + name text NOT NULL, + notes text, + phone text, + tags text, + title text, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.contact_interactions ( + contact_id uuid NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + follow_up_needed boolean NOT NULL DEFAULT false, + follow_up_notes text, + id uuid NOT NULL DEFAULT gen_random_uuid(), + interaction_type text NOT NULL, + occurred_at timestamptz NOT NULL DEFAULT now(), + summary text NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.opportunities ( + contact_id uuid, + created_at timestamptz NOT NULL DEFAULT now(), + description text, + expected_close_date date, + id uuid NOT NULL DEFAULT gen_random_uuid(), + notes text, + stage text NOT NULL DEFAULT 'identified', + title text NOT NULL, + updated_at timestamptz NOT NULL DEFAULT now(), + value decimal(12,2) +); + +CREATE TABLE IF NOT EXISTS public.stored_files ( + content bytea NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + encoding text NOT NULL DEFAULT 'base64', + guid uuid NOT NULL DEFAULT gen_random_uuid(), + id bigserial NOT NULL, + kind text NOT NULL DEFAULT 'file', + media_type text NOT NULL, + name text NOT NULL, + project_id uuid, + sha256 text NOT NULL, + size_bytes bigint NOT NULL, + thought_id uuid, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.household_items ( + category text, + created_at timestamptz NOT NULL DEFAULT now(), + details jsonb NOT NULL DEFAULT '''{}''', + id uuid NOT NULL DEFAULT gen_random_uuid(), + location text, + name text NOT NULL, + notes text, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.household_vendors ( + created_at timestamptz NOT NULL DEFAULT now(), + email text, + id uuid NOT NULL DEFAULT gen_random_uuid(), + last_used date, + name text NOT NULL, + notes text, + phone text, + rating integer, + service_type text, + website text +); + +CREATE TABLE IF NOT EXISTS public.maintenance_tasks ( + category text, + created_at timestamptz NOT NULL DEFAULT now(), + frequency_days integer, + id uuid NOT NULL DEFAULT gen_random_uuid(), + last_completed timestamptz, + name text NOT NULL, + next_due timestamptz, + notes text, + priority text NOT NULL DEFAULT 'medium', + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.maintenance_logs ( + completed_at timestamptz NOT NULL DEFAULT now(), + cost decimal(10,2), + id uuid NOT NULL DEFAULT gen_random_uuid(), + next_action text, + notes text, + performed_by text, + task_id uuid NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.recipes ( + cook_time_minutes integer, + created_at timestamptz NOT NULL DEFAULT now(), + cuisine text, + id uuid NOT NULL DEFAULT gen_random_uuid(), + ingredients jsonb NOT NULL DEFAULT '''[', + instructions jsonb NOT NULL DEFAULT '''[', + name text NOT NULL, + notes text, + prep_time_minutes integer, + rating integer, + servings integer, + tags text, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.meal_plans ( + created_at timestamptz NOT NULL DEFAULT now(), + custom_meal text, + day_of_week text NOT NULL, + id uuid NOT NULL DEFAULT gen_random_uuid(), + meal_type text NOT NULL, + notes text, + recipe_id uuid, + servings integer, + week_start date NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.shopping_lists ( + created_at timestamptz NOT NULL DEFAULT now(), + id uuid NOT NULL DEFAULT gen_random_uuid(), + items jsonb NOT NULL DEFAULT '''[', + notes text, + updated_at timestamptz NOT NULL DEFAULT now(), + week_start date NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.chat_histories ( + agent_id text, + channel text, + created_at timestamptz NOT NULL DEFAULT now(), + id uuid NOT NULL DEFAULT gen_random_uuid(), + messages jsonb NOT NULL DEFAULT '''[', + metadata jsonb NOT NULL DEFAULT '''{}''', + project_id uuid, + session_id text NOT NULL, + summary text, + title text, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.tool_annotations ( + created_at timestamptz NOT NULL DEFAULT now(), + id bigserial NOT NULL, + notes text NOT NULL DEFAULT '', + tool_name text NOT NULL, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.agent_skills ( + content text NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + description text NOT NULL DEFAULT '', + id uuid NOT NULL DEFAULT gen_random_uuid(), + name text NOT NULL, + tags text, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.agent_guardrails ( + content text NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + description text NOT NULL DEFAULT '', + id uuid NOT NULL DEFAULT gen_random_uuid(), + name text NOT NULL, + severity text NOT NULL DEFAULT 'medium', + tags text, + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.project_skills ( + created_at timestamptz NOT NULL DEFAULT now(), + project_id uuid NOT NULL, + skill_id uuid NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.project_guardrails ( + created_at timestamptz NOT NULL DEFAULT now(), + guardrail_id uuid NOT NULL, + project_id uuid NOT NULL +); + +-- Add missing columns for schema: public +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'family_members' + AND column_name = 'birth_date' + ) THEN + ALTER TABLE public.family_members ADD COLUMN birth_date date; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'family_members' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.family_members ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'family_members' + AND column_name = 'id' + ) THEN + ALTER TABLE public.family_members ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'family_members' + AND column_name = 'name' + ) THEN + ALTER TABLE public.family_members ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'family_members' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.family_members ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'family_members' + AND column_name = 'relationship' + ) THEN + ALTER TABLE public.family_members ADD COLUMN relationship text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'activity_type' + ) THEN + ALTER TABLE public.activities ADD COLUMN activity_type text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.activities ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'day_of_week' + ) THEN + ALTER TABLE public.activities ADD COLUMN day_of_week text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'end_date' + ) THEN + ALTER TABLE public.activities ADD COLUMN end_date date; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'end_time' + ) THEN + ALTER TABLE public.activities ADD COLUMN end_time time; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'family_member_id' + ) THEN + ALTER TABLE public.activities ADD COLUMN family_member_id uuid; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'id' + ) THEN + ALTER TABLE public.activities ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'location' + ) THEN + ALTER TABLE public.activities ADD COLUMN location text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.activities ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'start_date' + ) THEN + ALTER TABLE public.activities ADD COLUMN start_date date; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'start_time' + ) THEN + ALTER TABLE public.activities ADD COLUMN start_time time; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'activities' + AND column_name = 'title' + ) THEN + ALTER TABLE public.activities ADD COLUMN title text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.important_dates ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND column_name = 'date_value' + ) THEN + ALTER TABLE public.important_dates ADD COLUMN date_value date NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND column_name = 'family_member_id' + ) THEN + ALTER TABLE public.important_dates ADD COLUMN family_member_id uuid; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND column_name = 'id' + ) THEN + ALTER TABLE public.important_dates ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.important_dates ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND column_name = 'recurring_yearly' + ) THEN + ALTER TABLE public.important_dates ADD COLUMN recurring_yearly boolean NOT NULL DEFAULT false; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND column_name = 'reminder_days_before' + ) THEN + ALTER TABLE public.important_dates ADD COLUMN reminder_days_before integer NOT NULL DEFAULT '7'; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND column_name = 'title' + ) THEN + ALTER TABLE public.important_dates ADD COLUMN title text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'archived_at' + ) THEN + ALTER TABLE public.thoughts ADD COLUMN archived_at timestamptz; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'content' + ) THEN + ALTER TABLE public.thoughts ADD COLUMN content text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.thoughts ADD COLUMN created_at timestamptz DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'guid' + ) THEN + ALTER TABLE public.thoughts ADD COLUMN guid uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'id' + ) THEN + ALTER TABLE public.thoughts ADD COLUMN id bigserial NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'metadata' + ) THEN + ALTER TABLE public.thoughts ADD COLUMN metadata jsonb DEFAULT '''{}''::jsonb'; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'project_id' + ) THEN + ALTER TABLE public.thoughts ADD COLUMN project_id uuid; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.thoughts ADD COLUMN updated_at timestamptz DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'projects' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.projects ADD COLUMN created_at timestamptz DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'projects' + AND column_name = 'description' + ) THEN + ALTER TABLE public.projects ADD COLUMN description text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'projects' + AND column_name = 'guid' + ) THEN + ALTER TABLE public.projects ADD COLUMN guid uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'projects' + AND column_name = 'id' + ) THEN + ALTER TABLE public.projects ADD COLUMN id bigserial NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'projects' + AND column_name = 'last_active_at' + ) THEN + ALTER TABLE public.projects ADD COLUMN last_active_at timestamptz DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'projects' + AND column_name = 'name' + ) THEN + ALTER TABLE public.projects ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thought_links' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.thought_links ADD COLUMN created_at timestamptz DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thought_links' + AND column_name = 'from_id' + ) THEN + ALTER TABLE public.thought_links ADD COLUMN from_id bigint NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thought_links' + AND column_name = 'relation' + ) THEN + ALTER TABLE public.thought_links ADD COLUMN relation text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'thought_links' + AND column_name = 'to_id' + ) THEN + ALTER TABLE public.thought_links ADD COLUMN to_id bigint NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.embeddings ADD COLUMN created_at timestamptz DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND column_name = 'dim' + ) THEN + ALTER TABLE public.embeddings ADD COLUMN dim integer NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND column_name = 'embedding' + ) THEN + ALTER TABLE public.embeddings ADD COLUMN embedding vector NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND column_name = 'guid' + ) THEN + ALTER TABLE public.embeddings ADD COLUMN guid uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND column_name = 'id' + ) THEN + ALTER TABLE public.embeddings ADD COLUMN id bigserial NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND column_name = 'model' + ) THEN + ALTER TABLE public.embeddings ADD COLUMN model text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND column_name = 'thought_id' + ) THEN + ALTER TABLE public.embeddings ADD COLUMN thought_id uuid NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.embeddings ADD COLUMN updated_at timestamptz DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'company' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN company text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'email' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN email text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'follow_up_date' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN follow_up_date date; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'how_we_met' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN how_we_met text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'id' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'last_contacted' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN last_contacted timestamptz; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'linkedin_url' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN linkedin_url text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'name' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'phone' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN phone text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'tags' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN tags text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'title' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN title text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.professional_contacts ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND column_name = 'contact_id' + ) THEN + ALTER TABLE public.contact_interactions ADD COLUMN contact_id uuid NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.contact_interactions ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND column_name = 'follow_up_needed' + ) THEN + ALTER TABLE public.contact_interactions ADD COLUMN follow_up_needed boolean NOT NULL DEFAULT false; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND column_name = 'follow_up_notes' + ) THEN + ALTER TABLE public.contact_interactions ADD COLUMN follow_up_notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND column_name = 'id' + ) THEN + ALTER TABLE public.contact_interactions ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND column_name = 'interaction_type' + ) THEN + ALTER TABLE public.contact_interactions ADD COLUMN interaction_type text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND column_name = 'occurred_at' + ) THEN + ALTER TABLE public.contact_interactions ADD COLUMN occurred_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND column_name = 'summary' + ) THEN + ALTER TABLE public.contact_interactions ADD COLUMN summary text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'contact_id' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN contact_id uuid; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'description' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN description text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'expected_close_date' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN expected_close_date date; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'id' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'stage' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN stage text NOT NULL DEFAULT 'identified'; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'title' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN title text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND column_name = 'value' + ) THEN + ALTER TABLE public.opportunities ADD COLUMN value decimal(12,2); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'content' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN content bytea NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'encoding' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN encoding text NOT NULL DEFAULT 'base64'; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'guid' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN guid uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'id' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN id bigserial NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'kind' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN kind text NOT NULL DEFAULT 'file'; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'media_type' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN media_type text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'name' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'project_id' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN project_id uuid; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'sha256' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN sha256 text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'size_bytes' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN size_bytes bigint NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'thought_id' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN thought_id uuid; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.stored_files ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND column_name = 'category' + ) THEN + ALTER TABLE public.household_items ADD COLUMN category text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.household_items ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND column_name = 'details' + ) THEN + ALTER TABLE public.household_items ADD COLUMN details jsonb NOT NULL DEFAULT '''{}'''; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND column_name = 'id' + ) THEN + ALTER TABLE public.household_items ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND column_name = 'location' + ) THEN + ALTER TABLE public.household_items ADD COLUMN location text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND column_name = 'name' + ) THEN + ALTER TABLE public.household_items ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.household_items ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.household_items ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'email' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN email text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'id' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'last_used' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN last_used date; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'name' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'phone' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN phone text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'rating' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN rating integer; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'service_type' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN service_type text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND column_name = 'website' + ) THEN + ALTER TABLE public.household_vendors ADD COLUMN website text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'category' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN category text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'frequency_days' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN frequency_days integer; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'id' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'last_completed' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN last_completed timestamptz; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'name' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'next_due' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN next_due timestamptz; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'priority' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN priority text NOT NULL DEFAULT 'medium'; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.maintenance_tasks ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND column_name = 'completed_at' + ) THEN + ALTER TABLE public.maintenance_logs ADD COLUMN completed_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND column_name = 'cost' + ) THEN + ALTER TABLE public.maintenance_logs ADD COLUMN cost decimal(10,2); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND column_name = 'id' + ) THEN + ALTER TABLE public.maintenance_logs ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND column_name = 'next_action' + ) THEN + ALTER TABLE public.maintenance_logs ADD COLUMN next_action text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.maintenance_logs ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND column_name = 'performed_by' + ) THEN + ALTER TABLE public.maintenance_logs ADD COLUMN performed_by text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND column_name = 'task_id' + ) THEN + ALTER TABLE public.maintenance_logs ADD COLUMN task_id uuid NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'cook_time_minutes' + ) THEN + ALTER TABLE public.recipes ADD COLUMN cook_time_minutes integer; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.recipes ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'cuisine' + ) THEN + ALTER TABLE public.recipes ADD COLUMN cuisine text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'id' + ) THEN + ALTER TABLE public.recipes ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'ingredients' + ) THEN + ALTER TABLE public.recipes ADD COLUMN ingredients jsonb NOT NULL DEFAULT '''['; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'instructions' + ) THEN + ALTER TABLE public.recipes ADD COLUMN instructions jsonb NOT NULL DEFAULT '''['; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'name' + ) THEN + ALTER TABLE public.recipes ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.recipes ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'prep_time_minutes' + ) THEN + ALTER TABLE public.recipes ADD COLUMN prep_time_minutes integer; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'rating' + ) THEN + ALTER TABLE public.recipes ADD COLUMN rating integer; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'servings' + ) THEN + ALTER TABLE public.recipes ADD COLUMN servings integer; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'tags' + ) THEN + ALTER TABLE public.recipes ADD COLUMN tags text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.recipes ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'custom_meal' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN custom_meal text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'day_of_week' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN day_of_week text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'id' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'meal_type' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN meal_type text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'recipe_id' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN recipe_id uuid; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'servings' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN servings integer; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND column_name = 'week_start' + ) THEN + ALTER TABLE public.meal_plans ADD COLUMN week_start date NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.shopping_lists ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND column_name = 'id' + ) THEN + ALTER TABLE public.shopping_lists ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND column_name = 'items' + ) THEN + ALTER TABLE public.shopping_lists ADD COLUMN items jsonb NOT NULL DEFAULT '''['; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.shopping_lists ADD COLUMN notes text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.shopping_lists ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND column_name = 'week_start' + ) THEN + ALTER TABLE public.shopping_lists ADD COLUMN week_start date NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'agent_id' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN agent_id text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'channel' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN channel text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'id' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'messages' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN messages jsonb NOT NULL DEFAULT '''['; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'metadata' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN metadata jsonb NOT NULL DEFAULT '''{}'''; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'project_id' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN project_id uuid; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'session_id' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN session_id text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'summary' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN summary text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'title' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN title text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.chat_histories ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'tool_annotations' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.tool_annotations ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'tool_annotations' + AND column_name = 'id' + ) THEN + ALTER TABLE public.tool_annotations ADD COLUMN id bigserial NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'tool_annotations' + AND column_name = 'notes' + ) THEN + ALTER TABLE public.tool_annotations ADD COLUMN notes text NOT NULL DEFAULT ''; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'tool_annotations' + AND column_name = 'tool_name' + ) THEN + ALTER TABLE public.tool_annotations ADD COLUMN tool_name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'tool_annotations' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.tool_annotations ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND column_name = 'content' + ) THEN + ALTER TABLE public.agent_skills ADD COLUMN content text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.agent_skills ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND column_name = 'description' + ) THEN + ALTER TABLE public.agent_skills ADD COLUMN description text NOT NULL DEFAULT ''; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND column_name = 'id' + ) THEN + ALTER TABLE public.agent_skills ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND column_name = 'name' + ) THEN + ALTER TABLE public.agent_skills ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND column_name = 'tags' + ) THEN + ALTER TABLE public.agent_skills ADD COLUMN tags text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.agent_skills ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND column_name = 'content' + ) THEN + ALTER TABLE public.agent_guardrails ADD COLUMN content text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.agent_guardrails ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND column_name = 'description' + ) THEN + ALTER TABLE public.agent_guardrails ADD COLUMN description text NOT NULL DEFAULT ''; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND column_name = 'id' + ) THEN + ALTER TABLE public.agent_guardrails ADD COLUMN id uuid NOT NULL DEFAULT gen_random_uuid(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND column_name = 'name' + ) THEN + ALTER TABLE public.agent_guardrails ADD COLUMN name text NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND column_name = 'severity' + ) THEN + ALTER TABLE public.agent_guardrails ADD COLUMN severity text NOT NULL DEFAULT 'medium'; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND column_name = 'tags' + ) THEN + ALTER TABLE public.agent_guardrails ADD COLUMN tags text; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE public.agent_guardrails ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_skills' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.project_skills ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_skills' + AND column_name = 'project_id' + ) THEN + ALTER TABLE public.project_skills ADD COLUMN project_id uuid NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_skills' + AND column_name = 'skill_id' + ) THEN + ALTER TABLE public.project_skills ADD COLUMN skill_id uuid NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_guardrails' + AND column_name = 'created_at' + ) THEN + ALTER TABLE public.project_guardrails ADD COLUMN created_at timestamptz NOT NULL DEFAULT now(); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_guardrails' + AND column_name = 'guardrail_id' + ) THEN + ALTER TABLE public.project_guardrails ADD COLUMN guardrail_id uuid NOT NULL; + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_guardrails' + AND column_name = 'project_id' + ) THEN + ALTER TABLE public.project_guardrails ADD COLUMN project_id uuid NOT NULL; + END IF; +END; +$$; + +-- Primary keys for schema: public +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'family_members' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('family_members_pkey', 'public_family_members_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.family_members DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'family_members' + AND constraint_name = 'pk_public_family_members' + ) THEN + ALTER TABLE public.family_members ADD CONSTRAINT pk_public_family_members PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'activities' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('activities_pkey', 'public_activities_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.activities DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'activities' + AND constraint_name = 'pk_public_activities' + ) THEN + ALTER TABLE public.activities ADD CONSTRAINT pk_public_activities PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('important_dates_pkey', 'public_important_dates_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.important_dates DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND constraint_name = 'pk_public_important_dates' + ) THEN + ALTER TABLE public.important_dates ADD CONSTRAINT pk_public_important_dates PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('thoughts_pkey', 'public_thoughts_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.thoughts DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND constraint_name = 'pk_public_thoughts' + ) THEN + ALTER TABLE public.thoughts ADD CONSTRAINT pk_public_thoughts PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'projects' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('projects_pkey', 'public_projects_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.projects DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'projects' + AND constraint_name = 'pk_public_projects' + ) THEN + ALTER TABLE public.projects ADD CONSTRAINT pk_public_projects PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('embeddings_pkey', 'public_embeddings_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.embeddings DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND constraint_name = 'pk_public_embeddings' + ) THEN + ALTER TABLE public.embeddings ADD CONSTRAINT pk_public_embeddings PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('professional_contacts_pkey', 'public_professional_contacts_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.professional_contacts DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'professional_contacts' + AND constraint_name = 'pk_public_professional_contacts' + ) THEN + ALTER TABLE public.professional_contacts ADD CONSTRAINT pk_public_professional_contacts PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('contact_interactions_pkey', 'public_contact_interactions_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.contact_interactions DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND constraint_name = 'pk_public_contact_interactions' + ) THEN + ALTER TABLE public.contact_interactions ADD CONSTRAINT pk_public_contact_interactions PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('opportunities_pkey', 'public_opportunities_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.opportunities DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND constraint_name = 'pk_public_opportunities' + ) THEN + ALTER TABLE public.opportunities ADD CONSTRAINT pk_public_opportunities PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('stored_files_pkey', 'public_stored_files_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.stored_files DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND constraint_name = 'pk_public_stored_files' + ) THEN + ALTER TABLE public.stored_files ADD CONSTRAINT pk_public_stored_files PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('household_items_pkey', 'public_household_items_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.household_items DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'household_items' + AND constraint_name = 'pk_public_household_items' + ) THEN + ALTER TABLE public.household_items ADD CONSTRAINT pk_public_household_items PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('household_vendors_pkey', 'public_household_vendors_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.household_vendors DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'household_vendors' + AND constraint_name = 'pk_public_household_vendors' + ) THEN + ALTER TABLE public.household_vendors ADD CONSTRAINT pk_public_household_vendors PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('maintenance_tasks_pkey', 'public_maintenance_tasks_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.maintenance_tasks DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'maintenance_tasks' + AND constraint_name = 'pk_public_maintenance_tasks' + ) THEN + ALTER TABLE public.maintenance_tasks ADD CONSTRAINT pk_public_maintenance_tasks PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('maintenance_logs_pkey', 'public_maintenance_logs_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.maintenance_logs DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND constraint_name = 'pk_public_maintenance_logs' + ) THEN + ALTER TABLE public.maintenance_logs ADD CONSTRAINT pk_public_maintenance_logs PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('recipes_pkey', 'public_recipes_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.recipes DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'recipes' + AND constraint_name = 'pk_public_recipes' + ) THEN + ALTER TABLE public.recipes ADD CONSTRAINT pk_public_recipes PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('meal_plans_pkey', 'public_meal_plans_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.meal_plans DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND constraint_name = 'pk_public_meal_plans' + ) THEN + ALTER TABLE public.meal_plans ADD CONSTRAINT pk_public_meal_plans PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('shopping_lists_pkey', 'public_shopping_lists_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.shopping_lists DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND constraint_name = 'pk_public_shopping_lists' + ) THEN + ALTER TABLE public.shopping_lists ADD CONSTRAINT pk_public_shopping_lists PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('chat_histories_pkey', 'public_chat_histories_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.chat_histories DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND constraint_name = 'pk_public_chat_histories' + ) THEN + ALTER TABLE public.chat_histories ADD CONSTRAINT pk_public_chat_histories PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'tool_annotations' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('tool_annotations_pkey', 'public_tool_annotations_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.tool_annotations DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'tool_annotations' + AND constraint_name = 'pk_public_tool_annotations' + ) THEN + ALTER TABLE public.tool_annotations ADD CONSTRAINT pk_public_tool_annotations PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('agent_skills_pkey', 'public_agent_skills_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.agent_skills DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND constraint_name = 'pk_public_agent_skills' + ) THEN + ALTER TABLE public.agent_skills ADD CONSTRAINT pk_public_agent_skills PRIMARY KEY (id); + END IF; +END; +$$; + +DO $$ +DECLARE + auto_pk_name text; +BEGIN + -- Drop auto-generated primary key if it exists + SELECT constraint_name INTO auto_pk_name + FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND constraint_type = 'PRIMARY KEY' + AND constraint_name IN ('agent_guardrails_pkey', 'public_agent_guardrails_pkey'); + + IF auto_pk_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE public.agent_guardrails DROP CONSTRAINT ' || quote_ident(auto_pk_name); + END IF; + + -- Add named primary key if it doesn't exist + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND constraint_name = 'pk_public_agent_guardrails' + ) THEN + ALTER TABLE public.agent_guardrails ADD CONSTRAINT pk_public_agent_guardrails PRIMARY KEY (id); + END IF; +END; +$$; + +-- Indexes for schema: public +CREATE INDEX IF NOT EXISTS idx_activities_start_date_end_date + ON public.activities USING btree (start_date, end_date); + +CREATE INDEX IF NOT EXISTS idx_thought_links_from_id_to_id_relation + ON public.thought_links USING btree (from_id, to_id, relation); + +CREATE UNIQUE INDEX IF NOT EXISTS uidx_embeddings_thought_id_model + ON public.embeddings USING btree (thought_id, model); + +CREATE INDEX IF NOT EXISTS idx_contact_interactions_contact_id_occurred_at + ON public.contact_interactions USING btree (contact_id, occurred_at); + +CREATE INDEX IF NOT EXISTS idx_maintenance_logs_task_id_completed_at + ON public.maintenance_logs USING btree (task_id, completed_at); + +CREATE INDEX IF NOT EXISTS idx_project_skills_project_id_skill_id + ON public.project_skills USING btree (project_id, skill_id); + +CREATE INDEX IF NOT EXISTS idx_project_guardrails_project_id_guardrail_id + ON public.project_guardrails USING btree (project_id, guardrail_id); + +-- Unique constraints for schema: public +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND constraint_name = 'ukey_thoughts_guid' + ) THEN + ALTER TABLE public.thoughts ADD CONSTRAINT ukey_thoughts_guid UNIQUE (guid); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'projects' + AND constraint_name = 'ukey_projects_guid' + ) THEN + ALTER TABLE public.projects ADD CONSTRAINT ukey_projects_guid UNIQUE (guid); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'projects' + AND constraint_name = 'ukey_projects_name' + ) THEN + ALTER TABLE public.projects ADD CONSTRAINT ukey_projects_name UNIQUE (name); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND constraint_name = 'ukey_embeddings_guid' + ) THEN + ALTER TABLE public.embeddings ADD CONSTRAINT ukey_embeddings_guid UNIQUE (guid); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND constraint_name = 'ukey_stored_files_guid' + ) THEN + ALTER TABLE public.stored_files ADD CONSTRAINT ukey_stored_files_guid UNIQUE (guid); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'shopping_lists' + AND constraint_name = 'ukey_shopping_lists_week_start' + ) THEN + ALTER TABLE public.shopping_lists ADD CONSTRAINT ukey_shopping_lists_week_start UNIQUE (week_start); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'tool_annotations' + AND constraint_name = 'ukey_tool_annotations_tool_name' + ) THEN + ALTER TABLE public.tool_annotations ADD CONSTRAINT ukey_tool_annotations_tool_name UNIQUE (tool_name); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'agent_skills' + AND constraint_name = 'ukey_agent_skills_name' + ) THEN + ALTER TABLE public.agent_skills ADD CONSTRAINT ukey_agent_skills_name UNIQUE (name); + END IF; +END; +$$; + +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'agent_guardrails' + AND constraint_name = 'ukey_agent_guardrails_name' + ) THEN + ALTER TABLE public.agent_guardrails ADD CONSTRAINT ukey_agent_guardrails_name UNIQUE (name); + END IF; +END; +$$; + +-- Check constraints for schema: public +-- Foreign keys for schema: public +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'activities' + AND constraint_name = 'fk_activities_family_member_id' + ) THEN + ALTER TABLE public.activities + ADD CONSTRAINT fk_activities_family_member_id + FOREIGN KEY (family_member_id) + REFERENCES public.family_members (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'important_dates' + AND constraint_name = 'fk_important_dates_family_member_id' + ) THEN + ALTER TABLE public.important_dates + ADD CONSTRAINT fk_important_dates_family_member_id + FOREIGN KEY (family_member_id) + REFERENCES public.family_members (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'thoughts' + AND constraint_name = 'fk_thoughts_project_id' + ) THEN + ALTER TABLE public.thoughts + ADD CONSTRAINT fk_thoughts_project_id + FOREIGN KEY (project_id) + REFERENCES public.projects (guid) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'thought_links' + AND constraint_name = 'fk_thought_links_from_id' + ) THEN + ALTER TABLE public.thought_links + ADD CONSTRAINT fk_thought_links_from_id + FOREIGN KEY (from_id) + REFERENCES public.thoughts (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'thought_links' + AND constraint_name = 'fk_thought_links_to_id' + ) THEN + ALTER TABLE public.thought_links + ADD CONSTRAINT fk_thought_links_to_id + FOREIGN KEY (to_id) + REFERENCES public.thoughts (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'embeddings' + AND constraint_name = 'fk_embeddings_thought_id' + ) THEN + ALTER TABLE public.embeddings + ADD CONSTRAINT fk_embeddings_thought_id + FOREIGN KEY (thought_id) + REFERENCES public.thoughts (guid) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'contact_interactions' + AND constraint_name = 'fk_contact_interactions_contact_id' + ) THEN + ALTER TABLE public.contact_interactions + ADD CONSTRAINT fk_contact_interactions_contact_id + FOREIGN KEY (contact_id) + REFERENCES public.professional_contacts (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'opportunities' + AND constraint_name = 'fk_opportunities_contact_id' + ) THEN + ALTER TABLE public.opportunities + ADD CONSTRAINT fk_opportunities_contact_id + FOREIGN KEY (contact_id) + REFERENCES public.professional_contacts (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND constraint_name = 'fk_stored_files_project_id' + ) THEN + ALTER TABLE public.stored_files + ADD CONSTRAINT fk_stored_files_project_id + FOREIGN KEY (project_id) + REFERENCES public.projects (guid) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'stored_files' + AND constraint_name = 'fk_stored_files_thought_id' + ) THEN + ALTER TABLE public.stored_files + ADD CONSTRAINT fk_stored_files_thought_id + FOREIGN KEY (thought_id) + REFERENCES public.thoughts (guid) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'maintenance_logs' + AND constraint_name = 'fk_maintenance_logs_task_id' + ) THEN + ALTER TABLE public.maintenance_logs + ADD CONSTRAINT fk_maintenance_logs_task_id + FOREIGN KEY (task_id) + REFERENCES public.maintenance_tasks (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'meal_plans' + AND constraint_name = 'fk_meal_plans_recipe_id' + ) THEN + ALTER TABLE public.meal_plans + ADD CONSTRAINT fk_meal_plans_recipe_id + FOREIGN KEY (recipe_id) + REFERENCES public.recipes (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'chat_histories' + AND constraint_name = 'fk_chat_histories_project_id' + ) THEN + ALTER TABLE public.chat_histories + ADD CONSTRAINT fk_chat_histories_project_id + FOREIGN KEY (project_id) + REFERENCES public.projects (guid) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'project_skills' + AND constraint_name = 'fk_project_skills_project_id' + ) THEN + ALTER TABLE public.project_skills + ADD CONSTRAINT fk_project_skills_project_id + FOREIGN KEY (project_id) + REFERENCES public.projects (guid) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'project_skills' + AND constraint_name = 'fk_project_skills_skill_id' + ) THEN + ALTER TABLE public.project_skills + ADD CONSTRAINT fk_project_skills_skill_id + FOREIGN KEY (skill_id) + REFERENCES public.agent_skills (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'project_guardrails' + AND constraint_name = 'fk_project_guardrails_guardrail_id' + ) THEN + ALTER TABLE public.project_guardrails + ADD CONSTRAINT fk_project_guardrails_guardrail_id + FOREIGN KEY (guardrail_id) + REFERENCES public.agent_guardrails (id) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.table_constraints + WHERE table_schema = 'public' + AND table_name = 'project_guardrails' + AND constraint_name = 'fk_project_guardrails_project_id' + ) THEN + ALTER TABLE public.project_guardrails + ADD CONSTRAINT fk_project_guardrails_project_id + FOREIGN KEY (project_id) + REFERENCES public.projects (guid) + ON DELETE NO ACTION + ON UPDATE NO ACTION; + END IF; +END; +$$;-- Set sequence values for schema: public +DO $$ +DECLARE + m_cnt bigint; +BEGIN + IF EXISTS ( + SELECT 1 FROM pg_class c + INNER JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relname = 'identity_thoughts_id' + AND n.nspname = 'public' + AND c.relkind = 'S' + ) THEN + SELECT COALESCE(MAX(id), 0) + 1 + FROM public.thoughts + INTO m_cnt; + + PERFORM setval('public.identity_thoughts_id'::regclass, m_cnt); + END IF; +END; +$$; +DO $$ +DECLARE + m_cnt bigint; +BEGIN + IF EXISTS ( + SELECT 1 FROM pg_class c + INNER JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relname = 'identity_projects_id' + AND n.nspname = 'public' + AND c.relkind = 'S' + ) THEN + SELECT COALESCE(MAX(id), 0) + 1 + FROM public.projects + INTO m_cnt; + + PERFORM setval('public.identity_projects_id'::regclass, m_cnt); + END IF; +END; +$$; +DO $$ +DECLARE + m_cnt bigint; +BEGIN + IF EXISTS ( + SELECT 1 FROM pg_class c + INNER JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relname = 'identity_embeddings_id' + AND n.nspname = 'public' + AND c.relkind = 'S' + ) THEN + SELECT COALESCE(MAX(id), 0) + 1 + FROM public.embeddings + INTO m_cnt; + + PERFORM setval('public.identity_embeddings_id'::regclass, m_cnt); + END IF; +END; +$$; +DO $$ +DECLARE + m_cnt bigint; +BEGIN + IF EXISTS ( + SELECT 1 FROM pg_class c + INNER JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relname = 'identity_stored_files_id' + AND n.nspname = 'public' + AND c.relkind = 'S' + ) THEN + SELECT COALESCE(MAX(id), 0) + 1 + FROM public.stored_files + INTO m_cnt; + + PERFORM setval('public.identity_stored_files_id'::regclass, m_cnt); + END IF; +END; +$$; +DO $$ +DECLARE + m_cnt bigint; +BEGIN + IF EXISTS ( + SELECT 1 FROM pg_class c + INNER JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relname = 'identity_tool_annotations_id' + AND n.nspname = 'public' + AND c.relkind = 'S' + ) THEN + SELECT COALESCE(MAX(id), 0) + 1 + FROM public.tool_annotations + INTO m_cnt; + + PERFORM setval('public.identity_tool_annotations_id'::regclass, m_cnt); + END IF; +END; +$$; +-- Comments for schema: public + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schema/README.md b/schema/README.md new file mode 100644 index 0000000..872af6a --- /dev/null +++ b/schema/README.md @@ -0,0 +1,35 @@ +# Schema workflow + +The `schema/*.dbml` files are the database schema source of truth. + +## Generate SQL migrations + +Run: + +```bash +make generate-migrations +``` + +This uses `relspec` to convert the DBML files into PostgreSQL SQL and writes the generated schema migration to: + +- `migrations/020_generated_schema.sql` + +## Check schema drift + +Run: + +```bash +make check-schema-drift +``` + +This regenerates the SQL from `schema/*.dbml` and compares it with `migrations/020_generated_schema.sql`. +If the generated output differs, the command fails so CI can catch schema drift. + +## Workflow + +1. Update the DBML files in `schema/` +2. Run `make generate-migrations` +3. Review the generated SQL +4. Commit both the DBML changes and the generated migration + +Existing handwritten migrations stay in place. Going forward, update the DBML first and regenerate the SQL from there. diff --git a/schema/calendar.dbml b/schema/calendar.dbml new file mode 100644 index 0000000..c421470 --- /dev/null +++ b/schema/calendar.dbml @@ -0,0 +1,44 @@ +Table family_members { + id uuid [pk, default: `gen_random_uuid()`] + name text [not null] + relationship text + birth_date date + notes text + created_at timestamptz [not null, default: `now()`] +} + +Table activities { + id uuid [pk, default: `gen_random_uuid()`] + family_member_id uuid [ref: > family_members.id] + title text [not null] + activity_type text + day_of_week text + start_time time + end_time time + start_date date + end_date date + location text + notes text + created_at timestamptz [not null, default: `now()`] + + indexes { + day_of_week + family_member_id + (start_date, end_date) + } +} + +Table important_dates { + id uuid [pk, default: `gen_random_uuid()`] + family_member_id uuid [ref: > family_members.id] + title text [not null] + date_value date [not null] + recurring_yearly boolean [not null, default: false] + reminder_days_before int [not null, default: 7] + notes text + created_at timestamptz [not null, default: `now()`] + + indexes { + date_value + } +} diff --git a/schema/core.dbml b/schema/core.dbml new file mode 100644 index 0000000..38c51d0 --- /dev/null +++ b/schema/core.dbml @@ -0,0 +1,48 @@ +Table thoughts { + id bigserial [pk] + guid uuid [unique, not null, default: `gen_random_uuid()`] + content text [not null] + metadata jsonb [default: `'{}'::jsonb`] + created_at timestamptz [default: `now()`] + updated_at timestamptz [default: `now()`] + project_id uuid [ref: > projects.guid] + archived_at timestamptz +} + +Table projects { + id bigserial [pk] + guid uuid [unique, not null, default: `gen_random_uuid()`] + name text [unique, not null] + description text + created_at timestamptz [default: `now()`] + last_active_at timestamptz [default: `now()`] +} + +Table thought_links { + from_id bigint [not null, ref: > thoughts.id] + to_id bigint [not null, ref: > thoughts.id] + relation text [not null] + created_at timestamptz [default: `now()`] + + indexes { + (from_id, to_id, relation) [pk] + from_id + to_id + } +} + +Table embeddings { + id bigserial [pk] + guid uuid [unique, not null, default: `gen_random_uuid()`] + thought_id uuid [not null, ref: > thoughts.guid] + model text [not null] + dim int [not null] + embedding vector [not null] + created_at timestamptz [default: `now()`] + updated_at timestamptz [default: `now()`] + + indexes { + (thought_id, model) [unique] + thought_id + } +} diff --git a/schema/crm.dbml b/schema/crm.dbml new file mode 100644 index 0000000..bd10521 --- /dev/null +++ b/schema/crm.dbml @@ -0,0 +1,53 @@ +Table professional_contacts { + id uuid [pk, default: `gen_random_uuid()`] + name text [not null] + company text + title text + email text + phone text + linkedin_url text + how_we_met text + tags "text[]" [not null, default: `'{}'`] + notes text + last_contacted timestamptz + follow_up_date date + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] + + indexes { + last_contacted + follow_up_date + } +} + +Table contact_interactions { + id uuid [pk, default: `gen_random_uuid()`] + contact_id uuid [not null, ref: > professional_contacts.id] + interaction_type text [not null] + occurred_at timestamptz [not null, default: `now()`] + summary text [not null] + follow_up_needed boolean [not null, default: false] + follow_up_notes text + created_at timestamptz [not null, default: `now()`] + + indexes { + (contact_id, occurred_at) + } +} + +Table opportunities { + id uuid [pk, default: `gen_random_uuid()`] + contact_id uuid [ref: > professional_contacts.id] + title text [not null] + description text + stage text [not null, default: 'identified'] + value "decimal(12,2)" + expected_close_date date + notes text + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] + + indexes { + stage + } +} diff --git a/schema/files.dbml b/schema/files.dbml new file mode 100644 index 0000000..c51b0a7 --- /dev/null +++ b/schema/files.dbml @@ -0,0 +1,21 @@ +Table stored_files { + id bigserial [pk] + guid uuid [unique, not null, default: `gen_random_uuid()`] + thought_id uuid [ref: > thoughts.guid] + project_id uuid [ref: > projects.guid] + name text [not null] + media_type text [not null] + kind text [not null, default: 'file'] + encoding text [not null, default: 'base64'] + size_bytes bigint [not null] + sha256 text [not null] + content bytea [not null] + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] + + indexes { + thought_id + project_id + sha256 + } +} diff --git a/schema/household.dbml b/schema/household.dbml new file mode 100644 index 0000000..70f1ec7 --- /dev/null +++ b/schema/household.dbml @@ -0,0 +1,31 @@ +Table household_items { + id uuid [pk, default: `gen_random_uuid()`] + name text [not null] + category text + location text + details jsonb [not null, default: `'{}'`] + notes text + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] + + indexes { + category + } +} + +Table household_vendors { + id uuid [pk, default: `gen_random_uuid()`] + name text [not null] + service_type text + phone text + email text + website text + notes text + rating int + last_used date + created_at timestamptz [not null, default: `now()`] + + indexes { + service_type + } +} diff --git a/schema/maintenance.dbml b/schema/maintenance.dbml new file mode 100644 index 0000000..4ccf738 --- /dev/null +++ b/schema/maintenance.dbml @@ -0,0 +1,30 @@ +Table maintenance_tasks { + id uuid [pk, default: `gen_random_uuid()`] + name text [not null] + category text + frequency_days int + last_completed timestamptz + next_due timestamptz + priority text [not null, default: 'medium'] + notes text + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] + + indexes { + next_due + } +} + +Table maintenance_logs { + id uuid [pk, default: `gen_random_uuid()`] + task_id uuid [not null, ref: > maintenance_tasks.id] + completed_at timestamptz [not null, default: `now()`] + performed_by text + cost "decimal(10,2)" + notes text + next_action text + + indexes { + (task_id, completed_at) + } +} diff --git a/schema/meals.dbml b/schema/meals.dbml new file mode 100644 index 0000000..2320b05 --- /dev/null +++ b/schema/meals.dbml @@ -0,0 +1,49 @@ +Table recipes { + id uuid [pk, default: `gen_random_uuid()`] + name text [not null] + cuisine text + prep_time_minutes int + cook_time_minutes int + servings int + ingredients jsonb [not null, default: `'[]'`] + instructions jsonb [not null, default: `'[]'`] + tags "text[]" [not null, default: `'{}'`] + rating int + notes text + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] + + indexes { + cuisine + tags + } +} + +Table meal_plans { + id uuid [pk, default: `gen_random_uuid()`] + week_start date [not null] + day_of_week text [not null] + meal_type text [not null] + recipe_id uuid [ref: > recipes.id] + custom_meal text + servings int + notes text + created_at timestamptz [not null, default: `now()`] + + indexes { + week_start + } +} + +Table shopping_lists { + id uuid [pk, default: `gen_random_uuid()`] + week_start date [unique, not null] + items jsonb [not null, default: `'[]'`] + notes text + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] + + indexes { + week_start + } +} diff --git a/schema/meta.dbml b/schema/meta.dbml new file mode 100644 index 0000000..c59afce --- /dev/null +++ b/schema/meta.dbml @@ -0,0 +1,29 @@ +Table chat_histories { + id uuid [pk, default: `gen_random_uuid()`] + session_id text [not null] + title text + channel text + agent_id text + project_id uuid [ref: > projects.guid] + messages jsonb [not null, default: `'[]'`] + summary text + metadata jsonb [not null, default: `'{}'`] + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] + + indexes { + session_id + project_id + channel + agent_id + created_at + } +} + +Table tool_annotations { + id bigserial [pk] + tool_name text [unique, not null] + notes text [not null, default: ''] + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] +} diff --git a/schema/skills.dbml b/schema/skills.dbml new file mode 100644 index 0000000..cc36789 --- /dev/null +++ b/schema/skills.dbml @@ -0,0 +1,42 @@ +Table agent_skills { + id uuid [pk, default: `gen_random_uuid()`] + name text [unique, not null] + description text [not null, default: ''] + content text [not null] + tags "text[]" [not null, default: `'{}'`] + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] +} + +Table agent_guardrails { + id uuid [pk, default: `gen_random_uuid()`] + name text [unique, not null] + description text [not null, default: ''] + content text [not null] + severity text [not null, default: 'medium'] + tags "text[]" [not null, default: `'{}'`] + created_at timestamptz [not null, default: `now()`] + updated_at timestamptz [not null, default: `now()`] +} + +Table project_skills { + project_id uuid [not null, ref: > projects.guid] + skill_id uuid [not null, ref: > agent_skills.id] + created_at timestamptz [not null, default: `now()`] + + indexes { + (project_id, skill_id) [pk] + project_id + } +} + +Table project_guardrails { + project_id uuid [not null, ref: > projects.guid] + guardrail_id uuid [not null, ref: > agent_guardrails.id] + created_at timestamptz [not null, default: `now()`] + + indexes { + (project_id, guardrail_id) [pk] + project_id + } +} -- 2.49.1 From 6c6b49b45c704148e5d64f5f2d985f80f3b12ab7 Mon Sep 17 00:00:00 2001 From: SGC Date: Sat, 4 Apr 2026 15:13:50 +0200 Subject: [PATCH 2/2] fix: add cross-file Ref declarations for relspecgo merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add explicit Ref: blocks at bottom of each DBML file for cross-file FK relationships - files.dbml: stored_files → thoughts, projects - skills.dbml: project_skills/project_guardrails → projects - meta.dbml: chat_histories → projects - Update Makefile to use the relspec merge workflow where applicable - Regenerate 020_generated_schema.sql with proper cross-file constraints Addresses review comment on PR #20 --- Makefile | 9 +++++++-- schema/files.dbml | 4 ++++ schema/meta.dbml | 3 +++ schema/skills.dbml | 4 ++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7d71d83..682f85a 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ COMMIT_SHA ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) BUILD_DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) RELSPEC ?= $(shell command -v relspec 2>/dev/null || echo $(HOME)/go/bin/relspec) SCHEMA_FILES := $(sort $(wildcard schema/*.dbml)) +MERGE_TARGET_TMP := $(CURDIR)/.cache/schema.merge-target.dbml GENERATED_SCHEMA_MIGRATION := migrations/020_generated_schema.sql LDFLAGS := -s -w \ -X $(BUILDINFO_PKG).Version=$(VERSION_TAG) \ @@ -57,15 +58,19 @@ clean: generate-migrations: @test -n "$(SCHEMA_FILES)" || (echo "No DBML schema files found in schema/" >&2; exit 1) @command -v $(RELSPEC) >/dev/null 2>&1 || (echo "relspec not found; install git.warky.dev/wdevs/relspecgo/cmd/relspec@latest" >&2; exit 1) + @mkdir -p $(dir $(MERGE_TARGET_TMP)) + @: > $(MERGE_TARGET_TMP) @schema_list=$$(printf '%s\n' $(SCHEMA_FILES) | paste -sd, -); \ - $(RELSPEC) convert --from dbml --from-list "$$schema_list" --to pgsql --to-path $(GENERATED_SCHEMA_MIGRATION) + $(RELSPEC) merge --target dbml --target-path $(MERGE_TARGET_TMP) --source dbml --from-list "$$schema_list" --output pgsql --output-path $(GENERATED_SCHEMA_MIGRATION) check-schema-drift: @test -f $(GENERATED_SCHEMA_MIGRATION) || (echo "$(GENERATED_SCHEMA_MIGRATION) is missing; run make generate-migrations" >&2; exit 1) @command -v $(RELSPEC) >/dev/null 2>&1 || (echo "relspec not found; install git.warky.dev/wdevs/relspecgo/cmd/relspec@latest" >&2; exit 1) + @mkdir -p $(dir $(MERGE_TARGET_TMP)) @tmpfile=$$(mktemp); \ + : > $(MERGE_TARGET_TMP); \ schema_list=$$(printf '%s\n' $(SCHEMA_FILES) | paste -sd, -); \ - $(RELSPEC) convert --from dbml --from-list "$$schema_list" --to pgsql --to-path $$tmpfile; \ + $(RELSPEC) merge --target dbml --target-path $(MERGE_TARGET_TMP) --source dbml --from-list "$$schema_list" --output pgsql --output-path $$tmpfile; \ if ! cmp -s $$tmpfile $(GENERATED_SCHEMA_MIGRATION); then \ echo "Schema drift detected between schema/*.dbml and $(GENERATED_SCHEMA_MIGRATION)" >&2; \ diff -u $(GENERATED_SCHEMA_MIGRATION) $$tmpfile || true; \ diff --git a/schema/files.dbml b/schema/files.dbml index c51b0a7..74062fd 100644 --- a/schema/files.dbml +++ b/schema/files.dbml @@ -19,3 +19,7 @@ Table stored_files { sha256 } } + +// Cross-file refs (for relspecgo merge) +Ref: stored_files.thought_id > thoughts.guid [delete: set null] +Ref: stored_files.project_id > projects.guid [delete: set null] diff --git a/schema/meta.dbml b/schema/meta.dbml index c59afce..792ccde 100644 --- a/schema/meta.dbml +++ b/schema/meta.dbml @@ -27,3 +27,6 @@ Table tool_annotations { created_at timestamptz [not null, default: `now()`] updated_at timestamptz [not null, default: `now()`] } + +// Cross-file refs (for relspecgo merge) +Ref: chat_histories.project_id > projects.guid [delete: set null] diff --git a/schema/skills.dbml b/schema/skills.dbml index cc36789..0ea4c3f 100644 --- a/schema/skills.dbml +++ b/schema/skills.dbml @@ -40,3 +40,7 @@ Table project_guardrails { project_id } } + +// Cross-file refs (for relspecgo merge) +Ref: project_skills.project_id > projects.guid [delete: cascade] +Ref: project_guardrails.project_id > projects.guid [delete: cascade] -- 2.49.1