from sqlalchemy import BigInteger, Index, Text from sqlalchemy.orm import Mapped, mapped_column from scribe.models import Base from scribe.models.base import CreatedAtMixin, iso SURFACED = "surfaced" PULLED = "pulled" class SystemUsageEvent(Base, CreatedAtMixin): """One row per time a System's rulings were SHOWN to the agent because its files were touched, or the System was PULLED in full. The third of the usage tables, after `note_usage_events` and `rule_usage_events`, and separate from both for the reason the rule twin gives: identity at restore. A System id is its own namespace, mapped through the restore's system map; parked in either sibling's id column it would come back attached to whatever note or rule took that number. What it answers: whether an area's rulings, delivered by path rather than by a ranker, are then read (`get_system`) — and so whether delivery by path earns its line. FK-free on `system_id`, `user_id` and `project_id`, like its siblings: telemetry outlives the row it describes. """ __tablename__ = "system_usage_events" id: Mapped[int] = mapped_column(BigInteger, primary_key=True) user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) system_id: Mapped[int] = mapped_column(BigInteger, nullable=False) # 'surfaced' | 'pulled'. Plain Text, no CHECK, like the siblings. event: Mapped[str] = mapped_column(Text, nullable=False) # Which surface produced it — a convention, not a vocabulary (see the # note twin). `grep -rn record_system_ src/` is the authoritative list. source: Mapped[str] = mapped_column(Text, nullable=False) # The project the READER was in, not the System's own. project_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) __table_args__ = ( Index("ix_system_usage_system_event", "system_id", "event"), Index("ix_system_usage_created_at", "created_at"), ) def to_dict(self) -> dict: return { "id": self.id, "created_at": iso(self.created_at), "user_id": self.user_id, "system_id": self.system_id, "event": self.event, "source": self.source, "project_id": self.project_id, }