""" Tenants Domain Models The tenant registry (core-api T-8 / workspace D-73): every Book and Scribe `owner_tenant` column carries a foreign key to `tenants.id` (migration 007), and `TenantsService.require_tenant` is the one place a caller's tenant is checked known, active, and — for an Office caller — granted to the calling consumer. SQLAlchemy 2.0 Mapped/mapped_column style, timestamptz, following src/domains/book/models.py. `TenantConsumer.default_address` (migration 008, core-api T-11) is each grant's fallback address for a caller that names none and holds no session binding — claude.ai's connector keeps no MCP session between tool calls, so the Office needs this to answer without one. The composite FK keeps it honest: a default always names a real address in the same tenant. """ from datetime import datetime from typing import Optional from sqlalchemy import ( CheckConstraint, DateTime, ForeignKey, ForeignKeyConstraint, String, Text, func, ) from sqlalchemy.orm import Mapped, mapped_column from src.shared.database import Base TENANT_KINDS = ("household", "person", "test") TENANT_STATUSES = ("active", "suspended") # The slug shape a tenant id carries, identical to # src/domains/auth/callers.py's `_TENANT_RE` (tatlock's own tenant ids carry # underscores, e.g. `llm_tester`) — not imported from there because that # module's constants are private to it, following # src/domains/scribe/models.py's same note about ADDRESS_NAME_PATTERN. # Asserted equal in tests/test_tenants.py. TENANT_ID_PATTERN = r"^[a-z0-9_][a-z0-9_.-]{0,62}$" class Tenant(Base): """A tenant: tatlock's own per-household/person/test namespace.""" __tablename__ = "tenants" __table_args__ = ( CheckConstraint("kind IN ('household', 'person', 'test')", name="ck_tenants_kind"), CheckConstraint("status IN ('active', 'suspended')", name="ck_tenants_status"), ) id: Mapped[str] = mapped_column(String(63), primary_key=True) display_name: Mapped[str] = mapped_column(Text, nullable=False) kind: Mapped[str] = mapped_column(String(20), nullable=False) status: Mapped[str] = mapped_column(String(20), nullable=False, default="active") created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False, ) def __repr__(self) -> str: return f"" class TenantConsumer(Base): """A grant: consumer `consumer` may act in tenant `tenant_id`, with an optional default address (migration 008, core-api T-11) — the composite FK below means a non-null default always names a real `scribe_addresses` row in this same tenant.""" __tablename__ = "tenant_consumers" __table_args__ = ( ForeignKeyConstraint( ["tenant_id", "default_address"], ["scribe_addresses.owner_tenant", "scribe_addresses.name"], name="fk_tenant_consumers_default_address", ), ) tenant_id: Mapped[str] = mapped_column( String(63), ForeignKey("tenants.id"), primary_key=True ) consumer: Mapped[str] = mapped_column(String(63), primary_key=True) # FK to scribe_addresses (owner_tenant, name) added by migration 008. default_address: Mapped[Optional[str]] = mapped_column(String(63), nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) def __repr__(self) -> str: return f""