"""Durable EXIF projections per asset and stage (US07-03). Revision ID: 0015_exif_projections Revises: 0014_restore_plans Create Date: 2026-08-16 The concept's ``exif_projections`` table, added at the point it earns its keep: a checkpoint that finds a field it does not own changed must be able to say so after a restart. ``state`` is verified | divergent | failed, and only ``verified`` counts as a completed metadata stage. """ import sqlalchemy as sa from alembic import op revision = "0015_exif_projections" down_revision = "0014_restore_plans" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "exif_projections", sa.Column("asset_id", sa.String(), sa.ForeignKey("assets.id"), primary_key=True), sa.Column("stage", sa.String(), primary_key=True), # safety | analysis sa.Column("id", sa.String(), nullable=False), sa.Column("projection_version", sa.Integer(), nullable=False, server_default="0"), # What the stage asked for: {"add": [...], "remove": [...]}. sa.Column("desired_json", sa.String(), nullable=True), # Fields outside the stage's ownership that did not survive the write. sa.Column("divergent_fields", sa.String(), nullable=True), sa.Column("result_file_sha256", sa.String(), nullable=True), sa.Column("state", sa.String(), nullable=False), sa.Column("error_code", sa.String(), nullable=True), sa.Column("verified_at", sa.DateTime(timezone=True), nullable=True), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True), ) def downgrade() -> None: op.drop_table("exif_projections")