198 lines
8.5 KiB
SQL
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
|
|
)
|
|
)
|
|
);
|