"""The shape ledger: code_shapes (#2787, milestone 294) Revision ID: 0079 Revises: 0078 Create Date: 2026-08-19 The accounting half of the pattern system (governing note 2786): the snippet library records canon (small); this table accounts for EVERY shape the coverage extractor finds in a bound repo (total). Rows arrive `unclassified` from the coverage sync (step 2) and gain judgments — canonical / instance / variant / exempt — from audits, hooks, and the mechanical proposer. Unclassified IS the todo list. """ import sqlalchemy as sa from alembic import op revision = "0079" down_revision = "0078" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "code_shapes", sa.Column("id", sa.Integer(), primary_key=True), sa.Column( "project_id", sa.Integer(), sa.ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, ), sa.Column("repo_key", sa.Text(), 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("status", sa.Text(), nullable=False, server_default="unclassified"), sa.Column( "snippet_id", sa.BigInteger(), sa.ForeignKey("notes.id", ondelete="SET NULL"), nullable=True, ), sa.Column("reason", sa.Text(), nullable=True), sa.Column("classified_by", sa.Text(), nullable=True), sa.Column("classified_at", sa.DateTime(timezone=True), nullable=True), sa.Column("first_seen_commit", sa.Text(), nullable=False, server_default=""), sa.Column("last_seen_commit", sa.Text(), nullable=False, server_default=""), sa.Column("vanished_at", sa.DateTime(timezone=True), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), sa.UniqueConstraint( "project_id", "repo_key", "path", "symbol", "kind", name="uq_code_shapes_identity", ), ) op.create_index( "ix_code_shapes_project_status", "code_shapes", ["project_id", "status"] ) op.create_index("ix_code_shapes_snippet", "code_shapes", ["snippet_id"]) def downgrade() -> None: op.drop_index("ix_code_shapes_snippet", table_name="code_shapes") op.drop_index("ix_code_shapes_project_status", table_name="code_shapes") op.drop_table("code_shapes")