-- Agency-only scheduling for editorial creativities. -- aprobado + scheduled_at → cron publishes IG/FB; status becomes publicado. -- Idempotent. alter table public.creative_pipeline add column if not exists scheduled_at timestamptz; alter table public.creative_pipeline add column if not exists channels text[] not null default array['instagram','facebook']::text[]; alter table public.creative_pipeline add column if not exists publish_status text not null default 'idle'; do $$ begin if not exists ( select 1 from pg_constraint where conname = 'creative_pipeline_publish_status_chk' ) then alter table public.creative_pipeline add constraint creative_pipeline_publish_status_chk check (publish_status in ('idle', 'queued', 'publishing', 'published', 'failed')); end if; end $$; alter table public.creative_pipeline add column if not exists publish_attempts int not null default 0; alter table public.creative_pipeline add column if not exists published_at timestamptz; alter table public.creative_pipeline add column if not exists meta_ig_media_id text; alter table public.creative_pipeline add column if not exists meta_fb_post_id text; alter table public.creative_pipeline add column if not exists publish_error text; create index if not exists creative_pipeline_due_publish_idx on public.creative_pipeline (tenant_id, scheduled_at) where status = 'aprobado' and publish_status in ('idle', 'queued', 'failed'); create index if not exists creative_pipeline_scheduled_at_idx on public.creative_pipeline (tenant_id, scheduled_at) where scheduled_at is not null; comment on column public.creative_pipeline.scheduled_at is 'Agency-only: when to publish to Meta (IG/FB). Null = not scheduled.'; comment on column public.creative_pipeline.channels is 'Agency-only: subset of {instagram,facebook} to publish at scheduled_at.'; comment on column public.creative_pipeline.publish_status is 'idle|queued|publishing|published|failed — worker state separate from editorial status.';