Files
FabledScribe/src/scribe/models/canonical_system.py
T
bvandeusenandClaude Opus 5.5 f6734291c9 docs(6206): the remaining models, routes, mcp/tools/repos and config comments drop citations
A shipped tool docstring (mcp/tools/repos bind_repo) cited this instance's
"rule 1: dev is home"; it now says what a dev-branch project should do.
routes/knowledge, routes/tasks, routes/systems and models/retrieval_log state
the property each guards rather than the bug that once broke it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:44:31 -04:00

59 lines
2.5 KiB
Python

from sqlalchemy import Index, Integer, Text, text
from sqlalchemy.orm import Mapped, mapped_column
from scribe.models import Base
from scribe.models.base import SoftDeleteMixin, TimestampMixin, iso
class CanonicalSystem(Base, TimestampMixin, SoftDeleteMixin):
"""A GLOBAL area name — the shared vocabulary every project's Systems
can point at.
A `System` is per-project and NOT NULL on project_id, so nothing outside a
project can reference one: a rule that applies across projects has no way
to say "this is about CI" without chaining itself to one project's row.
This table is that join key. It carries no `user_id` on purpose — a shared
project must INHERIT the vocabulary rather than re-earn it, so the catalog
is global and the same word means the same thing in every install.
It is a convergence aid, never a gate: `systems.canonical_id` is nullable,
an unmapped System stays fully usable, and `record_systems` never sees this
table at all — the local name is a legitimate local label and is never
rewritten to match.
`slug` is the match key, not a display value. It folds the spelling
differences that produced four names for one area on the author's own
instance ("CI & Release" / "CI and Release" / "CI & release"): an exact slug
hit maps automatically, and anything short of that becomes a proposal for a
human to confirm. See services/canonical_systems.canonical_slug.
"""
__tablename__ = "canonical_systems"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(Text, nullable=False)
# Normalized match key — unique among LIVE rows, so a soft-deleted entry
# doesn't block recreating the same area (the partial-unique convention
# rules/topics already use).
slug: Mapped[str] = mapped_column(Text, nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
order_index: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
__table_args__ = (
Index(
"uq_canonical_systems_slug", "slug",
unique=True, postgresql_where=text("deleted_at IS NULL"),
),
)
def to_dict(self) -> dict:
return {
"id": self.id,
"name": self.name,
"slug": self.slug,
"description": self.description,
"order_index": self.order_index,
"created_at": iso(self.created_at),
"updated_at": iso(self.updated_at),
}