feat(identifiers): add reversible projection migration
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
parent
ce52e9d1e2
commit
cb1b028fd1
18 changed files with 921 additions and 27 deletions
|
|
@ -41,6 +41,7 @@ from api.models.workplan_launch_request import WorkplanLaunchRequest
|
|||
from api.models.fabric_graph import FabricGraphImport, FabricGraphNode, FabricGraphEdge
|
||||
from api.models.legacy_meter import LegacyInterface, LegacyInterfaceUsageBucket
|
||||
from api.models.write_idempotency_key import WriteIdempotencyKey
|
||||
from api.models.work_record_identifier_alias import WorkRecordIdentifierAlias
|
||||
from api.models.suggestion import (
|
||||
Suggestion,
|
||||
SuggestionNote,
|
||||
|
|
@ -81,5 +82,6 @@ __all__ = [
|
|||
"FabricGraphImport", "FabricGraphNode", "FabricGraphEdge",
|
||||
"LegacyInterface", "LegacyInterfaceUsageBucket",
|
||||
"WriteIdempotencyKey",
|
||||
"WorkRecordIdentifierAlias",
|
||||
"Suggestion", "SuggestionNote", "SuggestionRelevanceBump", "SuggestionStage",
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class CapabilityRequest(Base, TimestampMixin):
|
|||
)
|
||||
requesting_workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL"),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
requesting_agent: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
|
|
@ -47,7 +47,7 @@ class CapabilityRequest(Base, TimestampMixin):
|
|||
)
|
||||
fulfilling_workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL"),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
fulfilling_agent: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||
|
|
@ -55,7 +55,7 @@ class CapabilityRequest(Base, TimestampMixin):
|
|||
# Links
|
||||
blocking_task_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("tasks.id", ondelete="SET NULL"),
|
||||
ForeignKey("tasks.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
catalog_entry_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import enum
|
|||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, Enum, ForeignKey, String, Text
|
||||
from sqlalchemy import DateTime, Enum, ForeignKey, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
|
|
@ -48,7 +48,9 @@ class Contribution(Base, TimestampMixin):
|
|||
UUID(as_uuid=True), ForeignKey("topics.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
related_workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="SET NULL"), nullable=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
repo_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("managed_repos.id", ondelete="SET NULL"), nullable=True
|
||||
|
|
|
|||
|
|
@ -37,7 +37,10 @@ class Decision(Base, TimestampMixin):
|
|||
UUID(as_uuid=True), ForeignKey("topics.id", ondelete="RESTRICT"), nullable=True, index=True
|
||||
)
|
||||
workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="RESTRICT"), nullable=True, index=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="RESTRICT", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
title: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ class ExtensionPoint(Base, TimestampMixin):
|
|||
UUID(as_uuid=True), ForeignKey("topics.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="SET NULL"), nullable=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
domain: Mapped["Domain"] = relationship("Domain", lazy="selectin") # noqa: F821
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ class Intake(Base, TimestampMixin):
|
|||
UUID(as_uuid=True), ForeignKey("topics.id", ondelete="SET NULL"), nullable=True, index=True
|
||||
)
|
||||
workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="SET NULL"), nullable=True, index=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
repo_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("managed_repos.id", ondelete="SET NULL"), nullable=True, index=True
|
||||
|
|
|
|||
|
|
@ -20,10 +20,16 @@ class ProgressEvent(Base):
|
|||
UUID(as_uuid=True), ForeignKey("topics.id", ondelete="RESTRICT"), nullable=True, index=True
|
||||
)
|
||||
workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="RESTRICT"), nullable=True, index=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="RESTRICT", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
task_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("tasks.id", ondelete="RESTRICT"), nullable=True, index=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("tasks.id", ondelete="RESTRICT", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
decision_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("decisions.id", ondelete="RESTRICT"), nullable=True, index=True
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ class Suggestion(Base, TimestampMixin):
|
|||
UUID(as_uuid=True), ForeignKey("topics.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="SET NULL"), nullable=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
title: Mapped[str] = mapped_column(String(500), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
|
@ -61,7 +63,9 @@ class Suggestion(Base, TimestampMixin):
|
|||
Float, nullable=False, default=1.0, server_default="1"
|
||||
)
|
||||
promoted_task_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("tasks.id", ondelete="SET NULL"), nullable=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("tasks.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
domain: Mapped["Domain"] = relationship("Domain", lazy="selectin") # noqa: F821
|
||||
|
|
@ -117,4 +121,4 @@ class SuggestionRelevanceBump(Base):
|
|||
reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@ class Task(Base, TimestampMixin):
|
|||
UUID(as_uuid=True), primary_key=True, default=new_uuid
|
||||
)
|
||||
workplan_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="RESTRICT"), nullable=False, index=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="RESTRICT", onupdate="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
title: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
|
@ -47,7 +50,9 @@ class Task(Base, TimestampMixin):
|
|||
needs_human: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)
|
||||
intervention_note: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
parent_task_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("tasks.id", ondelete="SET NULL"), nullable=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("tasks.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
workplan: Mapped["Workplan"] = relationship("Workplan", back_populates="tasks") # noqa: F821
|
||||
|
|
|
|||
|
|
@ -77,7 +77,9 @@ class TechnicalDebt(Base, TimestampMixin):
|
|||
UUID(as_uuid=True), ForeignKey("topics.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="SET NULL"), nullable=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
domain: Mapped["Domain"] = relationship("Domain", lazy="selectin") # noqa: F821
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import DateTime, Float, ForeignKey, Integer, Text, UniqueConstraint, func
|
||||
from sqlalchemy import (
|
||||
DateTime,
|
||||
Float,
|
||||
ForeignKey,
|
||||
Integer,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
)
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
|
|
@ -25,10 +32,16 @@ class TokenEvent(Base):
|
|||
UUID(as_uuid=True), primary_key=True, default=new_uuid
|
||||
)
|
||||
task_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("tasks.id", ondelete="SET NULL"), nullable=True, index=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("tasks.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("workplans.id", ondelete="SET NULL"), nullable=True, index=True
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="SET NULL", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
repo_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("managed_repos.id", ondelete="SET NULL"), nullable=True, index=True
|
||||
|
|
|
|||
44
api/models/work_record_identifier_alias.py
Normal file
44
api/models/work_record_identifier_alias.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import CheckConstraint, DateTime, String, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from api.models.base import Base, TimestampMixin, new_uuid
|
||||
|
||||
|
||||
class WorkRecordIdentifierAlias(Base, TimestampMixin):
|
||||
"""Durable provenance for a canonical work-record UUID replacement."""
|
||||
|
||||
__tablename__ = "work_record_identifier_aliases"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"record_kind IN ('workplan', 'task')",
|
||||
name="ck_work_record_identifier_aliases_kind",
|
||||
),
|
||||
CheckConstraint(
|
||||
"migration_status IN ('prepared', 'applied', 'reversed')",
|
||||
name="ck_work_record_identifier_aliases_status",
|
||||
),
|
||||
UniqueConstraint("old_id", name="uq_work_record_identifier_aliases_old_id"),
|
||||
UniqueConstraint("new_id", name="uq_work_record_identifier_aliases_new_id"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), primary_key=True, default=new_uuid
|
||||
)
|
||||
old_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False)
|
||||
new_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False)
|
||||
record_kind: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
record_id: Mapped[str] = mapped_column(String(160), nullable=False, index=True)
|
||||
repo_slug: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
|
||||
namespace: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
plan_sha256: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
||||
migration_status: Mapped[str] = mapped_column(
|
||||
String(20), nullable=False, default="prepared", server_default="prepared"
|
||||
)
|
||||
applied_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
reversed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
|
@ -45,19 +45,19 @@ class WorkplanDependency(Base, TimestampMixin):
|
|||
)
|
||||
from_workplan_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="CASCADE"),
|
||||
ForeignKey("workplans.id", ondelete="CASCADE", onupdate="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
to_workplan_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="CASCADE"),
|
||||
ForeignKey("workplans.id", ondelete="CASCADE", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
to_task_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("tasks.id", ondelete="CASCADE"),
|
||||
ForeignKey("tasks.id", ondelete="CASCADE", onupdate="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
|
|
@ -72,4 +72,4 @@ class WorkplanDependency(Base, TimestampMixin):
|
|||
to_workplan: Mapped["Workplan | None"] = relationship( # noqa: F821
|
||||
"Workplan", foreign_keys=[to_workplan_id]
|
||||
)
|
||||
to_task: Mapped["Task | None"] = relationship("Task", foreign_keys=[to_task_id]) # noqa: F821
|
||||
to_task: Mapped["Task | None"] = relationship("Task", foreign_keys=[to_task_id]) # noqa: F821
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class WorkplanLaunchRequest(Base, TimestampMixin):
|
|||
)
|
||||
workplan_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("workplans.id", ondelete="CASCADE"),
|
||||
ForeignKey("workplans.id", ondelete="CASCADE", onupdate="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue