"""add account_id to script_builder_sessions, script_templates, script_generations Revision ID: 78fc200abac1 Revises: 7f136778f5a8 Create Date: 2026-04-09 00:00:00.000000 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa revision: str = '78fc200abac1' down_revision: Union[str, None] = '7f136778f5a8' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None PLATFORM_ACCOUNT_ID = '00000000-0000-0000-0000-000000000001' def upgrade() -> None: # Ensure the platform sentinel account exists before any fallback assignments. # Migration 3a40fe11b427 also inserts this with ON CONFLICT DO NOTHING — safe. op.execute(f""" INSERT INTO accounts (id, name, display_code, created_at, updated_at) VALUES ( '{PLATFORM_ACCOUNT_ID}', 'ResolutionFlow Platform', 'PLATFORM', NOW(), NOW() ) ON CONFLICT (id) DO NOTHING """) for table in ('script_builder_sessions', 'script_templates', 'script_generations'): op.add_column(table, sa.Column('account_id', sa.UUID(), nullable=True)) op.create_foreign_key( f'fk_{table}_account_id', table, 'accounts', ['account_id'], ['id'], ondelete='CASCADE', ) # script_builder_sessions: user_id → users.account_id op.execute(""" UPDATE script_builder_sessions sbs SET account_id = u.account_id FROM users u WHERE sbs.user_id = u.id AND sbs.account_id IS NULL """) # script_templates: created_by → users.account_id (nullable created_by) op.execute(""" UPDATE script_templates st SET account_id = u.account_id FROM users u WHERE st.created_by = u.id AND st.account_id IS NULL """) # Fallback: team_id → team admin user op.execute(""" UPDATE script_templates st SET account_id = u.account_id FROM users u WHERE u.team_id = st.team_id AND u.is_team_admin = TRUE AND u.account_id IS NOT NULL AND st.account_id IS NULL """) # Final fallback: platform-seeded templates with NULL team_id AND NULL created_by # (e.g. the 6 AD templates inserted by migration 057) → platform sentinel account op.execute(f""" UPDATE script_templates SET account_id = '{PLATFORM_ACCOUNT_ID}' WHERE account_id IS NULL """) # script_generations: user_id → users.account_id op.execute(""" UPDATE script_generations sg SET account_id = u.account_id FROM users u WHERE sg.user_id = u.id AND sg.account_id IS NULL """) # VERIFY for table in ('script_builder_sessions', 'script_templates', 'script_generations'): result = op.get_bind().execute( sa.text(f"SELECT COUNT(*) FROM {table} WHERE account_id IS NULL") ) count = result.scalar() if count > 0: raise RuntimeError(f"ROLLBACK: {count} NULL account_id rows in {table}.") for table in ('script_builder_sessions', 'script_templates', 'script_generations'): op.alter_column(table, 'account_id', nullable=False) op.create_index(f'ix_{table}_account_id', table, ['account_id']) def downgrade() -> None: for table in ('script_builder_sessions', 'script_templates', 'script_generations'): op.drop_index(f'ix_{table}_account_id', table_name=table) op.drop_constraint(f'fk_{table}_account_id', table, type_='foreignkey') op.drop_column(table, 'account_id')