Skip to content

Commit c175a2f

Browse files
nanotaboadaCopilot
andcommitted
docs(copilot): unify instructions as canonical single-file format
Consolidate .github/copilot-instructions.md and AGENTS.md into a single always-on file with a consistent section order (Overview, Tech Stack, Structure, Coding Guidelines, Commands, Agent Mode). - Replace two-file system with one canonical copilot-instructions.md - Absorb AGENTS.md content (autonomy levels, workflows) into Agent Mode Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 5911d82 commit c175a2f

File tree

2 files changed

+86
-344
lines changed

2 files changed

+86
-344
lines changed

.github/copilot-instructions.md

Lines changed: 86 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -1,228 +1,120 @@
1-
# Copilot Instructions
1+
# GitHub Copilot Instructions
22

3-
## Project Summary
3+
## Overview
44

5-
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.
5+
REST API for managing football players built with Python and FastAPI. Implements async CRUD operations with SQLAlchemy 2.0 (async), SQLite, Pydantic validation, and in-memory caching. Part of a cross-language comparison study (.NET, Go, Java, Rust, TypeScript).
66

7-
## Quick Start
7+
## Tech Stack
88

9-
```bash
10-
# Install dependencies
11-
pip install -r requirements.txt
12-
pip install -r requirements-lint.txt
13-
pip install -r requirements-test.txt
14-
15-
# Run development server
16-
uvicorn main:app --reload --port 9000
17-
# Access: http://localhost:9000/docs
18-
19-
# Run tests with coverage
20-
pytest --cov=./ --cov-report=html
21-
22-
# Lint and format
23-
flake8 .
24-
black --check . # or: black . (to auto-format)
25-
26-
# Docker
27-
docker compose up
28-
docker compose down -v # Reset database
29-
```
9+
- **Language**: Python 3.13
10+
- **Framework**: FastAPI + Uvicorn
11+
- **ORM**: SQLAlchemy 2.0 (async) + aiosqlite
12+
- **Database**: SQLite
13+
- **Validation**: Pydantic
14+
- **Caching**: aiocache (in-memory, 10-minute TTL)
15+
- **Testing**: pytest + pytest-cov + httpx
16+
- **Linting/Formatting**: Flake8 + Black
17+
- **Containerization**: Docker
3018

31-
## Stack
32-
33-
- Python 3.13.3 (`.python-version` - auto-detected by pyenv/asdf/mise)
34-
- FastAPI 0.128.6, Uvicorn
35-
- SQLite + SQLAlchemy 2.0 (async) + aiosqlite
36-
- pytest + pytest-cov + httpx
37-
- Flake8 + Black
38-
- aiocache (in-memory, 10min TTL)
39-
40-
## Architecture
19+
## Structure
4120

4221
```text
43-
Request → Routes → Services → SQLAlchemy → SQLite
44-
(API) (Logic) (Async ORM) (Storage)
45-
46-
Pydantic (Validation)
22+
main.py — application entry point: FastAPI setup, router registration
23+
routes/ — HTTP route definitions + dependency injection [HTTP layer]
24+
services/ — async business logic + cache management [business layer]
25+
schemas/ — SQLAlchemy ORM models (database schema) [data layer]
26+
databases/ — async SQLAlchemy session setup
27+
models/ — Pydantic models for request/response validation
28+
storage/ — SQLite database file (players-sqlite3.db, pre-seeded)
29+
tests/ — pytest integration tests
4730
```
4831

49-
**Key Directories:**
50-
51-
- `routes/` - API endpoints (player_route.py, health_route.py)
52-
- `services/` - Business logic (player_service.py)
53-
- `models/` - Pydantic validation (camelCase JSON API)
54-
- `schemas/` - SQLAlchemy ORM models
55-
- `databases/` - Async DB setup, session factory
56-
- `storage/` - SQLite file (pre-seeded, 26 players)
57-
- `tests/` - pytest suite (test_main.py, conftest.py)
32+
**Layer rule**: `Routes → Services → SQLAlchemy → SQLite`. Routes handle HTTP concerns only; business logic belongs in services.
5833

59-
**Config Files:**
34+
## Coding Guidelines
6035

61-
- `.flake8` - Linter (max-line-length=88, complexity=10)
62-
- `pyproject.toml` - Black formatter (line-length=88)
63-
- `.coveragerc` - Coverage config (80% target)
64-
- `compose.yaml` - Docker orchestration
65-
- `Dockerfile` - Multi-stage build
66-
67-
## API Endpoints
68-
69-
All async with `AsyncSession` injection:
70-
71-
- `POST /players/` → 201|409|422
72-
- `GET /players/` → 200 (cached 10min)
73-
- `GET /players/{player_id}` → 200|404
74-
- `GET /players/squadnumber/{squad_number}` → 200|404
75-
- `PUT /players/{player_id}` → 200|404|422
76-
- `DELETE /players/{player_id}` → 200|404
77-
- `GET /health` → 200
78-
79-
JSON: camelCase (e.g., `squadNumber`, `firstName`)
80-
81-
## CI/CD
82-
83-
**python-ci.yml** (push/PR to master):
84-
85-
1. Lint: commitlint → `flake8 .``black --check .`
86-
2. Test: `pytest -v` → coverage
87-
3. Upload to Codecov
88-
89-
**python-cd.yml** (tags `v*.*.*-*`):
90-
91-
1. Validate semver + coach name
92-
2. Run tests
93-
3. Build Docker (amd64/arm64)
94-
4. Push to GHCR (3 tags: semver/coach/latest)
95-
5. Create GitHub release
96-
97-
## Critical Patterns
98-
99-
### Async Everywhere
100-
101-
```python
102-
# Always use async/await
103-
async def get_player(async_session: AsyncSession, player_id: int):
104-
stmt = select(Player).where(Player.id == player_id)
105-
result = await async_session.execute(stmt)
106-
return result.scalar_one_or_none()
107-
```
36+
- **Naming**: snake_case (files, functions, variables), PascalCase (classes)
37+
- **Type hints**: Required everywhere — functions, variables, return types
38+
- **Async**: All routes and service functions must be `async def`; use `AsyncSession` (never `Session`); use `aiosqlite` (never `sqlite3`); use SQLAlchemy 2.0 `select()` (never `session.query()`)
39+
- **API contract**: camelCase JSON via Pydantic `alias_generator=to_camel`; Python internals stay snake_case
40+
- **Caching**: cache key `"players"` (hardcoded); clear on POST/PUT/DELETE; `X-Cache` header (HIT/MISS)
41+
- **Errors**: Catch specific exceptions with rollback in services; Pydantic validation returns 422 (not 400)
42+
- **Logging**: `logging` module only; never `print()`
43+
- **Line length**: 88; complexity ≤ 10
44+
- **Import order**: stdlib → third-party → local
45+
- **Tests**: naming pattern `test_request_{method}_{resource}_{context}_response_{outcome}`; docstrings single-line, concise; `tests/player_stub.py` for test data; `tests/test_main.py` excluded from Black
46+
- **Avoid**: sync DB access, mixing sync/async, `print()`, missing type hints, unhandled exceptions
10847

109-
- All routes: `async def`
110-
- Database: `AsyncSession` (never `Session`)
111-
- Driver: `aiosqlite` (not `sqlite3`)
112-
- SQLAlchemy 2.0: `select()` (not `session.query()`)
48+
## Commands
11349

114-
### camelCase API Contract
115-
116-
```python
117-
class PlayerModel(BaseModel):
118-
model_config = ConfigDict(alias_generator=to_camel)
119-
squad_number: int # Python: snake_case
120-
# JSON API: "squadNumber" (camelCase)
121-
```
122-
123-
### Database Schema Changes
124-
125-
⚠️ No Alembic yet - manual process:
126-
127-
1. Update `schemas/player_schema.py`
128-
2. Manually update `storage/players-sqlite3.db` (SQLite CLI/DB Browser)
129-
3. Preserve 26 players
130-
4. Update `models/player_model.py` if API changes
131-
5. Update services + tests
132-
133-
### Caching
134-
135-
- Key: `"players"` (hardcoded)
136-
- TTL: 600s (10min)
137-
- Cleared on POST/PUT/DELETE
138-
- Header: `X-Cache` (HIT/MISS)
139-
140-
## Common Issues
141-
142-
1. **SQLAlchemy errors** → Always catch + rollback in services
143-
2. **Test file**`test_main.py` excluded from Black
144-
3. **Database location** → Local: `./storage/`, Docker: `/storage/` (volume)
145-
4. **Pydantic validation** → Returns 422 (not 400)
146-
5. **Import order** → stdlib → third-party → local
147-
148-
## Validation Checklist
50+
### Quick Start
14951

15052
```bash
151-
flake8 . # Must pass
152-
black --check . # Must pass
153-
pytest # All pass
154-
pytest --cov=./ --cov-report=term # ≥80%
155-
curl http://localhost:9000/players # 200 OK
53+
pip install -r requirements.txt
54+
pip install -r requirements-test.txt
55+
pip install -r requirements-lint.txt
56+
uvicorn main:app --reload --port 9000 # http://localhost:9000/docs
57+
pytest # run tests
58+
pytest --cov=./ --cov-report=term # with coverage (target >=80%)
59+
flake8 .
60+
black --check .
61+
docker compose up
62+
docker compose down -v
15663
```
15764

158-
## Code Conventions
65+
### Pre-commit Checks
15966

160-
- Files: snake_case
161-
- Functions/vars: snake_case
162-
- Classes: PascalCase
163-
- Type hints: Required everywhere
164-
- Logging: `logging` module (never `print()`)
165-
- Errors: Catch specific exceptions
166-
- Line length: 88
167-
- Complexity: ≤10
67+
1. Update `CHANGELOG.md` `[Unreleased]` section (Added / Changed / Fixed / Removed)
68+
2. `flake8 .` — must pass
69+
3. `black --check .` — must pass
70+
4. `pytest` — all tests must pass
71+
5. `pytest --cov=./ --cov-report=term` — coverage must be >=80%
72+
6. Commit message follows Conventional Commits format (enforced by commitlint)
16873

169-
## Test Naming Convention
74+
### Commits
17075

171-
Integration tests follow an action-oriented pattern:
76+
Format: `type(scope): description (#issue)` — max 80 chars
77+
Types: `feat` `fix` `chore` `docs` `test` `refactor` `ci` `perf`
78+
Example: `feat(api): add player stats endpoint (#42)`
17279

173-
**Pattern:**
80+
## Agent Mode
17481

175-
```text
176-
test_request_{method}_{resource}_{param_or_context}_response_{outcome}
177-
```
82+
### Proceed freely
17883

179-
**Components:**
84+
- Add/modify routes and endpoints
85+
- Service layer logic and cache management
86+
- Tests (maintain async patterns and naming convention)
87+
- Documentation and docstring updates
88+
- Lint/format fixes
89+
- Refactoring within existing architectural patterns
18090

181-
- `method` - HTTP verb: `get`, `post`, `put`, `delete`
182-
- `resource` - `players` (collection) or `player` (single resource)
183-
- `param_or_context` - Request details: `id_existing`, `squadnumber_nonexistent`, `body_empty`
184-
- `response` - Literal separator
185-
- `outcome` - What's asserted: `status_ok`, `status_not_found`, `body_players`, `header_cache_miss`
91+
### Ask before changing
18692

187-
**Examples:**
93+
- Database schema (`schemas/player_schema.py` — no Alembic, manual process)
94+
- Dependencies (`requirements*.txt`)
95+
- CI/CD configuration (`.github/workflows/`)
96+
- Docker setup
97+
- API contracts (breaking Pydantic model changes)
98+
- Global error handling
18899

189-
```python
190-
def test_request_get_players_response_status_ok(client):
191-
"""GET /players/ returns 200 OK"""
192-
193-
def test_request_get_player_id_existing_response_body_player_match(client):
194-
"""GET /players/{player_id} with existing ID returns matching player"""
195-
196-
def test_request_post_player_body_empty_response_status_unprocessable(client):
197-
"""POST /players/ with empty body returns 422 Unprocessable Entity"""
198-
```
100+
### Never modify
199101

200-
**Docstrings:**
102+
- `.env` files (secrets)
103+
- Production configurations
104+
- Async/await patterns (mandatory throughout)
105+
- Type hints (mandatory throughout)
106+
- Core layered architecture
201107

202-
- Single-line, concise descriptions
203-
- Complements test name (doesn't repeat)
204-
- No "Expected:" prefix (redundant)
108+
### Key workflows
205109

206-
## Commit Messages
110+
**Add an endpoint**: Add Pydantic model in `models/` → add async service method in `services/` with error handling → add route in `routes/` with `Depends(generate_async_session)` → add tests following naming pattern → run pre-commit checks.
207111

208-
Follow Conventional Commits format (enforced by commitlint in CI):
112+
**Modify schema**: Update `schemas/player_schema.py` → manually update `storage/players-sqlite3.db` (preserve 26 players) → update `models/player_model.py` if API changes → update services and tests → run `pytest`.
209113

210-
**Format:** `type(scope): description (#issue)`
211-
212-
**Rules:**
213-
214-
- Max 80 characters
215-
- Types: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `ci`, `perf`, `style`, `build`
216-
- Scope: Optional (e.g., `api`, `db`, `service`, `route`)
217-
- Issue number: Required suffix
218-
219-
**Examples:**
114+
**After completing work**: Suggest a branch name (e.g. `feat/add-player-stats`) and a commit message following Conventional Commits including co-author line:
220115

221116
```text
222-
feat(api): add player stats endpoint (#42)
223-
fix(db): resolve async session leak (#88)
224-
```
225-
226-
**CI Check:** First step in python-ci.yml validates all commit messages
117+
feat(scope): description (#issue)
227118
228-
Trust these instructions. Search codebase only if info is incomplete/incorrect.
119+
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
120+
```

0 commit comments

Comments
 (0)