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"), 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")