"""Add budget models

Revision ID: 38c140ad96cf
Revises: 20260311_managed_agent_identity
Create Date: 2026-04-03 22:52:27.071006

"""

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 = "38c140ad96cf"
down_revision: Union[str, None] = "20260311_managed_agent_identity"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    """Upgrade schema."""
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table(
        "budget_policies",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("account_id", sa.UUID(), nullable=False),
        sa.Column("subject_type", sa.String(), nullable=False),
        sa.Column("subject_id", sa.UUID(), nullable=True),
        sa.Column("model_alias", sa.String(), nullable=True),
        sa.Column(
            "period",
            sa.Enum(
                "hourly",
                "daily",
                "weekly",
                "monthly",
                "yearly",
                "all_time",
                name="budgetperiod",
            ),
            nullable=False,
        ),
        sa.Column("hard_limit_usd", sa.Float(), nullable=True),
        sa.Column("soft_limit_usd", sa.Float(), nullable=True),
        sa.Column("notify_on_soft", sa.Boolean(), nullable=False),
        sa.Column("notify_on_hard", sa.Boolean(), nullable=False),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=True,
        ),
        sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
        sa.ForeignKeyConstraint(["account_id"], ["account.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "account_id",
            "subject_type",
            "subject_id",
            "model_alias",
            "period",
            name="uq_budget_policies_subject_model_period",
        ),
    )
    op.create_index(
        op.f("ix_budget_policies_model_alias"),
        "budget_policies",
        ["model_alias"],
        unique=False,
    )
    op.create_index(
        op.f("ix_budget_policies_subject_id"),
        "budget_policies",
        ["subject_id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_budget_policies_subject_type"),
        "budget_policies",
        ["subject_type"],
        unique=False,
    )
    op.create_table(
        "budget_spend_activities",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("account_id", sa.UUID(), nullable=False),
        sa.Column("subject_type", sa.String(), nullable=False),
        sa.Column("subject_id", sa.UUID(), nullable=True),
        sa.Column("model_alias", sa.String(), nullable=True),
        sa.Column(
            "period",
            sa.Enum(
                "hourly",
                "daily",
                "weekly",
                "monthly",
                "yearly",
                "all_time",
                name="budgetperiod",
            ),
            nullable=False,
        ),
        sa.Column("period_start", sa.DateTime(timezone=True), nullable=True),
        sa.Column("spend_usd", sa.Float(), nullable=False),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=True,
        ),
        sa.Column(
            "created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False
        ),
        sa.ForeignKeyConstraint(["account_id"], ["account.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "account_id",
            "subject_type",
            "subject_id",
            "model_alias",
            "period",
            "period_start",
            name="uq_budget_spend_activities_period_start",
        ),
    )
    op.create_index(
        "ix_budget_spend_activities_lookup",
        "budget_spend_activities",
        [
            "account_id",
            "subject_type",
            "subject_id",
            "model_alias",
            "period",
            "period_start",
        ],
        unique=False,
    )
    op.create_index(op.f("ix_ai_model_id"), "ai_model", ["id"], unique=False)
    op.create_index(op.f("ix_api_key_id"), "api_key", ["id"], unique=False)
    op.drop_index(op.f("ix_api_usage_user_ts"), table_name="api_usage")
    op.create_index(op.f("ix_api_usage_id"), "api_usage", ["id"], unique=False)
    op.alter_column(
        "approval_event",
        "approval_request_id",
        existing_type=sa.UUID(),
        comment="Reference to the approval request",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_event",
        "account_id",
        existing_type=sa.UUID(),
        comment="The account this event belongs to",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_event",
        "event_type",
        existing_type=sa.VARCHAR(length=50),
        comment="Event type: approval_requested, notification_sent, vote_received, escalation_triggered, approval_complete, tool_executed, expired, cancelled",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_event",
        "detail",
        existing_type=sa.TEXT(),
        comment="Human-readable description of the event",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_event",
        "comment",
        existing_type=sa.TEXT(),
        comment="Approver comment (for vote_received events)",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_event",
        "actor_id",
        existing_type=sa.UUID(),
        comment="User who triggered the event (if applicable)",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_event",
        "timestamp",
        existing_type=postgresql.TIMESTAMP(),
        comment="When the event occurred",
        existing_nullable=False,
    )
    op.create_index(
        op.f("ix_approval_event_id"), "approval_event", ["id"], unique=False
    )
    op.alter_column(
        "approval_request",
        "approval_workflow_id",
        existing_type=sa.UUID(),
        comment="Reference to the approval workflow",
        existing_comment="Reference to the approval policy",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_request",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier for the approval request",
        existing_nullable=False,
    )
    op.drop_index(
        op.f("ix_approval_request_approval_policy_id"), table_name="approval_request"
    )
    op.create_index(
        op.f("ix_approval_request_approval_workflow_id"),
        "approval_request",
        ["approval_workflow_id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_approval_request_id"), "approval_request", ["id"], unique=False
    )
    op.drop_column("approval_request", "approvals_list")
    op.drop_column("approval_request", "approvals_received")
    op.alter_column(
        "approval_workflow",
        "account_id",
        existing_type=sa.UUID(),
        comment="The account this workflow belongs to",
        existing_comment="The account this policy belongs to",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_workflow",
        "name",
        existing_type=sa.VARCHAR(length=255),
        comment="Human-readable name for the workflow",
        existing_comment="Human-readable name for the policy",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_workflow",
        "description",
        existing_type=sa.VARCHAR(),
        comment="Optional description of what this workflow does",
        existing_comment="Optional description of what this policy does",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_workflow",
        "is_default",
        existing_type=sa.BOOLEAN(),
        comment="Whether this is the default workflow for the account",
        existing_comment="Whether this is the default policy for the account",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_workflow",
        "ai_model",
        existing_type=sa.VARCHAR(length=100),
        comment="AI model to use for approval decisions (e.g., 'gpt-5.4', 'claude-sonnet-4.7')",
        existing_comment="AI model for approval decisions (e.g., 'gpt-5.4', 'claude-sonnet-4.7')",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_workflow",
        "escalation_workflow_id",
        existing_type=sa.UUID(),
        comment="workflow to escalate to when fallback_behavior='escalate'",
        existing_comment="Policy to escalate to when fallback_behavior='escalate'",
        existing_nullable=True,
    )
    op.drop_index(op.f("ix_approval_policy_account_id"), table_name="approval_workflow")
    op.drop_index(
        op.f("ix_approval_policy_approval_mode"), table_name="approval_workflow"
    )
    op.drop_index(
        op.f("ix_approval_policy_escalation_policy_id"), table_name="approval_workflow"
    )
    op.drop_index(op.f("ix_approval_policy_is_default"), table_name="approval_workflow")
    op.drop_index(op.f("ix_approval_policy_name"), table_name="approval_workflow")
    op.create_index(
        op.f("ix_approval_workflow_account_id"),
        "approval_workflow",
        ["account_id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_approval_workflow_id"), "approval_workflow", ["id"], unique=False
    )
    op.create_index(
        op.f("ix_approval_workflow_is_default"),
        "approval_workflow",
        ["is_default"],
        unique=False,
    )
    op.create_index(
        op.f("ix_approval_workflow_name"), "approval_workflow", ["name"], unique=False
    )
    op.drop_index(op.f("ix_audit_log_account_action_ts"), table_name="audit_log")
    op.alter_column(
        "event",
        "account_id",
        existing_type=sa.UUID(),
        comment="Account this event belongs to (required for most events)",
        existing_comment="Account this event belongs to",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "session_id",
        existing_type=sa.UUID(),
        comment="WebSocket session ID (null for system/async events)",
        existing_comment="WebSocket session ID (null for system events)",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "user_id",
        existing_type=sa.UUID(),
        comment="User who triggered event (null for system events or anonymous)",
        existing_comment="User who triggered event (null for system events)",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "event_type",
        existing_type=sa.VARCHAR(length=50),
        comment="Event type: session_start, page_view, action, conversion, flow_execution_*, etc.",
        existing_nullable=False,
    )
    op.alter_column(
        "event",
        "timestamp",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        comment="When the event occurred",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "event",
        "ip_address",
        existing_type=sa.VARCHAR(length=45),
        comment="IP address (IPv6 max length: 45)",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "user_agent",
        existing_type=sa.VARCHAR(length=512),
        comment="Browser user agent string",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "event",
        "updated_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.drop_index(op.f("ix_event_account_type_ts"), table_name="event")
    op.alter_column(
        "flow",
        "trigger_event_types",
        existing_type=postgresql.ARRAY(sa.VARCHAR()),
        comment=None,
        existing_comment="Array of event types that trigger this flow",
        existing_nullable=True,
    )
    op.alter_column(
        "flow",
        "trigger_project_ids",
        existing_type=postgresql.ARRAY(sa.VARCHAR()),
        comment=None,
        existing_comment="Array of project IDs that can trigger this flow",
        existing_nullable=True,
    )
    op.drop_index(
        op.f("ix_flow_trigger_event_types"), table_name="flow", postgresql_using="gin"
    )
    op.drop_index(
        op.f("ix_flow_trigger_project_ids"), table_name="flow", postgresql_using="gin"
    )
    op.drop_index(
        op.f("ix_flow_execution_active_status"),
        table_name="flow_execution",
        postgresql_where="((status)::text = ANY ((ARRAY['RUNNING'::character varying, 'PENDING'::character varying, 'INITIALIZING'::character varying, 'STARTING'::character varying])::text[]))",
    )
    op.drop_index(op.f("ix_flow_execution_flow_status"), table_name="flow_execution")
    op.drop_constraint(
        op.f("fk_flow_execution_retry_of_execution_id"),
        "flow_execution",
        type_="foreignkey",
    )
    op.create_foreign_key(
        None, "flow_execution", "flow_execution", ["retry_of_execution_id"], ["id"]
    )
    op.drop_index(
        op.f("ix_flow_execution_log_exec_ts"), table_name="flow_execution_log"
    )
    op.create_index(
        op.f("ix_flow_execution_log_timestamp"),
        "flow_execution_log",
        ["timestamp"],
        unique=False,
    )
    op.drop_constraint(
        op.f("gateway_usage_search_document_api_usage_id_key"),
        "gateway_usage_search_document",
        type_="unique",
    )
    op.drop_index(
        op.f("ix_gateway_usage_search_document_search_vector"),
        table_name="gateway_usage_search_document",
        postgresql_using="gin",
    )
    op.alter_column(
        "instances",
        "instance_uuid",
        existing_type=sa.UUID(),
        comment="Unique identifier generated by the instance",
        existing_nullable=False,
    )
    op.alter_column(
        "instances",
        "version",
        existing_type=sa.VARCHAR(length=50),
        comment="Current version of the instance",
        existing_nullable=False,
    )
    op.alter_column(
        "instances",
        "edition",
        existing_type=sa.VARCHAR(length=20),
        comment="Edition: 'oss' or 'enterprise'",
        existing_nullable=False,
        existing_server_default=sa.text("'oss'::character varying"),
    )
    op.alter_column(
        "instances",
        "ip_address",
        existing_type=sa.VARCHAR(length=45),
        comment="Last known IP address (IPv4 or IPv6)",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "first_seen",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        comment="When the instance was first created/seen",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "instances",
        "last_seen",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        comment="Most recent activity",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "instances",
        "check_count",
        existing_type=sa.VARCHAR(length=20),
        comment="Number of check-ins (string for large numbers)",
        existing_nullable=False,
        existing_server_default=sa.text("'1'::character varying"),
    )
    op.alter_column(
        "instances",
        "country",
        existing_type=sa.VARCHAR(length=100),
        comment="Country from IP geolocation",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "country_code",
        existing_type=sa.VARCHAR(length=3),
        comment="ISO country code",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "city",
        existing_type=sa.VARCHAR(length=100),
        comment="City from IP geolocation",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "region",
        existing_type=sa.VARCHAR(length=100),
        comment="Region from IP geolocation",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "lat",
        existing_type=sa.DOUBLE_PRECISION(precision=53),
        comment="Latitude",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "lon",
        existing_type=sa.DOUBLE_PRECISION(precision=53),
        comment="Longitude",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "is_active",
        existing_type=sa.BOOLEAN(),
        comment="Whether instance is considered active",
        existing_nullable=False,
        existing_server_default=sa.text("true"),
    )
    op.alter_column(
        "instances",
        "metadata",
        existing_type=postgresql.JSON(astext_type=sa.Text()),
        comment="Additional instance metadata (features, user count, etc.)",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "instances",
        "updated_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.drop_constraint(op.f("instances_instance_uuid_key"), "instances", type_="unique")
    op.create_index(op.f("ix_instances_id"), "instances", ["id"], unique=False)

    op.create_index(op.f("ix_mcp_server_id"), "mcp_server", ["id"], unique=False)
    op.create_index(op.f("ix_mcp_tool_id"), "mcp_tool", ["id"], unique=False)
    op.alter_column(
        "notification_preferences",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier for the notification preferences",
        existing_nullable=False,
    )
    op.create_index(
        op.f("ix_notification_preferences_id"),
        "notification_preferences",
        ["id"],
        unique=False,
    )
    op.alter_column(
        "oauth_app_installation",
        "target_type",
        existing_type=sa.VARCHAR(length=50),
        comment="Type of target: 'Organization', 'User', 'Group', etc.",
        existing_comment="Type of target: 'Organization' or 'User'",
        existing_nullable=False,
    )
    op.alter_column(
        "oauth_app_installation",
        "target_id",
        existing_type=sa.BIGINT(),
        comment="Provider's organization/user/group ID",
        existing_comment="GitHub organization or user ID",
        existing_nullable=False,
    )
    op.alter_column(
        "oauth_app_installation",
        "permissions",
        existing_type=postgresql.JSON(astext_type=sa.Text()),
        comment="Permissions/scopes granted to this installation",
        existing_comment="Permissions granted to this installation",
        existing_nullable=True,
    )
    op.alter_column(
        "oauth_app_installation",
        "suspended_by",
        existing_type=sa.VARCHAR(length=255),
        comment="Username who suspended the installation",
        existing_comment="GitHub username who suspended the installation",
        existing_nullable=True,
    )
    op.drop_constraint(
        op.f("uq_oauth_app_installation_provider_external_id"),
        "oauth_app_installation",
        type_="unique",
    )
    op.create_index(
        op.f("ix_oauth_app_installation_id"),
        "oauth_app_installation",
        ["id"],
        unique=False,
    )
    op.drop_index(
        op.f("ix_oauth_mcp_access_token_hash"), table_name="oauth_mcp_access_token"
    )
    op.create_index(
        op.f("ix_oauth_mcp_access_token_token_hash"),
        "oauth_mcp_access_token",
        ["token_hash"],
        unique=True,
    )
    op.drop_index(
        op.f("ix_oauth_mcp_auth_code_client_id"),
        table_name="oauth_mcp_authorization_code",
    )
    op.drop_index(
        op.f("ix_oauth_mcp_auth_code_hash"), table_name="oauth_mcp_authorization_code"
    )
    op.drop_index(
        op.f("ix_oauth_mcp_auth_code_id"), table_name="oauth_mcp_authorization_code"
    )
    op.create_index(
        op.f("ix_oauth_mcp_authorization_code_client_id"),
        "oauth_mcp_authorization_code",
        ["client_id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_oauth_mcp_authorization_code_code_hash"),
        "oauth_mcp_authorization_code",
        ["code_hash"],
        unique=True,
    )
    op.create_index(
        op.f("ix_oauth_mcp_authorization_code_id"),
        "oauth_mcp_authorization_code",
        ["id"],
        unique=False,
    )
    op.drop_index(
        op.f("ix_oauth_mcp_refresh_token_hash"), table_name="oauth_mcp_refresh_token"
    )
    op.create_index(
        op.f("ix_oauth_mcp_refresh_token_token_hash"),
        "oauth_mcp_refresh_token",
        ["token_hash"],
        unique=True,
    )
    op.create_index(op.f("ix_oauth_token_id"), "oauth_token", ["id"], unique=False)
    op.alter_column(
        "permission",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier for the permission",
        existing_nullable=False,
    )
    op.alter_column(
        "permission",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        comment=None,
        existing_comment="When the permission was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.create_index(op.f("ix_permission_id"), "permission", ["id"], unique=False)
    op.alter_column(
        "policy_snapshot",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "policy_snapshot",
        "updated_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.drop_index(
        op.f("ix_policy_snapshot_account_is_active"), table_name="policy_snapshot"
    )
    op.drop_constraint(
        op.f("uq_policy_snapshot_account_version"), "policy_snapshot", type_="unique"
    )
    op.create_index(
        op.f("ix_policy_snapshot_id"), "policy_snapshot", ["id"], unique=False
    )
    op.create_index(
        op.f("ix_policy_snapshot_is_active"),
        "policy_snapshot",
        ["is_active"],
        unique=False,
    )
    op.drop_constraint(
        op.f("fk_policy_snapshot_account_id"), "policy_snapshot", type_="foreignkey"
    )
    op.drop_constraint(
        op.f("fk_policy_snapshot_created_by_user_id"),
        "policy_snapshot",
        type_="foreignkey",
    )
    op.create_foreign_key(None, "policy_snapshot", "account", ["account_id"], ["id"])
    op.create_foreign_key(
        None, "policy_snapshot", "user", ["created_by_user_id"], ["id"]
    )
    op.alter_column(
        "registration_token",
        "user_id",
        existing_type=sa.UUID(),
        comment="The user this token is for",
        existing_nullable=False,
    )
    op.alter_column(
        "registration_token",
        "expires_at",
        existing_type=postgresql.TIMESTAMP(),
        comment="Token expiration time",
        existing_nullable=False,
    )
    op.alter_column(
        "registration_token",
        "used_at",
        existing_type=postgresql.TIMESTAMP(),
        comment="When the token was used (None if not yet used)",
        existing_nullable=True,
    )
    op.alter_column(
        "registration_token",
        "is_consumed",
        existing_type=sa.BOOLEAN(),
        comment="Whether token has been used",
        existing_nullable=False,
        existing_server_default=sa.text("false"),
    )
    op.drop_constraint(
        op.f("registration_token_token_key"), "registration_token", type_="unique"
    )
    op.drop_index(op.f("ix_registration_token_token"), table_name="registration_token")
    op.create_index(
        op.f("ix_registration_token_token"),
        "registration_token",
        ["token"],
        unique=True,
    )
    op.create_index(
        op.f("ix_registration_token_id"), "registration_token", ["id"], unique=False
    )
    op.alter_column(
        "role",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier for the role",
        existing_nullable=False,
    )
    op.alter_column(
        "role",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        comment=None,
        existing_comment="When the role was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "role",
        "updated_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        comment=None,
        existing_comment="When the role was last updated",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.create_index(op.f("ix_role_id"), "role", ["id"], unique=False)
    op.alter_column(
        "role_permission",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier",
        existing_nullable=False,
    )
    op.create_index(
        op.f("ix_role_permission_id"), "role_permission", ["id"], unique=False
    )
    op.create_index(
        op.f("ix_runtime_session_activity_id"),
        "runtime_session_activity",
        ["id"],
        unique=False,
    )
    op.alter_column(
        "team",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier for the team",
        existing_nullable=False,
    )
    op.alter_column(
        "team",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        comment=None,
        existing_comment="When the team was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "team",
        "updated_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        comment=None,
        existing_comment="When the team was last modified",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.create_index(op.f("ix_team_id"), "team", ["id"], unique=False)
    op.alter_column(
        "team_membership",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier for the membership",
        existing_nullable=False,
    )
    op.create_index(
        op.f("ix_team_membership_id"), "team_membership", ["id"], unique=False
    )
    op.alter_column(
        "team_role",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier",
        existing_nullable=False,
    )
    op.create_index(op.f("ix_team_role_id"), "team_role", ["id"], unique=False)
    op.alter_column(
        "tool_access_rules",
        "account_id",
        existing_type=sa.UUID(),
        comment="The account this rule belongs to",
        existing_nullable=False,
    )
    op.alter_column(
        "tool_access_rules",
        "tool_configuration_id",
        existing_type=sa.UUID(),
        comment="Reference to the tool configuration",
        existing_nullable=False,
    )
    op.alter_column(
        "tool_access_rules",
        "condition_expression",
        existing_type=sa.TEXT(),
        comment="Expression to evaluate (CEL or simple format)",
        existing_nullable=True,
    )
    op.alter_column(
        "tool_access_rules",
        "condition_type",
        existing_type=sa.VARCHAR(length=20),
        comment="Type of condition: 'simple' or 'cel'",
        existing_nullable=False,
        existing_server_default=sa.text("'simple'::character varying"),
    )
    op.alter_column(
        "tool_access_rules",
        "action",
        existing_type=sa.VARCHAR(length=20),
        comment="Action when condition matches: 'allow', 'deny', 'require_approval'",
        existing_nullable=False,
    )
    op.alter_column(
        "tool_access_rules",
        "priority",
        existing_type=sa.INTEGER(),
        comment="Evaluation order (lower = evaluated first)",
        existing_nullable=False,
        existing_server_default=sa.text("0"),
    )
    op.alter_column(
        "tool_access_rules",
        "description",
        existing_type=sa.TEXT(),
        comment="Optional description of what this rule does",
        existing_nullable=True,
    )
    op.alter_column(
        "tool_access_rules",
        "is_enabled",
        existing_type=sa.BOOLEAN(),
        comment="Whether the rule is currently active",
        existing_nullable=False,
        existing_server_default=sa.text("true"),
    )
    op.alter_column(
        "tool_access_rules",
        "approval_workflow_id",
        existing_type=sa.UUID(),
        comment="Approval workflow to use when action is 'require_approval'",
        existing_comment="Approval policy to use when action is 'require_approval'",
        existing_nullable=True,
    )
    op.alter_column(
        "tool_access_rules",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "tool_access_rules",
        "updated_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.drop_index(
        op.f("ix_tool_access_rules_approval_policy_id"), table_name="tool_access_rules"
    )
    op.create_index(
        op.f("ix_tool_access_rules_approval_workflow_id"),
        "tool_access_rules",
        ["approval_workflow_id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_tool_access_rules_id"), "tool_access_rules", ["id"], unique=False
    )
    op.alter_column(
        "tool_configuration",
        "approval_workflow_id",
        existing_type=sa.UUID(),
        comment="Reference to approval workflow (if set, tool requires approval)",
        existing_comment="Reference to approval policy (if set, tool requires approval)",
        existing_nullable=True,
    )
    op.alter_column(
        "tool_configuration",
        "justification_mode",
        existing_type=sa.VARCHAR(length=20),
        comment="Justification parameter mode: null/disabled, 'optional', or 'required'",
        existing_comment="Justification parameter mode: null/disabled, optional, or required",
        existing_nullable=True,
    )
    op.drop_index(
        op.f("ix_tool_configuration_approval_policy_id"),
        table_name="tool_configuration",
    )
    op.create_index(
        op.f("ix_tool_configuration_approval_workflow_id"),
        "tool_configuration",
        ["approval_workflow_id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_tool_configuration_id"), "tool_configuration", ["id"], unique=False
    )
    op.alter_column(
        "tracker",
        "auth_type",
        existing_type=sa.VARCHAR(length=50),
        comment="Authentication type: 'api_token', 'github_app', or 'oauth_app'",
        existing_comment="Authentication type: 'api_token' or 'github_app'",
        existing_nullable=False,
        existing_server_default=sa.text("'api_token'::character varying"),
    )
    op.alter_column(
        "user",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier for the user",
        existing_nullable=False,
    )
    op.alter_column(
        "user",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        comment=None,
        existing_comment="When the user was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "user",
        "updated_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        comment=None,
        existing_comment="When the user was last updated",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.create_index(op.f("ix_user_id"), "user", ["id"], unique=False)
    op.alter_column(
        "user_invitation",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier for the invitation",
        existing_nullable=False,
    )
    op.alter_column(
        "user_invitation",
        "created_at",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        type_=sa.DateTime(),
        comment=None,
        existing_comment="When the invitation was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.create_index(
        op.f("ix_user_invitation_id"), "user_invitation", ["id"], unique=False
    )
    op.alter_column(
        "user_role",
        "id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier",
        existing_nullable=False,
    )
    op.create_index(op.f("ix_user_role_id"), "user_role", ["id"], unique=False)
    # ### end Alembic commands ###


def downgrade() -> None:
    """Downgrade schema."""
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index(op.f("ix_user_role_id"), table_name="user_role")
    op.alter_column(
        "user_role",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_user_invitation_id"), table_name="user_invitation")
    op.alter_column(
        "user_invitation",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        comment="When the invitation was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "user_invitation",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier for the invitation",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_user_id"), table_name="user")
    op.alter_column(
        "user",
        "updated_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        comment="When the user was last updated",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "user",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        comment="When the user was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "user",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier for the user",
        existing_nullable=False,
    )
    op.alter_column(
        "tracker",
        "auth_type",
        existing_type=sa.VARCHAR(length=50),
        comment="Authentication type: 'api_token' or 'github_app'",
        existing_comment="Authentication type: 'api_token', 'github_app', or 'oauth_app'",
        existing_nullable=False,
        existing_server_default=sa.text("'api_token'::character varying"),
    )
    op.drop_index(op.f("ix_tool_configuration_id"), table_name="tool_configuration")
    op.drop_index(
        op.f("ix_tool_configuration_approval_workflow_id"),
        table_name="tool_configuration",
    )
    op.create_index(
        op.f("ix_tool_configuration_approval_policy_id"),
        "tool_configuration",
        ["approval_workflow_id"],
        unique=False,
    )
    op.alter_column(
        "tool_configuration",
        "justification_mode",
        existing_type=sa.VARCHAR(length=20),
        comment="Justification parameter mode: null/disabled, optional, or required",
        existing_comment="Justification parameter mode: null/disabled, 'optional', or 'required'",
        existing_nullable=True,
    )
    op.alter_column(
        "tool_configuration",
        "approval_workflow_id",
        existing_type=sa.UUID(),
        comment="Reference to approval policy (if set, tool requires approval)",
        existing_comment="Reference to approval workflow (if set, tool requires approval)",
        existing_nullable=True,
    )
    op.drop_index(op.f("ix_tool_access_rules_id"), table_name="tool_access_rules")
    op.drop_index(
        op.f("ix_tool_access_rules_approval_workflow_id"),
        table_name="tool_access_rules",
    )
    op.create_index(
        op.f("ix_tool_access_rules_approval_policy_id"),
        "tool_access_rules",
        ["approval_workflow_id"],
        unique=False,
    )
    op.alter_column(
        "tool_access_rules",
        "updated_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "tool_access_rules",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "tool_access_rules",
        "approval_workflow_id",
        existing_type=sa.UUID(),
        comment="Approval policy to use when action is 'require_approval'",
        existing_comment="Approval workflow to use when action is 'require_approval'",
        existing_nullable=True,
    )
    op.alter_column(
        "tool_access_rules",
        "is_enabled",
        existing_type=sa.BOOLEAN(),
        comment=None,
        existing_comment="Whether the rule is currently active",
        existing_nullable=False,
        existing_server_default=sa.text("true"),
    )
    op.alter_column(
        "tool_access_rules",
        "description",
        existing_type=sa.TEXT(),
        comment=None,
        existing_comment="Optional description of what this rule does",
        existing_nullable=True,
    )
    op.alter_column(
        "tool_access_rules",
        "priority",
        existing_type=sa.INTEGER(),
        comment=None,
        existing_comment="Evaluation order (lower = evaluated first)",
        existing_nullable=False,
        existing_server_default=sa.text("0"),
    )
    op.alter_column(
        "tool_access_rules",
        "action",
        existing_type=sa.VARCHAR(length=20),
        comment=None,
        existing_comment="Action when condition matches: 'allow', 'deny', 'require_approval'",
        existing_nullable=False,
    )
    op.alter_column(
        "tool_access_rules",
        "condition_type",
        existing_type=sa.VARCHAR(length=20),
        comment=None,
        existing_comment="Type of condition: 'simple' or 'cel'",
        existing_nullable=False,
        existing_server_default=sa.text("'simple'::character varying"),
    )
    op.alter_column(
        "tool_access_rules",
        "condition_expression",
        existing_type=sa.TEXT(),
        comment=None,
        existing_comment="Expression to evaluate (CEL or simple format)",
        existing_nullable=True,
    )
    op.alter_column(
        "tool_access_rules",
        "tool_configuration_id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Reference to the tool configuration",
        existing_nullable=False,
    )
    op.alter_column(
        "tool_access_rules",
        "account_id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="The account this rule belongs to",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_team_role_id"), table_name="team_role")
    op.alter_column(
        "team_role",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_team_membership_id"), table_name="team_membership")
    op.alter_column(
        "team_membership",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier for the membership",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_team_id"), table_name="team")
    op.alter_column(
        "team",
        "updated_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        comment="When the team was last modified",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "team",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        comment="When the team was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "team",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier for the team",
        existing_nullable=False,
    )
    op.drop_index(
        op.f("ix_runtime_session_activity_id"), table_name="runtime_session_activity"
    )
    op.drop_index(op.f("ix_role_permission_id"), table_name="role_permission")
    op.alter_column(
        "role_permission",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_role_id"), table_name="role")
    op.alter_column(
        "role",
        "updated_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        comment="When the role was last updated",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "role",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        comment="When the role was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "role",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier for the role",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_registration_token_id"), table_name="registration_token")
    op.drop_index(op.f("ix_registration_token_token"), table_name="registration_token")
    op.create_index(
        op.f("ix_registration_token_token"),
        "registration_token",
        ["token"],
        unique=False,
    )
    op.create_unique_constraint(
        op.f("registration_token_token_key"),
        "registration_token",
        ["token"],
        postgresql_nulls_not_distinct=False,
    )
    op.alter_column(
        "registration_token",
        "is_consumed",
        existing_type=sa.BOOLEAN(),
        comment=None,
        existing_comment="Whether token has been used",
        existing_nullable=False,
        existing_server_default=sa.text("false"),
    )
    op.alter_column(
        "registration_token",
        "used_at",
        existing_type=postgresql.TIMESTAMP(),
        comment=None,
        existing_comment="When the token was used (None if not yet used)",
        existing_nullable=True,
    )
    op.alter_column(
        "registration_token",
        "expires_at",
        existing_type=postgresql.TIMESTAMP(),
        comment=None,
        existing_comment="Token expiration time",
        existing_nullable=False,
    )
    op.alter_column(
        "registration_token",
        "user_id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="The user this token is for",
        existing_nullable=False,
    )
    op.drop_constraint(None, "policy_snapshot", type_="foreignkey")
    op.drop_constraint(None, "policy_snapshot", type_="foreignkey")
    op.create_foreign_key(
        op.f("fk_policy_snapshot_created_by_user_id"),
        "policy_snapshot",
        "user",
        ["created_by_user_id"],
        ["id"],
        ondelete="SET NULL",
    )
    op.create_foreign_key(
        op.f("fk_policy_snapshot_account_id"),
        "policy_snapshot",
        "account",
        ["account_id"],
        ["id"],
        ondelete="CASCADE",
    )
    op.drop_index(op.f("ix_policy_snapshot_is_active"), table_name="policy_snapshot")
    op.drop_index(op.f("ix_policy_snapshot_id"), table_name="policy_snapshot")
    op.create_unique_constraint(
        op.f("uq_policy_snapshot_account_version"),
        "policy_snapshot",
        ["account_id", "version_number"],
        postgresql_nulls_not_distinct=False,
    )
    op.create_index(
        op.f("ix_policy_snapshot_account_is_active"),
        "policy_snapshot",
        ["account_id", "is_active"],
        unique=False,
    )
    op.alter_column(
        "policy_snapshot",
        "updated_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "policy_snapshot",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.drop_index(op.f("ix_permission_id"), table_name="permission")
    op.alter_column(
        "permission",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        comment="When the permission was created",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "permission",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier for the permission",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_oauth_token_id"), table_name="oauth_token")
    op.drop_index(
        op.f("ix_oauth_mcp_refresh_token_token_hash"),
        table_name="oauth_mcp_refresh_token",
    )
    op.create_index(
        op.f("ix_oauth_mcp_refresh_token_hash"),
        "oauth_mcp_refresh_token",
        ["token_hash"],
        unique=True,
    )
    op.drop_index(
        op.f("ix_oauth_mcp_authorization_code_id"),
        table_name="oauth_mcp_authorization_code",
    )
    op.drop_index(
        op.f("ix_oauth_mcp_authorization_code_code_hash"),
        table_name="oauth_mcp_authorization_code",
    )
    op.drop_index(
        op.f("ix_oauth_mcp_authorization_code_client_id"),
        table_name="oauth_mcp_authorization_code",
    )
    op.create_index(
        op.f("ix_oauth_mcp_auth_code_id"),
        "oauth_mcp_authorization_code",
        ["id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_oauth_mcp_auth_code_hash"),
        "oauth_mcp_authorization_code",
        ["code_hash"],
        unique=True,
    )
    op.create_index(
        op.f("ix_oauth_mcp_auth_code_client_id"),
        "oauth_mcp_authorization_code",
        ["client_id"],
        unique=False,
    )
    op.drop_index(
        op.f("ix_oauth_mcp_access_token_token_hash"),
        table_name="oauth_mcp_access_token",
    )
    op.create_index(
        op.f("ix_oauth_mcp_access_token_hash"),
        "oauth_mcp_access_token",
        ["token_hash"],
        unique=True,
    )
    op.drop_index(
        op.f("ix_oauth_app_installation_id"), table_name="oauth_app_installation"
    )
    op.create_unique_constraint(
        op.f("uq_oauth_app_installation_provider_external_id"),
        "oauth_app_installation",
        ["provider", "external_id"],
        postgresql_nulls_not_distinct=False,
    )
    op.alter_column(
        "oauth_app_installation",
        "suspended_by",
        existing_type=sa.VARCHAR(length=255),
        comment="GitHub username who suspended the installation",
        existing_comment="Username who suspended the installation",
        existing_nullable=True,
    )
    op.alter_column(
        "oauth_app_installation",
        "permissions",
        existing_type=postgresql.JSON(astext_type=sa.Text()),
        comment="Permissions granted to this installation",
        existing_comment="Permissions/scopes granted to this installation",
        existing_nullable=True,
    )
    op.alter_column(
        "oauth_app_installation",
        "target_id",
        existing_type=sa.BIGINT(),
        comment="GitHub organization or user ID",
        existing_comment="Provider's organization/user/group ID",
        existing_nullable=False,
    )
    op.alter_column(
        "oauth_app_installation",
        "target_type",
        existing_type=sa.VARCHAR(length=50),
        comment="Type of target: 'Organization' or 'User'",
        existing_comment="Type of target: 'Organization', 'User', 'Group', etc.",
        existing_nullable=False,
    )
    op.drop_index(
        op.f("ix_notification_preferences_id"), table_name="notification_preferences"
    )
    op.alter_column(
        "notification_preferences",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier for the notification preferences",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_mcp_tool_id"), table_name="mcp_tool")
    op.drop_index(op.f("ix_mcp_server_id"), table_name="mcp_server")

    op.drop_index(op.f("ix_instances_id"), table_name="instances")
    op.create_unique_constraint(
        op.f("instances_instance_uuid_key"),
        "instances",
        ["instance_uuid"],
        postgresql_nulls_not_distinct=False,
    )
    op.alter_column(
        "instances",
        "updated_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "instances",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "instances",
        "metadata",
        existing_type=postgresql.JSON(astext_type=sa.Text()),
        comment=None,
        existing_comment="Additional instance metadata (features, user count, etc.)",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "is_active",
        existing_type=sa.BOOLEAN(),
        comment=None,
        existing_comment="Whether instance is considered active",
        existing_nullable=False,
        existing_server_default=sa.text("true"),
    )
    op.alter_column(
        "instances",
        "lon",
        existing_type=sa.DOUBLE_PRECISION(precision=53),
        comment=None,
        existing_comment="Longitude",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "lat",
        existing_type=sa.DOUBLE_PRECISION(precision=53),
        comment=None,
        existing_comment="Latitude",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "region",
        existing_type=sa.VARCHAR(length=100),
        comment=None,
        existing_comment="Region from IP geolocation",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "city",
        existing_type=sa.VARCHAR(length=100),
        comment=None,
        existing_comment="City from IP geolocation",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "country_code",
        existing_type=sa.VARCHAR(length=3),
        comment=None,
        existing_comment="ISO country code",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "country",
        existing_type=sa.VARCHAR(length=100),
        comment=None,
        existing_comment="Country from IP geolocation",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "check_count",
        existing_type=sa.VARCHAR(length=20),
        comment=None,
        existing_comment="Number of check-ins (string for large numbers)",
        existing_nullable=False,
        existing_server_default=sa.text("'1'::character varying"),
    )
    op.alter_column(
        "instances",
        "last_seen",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        comment=None,
        existing_comment="Most recent activity",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "instances",
        "first_seen",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        comment=None,
        existing_comment="When the instance was first created/seen",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "instances",
        "ip_address",
        existing_type=sa.VARCHAR(length=45),
        comment=None,
        existing_comment="Last known IP address (IPv4 or IPv6)",
        existing_nullable=True,
    )
    op.alter_column(
        "instances",
        "edition",
        existing_type=sa.VARCHAR(length=20),
        comment=None,
        existing_comment="Edition: 'oss' or 'enterprise'",
        existing_nullable=False,
        existing_server_default=sa.text("'oss'::character varying"),
    )
    op.alter_column(
        "instances",
        "version",
        existing_type=sa.VARCHAR(length=50),
        comment=None,
        existing_comment="Current version of the instance",
        existing_nullable=False,
    )
    op.alter_column(
        "instances",
        "instance_uuid",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Unique identifier generated by the instance",
        existing_nullable=False,
    )
    op.create_index(
        op.f("ix_gateway_usage_search_document_search_vector"),
        "gateway_usage_search_document",
        [sa.literal_column("to_tsvector('simple'::regconfig, searchable_text)")],
        unique=False,
        postgresql_using="gin",
    )
    op.create_unique_constraint(
        op.f("gateway_usage_search_document_api_usage_id_key"),
        "gateway_usage_search_document",
        ["api_usage_id"],
        postgresql_nulls_not_distinct=False,
    )
    op.drop_index(
        op.f("ix_flow_execution_log_timestamp"), table_name="flow_execution_log"
    )
    op.create_index(
        op.f("ix_flow_execution_log_exec_ts"),
        "flow_execution_log",
        ["execution_id", "timestamp"],
        unique=False,
    )
    op.drop_constraint(None, "flow_execution", type_="foreignkey")
    op.create_foreign_key(
        op.f("fk_flow_execution_retry_of_execution_id"),
        "flow_execution",
        "flow_execution",
        ["retry_of_execution_id"],
        ["id"],
        ondelete="SET NULL",
    )
    op.create_index(
        op.f("ix_flow_execution_flow_status"),
        "flow_execution",
        ["flow_id", "status"],
        unique=False,
    )
    op.create_index(
        op.f("ix_flow_execution_active_status"),
        "flow_execution",
        ["status"],
        unique=False,
        postgresql_where="((status)::text = ANY ((ARRAY['RUNNING'::character varying, 'PENDING'::character varying, 'INITIALIZING'::character varying, 'STARTING'::character varying])::text[]))",
    )
    op.create_index(
        op.f("ix_flow_trigger_project_ids"),
        "flow",
        ["trigger_project_ids"],
        unique=False,
        postgresql_using="gin",
    )
    op.create_index(
        op.f("ix_flow_trigger_event_types"),
        "flow",
        ["trigger_event_types"],
        unique=False,
        postgresql_using="gin",
    )
    op.alter_column(
        "flow",
        "trigger_project_ids",
        existing_type=postgresql.ARRAY(sa.VARCHAR()),
        comment="Array of project IDs that can trigger this flow",
        existing_nullable=True,
    )
    op.alter_column(
        "flow",
        "trigger_event_types",
        existing_type=postgresql.ARRAY(sa.VARCHAR()),
        comment="Array of event types that trigger this flow",
        existing_nullable=True,
    )
    op.create_index(
        op.f("ix_event_account_type_ts"),
        "event",
        ["account_id", "event_type", sa.literal_column("timestamp DESC")],
        unique=False,
    )
    op.alter_column(
        "event",
        "updated_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "event",
        "created_at",
        existing_type=sa.DateTime(),
        type_=postgresql.TIMESTAMP(timezone=True),
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "event",
        "user_agent",
        existing_type=sa.VARCHAR(length=512),
        comment=None,
        existing_comment="Browser user agent string",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "ip_address",
        existing_type=sa.VARCHAR(length=45),
        comment=None,
        existing_comment="IP address (IPv6 max length: 45)",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "timestamp",
        existing_type=postgresql.TIMESTAMP(timezone=True),
        comment=None,
        existing_comment="When the event occurred",
        existing_nullable=False,
        existing_server_default=sa.text("now()"),
    )
    op.alter_column(
        "event",
        "event_type",
        existing_type=sa.VARCHAR(length=50),
        comment=None,
        existing_comment="Event type: session_start, page_view, action, conversion, flow_execution_*, etc.",
        existing_nullable=False,
    )
    op.alter_column(
        "event",
        "user_id",
        existing_type=sa.UUID(),
        comment="User who triggered event (null for system events)",
        existing_comment="User who triggered event (null for system events or anonymous)",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "session_id",
        existing_type=sa.UUID(),
        comment="WebSocket session ID (null for system events)",
        existing_comment="WebSocket session ID (null for system/async events)",
        existing_nullable=True,
    )
    op.alter_column(
        "event",
        "account_id",
        existing_type=sa.UUID(),
        comment="Account this event belongs to",
        existing_comment="Account this event belongs to (required for most events)",
        existing_nullable=True,
    )
    op.create_index(
        op.f("ix_audit_log_account_action_ts"),
        "audit_log",
        ["account_id", "action", sa.literal_column("timestamp DESC")],
        unique=False,
    )
    op.drop_index(op.f("ix_approval_workflow_name"), table_name="approval_workflow")
    op.drop_index(
        op.f("ix_approval_workflow_is_default"), table_name="approval_workflow"
    )
    op.drop_index(op.f("ix_approval_workflow_id"), table_name="approval_workflow")
    op.drop_index(
        op.f("ix_approval_workflow_account_id"), table_name="approval_workflow"
    )
    op.create_index(
        op.f("ix_approval_policy_name"), "approval_workflow", ["name"], unique=False
    )
    op.create_index(
        op.f("ix_approval_policy_is_default"),
        "approval_workflow",
        ["is_default"],
        unique=False,
    )
    op.create_index(
        op.f("ix_approval_policy_escalation_policy_id"),
        "approval_workflow",
        ["escalation_workflow_id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_approval_policy_approval_mode"),
        "approval_workflow",
        ["approval_mode"],
        unique=False,
    )
    op.create_index(
        op.f("ix_approval_policy_account_id"),
        "approval_workflow",
        ["account_id"],
        unique=False,
    )
    op.alter_column(
        "approval_workflow",
        "escalation_workflow_id",
        existing_type=sa.UUID(),
        comment="Policy to escalate to when fallback_behavior='escalate'",
        existing_comment="workflow to escalate to when fallback_behavior='escalate'",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_workflow",
        "ai_model",
        existing_type=sa.VARCHAR(length=100),
        comment="AI model for approval decisions (e.g., 'gpt-5.4', 'claude-sonnet-4.7')",
        existing_comment="AI model to use for approval decisions (e.g., 'gpt-5.4', 'claude-sonnet-4.7')",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_workflow",
        "is_default",
        existing_type=sa.BOOLEAN(),
        comment="Whether this is the default policy for the account",
        existing_comment="Whether this is the default workflow for the account",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_workflow",
        "description",
        existing_type=sa.VARCHAR(),
        comment="Optional description of what this policy does",
        existing_comment="Optional description of what this workflow does",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_workflow",
        "name",
        existing_type=sa.VARCHAR(length=255),
        comment="Human-readable name for the policy",
        existing_comment="Human-readable name for the workflow",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_workflow",
        "account_id",
        existing_type=sa.UUID(),
        comment="The account this policy belongs to",
        existing_comment="The account this workflow belongs to",
        existing_nullable=False,
    )
    op.add_column(
        "approval_request",
        sa.Column(
            "approvals_received",
            sa.INTEGER(),
            server_default=sa.text("0"),
            autoincrement=False,
            nullable=False,
            comment="Number of approvals received so far (for quorum tracking)",
        ),
    )
    op.add_column(
        "approval_request",
        sa.Column(
            "approvals_list",
            postgresql.JSONB(astext_type=sa.Text()),
            autoincrement=False,
            nullable=True,
            comment="List of approval records: [{user_id, comment, timestamp}]",
        ),
    )
    op.drop_index(op.f("ix_approval_request_id"), table_name="approval_request")
    op.drop_index(
        op.f("ix_approval_request_approval_workflow_id"), table_name="approval_request"
    )
    op.create_index(
        op.f("ix_approval_request_approval_policy_id"),
        "approval_request",
        ["approval_workflow_id"],
        unique=False,
    )
    op.alter_column(
        "approval_request",
        "id",
        existing_type=sa.UUID(),
        comment="Unique identifier for the approval request",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_request",
        "approval_workflow_id",
        existing_type=sa.UUID(),
        comment="Reference to the approval policy",
        existing_comment="Reference to the approval workflow",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_approval_event_id"), table_name="approval_event")
    op.alter_column(
        "approval_event",
        "timestamp",
        existing_type=postgresql.TIMESTAMP(),
        comment=None,
        existing_comment="When the event occurred",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_event",
        "actor_id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="User who triggered the event (if applicable)",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_event",
        "comment",
        existing_type=sa.TEXT(),
        comment=None,
        existing_comment="Approver comment (for vote_received events)",
        existing_nullable=True,
    )
    op.alter_column(
        "approval_event",
        "detail",
        existing_type=sa.TEXT(),
        comment=None,
        existing_comment="Human-readable description of the event",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_event",
        "event_type",
        existing_type=sa.VARCHAR(length=50),
        comment=None,
        existing_comment="Event type: approval_requested, notification_sent, vote_received, escalation_triggered, approval_complete, tool_executed, expired, cancelled",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_event",
        "account_id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="The account this event belongs to",
        existing_nullable=False,
    )
    op.alter_column(
        "approval_event",
        "approval_request_id",
        existing_type=sa.UUID(),
        comment=None,
        existing_comment="Reference to the approval request",
        existing_nullable=False,
    )
    op.drop_index(op.f("ix_api_usage_id"), table_name="api_usage")
    op.create_index(
        op.f("ix_api_usage_user_ts"),
        "api_usage",
        ["user_id", sa.literal_column("timestamp DESC")],
        unique=False,
    )
    op.drop_index(op.f("ix_api_key_id"), table_name="api_key")
    op.drop_index(op.f("ix_ai_model_id"), table_name="ai_model")
    op.drop_index(
        "ix_budget_spend_activities_lookup", table_name="budget_spend_activities"
    )
    op.drop_table("budget_spend_activities")
    op.drop_index(op.f("ix_budget_policies_subject_type"), table_name="budget_policies")
    op.drop_index(op.f("ix_budget_policies_subject_id"), table_name="budget_policies")
    op.drop_index(op.f("ix_budget_policies_model_alias"), table_name="budget_policies")
    op.drop_table("budget_policies")
    # ### end Alembic commands ###
