- Add Jinja2 templates with data-testid attributes for testing - Create light/dark themes based on Gitea color scheme - Add theme switching with localStorage persistence - Create base CSS, components, and layout styles - Add mock web routes for UI demonstration - Register web router and static files in main.py - Add data-testid requirements to AGENTS.md - Install jinja2 dependency
269 lines
9.4 KiB
Markdown
269 lines
9.4 KiB
Markdown
# Blog AGENTS.md
|
|
|
|
## Stack
|
|
- Python 3.13+, FastAPI, pydantic, uvicorn
|
|
- SQLAlchemy 2.0 (async), aiosqlite
|
|
- Package manager: `uv`
|
|
- CI: Woodpecker (lint, test, type on push/PR to `dev`)
|
|
|
|
## Commands
|
|
```bash
|
|
uv sync --group dev # Install all dev dependencies
|
|
uv run pytest # Run tests (coverage >= 70% required)
|
|
uv run pytest tests/unit/ # Run single test directory
|
|
uv run ruff check . --fix # Lint
|
|
uv run ruff format # Format
|
|
uv run isort . # Sort imports
|
|
uv run mypy . # Type check (strict mode)
|
|
uv run blog # Start dev server (port 8000)
|
|
```
|
|
|
|
## Pre-commit order
|
|
`ruff check --fix` → `ruff format` → `isort` → `mypy`
|
|
|
|
## DDD Architecture
|
|
|
|
### Layer Structure
|
|
```
|
|
app/
|
|
├── domain/ # Domain Layer - business logic, no dependencies
|
|
│ ├── entities/ # Domain entities (Post, User, etc.)
|
|
│ │ ├── base.py # Base entity class
|
|
│ │ └── post.py # Post entity with business logic
|
|
│ ├── value_objects/ # Value objects (Title, Content, Slug)
|
|
│ │ ├── base.py
|
|
│ │ ├── title.py
|
|
│ │ ├── content.py
|
|
│ │ └── slug.py
|
|
│ ├── repositories/ # Repository interfaces (abstract)
|
|
│ │ ├── base.py
|
|
│ │ └── post.py
|
|
│ └── exceptions.py # Domain exceptions
|
|
│
|
|
├── application/ # Application Layer - use cases
|
|
│ ├── dtos/ # Data Transfer Objects
|
|
│ │ └── post.py
|
|
│ ├── interfaces/ # Abstract interfaces (UoW)
|
|
│ │ └── unit_of_work.py
|
|
│ └── use_cases/ # Use cases (CQRS-like)
|
|
│ ├── create_post.py
|
|
│ ├── get_post.py
|
|
│ ├── update_post.py
|
|
│ ├── delete_post.py
|
|
│ ├── list_posts.py
|
|
│ └── publish_post.py
|
|
│
|
|
├── infrastructure/ # Infrastructure Layer - external concerns
|
|
│ ├── config/ # Configuration
|
|
│ │ └── settings.py
|
|
│ ├── database/ # Database connection & ORM models
|
|
│ │ ├── connection.py
|
|
│ │ └── models.py
|
|
│ ├── repositories/ # Repository implementations
|
|
│ │ ├── post.py # SQLAlchemyPostRepository
|
|
│ │ └── unit_of_work.py # SQLAlchemyUnitOfWork
|
|
│ ├── di/ # Dependency Injection
|
|
│ │ └── container.py
|
|
│ └── middleware/ # Exception handlers
|
|
│ └── error_handler.py
|
|
│
|
|
├── presentation/ # Presentation Layer - API
|
|
│ ├── api/ # FastAPI routes
|
|
│ │ ├── v1/ # API version 1
|
|
│ │ │ ├── __init__.py
|
|
│ │ │ └── posts.py # Posts endpoints
|
|
│ │ ├── deps.py # FastAPI dependencies
|
|
│ │ └── __init__.py
|
|
│ └── schemas/ # Pydantic schemas
|
|
│ └── post.py
|
|
│
|
|
└── main.py # Application entry point
|
|
|
|
tests/
|
|
├── unit/ # Unit tests (domain, use cases)
|
|
│ ├── domain/ # Domain layer tests
|
|
│ ├── application/ # Application layer tests
|
|
│ └── infrastructure/ # Infrastructure tests
|
|
├── integration/ # Integration tests (DB, repos)
|
|
├── api/ # API endpoint tests
|
|
└── e2e/ # End-to-end tests
|
|
```
|
|
|
|
## Key Conventions
|
|
|
|
### Dependency Rule
|
|
- Domain layer has **NO dependencies** on other layers
|
|
- Application layer depends only on Domain
|
|
- Infrastructure depends on Domain and Application
|
|
- Presentation depends on all other layers
|
|
|
|
### Testing
|
|
- **Unit tests**: Test domain logic without DB/external services
|
|
- **Integration tests**: Test repository implementations with real DB
|
|
- **API tests**: Test endpoints with mocked use cases
|
|
- **E2E tests**: Full workflow testing
|
|
|
|
### Code Patterns
|
|
- Use **dataclasses** for entities and value objects
|
|
- Use **frozen dataclasses** for value objects (immutable)
|
|
- Use **Unit of Work** pattern for transactions
|
|
- Use **Repository** pattern for data access
|
|
- Use **Dependency Injection** via FastAPI's Depends()
|
|
|
|
## AI Code Generation Requirements
|
|
|
|
### Documentation Standards
|
|
- **Write self-documenting code** - use clear, descriptive variable names and function names
|
|
- **NO inline comments** - code should be readable without explanatory comments
|
|
- **Google-style docstrings** are REQUIRED for all modules, classes, and functions
|
|
|
|
### Docstring Requirements
|
|
|
|
#### Modules
|
|
Every module must have a module-level docstring:
|
|
```python
|
|
"""Module for managing blog posts.
|
|
|
|
This module provides use cases for creating, updating, and deleting
|
|
blog posts in the application layer.
|
|
"""
|
|
```
|
|
|
|
#### Classes
|
|
Every class must have a detailed docstring:
|
|
```python
|
|
class CreatePostUseCase:
|
|
"""Use case for creating a new blog post.
|
|
|
|
This class encapsulates the business logic for creating posts,
|
|
including validation and slug generation.
|
|
|
|
Attributes:
|
|
uow: Unit of Work for database transactions.
|
|
slug_service: Service for generating URL-friendly slugs.
|
|
|
|
Example:
|
|
>>> use_case = CreatePostUseCase(uow, slug_service)
|
|
>>> result = await use_case.execute(dto)
|
|
"""
|
|
```
|
|
|
|
#### Functions/Methods
|
|
Every function must have a detailed docstring with Args, Returns, Raises:
|
|
```python
|
|
async def execute(self, dto: CreatePostDTO) -> PostDTO:
|
|
"""Execute the use case to create a new post.
|
|
|
|
Args:
|
|
dto: Data transfer object containing post creation data
|
|
including title, content, and author information.
|
|
|
|
Returns:
|
|
PostDTO: The created post data transfer object with
|
|
generated ID and slug.
|
|
|
|
Raises:
|
|
TitleValidationError: If the title is empty or too long.
|
|
ContentValidationError: If the content is empty.
|
|
DuplicateSlugError: If a post with the same slug exists.
|
|
|
|
Note:
|
|
This method is idempotent - calling it multiple times with
|
|
the same data will create separate posts with unique slugs.
|
|
"""
|
|
```
|
|
|
|
### Google-Style Docstring Format
|
|
|
|
Use the following sections as appropriate:
|
|
- `Args` - Parameter descriptions with types
|
|
- `Returns` - Return value description with type
|
|
- `Raises` - Exceptions that may be raised
|
|
- `Yields` - For generator functions
|
|
- `Example` - Usage examples
|
|
- `Note` - Additional important information
|
|
- `Warning` - Critical warnings
|
|
- `Attributes` - For class attributes
|
|
- `See Also` - References to related code
|
|
|
|
## UI Development Requirements
|
|
|
|
### HTML Templates (Jinja2)
|
|
- All HTML templates use **Jinja2** templating engine
|
|
- Templates are located in `app/presentation/templates/`
|
|
- Base template: `base.html` with theme support (light/dark)
|
|
|
|
### data-testid Attributes (REQUIRED)
|
|
**Every interactive and significant HTML element MUST have a `data-testid` attribute** for automated testing.
|
|
|
|
#### Required Elements:
|
|
- **Navigation**: `data-testid="nav-link-{name}"`, `data-testid="nav-logo"`
|
|
- **Buttons**: `data-testid="btn-{action}"` (e.g., `btn-create`, `btn-save`, `btn-delete`)
|
|
- **Forms**: `data-testid="form-{name}"`, `data-testid="input-{field}"`, `data-testid="submit-{action}"`
|
|
- **Cards/Posts**: `data-testid="post-card-{id}"`, `data-testid="post-title"`, `data-testid="post-content"`
|
|
- **Lists**: `data-testid="list-{name}"`, `data-testid="list-item-{index}"`
|
|
- **Theme Switcher**: `data-testid="theme-toggle"`, `data-testid="theme-{light|dark}"`
|
|
- **Messages/Alerts**: `data-testid="alert-{type}"`, `data-testid="alert-message"
|
|
|
|
#### Example:
|
|
```html
|
|
<button data-testid="btn-create-post" class="btn btn-primary">
|
|
Create Post
|
|
</button>
|
|
|
|
<article data-testid="post-card-{{ post.id }}" class="card">
|
|
<h2 data-testid="post-title">{{ post.title }}</h2>
|
|
<p data-testid="post-content">{{ post.content }}</p>
|
|
</article>
|
|
```
|
|
|
|
### CSS Architecture (Gitea-inspired)
|
|
- **Theme files**: `static/css/themes/theme-{light|dark}.css` with CSS variables
|
|
- **Base styles**: `static/css/base.css` - reset, typography, CSS variables usage
|
|
- **Components**: `static/css/components.css` - buttons, cards, forms, inputs
|
|
- **Layout**: `static/css/layout.css` - grid, navigation, containers
|
|
|
|
### Theme Support
|
|
- Light and dark themes based on Gitea color scheme
|
|
- Theme switching via `data-theme` attribute on `<html>` element
|
|
- LocalStorage persistence for user preference
|
|
- All colors use CSS custom properties (variables)
|
|
|
|
### Static Assets
|
|
- **All assets are local** - no external CDN dependencies
|
|
- Location: `static/` directory at project root
|
|
- Served via FastAPI `StaticFiles` middleware
|
|
|
|
### DDD Concepts Used
|
|
|
|
### Entities
|
|
- Have identity (UUID)
|
|
- Mutable state
|
|
- Business logic methods (publish, update_title, etc.)
|
|
- Example: `Post` entity
|
|
|
|
### Value Objects
|
|
- Immutable
|
|
- Defined by attributes
|
|
- Validated on creation
|
|
- Examples: `Title`, `Content`, `Slug`
|
|
|
|
### Aggregates & Repositories
|
|
- `Post` is an aggregate root
|
|
- `PostRepository` interface in Domain
|
|
- `SQLAlchemyPostRepository` implementation in Infrastructure
|
|
|
|
### Domain Events
|
|
- Placeholder for future implementation
|
|
- Can be added via event bus in application layer
|
|
|
|
## Configuration
|
|
- `.env` file loaded by pydantic-settings
|
|
- Settings available via `app.infrastructure.config.settings`
|
|
|
|
## Database
|
|
- SQLAlchemy 2.0 with async support
|
|
- SQLite by default (aiosqlite)
|
|
- Tables auto-created on startup
|
|
- Use `init_db()` and `close_db()` in lifespan
|