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>
163 lines
6.5 KiB
Markdown
163 lines
6.5 KiB
Markdown
---
|
|
phase: 01-foundation
|
|
plan: 02
|
|
type: execute
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
~/.claude/get-shit-done/workflows/execute-phase.md
|
|
~/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<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)
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create base model and enums</name>
|
|
<files>src/moai/core/models.py</files>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>python -c "from moai.core.models import Base, DiscussionType, DiscussionStatus, RoundType"</verify>
|
|
<done>Base class and enums importable</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Create Project and Discussion models</name>
|
|
<files>src/moai/core/models.py</files>
|
|
<action>
|
|
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).
|
|
</action>
|
|
<verify>python -c "from moai.core.models import Project, Discussion; print(Project.__tablename__, Discussion.__tablename__)"</verify>
|
|
<done>Project and Discussion models defined with bidirectional relationships</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: Create Round, Message, and Consensus models</name>
|
|
<files>src/moai/core/models.py</files>
|
|
<action>
|
|
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).
|
|
</action>
|
|
<verify>python -c "from moai.core.models import Round, Message, Consensus; print('All models imported')"</verify>
|
|
<done>All 5 models defined with complete relationships</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
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
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
|
|
- All tasks completed
|
|
- All 5 models importable
|
|
- No linting errors
|
|
- Relationships match SPEC.md data model diagram
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`
|
|
</output>
|