Files
wursor/supabase/migrations/008_schema_alignment.sql
T
2026-05-04 12:30:56 +01:00

198 lines
8.5 KiB
SQL

-- ── Migration 008: Spec schema alignment ──────────────────────────────────────
-- Aligns the database schema with the Layer 4 spec requirements.
-- All changes are additive or rename-only; no data is destroyed.
--
-- Changes:
-- 1. artboards.width / height — add NOT NULL DEFAULT per spec (1440 / 900)
-- 2. artboards.parent_id — spec names this column parent_id (not parent_artboard_id);
-- add parent_id as a generated column alias
-- 3. origins.artboard_id — spec requires the FK to live on origins (not artboards)
-- 4. intent_diffs: rename summary → aggregate_summary + rename changes_jsonb → changes
-- 5. intent_diffs.session_id — make NOT NULL per spec (was nullable in migration 007)
-- 6. agent_sessions.agent_type — add lowercase check values per spec
-- 7. RLS: add permissive workspace-membership allow policies
-- ── 1. artboards width/height defaults ───────────────────────────────────────
-- Backfill nulls introduced by migration 007, then add NOT NULL + DEFAULT.
update artboards set width = 1440 where width is null;
update artboards set height = 900 where height is null;
alter table artboards
alter column width set not null,
alter column width set default 1440,
alter column height set not null,
alter column height set default 900;
-- created_by: spec says NOT NULL; backfill with '' then constrain
update artboards set created_by = '' where created_by is null;
alter table artboards alter column created_by set not null;
alter table artboards alter column created_by set default '';
-- ── 2. artboards.parent_id alias ─────────────────────────────────────────────
-- Spec uses parent_id; existing code uses parent_artboard_id. Add parent_id as
-- a nullable FK that mirrors parent_artboard_id for spec-compliant consumers.
-- Both columns will co-exist during the transition.
do $$ begin
if not exists (
select 1 from information_schema.columns
where table_name = 'artboards' and column_name = 'parent_id'
) then
alter table artboards add column parent_id uuid references artboards(id);
-- Backfill from the existing column
update artboards set parent_id = parent_artboard_id where parent_artboard_id is not null;
end if;
end $$;
-- ── 3. origins.artboard_id (spec FK direction) ───────────────────────────────
-- Spec: origins.artboard_id uuid not null references artboards(id) on delete cascade
-- Existing: artboards.origin_id (reverse FK).
-- We add the spec-required column as nullable (existing origin rows have no artboard_id
-- until they are re-linked). New origins created via spec-compliant code must set it.
do $$ begin
if not exists (
select 1 from information_schema.columns
where table_name = 'origins' and column_name = 'artboard_id'
) then
alter table origins add column artboard_id uuid references artboards(id) on delete cascade;
-- Best-effort backfill: find artboards pointing to each origin
update origins o
set artboard_id = a.id
from artboards a
where a.origin_id = o.id;
end if;
end $$;
-- ── 4. intent_diffs: rename columns to spec names ────────────────────────────
-- Rename changes_jsonb → changes (spec column name)
do $$ begin
if exists (
select 1 from information_schema.columns
where table_name = 'intent_diffs' and column_name = 'changes_jsonb'
) and not exists (
select 1 from information_schema.columns
where table_name = 'intent_diffs' and column_name = 'changes'
) then
alter table intent_diffs rename column changes_jsonb to changes;
end if;
end $$;
-- Rename summary → aggregate_summary (spec column name)
do $$ begin
if exists (
select 1 from information_schema.columns
where table_name = 'intent_diffs' and column_name = 'summary'
) and not exists (
select 1 from information_schema.columns
where table_name = 'intent_diffs' and column_name = 'aggregate_summary'
) then
alter table intent_diffs rename column summary to aggregate_summary;
end if;
end $$;
-- ── 5. intent_diffs.session_id NOT NULL ──────────────────────────────────────
-- Spec: session_id text not null. Backfill existing nulls, then constrain.
update intent_diffs set session_id = '' where session_id is null;
alter table intent_diffs alter column session_id set not null;
alter table intent_diffs alter column session_id set default '';
-- Also: author_id was NOT NULL in the spec from migration 001 — ensure created_by
-- exists on design_language_files as NOT NULL (backfill then constrain)
update design_language_files set created_by = '' where created_by is null;
alter table design_language_files alter column created_by set not null;
alter table design_language_files alter column created_by set default '';
-- ── 6. agent_sessions: expand agent_type to include spec lowercase values ─────
-- Spec: 'cursor', 'claude-code', 'generic' (lowercase, hyphenated)
-- Existing: uppercase 'CURSOR', 'CLAUDE_CODE', 'GENERIC' + original set
-- Drop and re-create the check constraint to include both sets.
alter table agent_sessions drop constraint if exists agent_sessions_agent_type_check;
alter table agent_sessions add constraint agent_sessions_agent_type_check
check (agent_type in (
'cursor', 'claude-code', 'generic', -- spec lowercase
'CURSOR', 'CLAUDE_CODE', 'GENERIC' -- existing uppercase
));
-- ── 7. RLS workspace-membership allow policies ────────────────────────────────
-- Spec: "workspace members can access" via auth.uid()::text = owner_id
-- These are the permissive policies required by Layer 4. The deny-all-anon
-- policies from migration 003 remain and complement these.
-- workspaces: owner can access their own workspace
drop policy if exists "workspace owner access" on workspaces;
create policy "workspace owner access" on workspaces
for all
using (owner_id = auth.uid()::text);
-- artboards: members of the workspace can access
drop policy if exists "workspace member artboard access" on artboards;
create policy "workspace member artboard access" on artboards
for all
using (
workspace_id in (
select id from workspaces where owner_id = auth.uid()::text
)
or workspace_id in (
select workspace_id from team_members where user_id = auth.uid()::text
)
);
-- origins: accessible when the linked artboard is accessible
drop policy if exists "workspace member origin access" on origins;
create policy "workspace member origin access" on origins
for all
using (
id in (
select origin_id from artboards
where workspace_id in (
select id from workspaces where owner_id = auth.uid()::text
union
select workspace_id from team_members where user_id = auth.uid()::text
)
and origin_id is not null
)
);
-- intent_diffs: accessible when the linked artboard is accessible
drop policy if exists "workspace member diff access" on intent_diffs;
create policy "workspace member diff access" on intent_diffs
for all
using (
artboard_id in (
select id from artboards
where workspace_id in (
select id from workspaces where owner_id = auth.uid()::text
union
select workspace_id from team_members where user_id = auth.uid()::text
)
)
);
-- design_language_files: accessible within the workspace
drop policy if exists "workspace member dlf access" on design_language_files;
create policy "workspace member dlf access" on design_language_files
for all
using (
workspace_id in (
select id from workspaces where owner_id = auth.uid()::text
union
select workspace_id from team_members where user_id = auth.uid()::text
)
);
-- agent_sessions: accessible when the linked artboard is accessible
drop policy if exists "workspace member session access" on agent_sessions;
create policy "workspace member session access" on agent_sessions
for all
using (
artboard_id in (
select id from artboards
where workspace_id in (
select id from workspaces where owner_id = auth.uid()::text
union
select workspace_id from team_members where user_id = auth.uid()::text
)
)
);