"""Drop the ``cmdb_cloud_scopes.is_ai_dedicated`` column. Revision ID: 153 Revises: 152 Create Date: 2026-05-12 Why --- ``is_ai_dedicated`` was a tenant-level boolean ("this entire Azure subscription / AWS account is 100% AI-purpose") used to bucket a scope's full billing into the AI Spending dashboard. The model collapsed three real states into one: 1. A subscription whose ONLY workload is AI (e.g. "PingCAP Azure OpenAI"). is_ai_dedicated=true sent its entire daily total to the AI bucket — correct. 2. A subscription with mixed workloads where AI is a minority (e.g. azure-azure-digital-bi-dev). is_ai_dedicated=true would send the full subscription total to AI even though most of it is Microsoft Fabric / storage / network — wrong, over-counts AI. 3. A subscription where some AI services are billed alongside other infrastructure. is_ai_dedicated=false caused those AI lines to silently drop from the AI bucket — wrong, under-counts AI. PR #253 (Phase 2) replaces the flag with service-level classification: any ``cmdb_cost_facts`` row whose ``service`` matches the configurable ``ai_spending.service_whitelist`` is AI; everything else isn't, regardless of which scope it lives in. This handles all three states correctly without admin-managed flags. Downgrade --------- Re-adds the column with default 0. Re-population is NOT attempted — the autogrant flow that previously set the flag (Azure OpenAI / AWS Bedrock) is also removed in this PR, so even a downgraded schema won't have a writer. Pre-deploy invariant -------------------- Code references to ``CmdbCloudScope.is_ai_dedicated`` must be removed in the same PR. Migration runs at startup before the app imports the ORM models; a model with a non-existent column raises only when a query refers to that column, not on import — but every production query path has been pruned. See the PR's commit chain for the mechanical changes in ``cmdb/models.py``, ``cmdb/admin_overview_service.py``, ``apikey/azure_openai_autogrant.py``, and the frontend i18n keys. """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "153" down_revision = "152" branch_labels = None depends_on = None TABLE = "cmdb_cloud_scopes" COLUMN = "is_ai_dedicated" def _column_exists(bind, table: str, name: str) -> bool: return bind.execute( sa.text( "SELECT 1 FROM information_schema.COLUMNS " "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t " "AND COLUMN_NAME = :c" ), {"t": table, "c": name}, ).scalar() is not None def upgrade() -> None: bind = op.get_bind() if _column_exists(bind, TABLE, COLUMN): op.drop_column(TABLE, COLUMN) def downgrade() -> None: bind = op.get_bind() if _column_exists(bind, TABLE, COLUMN): return op.add_column( TABLE, sa.Column( COLUMN, sa.Boolean(), nullable=False, server_default=sa.text("0"), ), )