"""create_l1_walk_sessions Revision ID: b3358ba0e48c Revises: a1e6a018af02 Create Date: 2026-05-28 16:33:52.120027 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. revision: str = 'b3358ba0e48c' down_revision: Union[str, None] = 'a1e6a018af02' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None _NULL_UUID = "00000000-0000-0000-0000-000000000000" _CURRENT_ACCOUNT = ( f"COALESCE(NULLIF(current_setting('app.current_account_id', TRUE), ''), " f"'{_NULL_UUID}')::uuid" ) def upgrade() -> None: op.create_table( 'l1_walk_sessions', sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('account_id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('created_by_user_id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('acting_as', sa.String(30), nullable=True), sa.Column('ticket_id', sa.String(64), nullable=False), sa.Column('ticket_kind', sa.String(10), nullable=False), sa.Column('session_kind', sa.String(20), nullable=False), sa.Column('flow_id', postgresql.UUID(as_uuid=True), nullable=True), sa.Column('flow_proposal_id', postgresql.UUID(as_uuid=True), nullable=True), sa.Column('current_node_id', sa.String(100), nullable=True), sa.Column('walked_path', postgresql.JSONB(), nullable=False, server_default=sa.text("'[]'::jsonb")), sa.Column('walk_notes', postgresql.JSONB(), nullable=False, server_default=sa.text("'[]'::jsonb")), sa.Column('status', sa.String(20), nullable=False, server_default='active'), sa.Column('resolution_notes', sa.Text(), nullable=True), sa.Column('helpful', sa.Boolean(), nullable=True), sa.Column('escalation_reason', sa.Text(), nullable=True), sa.Column('escalation_reason_category', sa.String(30), nullable=True), sa.Column('started_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')), sa.Column('last_step_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')), sa.Column('resolved_at', sa.DateTime(timezone=True), nullable=True), sa.PrimaryKeyConstraint('id'), sa.ForeignKeyConstraint(['account_id'], ['accounts.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['created_by_user_id'], ['users.id'], ondelete='RESTRICT'), sa.ForeignKeyConstraint(['flow_id'], ['trees.id'], ondelete='SET NULL'), sa.ForeignKeyConstraint(['flow_proposal_id'], ['flow_proposals.id'], ondelete='SET NULL'), sa.CheckConstraint( "ticket_kind IN ('psa', 'internal')", name='ck_l1_walk_sessions_ticket_kind', ), sa.CheckConstraint( "session_kind IN ('flow', 'proposal', 'adhoc')", name='ck_l1_walk_sessions_session_kind', ), sa.CheckConstraint( "status IN ('active', 'resolved', 'escalated', 'abandoned')", name='ck_l1_walk_sessions_status', ), sa.CheckConstraint( "(session_kind = 'flow' AND flow_id IS NOT NULL AND flow_proposal_id IS NULL) " "OR (session_kind = 'proposal' AND flow_proposal_id IS NOT NULL AND flow_id IS NULL) " "OR (session_kind = 'adhoc' AND flow_id IS NULL AND flow_proposal_id IS NULL)", name='ck_l1_walk_sessions_target_consistency', ), ) op.create_index('ix_l1_walk_sessions_account_id', 'l1_walk_sessions', ['account_id']) op.create_index('ix_l1_walk_sessions_created_by_user_id', 'l1_walk_sessions', ['created_by_user_id']) op.create_index('ix_l1_walk_sessions_status', 'l1_walk_sessions', ['status']) op.create_index('ix_l1_walk_sessions_last_step_at', 'l1_walk_sessions', ['last_step_at']) op.execute("ALTER TABLE l1_walk_sessions ENABLE ROW LEVEL SECURITY") op.execute("ALTER TABLE l1_walk_sessions FORCE ROW LEVEL SECURITY") op.execute(f""" CREATE POLICY tenant_isolation ON l1_walk_sessions USING (account_id = {_CURRENT_ACCOUNT}) WITH CHECK (account_id = {_CURRENT_ACCOUNT}) """) def downgrade() -> None: op.execute("DROP POLICY IF EXISTS tenant_isolation ON l1_walk_sessions") op.execute("ALTER TABLE l1_walk_sessions DISABLE ROW LEVEL SECURITY") op.execute("ALTER TABLE l1_walk_sessions NO FORCE ROW LEVEL SECURITY") op.drop_index('ix_l1_walk_sessions_last_step_at', 'l1_walk_sessions') op.drop_index('ix_l1_walk_sessions_status', 'l1_walk_sessions') op.drop_index('ix_l1_walk_sessions_created_by_user_id', 'l1_walk_sessions') op.drop_index('ix_l1_walk_sessions_account_id', 'l1_walk_sessions') op.drop_table('l1_walk_sessions')