"""approval hardening: mandatory operator signatures bound to content Revision ID: 0003 Revises: 0002 Create Date: 2026-10-20 - proposals.operator_signature - mail_messages.operator_signature - content_items.operator_signature Full signatures (128 hex chars) — never truncated. Existing NULL rows are refused at execution/send/publish time by the signing gate (no degraded mode). """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "0003" down_revision = "0002" branch_labels = None depends_on = None def upgrade() -> None: with op.batch_alter_table("proposals") as batch: batch.add_column(sa.Column("operator_signature", sa.Text(), nullable=True)) with op.batch_alter_table("mail_messages") as batch: batch.add_column(sa.Column("operator_signature", sa.Text(), nullable=True)) with op.batch_alter_table("content_items") as batch: batch.add_column(sa.Column("operator_signature", sa.Text(), nullable=True)) def downgrade() -> None: with op.batch_alter_table("content_items") as batch: batch.drop_column("operator_signature") with op.batch_alter_table("mail_messages") as batch: batch.drop_column("operator_signature") with op.batch_alter_table("proposals") as batch: batch.drop_column("operator_signature")