Some checks failed
CI / go-lint (push) Has been cancelled
CI / python-lint (push) Has been cancelled
CI / nodejs-lint (push) Has been cancelled
CI / test-go-consent (push) Has been cancelled
CI / test-python-voice (push) Has been cancelled
CI / test-bqas (push) Has been cancelled
Mandatory pre-push gates for all three language stacks with exact commands, common pitfalls, and architecture rules. CLAUDE.md updated with quick-reference section linking to the new files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.7 KiB
4.7 KiB
AGENTS.python.md — Python Agent Rules
Applies to: backend-compliance/, ai-compliance-sdk/ (Python path), compliance-tts-service/, document-crawler/, dsms-gateway/ (Python services)
NON-NEGOTIABLE: Pre-Push Checklist
BEFORE every git push, run ALL of the following from the service directory. A single failure blocks the push.
# 1. Fast lint (Ruff — catches syntax errors, unused imports, style violations)
ruff check .
# 2. Auto-fix safe issues, then re-check
ruff check --fix . && ruff check .
# 3. Type checking (mypy strict on new modules, standard on legacy)
mypy . --ignore-missing-imports --no-error-summary
# 4. Unit tests only (fast, no external deps)
pytest tests/unit/ -x -q --no-header
# 5. Verify the service starts (catches import errors, missing env vars with defaults)
python -c "import app" 2>/dev/null || python -c "import main" 2>/dev/null || true
One-liner pre-push gate (run from service root):
ruff check . && mypy . --ignore-missing-imports --no-error-summary && pytest tests/ -x -q --no-header
Why each check matters
| Check | Catches | Time |
|---|---|---|
ruff check |
Syntax errors, unused imports, undefined names | <2s |
mypy |
Type mismatches, wrong argument types | 5-15s |
pytest -x |
Logic errors, regressions | 10-60s |
| import check | Missing packages, circular imports | <1s |
Code Style (Ruff)
Config lives in pyproject.toml. Do not add per-file # noqa suppressions without a comment explaining why.
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "UP", "B", "C4", "SIM", "TCH"]
ignore = ["E501"] # line length handled by formatter
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"] # assert is fine in tests
Blocked patterns:
from module import *— always name imports explicitly- Bare
except:— useexcept Exception as e:at minimum print()in production code — uselogger- Mutable default arguments:
def f(x=[])→def f(x=None)
Type Annotations
All new functions must have complete type annotations. Use from __future__ import annotations for forward references.
# Required
async def get_tenant(tenant_id: str, db: AsyncSession) -> TenantModel | None:
...
# Required for complex types
from typing import Sequence
def list_risks(filters: dict[str, str]) -> Sequence[RiskModel]:
...
Mypy rules:
--disallow-untyped-defson new files--stricton new modules (not legacy)- Never use
type: ignorewithout a comment
FastAPI-Specific Rules
# Handlers stay thin — delegate to service layer
@router.get("/risks/{risk_id}", response_model=RiskResponse)
async def get_risk(risk_id: UUID, service: RiskService = Depends(get_risk_service)):
return await service.get(risk_id) # ≤5 lines per handler
# Always use response_model — never return raw dicts from endpoints
# Always validate input with Pydantic — no manual dict parsing
# Use HTTPException with specific status codes, never bare 500
Testing Requirements
tests/
├── unit/ # Pure logic tests, no DB/HTTP (run on every push)
├── integration/ # Requires running services (run in CI only)
└── contracts/ # OpenAPI snapshot tests (run on API changes)
Unit test requirements:
- Every new function → at least one happy-path test
- Every bug fix → regression test that would have caught it
- Mock all I/O: DB calls, HTTP calls, filesystem reads
# Run unit tests only (fast, for pre-push)
pytest tests/unit/ -x -q
# Run with coverage (for CI)
pytest tests/ --cov=. --cov-report=term-missing --cov-fail-under=70
Dependency Management
# Check new package license before adding
pip show <package> | grep -E "License|Home-page"
# After adding to requirements.txt — verify no GPL/AGPL
pip-licenses --fail-on="GPL;AGPL" 2>/dev/null || echo "Check licenses manually"
Never add:
- GPL/AGPL licensed packages
- Packages with known CVEs (
pip audit) - Packages that only exist for dev (
pytest,ruff) to production requirements
Common Pitfalls That Break CI
| Pitfall | Prevention |
|---|---|
const x = ... inside dict literal (wrong language!) |
Run ruff before push |
| Pydantic v1 syntax in v2 project | Use model_config, not class Config |
Sync function called inside async without run_in_executor |
mypy + async linter |
Missing await on coroutine |
mypy catches this |
datetime.utcnow() (deprecated) |
Use datetime.now(timezone.utc) |
Bare except: swallowing errors silently |
ruff B001/E722 catches this |
| Unused imports left in committed code | ruff F401 catches this |