moai/.planning/phases/01-foundation/01-02-PLAN.md
Mikkel Georgsen c48eb1cea7 docs(01): create phase 1 foundation plans
Phase 1: Foundation
- 3 plans created
- 9 total tasks defined
- Ready for execution

Plan 01: Project scaffolding (pyproject.toml, pre-commit, src layout)
Plan 02: SQLAlchemy models (Project, Discussion, Round, Message, Consensus)
Plan 03: Database setup and model tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:57:27 +00:00

6.5 KiB

phase plan type
01-foundation 02 execute
Create SQLAlchemy models for Project, Discussion, Round, Message, and Consensus.

Purpose: Define the data model that powers all discussion features. Output: Complete models.py with all relationships, ready for database creation.

<execution_context> ~/.claude/get-shit-done/workflows/execute-phase.md ~/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @SPEC.md @CLAUDE.md

Data model from SPEC.md:

Project (has many) -> Discussion (has many) -> Round (has many) -> Message
                   \-> Discussion (has one) -> Consensus

Field specifications:

  • Project: id (uuid), name, created_at, updated_at, models (JSON array), settings (JSON)
  • Discussion: id, project_id (FK), question, type (open|discuss), status (active|completed), created_at
  • Round: id, discussion_id (FK), round_number, type (parallel|sequential)
  • Message: id, round_id (FK), model, content, timestamp, is_direct
  • Consensus: id, discussion_id (FK), agreements (JSON array), disagreements (JSON array), generated_at, generated_by

Constraints:

  • Use SQLAlchemy 2.0 style (mapped_column, DeclarativeBase)
  • Type hints required
  • SQLite compatible (use JSON type, not ARRAY)
Task 1: Create base model and enums src/moai/core/models.py Create src/moai/core/models.py with:

Imports: SQLAlchemy 2.0 style (DeclarativeBase, Mapped, mapped_column, relationship), uuid, datetime, enum

Base class:

  • class Base(DeclarativeBase): pass

Enums (use Python Enum, store as string):

  • DiscussionType: OPEN = "open", DISCUSS = "discuss"
  • DiscussionStatus: ACTIVE = "active", COMPLETED = "completed"
  • RoundType: PARALLEL = "parallel", SEQUENTIAL = "sequential"

UUID helper:

  • Use uuid4() for default IDs
  • Store as String(36) for SQLite compatibility (NOT native UUID type)

Add module docstring explaining the data model. python -c "from moai.core.models import Base, DiscussionType, DiscussionStatus, RoundType" Base class and enums importable

Task 2: Create Project and Discussion models src/moai/core/models.py Add to models.py:

Project model:

  • id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid4()))
  • name: Mapped[str] = mapped_column(String(255))
  • created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
  • updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
  • models: Mapped[list] = mapped_column(JSON, default=list) - stores ["claude", "gpt", "gemini"]
  • settings: Mapped[dict] = mapped_column(JSON, default=dict) - stores {default_rounds, consensus_threshold, system_prompt_override}
  • discussions: Mapped[list["Discussion"]] = relationship(back_populates="project", cascade="all, delete-orphan")

Discussion model:

  • id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid4()))
  • project_id: Mapped[str] = mapped_column(ForeignKey("project.id"))
  • question: Mapped[str] = mapped_column(Text)
  • type: Mapped[DiscussionType] = mapped_column(Enum(DiscussionType))
  • status: Mapped[DiscussionStatus] = mapped_column(Enum(DiscussionStatus), default=DiscussionStatus.ACTIVE)
  • created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
  • project: Mapped["Project"] = relationship(back_populates="discussions")
  • rounds: Mapped[list["Round"]] = relationship(back_populates="discussion", cascade="all, delete-orphan")
  • consensus: Mapped["Consensus"] = relationship(back_populates="discussion", uselist=False, cascade="all, delete-orphan")

Use tablename = "project" and "discussion" (singular, lowercase). python -c "from moai.core.models import Project, Discussion; print(Project.tablename, Discussion.tablename)" Project and Discussion models defined with bidirectional relationships

Task 3: Create Round, Message, and Consensus models src/moai/core/models.py Add to models.py:

Round model:

  • id: Mapped[str] (uuid, primary key)
  • discussion_id: Mapped[str] = mapped_column(ForeignKey("discussion.id"))
  • round_number: Mapped[int]
  • type: Mapped[RoundType] = mapped_column(Enum(RoundType))
  • discussion: Mapped["Discussion"] = relationship(back_populates="rounds")
  • messages: Mapped[list["Message"]] = relationship(back_populates="round", cascade="all, delete-orphan")

Message model:

  • id: Mapped[str] (uuid, primary key)
  • round_id: Mapped[str] = mapped_column(ForeignKey("round.id"))
  • model: Mapped[str] = mapped_column(String(50)) - e.g., "claude", "gpt", "gemini"
  • content: Mapped[str] = mapped_column(Text)
  • timestamp: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
  • is_direct: Mapped[bool] = mapped_column(Boolean, default=False) - true if @mentioned
  • round: Mapped["Round"] = relationship(back_populates="messages")

Consensus model:

  • id: Mapped[str] (uuid, primary key)
  • discussion_id: Mapped[str] = mapped_column(ForeignKey("discussion.id"), unique=True)
  • agreements: Mapped[list] = mapped_column(JSON, default=list) - bullet point strings
  • disagreements: Mapped[list] = mapped_column(JSON, default=list) - [{topic, positions: {model: position}}]
  • generated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
  • generated_by: Mapped[str] = mapped_column(String(50)) - which model summarized
  • discussion: Mapped["Discussion"] = relationship(back_populates="consensus")

Use tablename = "round", "message", "consensus" (singular, lowercase). python -c "from moai.core.models import Round, Message, Consensus; print('All models imported')" All 5 models defined with complete relationships

Before declaring plan complete: - [ ] `python -c "from moai.core.models import Base, Project, Discussion, Round, Message, Consensus"` succeeds - [ ] `ruff check src/moai/core/models.py` passes - [ ] All foreign keys reference correct tables - [ ] All relationships are bidirectional - [ ] All fields have type hints

<success_criteria>

  • All tasks completed
  • All 5 models importable
  • No linting errors
  • Relationships match SPEC.md data model diagram </success_criteria>
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`