"""Shape history, recheck, and the divergence flag (#2793, milestone 294) Revision ID: 0081 Revises: 0080 Create Date: 2026-08-21 The payoff surface of the ledger. `classified_sha` remembers the fingerprint a judgment was made at so a later body change under an instance/variant can flag `recheck_at`; `diverges_from` is the button-B flag (a shape new since the previous refresh, where one canon dominates its directory+kind, and not proposed as that canon). `code_shape_events` is the what-was-used-when record: every classification, vanish, reappearance, and drift as it happened — history the row alone cannot keep once it moves on. """ import sqlalchemy as sa from alembic import op revision = "0081" down_revision = "0080" branch_labels = None depends_on = None def upgrade() -> None: op.add_column("code_shapes", sa.Column("classified_sha", sa.Text(), nullable=False, server_default="")) op.add_column("code_shapes", sa.Column("recheck_at", sa.DateTime(timezone=True), nullable=True)) op.add_column( "code_shapes", sa.Column( "diverges_from", sa.BigInteger(), sa.ForeignKey("notes.id", ondelete="SET NULL"), nullable=True, ), ) op.create_index("ix_code_shapes_diverges", "code_shapes", ["project_id", "diverges_from"]) op.create_table( "code_shape_events", sa.Column("id", sa.Integer(), primary_key=True), sa.Column( "shape_id", sa.Integer(), sa.ForeignKey("code_shapes.id", ondelete="CASCADE"), nullable=False, ), sa.Column("project_id", sa.Integer(), nullable=False), sa.Column("path", sa.Text(), nullable=False), sa.Column("symbol", sa.Text(), nullable=False), sa.Column("kind", sa.Text(), nullable=False), sa.Column("event", sa.Text(), nullable=False), sa.Column("status", sa.Text(), nullable=True), sa.Column("snippet_id", sa.BigInteger(), nullable=True), sa.Column("classified_by", sa.Text(), nullable=True), sa.Column("reason", sa.Text(), nullable=True), sa.Column("commit", sa.Text(), nullable=False, server_default=""), sa.Column("at", sa.DateTime(timezone=True), nullable=False), ) op.create_index("ix_code_shape_events_shape", "code_shape_events", ["shape_id", "at"]) op.create_index( "ix_code_shape_events_project_path", "code_shape_events", ["project_id", "path"] ) def downgrade() -> None: op.drop_index("ix_code_shape_events_project_path", table_name="code_shape_events") op.drop_index("ix_code_shape_events_shape", table_name="code_shape_events") op.drop_table("code_shape_events") op.drop_index("ix_code_shapes_diverges", table_name="code_shapes") for col in ("diverges_from", "recheck_at", "classified_sha"): op.drop_column("code_shapes", col)