feat(l1): extend FlowProposal with source/linked_ticket/validated_by_outcome
Adds source (NOT NULL, backfilled to 'manual_draft'), linked_ticket_id, linked_ticket_kind, validated_by_outcome columns. CHECK constraints on source values and linked_ticket_kind values. walked_path lives on the new l1_walk_sessions table (Task 6) — NOT on FlowProposal. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
|||||||
|
"""extend_flow_proposals_l1
|
||||||
|
|
||||||
|
Revision ID: ff6fe5895ea2
|
||||||
|
Revises: a8186f22506d
|
||||||
|
Create Date: 2026-05-28 16:26:06.932886
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'ff6fe5895ea2'
|
||||||
|
down_revision: Union[str, None] = 'a8186f22506d'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.add_column('flow_proposals', sa.Column('source', sa.String(30), nullable=True))
|
||||||
|
op.add_column('flow_proposals', sa.Column('linked_ticket_id', sa.String(64), nullable=True))
|
||||||
|
op.add_column('flow_proposals', sa.Column('linked_ticket_kind', sa.String(10), nullable=True))
|
||||||
|
op.add_column(
|
||||||
|
'flow_proposals',
|
||||||
|
sa.Column('validated_by_outcome', sa.Boolean(), nullable=False, server_default='false'),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Backfill existing rows then enforce NOT NULL on source
|
||||||
|
op.execute("UPDATE flow_proposals SET source = 'manual_draft' WHERE source IS NULL")
|
||||||
|
op.alter_column('flow_proposals', 'source', nullable=False)
|
||||||
|
|
||||||
|
op.create_check_constraint(
|
||||||
|
'ck_flow_proposals_source',
|
||||||
|
'flow_proposals',
|
||||||
|
"source IN ('ai_realtime_l1', 'kb_accelerator', 'manual_draft', 'ai_promoted')",
|
||||||
|
)
|
||||||
|
op.create_check_constraint(
|
||||||
|
'ck_flow_proposals_linked_ticket_kind',
|
||||||
|
'flow_proposals',
|
||||||
|
"linked_ticket_kind IS NULL OR linked_ticket_kind IN ('psa', 'internal')",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_constraint('ck_flow_proposals_linked_ticket_kind', 'flow_proposals', type_='check')
|
||||||
|
op.drop_constraint('ck_flow_proposals_source', 'flow_proposals', type_='check')
|
||||||
|
op.drop_column('flow_proposals', 'validated_by_outcome')
|
||||||
|
op.drop_column('flow_proposals', 'linked_ticket_kind')
|
||||||
|
op.drop_column('flow_proposals', 'linked_ticket_id')
|
||||||
|
op.drop_column('flow_proposals', 'source')
|
||||||
@@ -7,7 +7,7 @@ import uuid
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Optional, Any, TYPE_CHECKING
|
from typing import Optional, Any, TYPE_CHECKING
|
||||||
|
|
||||||
from sqlalchemy import String, Text, DateTime, ForeignKey, Integer, Float, CheckConstraint
|
from sqlalchemy import String, Text, DateTime, ForeignKey, Integer, Float, Boolean, CheckConstraint, text as sa_text
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||||
|
|
||||||
@@ -48,6 +48,14 @@ class FlowProposal(Base):
|
|||||||
"status IN ('pending', 'approved', 'modified', 'rejected', 'dismissed', 'auto_reinforced')",
|
"status IN ('pending', 'approved', 'modified', 'rejected', 'dismissed', 'auto_reinforced')",
|
||||||
name="ck_flow_proposals_status",
|
name="ck_flow_proposals_status",
|
||||||
),
|
),
|
||||||
|
CheckConstraint(
|
||||||
|
"source IN ('ai_realtime_l1', 'kb_accelerator', 'manual_draft', 'ai_promoted')",
|
||||||
|
name="ck_flow_proposals_source",
|
||||||
|
),
|
||||||
|
CheckConstraint(
|
||||||
|
"linked_ticket_kind IS NULL OR linked_ticket_kind IN ('psa', 'internal')",
|
||||||
|
name="ck_flow_proposals_linked_ticket_kind",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
id: Mapped[uuid.UUID] = mapped_column(
|
id: Mapped[uuid.UUID] = mapped_column(
|
||||||
@@ -135,6 +143,16 @@ class FlowProposal(Base):
|
|||||||
comment="The flow that was created/updated when this proposal was approved",
|
comment="The flow that was created/updated when this proposal was approved",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ── L1 workspace ──
|
||||||
|
source: Mapped[str] = mapped_column(
|
||||||
|
String(30), nullable=False, server_default=sa_text("'manual_draft'"),
|
||||||
|
)
|
||||||
|
linked_ticket_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
|
||||||
|
linked_ticket_kind: Mapped[Optional[str]] = mapped_column(String(10), nullable=True)
|
||||||
|
validated_by_outcome: Mapped[bool] = mapped_column(
|
||||||
|
Boolean(), nullable=False, server_default=sa_text('false'),
|
||||||
|
)
|
||||||
|
|
||||||
# ── Timestamps ──
|
# ── Timestamps ──
|
||||||
created_at: Mapped[datetime] = mapped_column(
|
created_at: Mapped[datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
|
||||||
|
|||||||
Reference in New Issue
Block a user