Your assistant writes raw SQL when your team mandates the repository layer; it uses console.log where you use a structured logger; every scaffolded endpoint needs the same 15 corrections. The fix is not better per-prompt nagging — it is a checked-in instruction file (CLAUDE.md, AGENTS.md, .github/copilot-instructions.md, Cursor rules) that conditions every generation.
Tier: Core · Workflow area: Feature development
This playbook covers writing one that measurably changes behavior, and using it to make boilerplate generation actually convention-conformant.
Skip elaborate instruction files for throwaway repos and spikes.
Collect the correction log. Skim recent PR comments and your own fixes to AI output. Write each as a rule candidate: "used raw asyncpg query → must go through app/repositories/", "invented error format → must use ApiError".
Write the file: facts, rules, and pointers. Keep it under ~150 lines. Structure that works:
# Project notes for AI assistants
## Stack facts
- FastAPI + SQLAlchemy 2.0 (async), Postgres, Alembic migrations.
- Tests: pytest + pytest-asyncio; run `make test-fast` (no network).
## Hard rules
- All DB access goes through app/repositories/*. Never inline SQL in routes.
- Errors: raise ApiError(code, http_status) from app/errors.py. Never
return ad-hoc {"error": ...} dicts.
- Logging: `logger = structlog.get_logger()`; no print(), no f-string logs.
- New endpoints: router in app/api/<domain>.py, schema in app/schemas/,
repository method, and a test in tests/api/ - all four, every time.
## Look at these examples first
- app/api/orders.py - canonical endpoint shape.
- tests/api/test_orders.py - canonical test shape.
Test it with an A/B generation. In a scratch branch, run the same scaffold request twice — once before committing the file, once after:
Add a CRUD endpoint set for "warehouses" (id, name, region, active).
Follow project conventions.
Diff the outputs against your rules. Rules the model still violates are worded too vaguely — rewrite them as imperatives naming files ("use X from Y"), not prose ("prefer clean error handling").
Use pointers over prose. The single highest-leverage line is "copy the shape of app/api/orders.py". Models follow concrete exemplar files far more reliably than abstract rules.
Maintain it like code. When a new correction pattern shows up twice in review, it becomes a rule. When the stack changes (SQLAlchemy 2.0 migration done, logger swapped), prune stale rules the same day — a wrong instruction file is worse than none.
End the file with "If a rule here conflicts with what you infer from the code, follow this file and mention the conflict" — you will learn about drift in your own codebase from the agent's mentions.
CLAUDE.md at repo root (plus per-directory files for monorepos); loaded automatically every session..github/copilot-instructions.md conditions chat, agent mode, and code review; path-scoped *.instructions.md files for per-area rules..cursor/rules (or AGENTS.md, an emerging cross-tool convention). Same content works across all three — write once, symlink or duplicate.Instruction files are part of the repo and never leave it — they work unchanged with client-sanctioned enterprise tools (Copilot Business reads copilot-instructions.md from the client repo). For local-model workflows, paste the instruction file at the top of each session as the system-style preamble; keep a short version (under 40 lines) for small local models, which follow fewer rules reliably. Never encode client-confidential architecture secrets in the file if the repo is ever shared with third-party contractors.