"""Album naming proposal persistence (US03-03). One current proposal row per album label (the US03-01 identity). The row records what was proposed, from which evidence version and model/prompt version, the user's edit, the approval state, and an optimistic ``version`` that every edit/approval must present so stale browser state cannot overwrite a newer decision. Approval records intent only — no rename happens here (that is Phase D). """ from __future__ import annotations from datetime import datetime from sqlalchemy import DateTime, Float, Integer, String, func from sqlalchemy.orm import Mapped, mapped_column from photo_pipeline.db import Base class AlbumProposal(Base): __tablename__ = "album_proposals" id: Mapped[str] = mapped_column(String, primary_key=True) album: Mapped[str] = mapped_column(String, nullable=False, unique=True, index=True) proposed_name: Mapped[str | None] = mapped_column(String) final_name: Mapped[str | None] = mapped_column(String) rationale: Mapped[str | None] = mapped_column(String) confidence: Mapped[float | None] = mapped_column(Float) # pending | proposed | edited | approved | error status: Mapped[str] = mapped_column(String, nullable=False, default="pending") error_code: Mapped[str | None] = mapped_column(String) error_message: Mapped[str | None] = mapped_column(String) evidence_version: Mapped[str | None] = mapped_column(String) model: Mapped[str | None] = mapped_column(String) prompt_version: Mapped[str | None] = mapped_column(String) raw_response: Mapped[str | None] = mapped_column(String) version: Mapped[int] = mapped_column(Integer, nullable=False, default=1) approved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() )