"""workplan projection retirement (STATE-WP-0083-T03) A forge-derived projection must be able to say that a record no longer derives from the forge. It cannot say so by deleting the row: progress events, tasks, decisions and review contracts reference workplans with ON DELETE RESTRICT, and those hub-native records must survive (ADR-010 decision 4, ADR-012 decision 7 as amended 2026-08-25). Nor can it say so through `status`. A workplan that is `archived` was closed by its owner; one that no longer derives is a different fact, and overloading the same field would destroy the distinction exactly where it matters. Revision ID: d1a2b3c4e5f6 Revises: c9e5a1b3d7f2 """ from alembic import op import sqlalchemy as sa revision = "d1a2b3c4e5f6" down_revision = "c9e5a1b3d7f2" branch_labels = None depends_on = None def upgrade() -> None: op.add_column( "workplans", sa.Column("projection_retired_at", sa.DateTime(timezone=True), nullable=True), ) op.add_column( "workplans", sa.Column("projection_retired_reason", sa.String(length=255), nullable=True), ) op.add_column( "workplans", sa.Column("derived_from_commit", sa.String(length=40), nullable=True), ) op.create_index( "ix_workplans_projection_retired_at", "workplans", ["projection_retired_at"], ) def downgrade() -> None: op.drop_index("ix_workplans_projection_retired_at", table_name="workplans") op.drop_column("workplans", "derived_from_commit") op.drop_column("workplans", "projection_retired_reason") op.drop_column("workplans", "projection_retired_at")