Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
129 lines
4.7 KiB
Python
129 lines
4.7 KiB
Python
import enum
|
|
import uuid
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import CheckConstraint, DateTime, Enum, ForeignKey, String, Text
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from sqlalchemy.sql import func
|
|
|
|
from api.models.base import Base, TimestampMixin, new_uuid7
|
|
|
|
|
|
class IntakeLane(str, enum.Enum):
|
|
green = "green"
|
|
blue = "blue"
|
|
yellow = "yellow"
|
|
orange = "orange"
|
|
red = "red"
|
|
|
|
|
|
class IntakeStatus(str, enum.Enum):
|
|
open = "open"
|
|
vetted = "vetted"
|
|
routed = "routed"
|
|
closed = "closed"
|
|
|
|
|
|
class IntakeOutcome(str, enum.Enum):
|
|
promoted = "promoted"
|
|
declined = "declined"
|
|
absorbed = "absorbed"
|
|
|
|
|
|
OPEN_INTAKE_STATUSES = (IntakeStatus.open, IntakeStatus.vetted, IntakeStatus.routed)
|
|
|
|
|
|
class Intake(Base, TimestampMixin):
|
|
"""A `kind: intake` work record — a spark: idea, finding, directive, or
|
|
request, per canon/standards/work-record-types_v0.1.md. Lifecycle:
|
|
open -> vetted -> routed -> closed(promoted|declined|absorbed).
|
|
|
|
Fresh entity per the founder-reviewed architecture draft (2026-07-20,
|
|
WorkOrchestrationArchitectureDraft.md §8 item 6): not a rename/reuse of
|
|
the legacy `suggestions` table.
|
|
"""
|
|
|
|
__tablename__ = "intakes"
|
|
__table_args__ = (
|
|
CheckConstraint(
|
|
"topic_id IS NOT NULL OR workplan_id IS NOT NULL OR repo_id IS NOT NULL",
|
|
name="ck_intakes_topic_or_workplan_or_repo",
|
|
),
|
|
CheckConstraint(
|
|
"(status != 'closed') OR (outcome IS NOT NULL)",
|
|
name="ck_intakes_closed_requires_outcome",
|
|
),
|
|
CheckConstraint(
|
|
"(outcome != 'promoted') OR (promoted_to IS NOT NULL)",
|
|
name="ck_intakes_promoted_requires_promoted_to",
|
|
),
|
|
)
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True), primary_key=True, default=new_uuid7
|
|
)
|
|
topic_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
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", 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
|
|
)
|
|
title: Mapped[str] = mapped_column(String(500), nullable=False)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
lane: Mapped[IntakeLane] = mapped_column(
|
|
Enum(IntakeLane, name="intakelane"), nullable=False, default=IntakeLane.green
|
|
)
|
|
status: Mapped[IntakeStatus] = mapped_column(
|
|
Enum(IntakeStatus, name="intakestatus"),
|
|
nullable=False,
|
|
default=IntakeStatus.open,
|
|
index=True,
|
|
)
|
|
outcome: Mapped[IntakeOutcome | None] = mapped_column(
|
|
Enum(IntakeOutcome, name="intakeoutcome"), nullable=True
|
|
)
|
|
origin: Mapped[str | None] = mapped_column(String(200), nullable=True)
|
|
origin_ref: Mapped[str | None] = mapped_column(String(200), nullable=True, index=True)
|
|
promoted_to: Mapped[str | None] = mapped_column(String(200), nullable=True)
|
|
source_repo_path: Mapped[str | None] = mapped_column(
|
|
Text, nullable=True, doc="Repo-relative path of the source file this record was authored in."
|
|
)
|
|
routed_note: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
closed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
|
|
topic: Mapped["Topic | None"] = relationship("Topic", lazy="selectin") # noqa: F821
|
|
workplan: Mapped["Workplan | None"] = relationship("Workplan", lazy="selectin") # noqa: F821
|
|
repo: Mapped["ManagedRepo | None"] = relationship("ManagedRepo", lazy="selectin") # noqa: F821
|
|
notes: Mapped[list["IntakeNote"]] = relationship(
|
|
"IntakeNote",
|
|
back_populates="intake",
|
|
lazy="selectin",
|
|
order_by="IntakeNote.created_at",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
|
|
|
|
class IntakeNote(Base):
|
|
__tablename__ = "intake_notes"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=new_uuid7)
|
|
intake_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("intakes.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
)
|
|
author: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
|
content: Mapped[str] = mapped_column(Text, nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), nullable=False
|
|
)
|
|
|
|
intake: Mapped["Intake"] = relationship("Intake", back_populates="notes")
|