From dee11727dfb4583d18ebde6fcc3591dbef3c41c9 Mon Sep 17 00:00:00 2001 From: Nano Taboada <87288+nanotaboada@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:33:00 -0300 Subject: [PATCH 1/2] docs: restructure AI instruction files --- .github/copilot-instructions.md | 210 +++++++++---- AGENTS.md | 524 ++++++-------------------------- 2 files changed, 254 insertions(+), 480 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f57e55a..44332a8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,78 +1,178 @@ -# GitHub Copilot Instructions +# Copilot Instructions -> **⚡ Token Efficiency Note**: This is a minimal pointer file (~500 tokens, auto-loaded by Copilot). -> For complete operational details, reference: `#file:AGENTS.md` (~2,500 tokens, loaded on-demand) -> For specialized knowledge, use: `#file:SKILLS//SKILL.md` (loaded on-demand when needed) +## Project Summary -## 🎯 Quick Context +RESTful API with Python 3.13 + FastAPI demonstrating modern async patterns. Player registry with CRUD operations, SQLite + SQLAlchemy 2.0 (async), Pydantic validation, containerization. Part of multi-language comparison study (Java, .NET, TypeScript, Python, Go, Rust). Target: 80%+ test coverage. -**Project**: FastAPI REST API demonstrating modern Python async patterns -**Stack**: Python 3.13 • FastAPI • SQLAlchemy (async) • SQLite • Docker • pytest -**Pattern**: Routes → Services → Database (layered architecture) -**Philosophy**: Learning-focused PoC emphasizing async/await and type safety +## Quick Start -## 📐 Core Conventions +```bash +# Install dependencies +pip install -r requirements.txt +pip install -r requirements-lint.txt +pip install -r requirements-test.txt + +# Run development server +uvicorn main:app --reload --port 9000 +# Access: http://localhost:9000/docs -- **Naming**: snake_case for functions/variables, PascalCase for classes -- **Type Hints**: Mandatory throughout (enforced by mypy if enabled) -- **Async**: All I/O operations use `async`/`await` -- **Testing**: pytest with fixtures and async support -- **Formatting**: black (opinionated), flake8 (linting) +# Run tests with coverage +pytest --cov=./ --cov-report=html -## 🏗️ Architecture at a Glance +# Lint and format +flake8 . +black --check . # or: black . (to auto-format) -```text -Route → Service → Database - ↓ ↓ -Cache Session +# Docker +docker compose up +docker compose down -v # Reset database ``` -- **Routes**: FastAPI endpoints with dependency injection -- **Services**: Async database operations via SQLAlchemy -- **Database**: SQLite with async support (`aiosqlite`) -- **Models**: Pydantic for validation, SQLAlchemy for ORM -- **Cache**: aiocache SimpleMemoryCache (TTL: 600s / 10 min) +## Stack -## ✅ Copilot Should +- Python 3.13.3 (`.python-version` - auto-detected by pyenv/asdf/mise) +- FastAPI 0.128.6, Uvicorn +- SQLite + SQLAlchemy 2.0 (async) + aiosqlite +- pytest + pytest-cov + httpx +- Flake8 + Black +- aiocache (in-memory, 10min TTL) -- Generate idiomatic async FastAPI code with proper type hints -- Use SQLAlchemy async APIs (`select()`, `scalars()`, `session.commit()`) -- Follow dependency injection pattern with `Depends()` -- Write tests with pytest async fixtures -- Apply Pydantic models for request/response validation -- Use structured logging (avoid print statements) -- Implement proper HTTP status codes and responses +## Architecture -## 🚫 Copilot Should Avoid +``` +Request → Routes → Services → SQLAlchemy → SQLite + (API) (Logic) (Async ORM) (Storage) + ↓ + Pydantic (Validation) +``` -- Synchronous database operations -- Mixing sync and async code -- Missing type hints on functions -- Using `print()` instead of logging -- Creating routes without caching consideration -- Ignoring Pydantic validation +**Key Directories:** +- `routes/` - API endpoints (player_route.py, health_route.py) +- `services/` - Business logic (player_service.py) +- `models/` - Pydantic validation (camelCase JSON API) +- `schemas/` - SQLAlchemy ORM models +- `databases/` - Async DB setup, session factory +- `storage/` - SQLite file (pre-seeded, 26 players) +- `tests/` - pytest suite (test_main.py, conftest.py) + +**Config Files:** +- `.flake8` - Linter (max-line-length=88, complexity=10) +- `pyproject.toml` - Black formatter (line-length=88) +- `.coveragerc` - Coverage config (80% target) +- `compose.yaml` - Docker orchestration +- `Dockerfile` - Multi-stage build + +## API Endpoints + +All async with `AsyncSession` injection: +- `POST /players/` → 201|409|422 +- `GET /players/` → 200 (cached 10min) +- `GET /players/{player_id}` → 200|404 +- `GET /players/squadnumber/{squad_number}` → 200|404 +- `PUT /players/{player_id}` → 200|404|422 +- `DELETE /players/{player_id}` → 200|404 +- `GET /health` → 200 + +JSON: camelCase (e.g., `squadNumber`, `firstName`) + +## CI/CD + +**python-ci.yml** (push/PR to master): +1. Lint: commitlint → `flake8 .` → `black --check .` +2. Test: `pytest -v` → coverage +3. Upload to Codecov + +**python-cd.yml** (tags `v*.*.*-*`): +1. Validate semver + coach name +2. Run tests +3. Build Docker (amd64/arm64) +4. Push to GHCR (3 tags: semver/coach/latest) +5. Create GitHub release + +## Critical Patterns + +### Async Everywhere +```python +# Always use async/await +async def get_player(async_session: AsyncSession, player_id: int): + stmt = select(Player).where(Player.id == player_id) + result = await async_session.execute(stmt) + return result.scalar_one_or_none() +``` +- All routes: `async def` +- Database: `AsyncSession` (never `Session`) +- Driver: `aiosqlite` (not `sqlite3`) +- SQLAlchemy 2.0: `select()` (not `session.query()`) + +### camelCase API Contract +```python +class PlayerModel(BaseModel): + model_config = ConfigDict(alias_generator=to_camel) + squad_number: int # Python: snake_case + # JSON API: "squadNumber" (camelCase) +``` -## ⚡ Quick Commands +### Database Schema Changes +⚠️ No Alembic yet - manual process: +1. Update `schemas/player_schema.py` +2. Manually update `storage/players-sqlite3.db` (SQLite CLI/DB Browser) +3. Preserve 26 players +4. Update `models/player_model.py` if API changes +5. Update services + tests -```bash -# Run with hot reload -uvicorn main:app --reload --host 0.0.0.0 --port 9000 +### Caching +- Key: `"players"` (hardcoded) +- TTL: 600s (10min) +- Cleared on POST/PUT/DELETE +- Header: `X-Cache` (HIT/MISS) -# Test with coverage -pytest --cov=. --cov-report=term-missing +## Common Issues -# Docker -docker compose up +1. **SQLAlchemy errors** → Always catch + rollback in services +2. **Test file** → `test_main.py` excluded from Black (preserves long names) +3. **Database location** → Local: `./storage/`, Docker: `/storage/` (volume) +4. **Pydantic validation** → Returns 422 (not 400) +5. **Import order** → stdlib → third-party → local + +## Validation Checklist -# Swagger: http://localhost:9000/docs +```bash +flake8 . # Must pass +black --check . # Must pass +pytest # All pass +pytest --cov=./ --cov-report=term # ≥80% +curl http://localhost:9000/players # 200 OK ``` -## 📚 Need More Detail? +## Code Conventions + +- Files: snake_case +- Functions/vars: snake_case +- Classes: PascalCase +- Type hints: Required everywhere +- Logging: `logging` module (never `print()`) +- Errors: Catch specific exceptions +- Line length: 88 +- Complexity: ≤10 + +## Commit Messages -**For operational procedures**: Load `#file:AGENTS.md` -**For Docker expertise**: *(Planned)* `#file:SKILLS/docker-containerization/SKILL.md` -**For testing patterns**: *(Planned)* `#file:SKILLS/testing-patterns/SKILL.md` +Follow Conventional Commits format (enforced by commitlint in CI): + +**Format:** `type(scope): description (#issue)` + +**Rules:** +- Max 80 characters +- Types: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `ci`, `perf`, `style`, `build` +- Scope: Optional (e.g., `api`, `db`, `service`, `route`) +- Issue number: Required suffix + +**Examples:** +``` +feat(api): add player stats endpoint (#42) +fix(db): resolve async session leak (#88) +``` ---- +**CI Check:** First step in python-ci.yml validates all commit messages -💡 **Why this structure?** Copilot auto-loads this file on every chat (~500 tokens). Loading `AGENTS.md` or `SKILLS/` explicitly gives you deep context only when needed, saving 80% of your token budget! +Trust these instructions. Search codebase only if info is incomplete/incorrect. diff --git a/AGENTS.md b/AGENTS.md index c75154f..a21448f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,463 +1,137 @@ -# AGENTS.md +# Agent Instructions -> **⚡ Token Efficiency Note**: This file contains complete operational instructions (~2,500 tokens). -> **Auto-loaded**: NO (load explicitly with `#file:AGENTS.md` when you need detailed procedures) -> **When to load**: Complex workflows, troubleshooting, CI/CD setup, detailed architecture questions -> **Related files**: See `#file:.github/copilot-instructions.md` for quick context (auto-loaded, ~500 tokens) +## Autonomy Guidelines ---- +### Proceed Freely -## Quick Start +- Add/modify routes and endpoints +- Implement service layer logic +- Write tests (maintain async patterns) +- Update docs (README, docstrings) +- Fix lint/format errors +- Refactor within architectural patterns +- Optimize queries -```bash -# Install all dependencies -pip install -r requirements.txt -pip install -r requirements-lint.txt -pip install -r requirements-test.txt +### Ask First -# Start development server -uvicorn main:app --reload --port 9000 +- Database schema (no Alembic - manual updates) +- Dependencies (requirements*.txt) +- CI/CD (.github/workflows/) +- Docker (Dockerfile, compose.yaml) +- Environment variables +- API contracts (breaking changes) +- Global error handling -# View API documentation -# Open http://localhost:9000/docs in browser -``` - -## Python Version - -This project requires **Python 3.13.3** (specified in `.python-version`). - -If using pyenv, asdf, or mise, the correct version activates automatically. Otherwise, ensure Python 3.13.3 is installed before running any commands. - -## Coding Guidelines - -### Python Style (Strict Enforcement) - -- **Formatter**: Black (line length: 88, target: Python 3.13) -- **Linter**: flake8 (max-complexity: 10, ignores: E203, W503) -- **Run Before Commit**: `black .` and `flake8` -- **Imports**: SQLAlchemy 2.0+ style (use `select()` not legacy `Query`) -- **Docstrings**: Google-style docstrings for all modules, classes, and functions -- **Type Hints**: Use type annotations for function parameters and return values - -### File Exclusions - -Black and flake8 exclude: - -- `.venv`, `.git`, `.github`, `.pytest_cache`, `__pycache__` -- `assets/`, `htmlcov/`, `postman_collections/`, `scripts/`, `storage/` -- Exception: `tests/test_main.py` allows E501 (long lines for test names) - -### Commit Conventions - -Follow **Conventional Commits** (enforced by commitlint): - -- `feat:` for new features -- `fix:` for bug fixes -- `chore:` for maintenance/tooling -- Max header length: 80 characters -- Max body line length: 80 characters - -## Development Workflow - -### Running Tests - -```bash -# Run all tests with verbose output -pytest -v - -# Run tests with coverage report (matches CI) -pytest --cov=./ --cov-report=xml --cov-report=term - -# Run specific test file -pytest tests/test_main.py -v - -# Run specific test function -pytest tests/test_main.py::test_health_check -v -``` - -**Coverage requirement**: Tests must maintain 80% coverage. Local coverage measurement uses `.coveragerc` configuration, while the CI-enforced 80% target is defined in `codecov.yml`. - -### Code Quality - -```bash -# Lint code (must pass before committing) -flake8 . - -# Check code formatting (must pass before committing) -black --check . - -# Auto-format code -black . -``` - -**Pre-commit checklist**: - -1. Update CHANGELOG.md `[Unreleased]` section with your changes (Added/Changed/Deprecated/Removed/Fixed/Security) -2. Run `flake8 .` - must pass with no errors -3. Run `black --check .` - must pass with no formatting changes needed (or run `black .` to auto-fix) -4. Run `pytest --cov=./ --cov-report=term` - all tests must pass -5. Follow conventional commit format (enforced by commitlint) - -**Style rules** (enforced by Black + Flake8): - -- Line length: 88 characters -- Target: Python 3.13 -- Black configuration in `pyproject.toml` -- Flake8 configuration in `.flake8` - -### Database Management - -```bash -# Database auto-initializes on first app startup via lifespan handler in main.py -# Pre-seeded database ships in storage/players.db - -# To reset database to seed state (local development) -rm storage/players.db -# Next app startup will recreate from seed data - -# Docker: Reset database by removing volume -docker compose down -v -# Next startup will reinitialize from built-in seed -``` - -**Important**: The database is SQLite stored in `storage/players.db`. It auto-seeds with football player data on first run. - -## Docker Workflow - -```bash -# Build container image -docker compose build - -# Start application in container -docker compose up - -# Start in detached mode (background) -docker compose up -d - -# View logs -docker compose logs -f - -# Stop application -docker compose down - -# Stop and remove database volume (full reset) -docker compose down -v - -# Health check (when running) -curl http://localhost:9000/health -``` - -**First run behavior**: Container copies pre-seeded SQLite database into persistent volume. Subsequent runs reuse that volume to preserve data. - -## Release Management - -### CHANGELOG Maintenance - -**Important**: Update CHANGELOG.md continuously as you work, not just before releases. - -**For every meaningful commit**: - -1. Add your changes to the `[Unreleased]` section in CHANGELOG.md -2. Categorize under the appropriate heading: - - **Added**: New features - - **Changed**: Changes in existing functionality - - **Deprecated**: Soon-to-be removed features - - **Removed**: Removed features - - **Fixed**: Bug fixes - - **Security**: Security vulnerability fixes -3. Use clear, user-facing descriptions (not just commit messages) -4. Include PR/issue numbers when relevant (#123) - -**Example**: - -```markdown -## [Unreleased] - -### Added -- User authentication with JWT tokens (#145) -- Rate limiting middleware for API endpoints - -### Deprecated -- Legacy authentication endpoint /api/v1/auth (use /api/v2/auth instead) - -### Fixed -- Null reference exception in player service (#147) - -### Security -- Fix SQL injection vulnerability in search endpoint (#148) -``` - -### Creating a Release +### Never Change -When ready to release: +- .env files (secrets) +- Production configs (without approval) +- Core architecture (layered pattern) +- Async/await patterns (mandatory) +- Type hints (mandatory) -1. **Update CHANGELOG.md**: Move items from `[Unreleased]` to a new versioned section, then commit and push: +## Workflow: Add Endpoint - ```markdown - ## [1.1.0 - bielsa] - 2026-02-15 - ``` +1. Pydantic model (`models/player_model.py`): - ```bash - git add CHANGELOG.md - git commit -m "docs: prepare changelog for v1.1.0-bielsa release" - git push - ``` - -2. **Create and push tag**: - - ```bash - git tag -a v1.1.0-bielsa -m "Release 1.1.0 - Bielsa" - git push origin v1.1.0-bielsa - ``` - -3. **CD workflow runs automatically** to publish Docker images and create GitHub Release - -See [CHANGELOG.md](CHANGELOG.md#how-to-release) for complete release instructions and coach naming convention. - -## CI/CD Pipeline - -### Continuous Integration (python-ci.yml) - -**Trigger**: Push to `master` or PR to `master` - -**Jobs**: - -1. **Lint**: Commit messages (commitlint) → Flake8 → Black check -2. **Test**: pytest with verbose output → coverage report generation -3. **Coverage**: Upload to Codecov (requires secrets) - -**Local validation** (run this before pushing): - -```bash -# Matches CI exactly -flake8 . && \ -black --check . && \ -pytest -v && \ -pytest --cov=./ --cov-report=xml --cov-report=term +```python +class PlayerStatsModel(MainModel): + wins: int + losses: int ``` -### Continuous Deployment (python-cd.yml) - -**Trigger**: Version tags in format `v{MAJOR}.{MINOR}.{PATCH}-{COACH}` +1. Service method (`services/player_service.py`): -Example: - -```bash -git tag -a v1.0.0-ancelotti -m "Release 1.0.0 - Ancelotti" -git push origin v1.0.0-ancelotti +```python +async def get_player_stats_async( + async_session: AsyncSession, + player_id: int +) -> PlayerStatsModel: + # Logic with error handling + ... ``` -**Pipeline automatically**: - -- Runs full test suite with coverage -- Builds multi-stage Docker image -- Pushes to GHCR with multiple tags (version, coach name, latest) -- Generates changelog from commits -- Creates GitHub Release with auto-generated notes - -**Coach naming convention**: Famous football coaches A-Z (see README.md for full list) - -## Project Architecture - -**Structure**: Layered architecture (Routes → Services → Database) - -```text -routes/ # FastAPI endpoints with caching - ├── player_route.py # CRUD endpoints - └── health_route.py # Health check - -services/ # Business logic layer - └── player_service.py # Async database operations - -databases/ # Database setup - └── player_database.py # SQLAlchemy engine, session, Base - -schemas/ # ORM models - └── player_schema.py # Player table definition - -models/ # API models - └── player_model.py # Pydantic validation (camelCase) - -tests/ # Test suite - ├── conftest.py # Fixtures (TestClient) - ├── test_main.py # Endpoint tests - └── player_stub.py # Test data +1. Route (`routes/player_route.py`): + +```python +@api_router.get( + "/players/{player_id}/stats", + response_model=PlayerStatsModel, + status_code=200 +) +async def get_stats( + player_id: int, + async_session: AsyncSession = Depends(generate_async_session) +): + return await player_service.get_player_stats_async( + async_session, player_id + ) ``` -**Key patterns**: - -- Dependency injection: `AsyncSession` via `Depends(generate_async_session)` -- Async everywhere: SQLAlchemy async, aiocache, FastAPI async endpoints -- Pydantic validation: Request/response with camelCase aliasing -- In-memory caching: aiocache on GET endpoints -- Lifespan handler: DB initialization on app startup - -## API Endpoints - -| Method | Path | Description | Cache | -|--------|--------------------------------------|------------------------------|-------| -| GET | `/health` | Health check | No | -| GET | `/players/` | Get all players | Yes | -| GET | `/players/{player_id}` | Get player by ID | No | -| GET | `/players/squadnumber/{squad_number}`| Get player by squad number | No | -| POST | `/players/` | Create new player | Clears| -| PUT | `/players/{player_id}` | Update existing player | Clears| -| DELETE | `/players/{player_id}` | Delete player | Clears| - -**Cache Notes**: - -- Cache key: `"players"`, TTL: 600s (10 min) -- Cache is cleared on POST/PUT/DELETE operations -- Response header `X-Cache: HIT` or `MISS` indicates cache status - -## Testing - -- **Framework**: pytest with `TestClient` from FastAPI -- **Fixture**: `client` fixture in `conftest.py` (function scope for test isolation) -- **Coverage Target**: 80% (configured in `codecov.yml`) -- **Test Data**: Use stubs from `tests/player_stub.py` -- **Warnings**: DeprecationWarning from httpx is suppressed in conftest +1. Tests (`tests/test_main.py`): -## Troubleshooting - -### Port already in use - -```bash -# Kill process on port 9000 -lsof -ti:9000 | xargs kill -9 -``` - -### Module import errors - -```bash -# Ensure all dependencies installed -pip install -r requirements.txt -r requirements-lint.txt -r requirements-test.txt - -# Verify Python version -python --version # Should be 3.13.3 +```python +async def test_given_get_when_player_stats_then_returns_200(): + ... ``` -### Database locked errors - -```bash -# Stop all running instances -pkill -f uvicorn - -# Reset database -rm storage/players.db -``` - -### Docker issues - -```bash -# Clean slate -docker compose down -v -docker compose build --no-cache -docker compose up -``` - -## Testing the API - -### Using FastAPI Docs (Recommended) - -Open - Interactive Swagger UI with "Try it out" buttons - -### Using Postman - -Pre-configured collection available in `postman_collections/` - -### Using curl - -```bash -# Health check -curl http://localhost:9000/health - -# Get all players -curl http://localhost:9000/players - -# Get player by ID -curl http://localhost:9000/players/1 - -# Create player -curl -X POST http://localhost:9000/players \ - -H "Content-Type: application/json" \ - -d '{"firstName":"Pele","lastName":"Nascimento","club":"Santos","nationality":"Brazil","dateOfBirth":"1940-10-23"}' - -# Update player -curl -X PUT http://localhost:9000/players/1 \ - -H "Content-Type: application/json" \ - -d '{"firstName":"Diego","lastName":"Maradona","club":"Napoli","nationality":"Argentina","dateOfBirth":"1960-10-30"}' - -# Delete player -curl -X DELETE http://localhost:9000/players/1 -``` - -## Common Pitfalls & Solutions - -1. **Virtual Environment**: Always activate `.venv` before running black, flake8, or pytest: - - ```bash - source .venv/bin/activate - ``` - -2. **FastAPI Route Ordering**: Static routes MUST be defined before dynamic path parameters. Place `/players/statistics` before `/players/{player_id}`, or FastAPI will try to parse "statistics" as a player_id. +1. Validate: `flake8 .` → `black .` → `pytest` - ```python - # CORRECT order: - @api_router.get("/players/statistics") # Static route first - @api_router.get("/players/{player_id}") # Dynamic route after - ``` +## Workflow: Modify Schema -3. **SQLAlchemy 2.0 Migration**: Use `select()` not `session.query()`. Example: +No Alembic migrations: - ```python - statement = select(Player).where(Player.id == player_id) - result = await async_session.execute(statement) - ``` +1. Update `schemas/player_schema.py` (SQLAlchemy ORM) +2. Manually update `storage/players-sqlite3.db` +3. Preserve 26 players +4. Update `models/player_model.py` if API changes +5. Update services +6. Add tests -4. **Async Session Usage**: Always use `Depends(generate_async_session)` in routes, never create sessions manually. +## Design Philosophy -5. **Cache Invalidation**: Remember to call `await simple_memory_cache.clear(CACHE_KEY)` after mutations (POST/PUT/DELETE). +### Layered Architecture -6. **Pydantic Model Conversion**: Use `player_model.model_dump()` to convert Pydantic to dict for SQLAlchemy: +- Routes: HTTP concerns (validation, status codes) +- Services: Business logic, transactions +- Separation: Test logic without HTTP - ```python - player = Player(**player_model.model_dump()) - ``` +### Async Throughout -7. **Database Path in Docker**: Use `STORAGE_PATH` env var, not hardcoded paths. +- FastAPI: Concurrent requests +- SQLAlchemy: Non-blocking I/O +- Never mix sync/async DB access -8. **Port Conflicts**: Default port is 9000. If occupied, use `--port` flag with uvicorn. +### camelCase JSON -## VS Code Configuration +- API: JavaScript conventions +- Python: snake_case (Pythonic) +- Consistency across comparison repos -Recommended extensions (`.vscode/extensions.json`): +## Testing Strategy -- `ms-python.python`, `ms-python.flake8`, `ms-python.black-formatter` -- `github.vscode-pull-request-github`, `github.vscode-github-actions` -- `ms-azuretools.vscode-containers`, `sonarsource.sonarlint-vscode` +- Use `tests/player_stub.py` for data +- Test real DB (fixtures handle setup) +- Cover: happy paths + errors + edges +- Cache tests: verify `X-Cache` header -Settings (`.vscode/settings.json`): +## Planning Tips -- Auto-format on save with Black -- Pytest enabled (not unittest) -- Flake8 integration with matching CLI args -- Editor ruler at column 88 +1. Check `.github/copilot-instructions.md` for commands +2. Read existing tests for behavior patterns +3. Consider cache invalidation (player data changes) +4. Remember: 80% coverage requirement +5. Schema changes = manual DB updates -## Additional Resources +## Common Gotchas -- **Postman Collection**: `postman_collections/python-samples-fastapi-restful.postman_collection.json` -- **Architecture Diagram**: `assets/images/structure.svg` -- **FastAPI Docs**: -- **SQLAlchemy 2.0**: -- **Conventional Commits**: +- Query results: `.scalars()` for ORM objects +- Cache key: `"players"` (hardcoded) +- `test_main.py`: Excluded from Black +- SQLAlchemy errors: Catch + rollback in services +- Validation errors: 422 (not 400) +- Test file naming: `test_given_when_then` pattern -## Important Notes +## Cross-Tool Notes -- **CHANGELOG maintenance**: Update CHANGELOG.md `[Unreleased]` section with every meaningful change -- **Never commit secrets**: No API keys, tokens, or credentials in code -- **Test coverage**: Maintain existing coverage levels (currently high) -- **Commit messages**: Follow conventional commits (enforced by commitlint) -- **Python version**: Must use 3.13.3 for consistency with CI/CD -- **Dependencies**: Keep requirements files in sync with actual usage -- **Database**: SQLite is for demo/development only - not production-ready +For comprehensive onboarding, see `.github/copilot-instructions.md` (auto-loaded). This file provides agent-specific autonomy boundaries and workflows. From eefbcb5ac1ac8412da8944845f79a5c4947f8f75 Mon Sep 17 00:00:00 2001 From: Nano Taboada <87288+nanotaboada@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:49:46 -0300 Subject: [PATCH 2/2] docs(ai): add language identifier to fenced code block --- .github/copilot-instructions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 44332a8..7669ab2 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -39,7 +39,7 @@ docker compose down -v # Reset database ## Architecture -``` +```text Request → Routes → Services → SQLAlchemy → SQLite (API) (Logic) (Async ORM) (Storage) ↓ @@ -168,7 +168,7 @@ Follow Conventional Commits format (enforced by commitlint in CI): - Issue number: Required suffix **Examples:** -``` +```text feat(api): add player stats endpoint (#42) fix(db): resolve async session leak (#88) ```