Create comprehensive OpenSpec proposal for implementing a minimal GraphQL API for member management with flexible validation requirements. Key features: - Only firstName required for member creation - Conditional validation (email/phone format validated only when provided) - Full CRUD operations via GraphQL - SQLite database with Alembic migrations - FastAPI + Strawberry GraphQL integration Includes 4 capability specs: - project-setup: Python project structure with uv dependency management - database-layer: SQLAlchemy async models with nullable fields - graphql-api: Strawberry schema with minimal required fields - member-crud: Business logic with conditional validation Implementation broken down into 20 discrete tasks across 5 phases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
236 lines
8.3 KiB
Markdown
236 lines
8.3 KiB
Markdown
# Spec: Project Setup
|
|
|
|
**Capability:** project-setup
|
|
**Status:** Draft
|
|
**Last Updated:** 2025-11-20
|
|
|
|
## Overview
|
|
|
|
This capability establishes the foundational Python project structure, dependency management, tooling configuration, and development environment for the Clubber application.
|
|
|
|
## ADDED Requirements
|
|
|
|
### Requirement: Python project structure with uv dependency management
|
|
|
|
The project MUST use Python 3.11+ with uv for fast, reliable dependency management and provide a standard src/ layout for code organization.
|
|
|
|
#### Scenario: Developer initializes new development environment
|
|
|
|
**Given** a developer has cloned the repository
|
|
**When** they run `uv sync`
|
|
**Then** all dependencies are installed in a virtual environment
|
|
**And** the environment is ready for development within 30 seconds
|
|
|
|
#### Scenario: Developer runs the application
|
|
|
|
**Given** dependencies are installed
|
|
**When** developer runs `uv run uvicorn src.main:app --reload`
|
|
**Then** the FastAPI server starts on http://localhost:8000
|
|
**And** GraphQL playground is available at http://localhost:8000/graphql
|
|
|
|
### Requirement: Project configuration in pyproject.toml
|
|
|
|
The project MUST define all metadata, dependencies, and tool configurations in pyproject.toml following modern Python packaging standards.
|
|
|
|
**Dependencies required:**
|
|
- fastapi >= 0.104.0 (Web framework)
|
|
- strawberry-graphql[fastapi] >= 0.215.0 (GraphQL integration)
|
|
- sqlalchemy[asyncio] >= 2.0.0 (ORM with async support)
|
|
- aiosqlite >= 0.19.0 (Async SQLite driver)
|
|
- alembic >= 1.12.0 (Database migrations)
|
|
- pydantic-settings >= 2.0.0 (Configuration management)
|
|
- uvicorn[standard] >= 0.24.0 (ASGI server)
|
|
|
|
**Development dependencies required:**
|
|
- pytest >= 7.4.0 (Test framework)
|
|
- pytest-asyncio >= 0.21.0 (Async test support)
|
|
- black >= 23.0.0 (Code formatting)
|
|
- ruff >= 0.1.0 (Linting)
|
|
- isort >= 5.12.0 (Import sorting)
|
|
|
|
#### Scenario: Dependencies are declared with version constraints
|
|
|
|
**Given** pyproject.toml exists
|
|
**When** developer inspects [project.dependencies]
|
|
**Then** all required packages are listed with minimum versions
|
|
**And** version constraints allow patch/minor updates
|
|
|
|
#### Scenario: Development tools are configured
|
|
|
|
**Given** pyproject.toml contains tool configurations
|
|
**When** developer runs `black .`
|
|
**Then** code is formatted with line length 88
|
|
**When** developer runs `ruff check .`
|
|
**Then** code is linted against configured rules
|
|
|
|
### Requirement: Source directory structure following layered architecture
|
|
|
|
The project MUST organize code into src/ directory with clear separation of concerns across API, business logic, and data layers.
|
|
|
|
**Required directory structure:**
|
|
```
|
|
src/
|
|
├── __init__.py
|
|
├── main.py # FastAPI application, startup/shutdown
|
|
├── config.py # Settings and configuration
|
|
├── database.py # Database connection, session factory
|
|
├── models/ # SQLAlchemy ORM models
|
|
│ ├── __init__.py
|
|
│ └── member.py
|
|
├── schemas/ # Strawberry GraphQL types
|
|
│ ├── __init__.py
|
|
│ └── member.py
|
|
└── resolvers/ # GraphQL query/mutation resolvers
|
|
├── __init__.py
|
|
└── member.py
|
|
```
|
|
|
|
#### Scenario: Code is organized by architectural layer
|
|
|
|
**Given** the src/ directory exists
|
|
**When** a developer navigates the codebase
|
|
**Then** models/ contains only SQLAlchemy ORM definitions
|
|
**And** schemas/ contains only Strawberry GraphQL type definitions
|
|
**And** resolvers/ contains only GraphQL resolver functions
|
|
**And** each module has clear, single responsibility
|
|
|
|
#### Scenario: Main application entry point is defined
|
|
|
|
**Given** src/main.py exists
|
|
**When** the file is imported
|
|
**Then** it exports a FastAPI `app` instance
|
|
**And** app includes GraphQL route at /graphql
|
|
**And** app includes startup event to verify database connection
|
|
|
|
### Requirement: Testing infrastructure with pytest
|
|
|
|
The project MUST provide pytest configuration for running async tests with database fixtures and code coverage reporting.
|
|
|
|
#### Scenario: Async tests can be executed
|
|
|
|
**Given** pytest and pytest-asyncio are installed
|
|
**When** developer runs `pytest`
|
|
**Then** all tests in tests/ directory are discovered
|
|
**And** async test functions execute correctly
|
|
**And** test results are displayed with pass/fail status
|
|
|
|
#### Scenario: Database fixtures are available for tests
|
|
|
|
**Given** tests/conftest.py defines database fixtures
|
|
**When** a test function requests `db_session` fixture
|
|
**Then** an isolated test database session is provided
|
|
**And** session is rolled back after test completion
|
|
**And** no test data persists between test runs
|
|
|
|
### Requirement: Code quality tooling configuration
|
|
|
|
The project MUST configure black, ruff, and isort for consistent code formatting and linting with settings in pyproject.toml.
|
|
|
|
**Black configuration:**
|
|
- Line length: 88 characters
|
|
- Target version: Python 3.11
|
|
- Skip string normalization: false
|
|
|
|
**Ruff configuration:**
|
|
- Line length: 88 characters
|
|
- Select: E, F, W, I (pycodestyle, pyflakes, warnings, isort)
|
|
- Ignore: E501 (line too long, handled by black)
|
|
|
|
**Isort configuration:**
|
|
- Profile: black (compatible settings)
|
|
- Multi-line output: 3 (vertical hanging indent)
|
|
|
|
#### Scenario: Code formatting is enforced
|
|
|
|
**Given** black is configured in pyproject.toml
|
|
**When** developer runs `black --check .`
|
|
**Then** all Python files are checked for formatting
|
|
**And** exit code is 0 if all files are formatted correctly
|
|
**And** exit code is 1 if any files need formatting
|
|
|
|
#### Scenario: Code quality checks pass
|
|
|
|
**Given** ruff is configured in pyproject.toml
|
|
**When** developer runs `ruff check .`
|
|
**Then** all Python files are linted
|
|
**And** no errors are reported for compliant code
|
|
**And** clear error messages are shown for violations
|
|
|
|
### Requirement: Environment configuration with .env support
|
|
|
|
The project MUST support environment-based configuration using .env files with pydantic-settings for type-safe config values.
|
|
|
|
#### Scenario: Default configuration works for development
|
|
|
|
**Given** no .env file exists
|
|
**When** application starts
|
|
**Then** it uses default SQLite database path
|
|
**And** it runs in debug mode
|
|
**And** application starts successfully
|
|
|
|
#### Scenario: Environment variables override defaults
|
|
|
|
**Given** .env file contains `DATABASE_URL=sqlite+aiosqlite:///./test.db`
|
|
**When** application loads configuration
|
|
**Then** settings.database_url equals "sqlite+aiosqlite:///./test.db"
|
|
**And** custom database path is used
|
|
|
|
#### Scenario: Configuration is type-safe
|
|
|
|
**Given** src/config.py defines Settings class
|
|
**When** invalid configuration value is provided
|
|
**Then** pydantic validation raises clear error
|
|
**And** application fails fast on startup
|
|
|
|
### Requirement: Development scripts for common tasks
|
|
|
|
The project MUST provide executable scripts for database seeding and common development tasks.
|
|
|
|
**Required scripts:**
|
|
- scripts/seed.py - Populate database with sample member
|
|
|
|
#### Scenario: Seed script creates sample data
|
|
|
|
**Given** database schema exists from migrations
|
|
**When** developer runs `uv run python scripts/seed.py`
|
|
**Then** sample member is created in database
|
|
**And** script is idempotent (safe to run multiple times)
|
|
**And** success message is displayed
|
|
|
|
## MODIFIED Requirements
|
|
|
|
None (new capability)
|
|
|
|
## REMOVED Requirements
|
|
|
|
None (new capability)
|
|
|
|
## Cross-References
|
|
|
|
### Depends On
|
|
None (foundational capability)
|
|
|
|
### Enables
|
|
- **database-layer** - Requires project structure and dependencies
|
|
- **graphql-api** - Requires FastAPI app and Strawberry installation
|
|
- **member-crud** - Requires complete project infrastructure
|
|
|
|
## Implementation Notes
|
|
|
|
1. **Dependency resolution:** Use `uv add <package>` to ensure lock file is updated
|
|
2. **Python version:** Minimum 3.11 for modern async features and performance
|
|
3. **Import ordering:** Isort configured to work with black (no conflicts)
|
|
4. **Testing:** Pytest configuration in pyproject.toml, not separate pytest.ini
|
|
5. **Scripts:** Use `uv run` prefix to execute in correct virtual environment
|
|
|
|
## Validation Checklist
|
|
|
|
- [ ] `uv sync` completes without errors
|
|
- [ ] `uv run pytest` discovers and runs tests (even if no tests exist yet)
|
|
- [ ] `uv run black --check .` passes
|
|
- [ ] `uv run ruff check .` passes
|
|
- [ ] `uv run uvicorn src.main:app --reload` starts server
|
|
- [ ] pyproject.toml includes all required dependencies
|
|
- [ ] src/ directory structure matches specification
|
|
- [ ] .env.example file documents available settings
|