Merge pull request #381 from christianlouis/copilot/setup-copilot-instructions
docs(copilot): overhaul Copilot instructions — Ruff toolchain, 100% coverage, agent workflow, modern Python
This commit is contained in:
@@ -66,42 +66,59 @@ pytest --cov=app --cov-report=html
|
|||||||
## Lint / Format Commands
|
## Lint / Format Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Format code with Black (line length 120)
|
# Format and lint with Ruff (replaces Black, isort, Flake8, Bandit — all-in-one)
|
||||||
black app/ tests/
|
ruff format app/ tests/
|
||||||
|
ruff check app/ tests/ --fix
|
||||||
# Sort imports with isort (Black-compatible profile)
|
|
||||||
isort app/ tests/
|
|
||||||
|
|
||||||
# Lint with flake8 (max line length 120, ignores E203/W503)
|
|
||||||
flake8 app/ --max-line-length=120
|
|
||||||
|
|
||||||
# Type checking with mypy
|
# Type checking with mypy
|
||||||
mypy app/
|
mypy app/
|
||||||
|
|
||||||
# Security lint with bandit (excludes tests)
|
|
||||||
bandit -r app/
|
|
||||||
|
|
||||||
# Check for dependency vulnerabilities
|
# Check for dependency vulnerabilities
|
||||||
safety check
|
safety check
|
||||||
|
|
||||||
# Run all pre-commit hooks at once
|
# Run all pre-commit hooks at once (recommended — runs ruff, mypy, secret detection, etc.)
|
||||||
pre-commit run --all-files
|
pre-commit run --all-files
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Agent Workflow (Follow for Every Task)
|
||||||
|
|
||||||
|
Follow these steps **in order** for every task — do not skip any:
|
||||||
|
|
||||||
|
1. **Understand** — read the issue/request in full before writing any code
|
||||||
|
2. **Explore** — search the codebase for existing patterns and relevant implementations
|
||||||
|
3. **Plan** — outline your changes as a checklist before starting
|
||||||
|
4. **Implement** — make the smallest correct change that solves the problem
|
||||||
|
5. **Test** — write or update tests; new code requires 100% test coverage
|
||||||
|
6. **Document** — update all relevant docs in `docs/`; this is mandatory, not optional
|
||||||
|
7. **Quality Gate** — run the single gate command below and fix every failure before committing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ruff format app/ tests/ && \
|
||||||
|
ruff check app/ tests/ --fix && \
|
||||||
|
safety check && \
|
||||||
|
pytest --tb=short -q
|
||||||
|
```
|
||||||
|
|
||||||
|
8. **Review** — re-read your own diff; confirm it is clean, secure, minimal, and well-documented
|
||||||
|
|
||||||
|
> All commands in the quality gate must exit with code 0. Never submit with failures.
|
||||||
|
|
||||||
## Core Principles
|
## Core Principles
|
||||||
|
|
||||||
### Code Quality
|
### Code Quality
|
||||||
- Always use **Black** for formatting (line length: 120)
|
- **Security first**: treat security as a non-negotiable requirement, not an afterthought — review [SECURITY_AUDIT.md](../SECURITY_AUDIT.md) for every change
|
||||||
- Use **isort** with Black profile for import sorting
|
- Write **clean, modern, well-documented code** — prioritize readability, maintainability, and idiomatic Python
|
||||||
- Use **flake8** for linting (ignore E203, W503)
|
- Use **Ruff** for all formatting, linting, import sorting, and security scanning — `ruff format` + `ruff check --fix` (replaces Black, isort, Flake8, Bandit)
|
||||||
|
- Line length: 120 characters (configured in `pyproject.toml`)
|
||||||
- Use **type hints** for all function parameters and return values
|
- Use **type hints** for all function parameters and return values
|
||||||
- Write **docstrings** for all public functions, classes, and modules
|
- Write **docstrings** for all public functions, classes, and modules
|
||||||
- Maintain **80% test coverage** for new code
|
- Maintain **100% test coverage** for new code
|
||||||
|
|
||||||
### Python Conventions
|
### Python Conventions
|
||||||
- Use descriptive variable names (e.g., `user_document_path`, not `udp`)
|
- Use descriptive variable names (e.g., `user_document_path`, not `udp`)
|
||||||
- Follow PEP 8 naming: `snake_case` for functions/variables, `PascalCase` for classes
|
- Follow PEP 8 naming: `snake_case` for functions/variables, `PascalCase` for classes
|
||||||
- Use type hints from `typing` module (Dict, List, Optional, etc.)
|
- Use modern Python 3.10+ type hints: `list[str]`, `dict[str, Any]`, `str | None` — avoid `List`, `Dict`, `Optional` from `typing`
|
||||||
|
- Only import from `typing` for `Any`, `Callable`, `TypeVar`, `Protocol`, and other constructs unavailable natively
|
||||||
- Prefer `pathlib.Path` over string paths for file operations
|
- Prefer `pathlib.Path` over string paths for file operations
|
||||||
- Use f-strings for string formatting, not `.format()` or `%`
|
- Use f-strings for string formatting, not `.format()` or `%`
|
||||||
- Handle exceptions explicitly - avoid bare `except:` clauses
|
- Handle exceptions explicitly - avoid bare `except:` clauses
|
||||||
@@ -112,7 +129,8 @@ pre-commit run --all-files
|
|||||||
- Validate and sanitize all user inputs
|
- Validate and sanitize all user inputs
|
||||||
- Use parameterized queries with SQLAlchemy (never raw SQL with user input)
|
- Use parameterized queries with SQLAlchemy (never raw SQL with user input)
|
||||||
- Review [SECURITY_AUDIT.md](../SECURITY_AUDIT.md) before making security-related changes
|
- Review [SECURITY_AUDIT.md](../SECURITY_AUDIT.md) before making security-related changes
|
||||||
- Run `bandit` to check for security issues in Python code
|
- Security linting is built into Ruff via `S` rules — runs automatically with `ruff check`; fix all `S`-prefixed findings
|
||||||
|
- Run `safety check` to scan dependencies for known CVEs before submitting any PR
|
||||||
|
|
||||||
### FastAPI Patterns
|
### FastAPI Patterns
|
||||||
- Organize endpoints by feature in `app/api/` directory
|
- Organize endpoints by feature in `app/api/` directory
|
||||||
@@ -152,6 +170,8 @@ pre-commit run --all-files
|
|||||||
- Use `pytest.fixture` for test setup and teardown
|
- Use `pytest.fixture` for test setup and teardown
|
||||||
- Run tests with: `pytest -v`
|
- Run tests with: `pytest -v`
|
||||||
- Check coverage with: `pytest --cov=app --cov-report=term-missing`
|
- Check coverage with: `pytest --cov=app --cov-report=term-missing`
|
||||||
|
- **All tests must pass** before submitting changes — never leave failing tests
|
||||||
|
- **All linters must pass** before submitting — run `pre-commit run --all-files`
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
- All configuration is in `app/config.py` using Pydantic Settings
|
- All configuration is in `app/config.py` using Pydantic Settings
|
||||||
@@ -161,7 +181,7 @@ pre-commit run --all-files
|
|||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
- Keep documentation in `docs/` directory in Markdown format
|
- Keep documentation in `docs/` directory in Markdown format
|
||||||
- Update relevant docs when adding features or changing behavior
|
- **Always update** relevant docs when adding or changing any feature — documentation updates are mandatory, never optional
|
||||||
- User-facing documentation should be clear and include examples
|
- User-facing documentation should be clear and include examples
|
||||||
- Reference existing docs: `docs/UserGuide.md`, `docs/API.md`, `docs/DeploymentGuide.md`
|
- Reference existing docs: `docs/UserGuide.md`, `docs/API.md`, `docs/DeploymentGuide.md`
|
||||||
- See [AGENTIC_CODING.md](../AGENTIC_CODING.md) for detailed development guide
|
- See [AGENTIC_CODING.md](../AGENTIC_CODING.md) for detailed development guide
|
||||||
@@ -223,7 +243,8 @@ These files and directories are managed by automation or are critical infrastruc
|
|||||||
- Write clear, descriptive commit messages
|
- Write clear, descriptive commit messages
|
||||||
- **ALWAYS follow Conventional Commits format** (see below)
|
- **ALWAYS follow Conventional Commits format** (see below)
|
||||||
- Keep commits focused and atomic
|
- Keep commits focused and atomic
|
||||||
- Run tests and linters before committing
|
- **All tests must pass** before committing — `pytest` must succeed with no failures
|
||||||
|
- **All linters must pass** before committing — `pre-commit run --all-files` must succeed
|
||||||
- Pre-commit hooks are configured (`.pre-commit-config.yaml`)
|
- Pre-commit hooks are configured (`.pre-commit-config.yaml`)
|
||||||
|
|
||||||
## Conventional Commits (REQUIRED)
|
## Conventional Commits (REQUIRED)
|
||||||
@@ -313,7 +334,7 @@ These files are managed entirely by the semantic-release automation.
|
|||||||
- Configuration in `app/config.py`
|
- Configuration in `app/config.py`
|
||||||
|
|
||||||
### Common Patterns
|
### Common Patterns
|
||||||
- Use `from typing import Optional, Dict, List, Any` for type hints
|
- Use modern Python 3.10+ type hints: `list[str]`, `dict[str, Any]`, `str | None`; only import from `typing` for `Any`, `Callable`, `TypeVar`, `Protocol`
|
||||||
- Import FastAPI dependencies: `from fastapi import Depends, HTTPException, status`
|
- Import FastAPI dependencies: `from fastapi import Depends, HTTPException, status`
|
||||||
- Get DB session: `db: Session = Depends(get_db)`
|
- Get DB session: `db: Session = Depends(get_db)`
|
||||||
- Current user: `current_user: User = Depends(get_current_user)`
|
- Current user: `current_user: User = Depends(get_current_user)`
|
||||||
|
|||||||
@@ -230,12 +230,15 @@ For API details, refer to the [API Documentation](./API.md).
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Updating Documentation
|
## Updating Documentation
|
||||||
|
Documentation updates are **mandatory** — every PR that changes code must include matching documentation updates in the same PR. There are no exceptions.
|
||||||
|
|
||||||
When making code changes:
|
When making code changes:
|
||||||
1. Update relevant documentation in the same PR
|
1. **Update relevant documentation** in the same PR — never defer docs to a follow-up
|
||||||
2. Check for outdated information
|
2. Check for outdated information in existing docs
|
||||||
3. Add new sections for new features
|
3. Add new sections for new features
|
||||||
4. Update examples if behavior changes
|
4. Update examples if behavior changes
|
||||||
5. Review related documentation for consistency
|
5. Review related documentation for consistency
|
||||||
|
6. Update `docs/ConfigurationGuide.md` and `.env.demo` for any new or changed configuration options
|
||||||
|
|
||||||
## Screenshots and Diagrams
|
## Screenshots and Diagrams
|
||||||
- Use clear, high-quality images
|
- Use clear, high-quality images
|
||||||
|
|||||||
@@ -7,16 +7,18 @@ applyTo: "app/**/*.py"
|
|||||||
These instructions apply to all Python code in the `app/` directory.
|
These instructions apply to all Python code in the `app/` directory.
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
- Use **Ruff** for linting and formatting with 120 character line length
|
- Use **Ruff** for all formatting, linting, import sorting, and security scanning — `ruff format app/ tests/ && ruff check app/ tests/ --fix`
|
||||||
|
- Line length: 120 characters (configured in `pyproject.toml` `[tool.ruff]`)
|
||||||
- All functions must have type hints for parameters and return values
|
- All functions must have type hints for parameters and return values
|
||||||
- Use `from typing import Optional, Dict, List, Any, Union` as needed
|
- Use modern Python 3.10+ type hints: `list[str]`, `dict[str, Any]`, `str | None`
|
||||||
|
- Only import from `typing` for `Any`, `Callable`, `TypeVar`, `Protocol` (not `Dict`, `List`, `Optional`, `Union`)
|
||||||
|
|
||||||
## Import Order (Ruff enforces isort-compatible ordering)
|
## Import Order (enforced by Ruff `I` rules)
|
||||||
```python
|
```python
|
||||||
# Standard library imports
|
# Standard library imports
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Dict, List
|
from typing import Any # Only for Any, Callable, TypeVar, Protocol
|
||||||
|
|
||||||
# Third-party imports
|
# Third-party imports
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
@@ -33,7 +35,7 @@ from app.models import Document, User
|
|||||||
def process_document(
|
def process_document(
|
||||||
file_path: Path,
|
file_path: Path,
|
||||||
user_id: int,
|
user_id: int,
|
||||||
metadata: Optional[Dict[str, Any]] = None
|
metadata: dict[str, Any] | None = None
|
||||||
) -> DocumentMetadata:
|
) -> DocumentMetadata:
|
||||||
"""
|
"""
|
||||||
Process a document and extract metadata.
|
Process a document and extract metadata.
|
||||||
@@ -127,7 +129,7 @@ import logging
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@shared_task(bind=True, max_retries=3)
|
@shared_task(bind=True, max_retries=3)
|
||||||
def process_ocr(self, document_id: int) -> Dict[str, Any]:
|
def process_ocr(self, document_id: int) -> dict[str, Any]:
|
||||||
"""Process OCR for a document."""
|
"""Process OCR for a document."""
|
||||||
try:
|
try:
|
||||||
# Processing logic
|
# Processing logic
|
||||||
@@ -153,10 +155,11 @@ class Settings(BaseSettings):
|
|||||||
env_file = ".env"
|
env_file = ".env"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Security
|
## Security (First and Foremost)
|
||||||
- Never commit secrets
|
- **Security first**: treat every change as a potential attack surface — review `SECURITY_AUDIT.md` before making any security-related change
|
||||||
- Validate all user inputs
|
- Never commit secrets, tokens, or credentials
|
||||||
- Use parameterized queries
|
- Validate and sanitize all user inputs
|
||||||
- Sanitize file paths
|
- Use parameterized queries — never raw SQL with user data
|
||||||
- Check file permissions
|
- Sanitize file paths; check file permissions before access
|
||||||
- Review SECURITY_AUDIT.md for guidelines
|
- Security linting is built into Ruff via `S` rules — fix all `S`-prefixed findings before committing
|
||||||
|
- Run `safety check` before submitting any PR to catch dependency CVEs
|
||||||
|
|||||||
@@ -174,7 +174,8 @@ def test_create_document(db_session):
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Test Coverage Goals
|
## Test Coverage Goals
|
||||||
- Aim for **80% code coverage** for all new code
|
- Achieve **100% test coverage** for all new code — use `# pragma: no cover` only for genuinely unreachable or platform-specific branches, with an inline comment explaining why
|
||||||
|
- Enforce the threshold: `pytest --cov=app --cov-fail-under=100`
|
||||||
- Focus on critical paths and error handling
|
- Focus on critical paths and error handling
|
||||||
- Test both success and failure scenarios
|
- Test both success and failure scenarios
|
||||||
- Don't test third-party library code
|
- Don't test third-party library code
|
||||||
|
|||||||
Reference in New Issue
Block a user