matrix-builder-bot
deploy: matrix-builder@ae902df6985a
66d0c75
Raw
History Blame Contribute Delete
13.3 kB
"""SQLAlchemy ORM — the real persistence layer (Batch C1).
Replaces the placeholder dataclass ``Entity``/``InMemoryRepository`` with mapped Postgres
tables. The schema follows the design document's SQL baseline (users, projects, and the seven
workflow tables) with one deliberate addition: every workflow table carries a denormalized
``owner_id`` so row-level security can be a simple, fast ``owner_id = app_current_user()``
policy rather than a deep parent-chain subquery.
Tables (DDL + RLS) are created by the Alembic migration, not by ``create_all``, so the RLS
policies and the ``app_current_user()`` helper are part of the versioned schema.
"""
from __future__ import annotations
from datetime import datetime
from sqlalchemy import (
BigInteger,
DateTime,
ForeignKey,
Integer,
String,
Text,
UniqueConstraint,
func,
)
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
# Tables that are exposed to API callers and therefore get owner-scoped RLS.
RLS_TABLES: tuple[str, ...] = (
"projects",
"bundle_versions",
"build_batches",
"matrix_commits",
"prompt_versions",
"validation_runs",
"validation_findings",
"artifacts",
"run_events",
"gitpilot_runs",
"design_bundles",
)
class Base(DeclarativeBase):
pass
def _pk() -> Mapped[str]:
# UUIDs generated app-side (or by gen_random_uuid in the migration default).
return mapped_column(String(36), primary_key=True)
def _ts() -> Mapped[datetime]:
return mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
class User(Base):
__tablename__ = "users"
id: Mapped[str] = _pk()
email: Mapped[str | None] = mapped_column(String(320), unique=True)
display_name: Mapped[str | None] = mapped_column(String(200))
plan: Mapped[str] = mapped_column(String(32), default="free", nullable=False)
created_at: Mapped[datetime] = _ts()
class Project(Base):
__tablename__ = "projects"
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(
String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
)
title: Mapped[str] = mapped_column(Text, nullable=False)
slug: Mapped[str] = mapped_column(String(120), nullable=False)
description: Mapped[str] = mapped_column(Text, default="", nullable=False)
status: Mapped[str] = mapped_column(String(32), default="active", nullable=False)
privacy: Mapped[str] = mapped_column(String(16), default="private", nullable=False)
created_at: Mapped[datetime] = _ts()
updated_at: Mapped[datetime] = _ts()
class BundleVersion(Base):
__tablename__ = "bundle_versions"
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
project_id: Mapped[str] = mapped_column(
String(36), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True
)
parent_version_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("bundle_versions.id")
)
version_label: Mapped[str] = mapped_column(String(32), nullable=False) # v1.0.0
title: Mapped[str] = mapped_column(Text, nullable=False)
requirements_md: Mapped[str] = mapped_column(Text, default="", nullable=False)
blueprint_artifact_id: Mapped[str | None] = mapped_column(String(36))
status: Mapped[str] = mapped_column(String(32), default="active", nullable=False)
created_by: Mapped[str] = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
created_at: Mapped[datetime] = _ts()
class BuildBatch(Base):
__tablename__ = "build_batches"
__table_args__ = (UniqueConstraint("version_id", "ordinal", name="uq_batch_ordinal"),)
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
version_id: Mapped[str] = mapped_column(
String(36), ForeignKey("bundle_versions.id", ondelete="CASCADE"), nullable=False, index=True
)
parent_commit_id: Mapped[str | None] = mapped_column(String(36))
ordinal: Mapped[int] = mapped_column(Integer, nullable=False)
title: Mapped[str] = mapped_column(Text, nullable=False)
goal_md: Mapped[str] = mapped_column(Text, default="", nullable=False)
change_type: Mapped[str] = mapped_column(
String(32), nullable=False
) # small-update|add-feature|fix-issue
status: Mapped[str] = mapped_column(String(32), default="draft", nullable=False)
requested_by: Mapped[str] = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
created_at: Mapped[datetime] = _ts()
class MatrixCommit(Base):
__tablename__ = "matrix_commits"
__table_args__ = (UniqueConstraint("version_id", "commit_no", name="uq_commit_no"),)
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
batch_id: Mapped[str] = mapped_column(
String(36), ForeignKey("build_batches.id", ondelete="CASCADE"), nullable=False, index=True
)
version_id: Mapped[str] = mapped_column(
String(36), ForeignKey("bundle_versions.id", ondelete="CASCADE"), nullable=False, index=True
)
parent_commit_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("matrix_commits.id")
)
commit_no: Mapped[int] = mapped_column(Integer, nullable=False)
summary: Mapped[str] = mapped_column(Text, default="", nullable=False)
tree_hash: Mapped[str] = mapped_column(String(80), nullable=False)
validation_status: Mapped[str] = mapped_column(String(32), default="not-run", nullable=False)
prompt_pack_id: Mapped[str | None] = mapped_column(String(36))
# The commit manifest (added/changed/deleted file lists + scope), persisted so diffs and
# the build timeline can be reconstructed without re-reading object storage (Batch C2).
manifest: Mapped[dict] = mapped_column("manifest_jsonb", JSONB, default=dict, nullable=False)
created_at: Mapped[datetime] = _ts()
class PromptVersion(Base):
__tablename__ = "prompt_versions"
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
batch_id: Mapped[str] = mapped_column(
String(36), ForeignKey("build_batches.id", ondelete="CASCADE"), nullable=False, index=True
)
coder: Mapped[str] = mapped_column(String(32), nullable=False)
prompt_text: Mapped[str] = mapped_column(Text, nullable=False)
constraints: Mapped[dict] = mapped_column(
"constraints_jsonb", JSONB, default=dict, nullable=False
)
artifact_id: Mapped[str | None] = mapped_column(String(36))
created_at: Mapped[datetime] = _ts()
class ValidationRun(Base):
__tablename__ = "validation_runs"
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
# Nullable since C3: an async run is validated *before* a commit exists; the commit is
# created only on approval, otherwise the run carries a repair suggestion instead.
commit_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("matrix_commits.id", ondelete="CASCADE"), index=True
)
batch_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("build_batches.id", ondelete="CASCADE"), index=True
)
status: Mapped[str] = mapped_column(
String(32), nullable=False
) # running|approved|needs-repair|rejected|failed
score: Mapped[int | None] = mapped_column(Integer)
runner: Mapped[str] = mapped_column(String(32), default="local", nullable=False)
started_at: Mapped[datetime] = _ts()
ended_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
class ValidationFinding(Base):
__tablename__ = "validation_findings"
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
validation_run_id: Mapped[str] = mapped_column(
String(36), ForeignKey("validation_runs.id", ondelete="CASCADE"), nullable=False, index=True
)
severity: Mapped[str] = mapped_column(String(16), nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False)
check_name: Mapped[str] = mapped_column(String(120), nullable=False)
file_path: Mapped[str | None] = mapped_column(Text)
message: Mapped[str] = mapped_column(Text, nullable=False)
remediation: Mapped[str | None] = mapped_column(Text)
class RunEvent(Base):
"""Append-only event log for a validation run (Batch C3).
Rows are never updated or deleted. ``seq`` is a per-run monotonic cursor: clients page with
``?after=`` and WebSocket subscribers replay missed events by ``seq`` before streaming live.
"""
__tablename__ = "run_events"
__table_args__ = (UniqueConstraint("run_id", "seq", name="uq_run_event_seq"),)
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
run_id: Mapped[str] = mapped_column(
String(36), ForeignKey("validation_runs.id", ondelete="CASCADE"), nullable=False, index=True
)
seq: Mapped[int] = mapped_column(BigInteger, nullable=False)
event_type: Mapped[str] = mapped_column(String(48), nullable=False)
payload: Mapped[dict] = mapped_column("payload_jsonb", JSONB, default=dict, nullable=False)
created_at: Mapped[datetime] = _ts()
class Artifact(Base):
__tablename__ = "artifacts"
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
project_id: Mapped[str] = mapped_column(
String(36), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True
)
version_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("bundle_versions.id", ondelete="CASCADE")
)
commit_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("matrix_commits.id", ondelete="CASCADE")
)
artifact_type: Mapped[str] = mapped_column(
String(32), nullable=False
) # bundle_zip|prompt_md|patch_diff|log|manifest|thumbnail_svg
storage_key: Mapped[str] = mapped_column(Text, nullable=False)
sha256: Mapped[str] = mapped_column(String(80), nullable=False)
size_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False)
created_at: Mapped[datetime] = _ts()
class GitPilotRun(Base):
"""A GitPilot run (cloud or local) recorded for history + audit (Batch 10).
Owner-scoped like every other workflow table. Records the run id, the bundle
it targeted, the synced GitPilot status, and the Matrix verdict — so a user's
runs survive restarts and are queryable.
"""
__tablename__ = "gitpilot_runs"
id: Mapped[str] = mapped_column(String(64), primary_key=True) # the gp-run-... id
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
bundle_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
task_id: Mapped[str | None] = mapped_column(String(64))
source: Mapped[str] = mapped_column(String(32), default="cloud") # cloud|local
status: Mapped[str] = mapped_column(String(32), default="queued")
test_status: Mapped[str] = mapped_column(String(32), default="not_run")
summary: Mapped[str] = mapped_column(Text, default="")
changed_files: Mapped[dict] = mapped_column(JSONB, default=list)
# Matrix verdict (approved|needs-repair|rejected) — null until validated.
validation_status: Mapped[str | None] = mapped_column(String(32))
can_commit: Mapped[bool] = mapped_column(default=False)
parent_run_id: Mapped[str | None] = mapped_column(String(64))
created_at: Mapped[datetime] = _ts()
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
)
class DesignBundleRecord(Base):
"""A saved Matrix Designer Blueprint Details + chat for a build (batch-10).
Owner-scoped like every other workflow table. Keyed by (owner, build, candidate)
so reopening a build restores the same blueprint and Talk-to-blueprint history.
"""
__tablename__ = "design_bundles"
id: Mapped[str] = _pk()
owner_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
build_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
candidate_id: Mapped[str] = mapped_column(String(32), nullable=False)
idea: Mapped[str] = mapped_column(Text, default="")
details: Mapped[dict] = mapped_column(JSONB, default=dict)
chat_history: Mapped[list] = mapped_column(JSONB, default=list)
created_at: Mapped[datetime] = _ts()
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
)
__table_args__ = (UniqueConstraint("owner_id", "build_id", "candidate_id", name="uq_design_bundle_owner_build_cand"),)
__all__ = [
"Base",
"RLS_TABLES",
"User",
"Project",
"BundleVersion",
"BuildBatch",
"MatrixCommit",
"PromptVersion",
"ValidationRun",
"ValidationFinding",
"RunEvent",
"Artifact",
"GitPilotRun",
"DesignBundleRecord",
]