"""Shape fingerprints + the mechanical proposer's columns (#2792, milestone 294) Revision ID: 0080 Revises: 0079 Create Date: 2026-08-21 Two additions to the ledger. `signature` / `body_sha` fingerprint each shape (definition line + a whitespace/comment-insensitive hash of its block) so the proposer can match on content and a later drift recheck can notice change, without the ledger ever storing code. The proposal columns carry the proposer's standing suggestion for an unclassified row — instance-of-#N with a basis and score, or a derive-first group key — and `proposed_sha` remembers the content it was judged at so a refresh re-examines only what changed. Mechanical and recomputable: a restore that lacks them loses nothing the next refresh does not rebuild. """ import sqlalchemy as sa from alembic import op revision = "0080" down_revision = "0079" branch_labels = None depends_on = None def upgrade() -> None: op.add_column("code_shapes", sa.Column("signature", sa.Text(), nullable=False, server_default="")) op.add_column("code_shapes", sa.Column("body_sha", sa.Text(), nullable=False, server_default="")) op.add_column( "code_shapes", sa.Column( "proposed_snippet_id", sa.BigInteger(), sa.ForeignKey("notes.id", ondelete="SET NULL"), nullable=True, ), ) op.add_column("code_shapes", sa.Column("proposal_basis", sa.Text(), nullable=True)) op.add_column("code_shapes", sa.Column("proposal_score", sa.Float(), nullable=True)) op.add_column("code_shapes", sa.Column("proposal_group", sa.Text(), nullable=True)) op.add_column("code_shapes", sa.Column("proposed_at", sa.DateTime(timezone=True), nullable=True)) op.add_column("code_shapes", sa.Column("proposed_sha", sa.Text(), nullable=False, server_default="")) op.create_index( "ix_code_shapes_proposed", "code_shapes", ["project_id", "proposed_snippet_id"] ) def downgrade() -> None: op.drop_index("ix_code_shapes_proposed", table_name="code_shapes") for col in ( "proposed_sha", "proposed_at", "proposal_group", "proposal_score", "proposal_basis", "proposed_snippet_id", "body_sha", "signature", ): op.drop_column("code_shapes", col)