-- Backfill ON DELETE clauses on FKs from 0001-0006 and 0026. The pattern -- (CASCADE for tenant-scoped children, SET NULL for historical references) -- was learned after v1 shipped and applied consistently from 0010 onward; -- this migration retrofits the earlier tables so DELETEs succeed instead of -- either failing with a FK violation or leaving orphans. -- -- Two FKs stay as NO ACTION deliberately: -- * audit_log.workspace_id — audit is append-only and must outlive any -- workspace delete (the trigger blocks UPDATE/DELETE on audit rows). -- * thread_messages.from_agent — history stays attributable via the -- agents.deleted_at soft-delete already in schema. -- -- Two FKs stay NOT NULL and become explicit RESTRICT rather than SET NULL: -- * agents.managed_by — the Rust domain type is `UserId` (not Option). -- Making it nullable would ripple through cm-domain/cm-db/cm-api/tests. -- RESTRICT preserves current behavior (delete blocked) but documents it. -- * installed_skills.installed_by — same reasoning. -- To hard-delete a user in either case, reassign or hard-delete the -- dependent rows first — which matches real org-ownership semantics. -- ---- 0001_init: workspaces cascade ----------------------------------------- ALTER TABLE users DROP CONSTRAINT IF EXISTS users_workspace_id_fkey, ADD CONSTRAINT users_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE; ALTER TABLE agents DROP CONSTRAINT IF EXISTS agents_workspace_id_fkey, ADD CONSTRAINT agents_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE; ALTER TABLE agents DROP CONSTRAINT IF EXISTS agents_managed_by_fkey, ADD CONSTRAINT agents_managed_by_fkey FOREIGN KEY (managed_by) REFERENCES users (id) ON DELETE RESTRICT; ALTER TABLE sessions DROP CONSTRAINT IF EXISTS sessions_agent_id_fkey, ADD CONSTRAINT sessions_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS sessions_workspace_id_fkey, ADD CONSTRAINT sessions_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE; ALTER TABLE messages DROP CONSTRAINT IF EXISTS messages_session_id_fkey, ADD CONSTRAINT messages_session_id_fkey FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE; ALTER TABLE steps DROP CONSTRAINT IF EXISTS steps_message_id_fkey, ADD CONSTRAINT steps_message_id_fkey FOREIGN KEY (message_id) REFERENCES messages (id) ON DELETE CASCADE; ALTER TABLE agent_runs DROP CONSTRAINT IF EXISTS agent_runs_session_id_fkey, ADD CONSTRAINT agent_runs_session_id_fkey FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE; ALTER TABLE approvals DROP CONSTRAINT IF EXISTS approvals_workspace_id_fkey, ADD CONSTRAINT approvals_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS approvals_run_id_fkey, ADD CONSTRAINT approvals_run_id_fkey FOREIGN KEY (run_id) REFERENCES agent_runs (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS approvals_requested_by_agent_fkey, ADD CONSTRAINT approvals_requested_by_agent_fkey FOREIGN KEY (requested_by_agent) REFERENCES agents (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS approvals_decided_by_fkey, ADD CONSTRAINT approvals_decided_by_fkey FOREIGN KEY (decided_by) REFERENCES users (id) ON DELETE SET NULL; -- Single-use execution grants: deleting the approval should drop the grant. ALTER TABLE execution_grants DROP CONSTRAINT IF EXISTS execution_grants_approval_id_fkey, ADD CONSTRAINT execution_grants_approval_id_fkey FOREIGN KEY (approval_id) REFERENCES approvals (id) ON DELETE CASCADE; -- audit_log.workspace_id — intentionally left as NO ACTION. ALTER TABLE skills DROP CONSTRAINT IF EXISTS skills_workspace_id_fkey, ADD CONSTRAINT skills_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE; ALTER TABLE installed_skills DROP CONSTRAINT IF EXISTS installed_skills_skill_id_fkey, ADD CONSTRAINT installed_skills_skill_id_fkey FOREIGN KEY (skill_id) REFERENCES skills (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS installed_skills_installed_by_fkey, ADD CONSTRAINT installed_skills_installed_by_fkey FOREIGN KEY (installed_by) REFERENCES users (id) ON DELETE RESTRICT; ALTER TABLE secrets DROP CONSTRAINT IF EXISTS secrets_workspace_id_fkey, ADD CONSTRAINT secrets_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE; ALTER TABLE app_connections DROP CONSTRAINT IF EXISTS app_connections_workspace_id_fkey, ADD CONSTRAINT app_connections_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS app_connections_agent_id_fkey, ADD CONSTRAINT app_connections_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS app_connections_secret_ref_fkey, ADD CONSTRAINT app_connections_secret_ref_fkey FOREIGN KEY (secret_ref) REFERENCES secrets (id) ON DELETE SET NULL; ALTER TABLE threads DROP CONSTRAINT IF EXISTS threads_workspace_id_fkey, ADD CONSTRAINT threads_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE; ALTER TABLE thread_participants DROP CONSTRAINT IF EXISTS thread_participants_agent_id_fkey, ADD CONSTRAINT thread_participants_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents (id) ON DELETE CASCADE; -- thread_messages.thread_id → CASCADE; from_agent stays NO ACTION. ALTER TABLE thread_messages DROP CONSTRAINT IF EXISTS thread_messages_thread_id_fkey, ADD CONSTRAINT thread_messages_thread_id_fkey FOREIGN KEY (thread_id) REFERENCES threads (id) ON DELETE CASCADE; ALTER TABLE file_nodes DROP CONSTRAINT IF EXISTS file_nodes_workspace_id_fkey, ADD CONSTRAINT file_nodes_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS file_nodes_agent_id_fkey, ADD CONSTRAINT file_nodes_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents (id) ON DELETE CASCADE; ALTER TABLE credit_lots DROP CONSTRAINT IF EXISTS credit_lots_workspace_id_fkey, ADD CONSTRAINT credit_lots_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE; ALTER TABLE usage_events DROP CONSTRAINT IF EXISTS usage_events_workspace_id_fkey, ADD CONSTRAINT usage_events_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS usage_events_agent_id_fkey, ADD CONSTRAINT usage_events_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents (id) ON DELETE SET NULL, DROP CONSTRAINT IF EXISTS usage_events_run_id_fkey, ADD CONSTRAINT usage_events_run_id_fkey FOREIGN KEY (run_id) REFERENCES agent_runs (id) ON DELETE SET NULL; -- ---- 0002_auth_sessions --------------------------------------------------- ALTER TABLE auth_sessions DROP CONSTRAINT IF EXISTS auth_sessions_user_id_fkey, ADD CONSTRAINT auth_sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE; -- ---- 0003_run_events ------------------------------------------------------ ALTER TABLE run_events DROP CONSTRAINT IF EXISTS run_events_run_id_fkey, ADD CONSTRAINT run_events_run_id_fkey FOREIGN KEY (run_id) REFERENCES agent_runs (id) ON DELETE CASCADE; -- ---- 0004_outbox ---------------------------------------------------------- ALTER TABLE outbox DROP CONSTRAINT IF EXISTS outbox_workspace_id_fkey, ADD CONSTRAINT outbox_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS outbox_agent_id_fkey, ADD CONSTRAINT outbox_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents (id) ON DELETE CASCADE; -- ---- 0005_oauth_states ---------------------------------------------------- ALTER TABLE oauth_states DROP CONSTRAINT IF EXISTS oauth_states_workspace_id_fkey, ADD CONSTRAINT oauth_states_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS oauth_states_user_id_fkey, ADD CONSTRAINT oauth_states_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, DROP CONSTRAINT IF EXISTS oauth_states_agent_id_fkey, ADD CONSTRAINT oauth_states_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents (id) ON DELETE CASCADE; -- ---- 0006_promo_codes ----------------------------------------------------- ALTER TABLE promo_codes DROP CONSTRAINT IF EXISTS promo_codes_redeemed_by_fkey, ADD CONSTRAINT promo_codes_redeemed_by_fkey FOREIGN KEY (redeemed_by) REFERENCES workspaces (id) ON DELETE SET NULL; -- ---- 0026_group_rooms ----------------------------------------------------- -- The threads.created_by / thread_participants.added_by columns were added -- nullable by 0026, so SET NULL is legal. ALTER TABLE threads DROP CONSTRAINT IF EXISTS threads_created_by_fkey, ADD CONSTRAINT threads_created_by_fkey FOREIGN KEY (created_by) REFERENCES agents (id) ON DELETE SET NULL; ALTER TABLE thread_participants DROP CONSTRAINT IF EXISTS thread_participants_added_by_fkey, ADD CONSTRAINT thread_participants_added_by_fkey FOREIGN KEY (added_by) REFERENCES agents (id) ON DELETE SET NULL;