moai/.planning/phases/01-foundation/01-03-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

184 lines
5 KiB
Markdown

---
phase: 01-foundation
plan: 03
type: execute
---
<objective>
Create database module with async session management and write model tests.
Purpose: Enable database operations and validate models work correctly.
Output: Working database.py with async session factory, passing model tests.
</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
@src/moai/core/models.py
**Tech choices:**
- SQLAlchemy 2.0 async (create_async_engine, AsyncSession)
- aiosqlite for async SQLite
- pytest-asyncio for async tests
**From CLAUDE.md:**
- Testing: pytest, target 80%+ coverage on core logic
- Database: SQLAlchemy + SQLite, upgrades to PostgreSQL in Phase 2
</context>
<tasks>
<task type="auto">
<name>Task 1: Create database module with async session management</name>
<files>src/moai/core/database.py</files>
<action>
Create src/moai/core/database.py with:
**Imports:** sqlalchemy.ext.asyncio (create_async_engine, AsyncSession, async_sessionmaker), contextlib
**Module-level:**
- DATABASE_URL: str = "sqlite+aiosqlite:///./moai.db" (default, can be overridden)
- engine: AsyncEngine = None (initialized lazily)
- async_session_factory: async_sessionmaker = None
**Functions:**
1. init_db(url: str | None = None) -> None:
- Creates engine with echo=False
- Creates async_session_factory
- Stores in module globals
- Use: `create_async_engine(url, echo=False)`
2. async create_tables() -> None:
- Imports Base from models
- Runs `async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all)`
3. @contextlib.asynccontextmanager
async def get_session() -> AsyncGenerator[AsyncSession, None]:
- Yields session from factory
- Handles commit on success, rollback on exception
- Pattern: `async with async_session_factory() as session: yield session; await session.commit()`
4. async def close_db() -> None:
- Disposes engine: `await engine.dispose()`
Add module docstring explaining session management pattern.
</action>
<verify>python -c "from moai.core.database import init_db, create_tables, get_session, close_db"</verify>
<done>Database module importable with all functions</done>
</task>
<task type="auto">
<name>Task 2: Create model tests with in-memory database</name>
<files>tests/test_models.py</files>
<action>
Create tests/test_models.py with:
**Fixtures:**
- @pytest.fixture
async def db_session():
- init_db("sqlite+aiosqlite:///:memory:")
- await create_tables()
- async with get_session() as session: yield session
- await close_db()
**Test cases:**
1. test_create_project:
- Create Project(name="Test Project", models=["claude", "gpt"])
- Add to session, commit
- Assert id is set (UUID string), name correct, models correct
2. test_create_discussion_with_project:
- Create Project, add Discussion linked to it
- Assert discussion.project_id matches project.id
- Assert project.discussions contains the discussion
3. test_create_full_discussion_chain:
- Create Project -> Discussion -> Round -> Message
- Verify all relationships work
- Verify cascade (all linked when navigating relationships)
4. test_create_consensus:
- Create Discussion with Consensus
- Assert discussion.consensus is set
- Assert consensus.discussion links back
5. test_project_cascade_delete:
- Create Project with Discussion with Round with Message
- Delete Project
- Assert all children deleted (cascade)
Use pytest.mark.asyncio on all async tests.
Import all models and database functions.
</action>
<verify>pytest tests/test_models.py -v passes all tests</verify>
<done>5 model tests passing, cascade behavior verified</done>
</task>
<task type="auto">
<name>Task 3: Add .gitignore entries and verify full test suite</name>
<files>.gitignore</files>
<action>
Update .gitignore to add:
```
# Database
*.db
*.sqlite
*.sqlite3
# Python
__pycache__/
*.pyc
.pytest_cache/
.coverage
htmlcov/
# Virtual environments
.venv/
venv/
# IDE
.idea/
.vscode/
*.swp
```
Then run full test suite with coverage to verify everything works together.
</action>
<verify>pytest --cov=moai --cov-report=term-missing shows coverage, all tests pass</verify>
<done>.gitignore updated, tests pass with coverage report</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `pytest tests/test_models.py -v` passes all 5 tests
- [ ] `pytest --cov=moai --cov-report=term-missing` runs successfully
- [ ] `ruff check src tests` passes
- [ ] Database file (moai.db) is gitignored
- [ ] Phase 1 complete: scaffolding, models, database all working
</verification>
<success_criteria>
- All tasks completed
- All tests pass
- No linting errors
- Phase 1: Foundation complete
</success_criteria>
<output>
After completion, create `.planning/phases/01-foundation/01-03-SUMMARY.md` with:
- Summary of all 3 plans in Phase 1
- Final verification that foundation is complete
- Ready for Phase 2: Bot Core
</output>