feat(projection): add workplan retirement columns, and record the migration gap
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s

Adds projection_retired_at, projection_retired_reason and derived_from_commit.
Retirement cannot be expressed by deletion — hub-native records reference
workplans with RESTRICT — nor by `status`, since an archived workplan was closed
by its owner while a retired one is simply no longer derived by the forge. Those
are different facts and must not share a field.

Discovered while applying this: central's schema is two revisions behind the
code it runs. review_contracts does not exist there although its migration ships
in the serving image, and there is no migration mechanism at all — bare uvicorn
CMD, no chart-declared job. Recorded as STATE-WP-0083-T07, which now blocks T03.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
This commit is contained in:
tegwick 2026-08-25 23:49:13 +02:00
parent fd0d0d537b
commit 89ff2b2ea3
3 changed files with 95 additions and 0 deletions

View file

@ -53,6 +53,16 @@ class Workplan(Base, TimestampMixin):
index=True,
)
backing_filename: Mapped[str | None] = mapped_column(String(255), nullable=True)
# Projection provenance and retirement (ADR-012 decisions 2 and 7).
# A retired record is one the forge no longer derives. It is not deleted:
# hub-native records reference it with RESTRICT and must survive. It is not
# `archived` either — that says the owner closed the work, which is a
# different fact.
projection_retired_at: Mapped["datetime | None"] = mapped_column( # noqa: F821
DateTime(timezone=True), nullable=True, index=True
)
projection_retired_reason: Mapped[str | None] = mapped_column(String(255), nullable=True)
derived_from_commit: Mapped[str | None] = mapped_column(String(40), nullable=True)
backing_relative_path: Mapped[str | None] = mapped_column(Text, nullable=True)
backing_archived: Mapped[bool | None] = mapped_column(nullable=True)
backing_synced_at: Mapped[datetime | None] = mapped_column(