init commit

This commit is contained in:
2026-03-18 06:40:53 -05:00
commit dd227be9b0
53 changed files with 18575 additions and 0 deletions

221
backend/app/models.py Normal file
View File

@@ -0,0 +1,221 @@
import enum
from datetime import datetime, timezone
from sqlalchemy import Enum, ForeignKey, Index, Integer, String, Text, Boolean, DateTime, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from .database import Base
class Priority(str, enum.Enum):
critical = "critical"
high = "high"
medium = "medium"
low = "low"
class Status(str, enum.Enum):
idea = "idea"
draft = "draft"
todo = "todo"
in_progress = "in_progress"
done = "done"
class NodeType(str, enum.Enum):
pillar = "pillar"
requirement = "requirement"
sub_requirement = "sub_requirement"
leaf = "leaf"
planning = "planning"
class CodeLanguage(str, enum.Enum):
python = "python"
cpp = "cpp"
typescript = "typescript"
class TestStatus(str, enum.Enum):
not_written = "not_written"
written = "written"
passing = "passing"
failing = "failing"
def _utcnow():
return datetime.now(timezone.utc)
class RequirementNode(Base):
__tablename__ = "requirement_nodes"
id: Mapped[str] = mapped_column(String(64), primary_key=True)
title: Mapped[str] = mapped_column(String(256), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
priority: Mapped[Priority] = mapped_column(Enum(Priority), default=Priority.medium)
phase: Mapped[int | None] = mapped_column(Integer, nullable=True)
status: Mapped[Status] = mapped_column(Enum(Status), default=Status.draft)
node_type: Mapped[NodeType] = mapped_column(Enum(NodeType), default=NodeType.requirement)
parent_id: Mapped[str | None] = mapped_column(
String(64), ForeignKey("requirement_nodes.id", ondelete="CASCADE"), nullable=True
)
sort_order: Mapped[int] = mapped_column(Integer, default=0)
project_id: Mapped[int | None] = mapped_column(
Integer, ForeignKey("projects.id", ondelete="CASCADE"), nullable=True
)
# Quality gate flags
traceable: Mapped[bool] = mapped_column(Boolean, default=False)
measurable: Mapped[bool] = mapped_column(Boolean, default=False)
consistent: Mapped[bool] = mapped_column(Boolean, default=False)
single_purpose: Mapped[bool] = mapped_column(Boolean, default=False)
# Issue tracker integration
gitea_issue_number: Mapped[int | None] = mapped_column(Integer, nullable=True)
gitea_repo: Mapped[str | None] = mapped_column(String(256), nullable=True)
# Acceptance criteria and code (all node types)
acceptance_criteria: Mapped[str | None] = mapped_column(Text, nullable=True)
test_spec: Mapped[str | None] = mapped_column(Text, nullable=True)
test_code: Mapped[str | None] = mapped_column(Text, nullable=True)
impl_code: Mapped[str | None] = mapped_column(Text, nullable=True)
code_language: Mapped[CodeLanguage | None] = mapped_column(
Enum(CodeLanguage), nullable=True
)
target_test_file: Mapped[str | None] = mapped_column(String(512), nullable=True)
target_impl_file: Mapped[str | None] = mapped_column(String(512), nullable=True)
test_status: Mapped[TestStatus] = mapped_column(
Enum(TestStatus), default=TestStatus.not_written
)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
)
# Relationships
parent: Mapped["RequirementNode | None"] = relationship(
"RequirementNode", remote_side="RequirementNode.id", back_populates="children"
)
children: Mapped[list["RequirementNode"]] = relationship(
"RequirementNode", back_populates="parent", cascade="all, delete-orphan",
order_by="RequirementNode.sort_order"
)
project: Mapped["Project | None"] = relationship("Project", back_populates="requirements")
# Cross-pillar links (outgoing)
outgoing_links: Mapped[list["CrossPillarLink"]] = relationship(
"CrossPillarLink", foreign_keys="CrossPillarLink.source_id",
back_populates="source", cascade="all, delete-orphan"
)
incoming_links: Mapped[list["CrossPillarLink"]] = relationship(
"CrossPillarLink", foreign_keys="CrossPillarLink.target_id",
back_populates="target", cascade="all, delete-orphan"
)
history: Mapped[list["RequirementHistory"]] = relationship(
"RequirementHistory", back_populates="requirement",
cascade="all, delete-orphan", order_by="RequirementHistory.version"
)
class CrossPillarLink(Base):
__tablename__ = "cross_pillar_links"
source_id: Mapped[str] = mapped_column(
String(64), ForeignKey("requirement_nodes.id", ondelete="CASCADE"), primary_key=True
)
target_id: Mapped[str] = mapped_column(
String(64), ForeignKey("requirement_nodes.id", ondelete="CASCADE"), primary_key=True
)
relationship_type: Mapped[str] = mapped_column(
String(64), default="shared_parent"
)
source: Mapped[RequirementNode] = relationship(
"RequirementNode", foreign_keys=[source_id], back_populates="outgoing_links"
)
target: Mapped[RequirementNode] = relationship(
"RequirementNode", foreign_keys=[target_id], back_populates="incoming_links"
)
class Project(Base):
__tablename__ = "projects"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(256), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
repo_path: Mapped[str | None] = mapped_column(String(512), nullable=True)
repo_url: Mapped[str | None] = mapped_column(String(512), nullable=True)
requirements_yaml_path: Mapped[str | None] = mapped_column(String(512), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
requirements: Mapped[list[RequirementNode]] = relationship(
"RequirementNode", back_populates="project", cascade="all, delete-orphan"
)
layouts: Mapped[list["GraphLayout"]] = relationship(
"GraphLayout", back_populates="project", cascade="all, delete-orphan"
)
class RequirementHistory(Base):
__tablename__ = "requirement_history"
__table_args__ = (
UniqueConstraint("requirement_id", "version", name="uq_requirement_version"),
Index("idx_req_history_req_version", "requirement_id", "version"),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
requirement_id: Mapped[str] = mapped_column(
String(64), ForeignKey("requirement_nodes.id", ondelete="CASCADE"), nullable=False
)
version: Mapped[int] = mapped_column(Integer, nullable=False)
# Snapshot of editable fields
title: Mapped[str] = mapped_column(String(256), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
priority: Mapped[str] = mapped_column(String(16), nullable=False)
phase: Mapped[int | None] = mapped_column(Integer, nullable=True)
status: Mapped[str] = mapped_column(String(16), nullable=False)
node_type: Mapped[str] = mapped_column(String(32), nullable=False)
acceptance_criteria: Mapped[str | None] = mapped_column(Text, nullable=True)
test_spec: Mapped[str | None] = mapped_column(Text, nullable=True)
test_code: Mapped[str | None] = mapped_column(Text, nullable=True)
impl_code: Mapped[str | None] = mapped_column(Text, nullable=True)
code_language: Mapped[str | None] = mapped_column(String(16), nullable=True)
target_test_file: Mapped[str | None] = mapped_column(String(512), nullable=True)
target_impl_file: Mapped[str | None] = mapped_column(String(512), nullable=True)
measurable: Mapped[bool] = mapped_column(Boolean, default=False)
consistent: Mapped[bool] = mapped_column(Boolean, default=False)
single_purpose: Mapped[bool] = mapped_column(Boolean, default=False)
gitea_issue_number: Mapped[int | None] = mapped_column(Integer, nullable=True)
gitea_repo: Mapped[str | None] = mapped_column(String(256), nullable=True)
# Metadata
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
change_summary: Mapped[str | None] = mapped_column(String(512), nullable=True)
requirement: Mapped[RequirementNode] = relationship(
"RequirementNode", back_populates="history"
)
class GraphLayout(Base):
__tablename__ = "graph_layouts"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(128), nullable=False)
description: Mapped[str | None] = mapped_column(String(512), nullable=True)
project_id: Mapped[int | None] = mapped_column(
Integer, ForeignKey("projects.id", ondelete="CASCADE"), nullable=True
)
node_positions: Mapped[str] = mapped_column(Text, nullable=False)
viewport: Mapped[str] = mapped_column(Text, nullable=False)
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
)
project: Mapped["Project | None"] = relationship("Project", back_populates="layouts")