AGENTS.md — build & deploy conventions
This file is a contract. Any AI coding session (Claude Code, Cursor, Copilot, v0, …) working in a repo that vendors this file MUST follow it. The human-readable version is
HANDBOOK.md. Source of truth lives in the engineering-playbook repo — do not diverge a project's copy silently.
Using this in a project
Copy this file to the repo root. Add a one-line CLAUDE.md that says
See AGENTS.md. Then set these two lines for the project and delete the others:
DEPLOY CLASS: container-image | managed-source
DEPLOY TARGET: coolify | digitalocean | render | fly | railway | vercel | cloudflare | netlify | <other-in-class>
Everything in Core applies to every project. Then apply the one deploy class that matches. The per-target notes at the end are just reminders for named instances — the class section is what's binding.
Core — every project, every stack
The loop (trunk-based)
mainis the single source of truth and is always deployable.- Branch off the latest
main, namedinitials/short-description. A branch lives hours, not days. - Commit small and often. Open a PR early — ideally the same day you branched.
- Every PR gets a preview URL; review the change on it, not just the diff.
- Merge only when checks are green and a human approved. Then delete the branch.
- Never commit directly to
main. Never merge on red.
AI-assisted work
- Keep each change small enough to review. A large AI-generated diff is code
nobody actually reviewed — that is how unsafe code reaches
main. - A passing formatter proves nothing about safety. Formatting, linting/types, and security scanning are three different nets — the pipeline runs all three.
- A preview shows the experience (UI, copy, happy path). It hides leaked secrets, security holes, and off-happy-path logic. Judge the experience on the preview; let the gates and a human judge the rest.
Secrets
- Never put keys, tokens, or passwords in code — not even briefly. Git keeps them forever and previews can expose them.
- Values live in the platform's env/secret store, per environment.
.envis git-ignored; commit.env.examplelisting the keys only (no values).
Dependencies
- Track latest stable. Do not sit on an end-of-life major without a reason written down (e.g. an adapter that doesn't support it yet).
- Commit the lockfile (
package-lock.json/pnpm-lock.yaml). CI installs withnpm ci, which fails without a committed lockfile. - A framework-major upgrade is its own small PR, verified green — never bundled with feature work.
Gates (block the merge; run them locally before you push)
Wire these as required status checks and protect main (no direct pushes, no
merge while red):
- format + lint (Biome, or ESLint + Prettier)
- type-check (
tsc --noEmit) - tests (unit/component; end-to-end where they exist)
- content/schema validation where the project has it
- build
Security scanning is part of the pipeline too — see your deploy class for where it runs.
Language
- British English in user-facing copy.
Deploy class: container-image
Coolify · DigitalOcean App Platform (image mode) · a VPS/Droplet + Docker · Render · Fly.io · Railway · Kamal
Principle — build once, scan the artefact, the platform pulls it. CI builds one image, scans that image, pushes it to a registry; the host pulls the exact tag. Never build on the production box: you'd ship something you never scanned, and heavy builds crash small hosts.
- App shape: one
Dockerfile; Next.jsoutput: 'standalone'; a real/healthroute; a single container with default naming; no host port mapping (it disables the health check and breaks zero-downtime rollouts). - Pipeline: gates → build image → scan image (Trivy; block on criticals)
→ push to registry (GHCR) → trigger the host to pull that tag. This is the
security gate for this class. See
ci/deploy-image.yml. - Registry auth: the host needs
docker login <registry>with a token/PAT to pull private images, or the pull failsunauthorized. - Stateful services (Postgres, Redis, feature flags) are separate resources, never baked into the app container — so an app deploy never restarts your database.
- Previews: enable the platform's per-PR preview deployments; keep preview env vars separate from production (never expose prod secrets to a preview).
Deploy class: managed-source
Vercel · Cloudflare (via the OpenNext adapter) · Netlify
Principle — the platform builds from your repo. Git integration gives per-PR previews and trunk→production automatically. There is no image, so the "scan the artefact" gate is replaced by source + dependency scanning in CI.
- Framework preset/adapter is explicit — never rely on autodetect. (Vercel:
pin
frameworkinvercel.json/vercel.ts. Cloudflare: the OpenNext adapter, not the deprecatednext-on-pages.) - Gates still run in CI on every PR (
ci/gates.yml); the platform owns build + deploy + previews. - Security gate for this class:
npm audit/ dependency review /trivy fs, plus Dependabot (or similar). This replaces image scanning. - Env vars are set per-environment in the platform (Production and Preview), via its CLI/token — never in git.
- Version floors: some adapters track only recent framework majors (e.g. Cloudflare's OpenNext drops old Next majors) — keep the framework current or you can't deploy.
output: 'standalone'is irrelevant here; the platform uses its own build.
Per-target reminders (instances of the classes above)
| Target | Class | Deploy | Previews | DB option | Watch out for |
|---|---|---|---|---|---|
| Coolify | container-image | pulls GHCR image | built-in per-PR | Postgres as a separate resource | docker login PAT; single-container shape; no port mapping |
| DigitalOcean | container-image (App Platform image / Droplet+Docker) | image or source | Preview Apps via a GH Action | managed PG add-on or Neon | Preview Apps need the delete-action to clean up on close |
| Render / Fly / Railway | container-image | pulls image | per-PR (varies) | managed PG / external | confirm health-check + single-container assumptions |
| Vercel | managed-source | git integration | automatic | Neon (marketplace) | Vercel Postgres/KV retired; pin the framework preset |
| Cloudflare | managed-source | OpenNext build | Pages/Workers git previews | Neon via Hyperdrive, or D1 | Next version floor; adapter feature caveats |
| Netlify | managed-source | git integration | automatic | external (Neon/Supabase) | confirm the Next adapter covers the features you use |
Any other host: ask does CI hand over an image, or does the platform build from source? → apply that class. Do not special-case per vendor beyond this table.