"""Re-enable the ``cmdb.auto_owner_grant_backfill`` scheduled job. Revision ID: 241 Revises: 240 Create Date: 2026-06-02 Companion to the same-PR code change that adds ``is_backfill=True`` to ``_post_provision_finalize`` (and downstream ``provision_initial_owner_grant`` → ``_dispatch_grant``) so the cron writes silent + perpetual rows instead of firing misleading "[已开通] X 已生效" notifications. Context: PR #671 launched the cron at 2026-06-02 08:42 UTC. By 09:18 UTC it had fired 4 ticks and dispatched 14 "已开通" cards to scope owners who had been Owner on those projects for months (the scopes were discovered via sync, not created through scope_request — the cron caught all of them as "owner has no source='helix' grant"). The cron was manually disabled in prod at 09:21 UTC via a direct ``UPDATE scheduled_jobs SET enabled=0`` while the fix was prepared. With the ``is_backfill=True`` plumbing in place, the cron is safe to run again — the remaining ~77 owners / 182 scopes get their bookkeeping rows written without any user-visible notification. The migration is intentionally unconditional ``SET enabled=1`` for this one job — not gated on ``WHERE enabled=0`` — so it is safe to re-run and converges fresh deploys (where 239 already set enabled=1) and the patched prod (where the manual ``SET enabled=0`` is the current state) to the same end state. The ``updated_at`` bump tells the scheduler tick its next-fire calculation needs to refresh. """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "241" down_revision = "240" branch_labels = None depends_on = None def _table_exists(bind, table: str) -> bool: row = bind.execute( sa.text( "SELECT 1 FROM information_schema.TABLES " "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t" ), {"t": table}, ).fetchone() return row is not None def upgrade() -> None: bind = op.get_bind() if not _table_exists(bind, "scheduled_jobs"): return bind.execute( sa.text( "UPDATE scheduled_jobs " "SET enabled = 1, updated_at = NOW() " "WHERE name = 'cmdb.auto_owner_grant_backfill'" ) ) def downgrade() -> None: # No-op — re-disabling would be a regression, not a rollback. The # cron's purpose is to bottom-out the auto-Owner grant flow; once # operations re-enable it, downgrading the migration must not # silently turn it back off. pass