Six reusable techniques (fill, aspect-ratio, object-fit, derived heights, sibling sync, sizes) with a decision tree and a worked example tied to the homepage Basecamp card. Registered in root and web AGENTS.md.
8.9 KiB
AGENTS.md
Operating guide for AI coding agents working in this repo. Keep changes minimal, match the conventions below, and prefer reading the linked docs over inferring intent.
Repo shape
pnpm + Turborepo monorepo. Node 24, pnpm 11.1.
| Path | Role |
|---|---|
apps/web |
Public Next.js 16 site. Tailwind v4, next-intl, static export. Port 3000. |
apps/cms |
Payload CMS 3.x admin app (Next.js 16 + Postgres). Port 3001, admin at /admin. |
apps/civi-crm |
CiviCRM internal web layer (Next.js 16). Keycloak-protected. Port 3002. See docs/civi-crm/architecture.md. |
packages/content |
Content schemas, loaders for content/**, GitHub mutation helpers, locale registry. |
packages/ui |
Shared React primitives + SVG icon components. |
packages/tokens |
Design tokens. |
packages/types |
Shared types incl. Payload-generated (generate-types). |
packages/config |
Shared ESLint / TypeScript / Prettier config. |
content/** |
Production source of truth for page copy and structured data (pages, press, circles, builders-hub, site). |
docs/** |
Deployment, plan, page/component specs. Read before designing. |
Apps are entrypoints only. New shared logic goes in a package, not duplicated across apps.
Commands
Run from repo root unless noted.
pnpm install
pnpm dev # turbo: web on :3000, cms on :3001, civi-crm on :3002
pnpm build # web static export + cms next build
pnpm test # vitest in apps/web
pnpm lint # eslint --max-warnings 0 across workspaces
pnpm check-types # next typegen + tsc --noEmit
pnpm generate-types # cms only — regenerates packages/types/src/payload.ts
apps/web/start is python3 -m http.server over the static out/ build, not next start. Treat the web app as static.
Architecture rules
apps/webdoes not call the GitHub API at request time. It readscontent/**at build. Preview deploys build per PR; production deploys frommaster.- CMS edits are PRs, not DB writes. Payload's Postgres only stores users, sessions, drafts, and PR cache. All published content changes go through
developvia GitHub PR (seeapps/cms/src/services/content-workflow/). - Branch model:
developis the default + staging branch.masteris production (created when production deploys spin up). Direct commits to either are blocked by branch protection. CMS-generated PRs targetdevelop. - Env access is typed. Use
apps/web/lib/env.ts; never reach intoprocess.envfrom feature code. Add new vars toenv.ts. - The CMS refuses to boot in production-like envs without
NEXT_PUBLIC_SERVER_URL/NEXT_PUBLIC_WEB_URL— silent localhost fallback breaks CORS/cookies. Don't "fix" this by removing the assertion.
i18n
- Active locales come from
apps/web/i18n/routing.ts(today:['en']).Languagetype is pre-declared as'en' | 'fr' | 'ko'so adding a locale is a routing change, not a schema change. - UI chrome →
apps/web/messages/<locale>.json(next-intl). - Page copy / structured data →
content/**/<locale>/...JSON files. - All user-facing strings go through i18n keys. Includes titles, descriptions, labels, and
createDefaultMetadataargs. UsegetTranslationsin server components,useTranslationsin client components. Hardcoded strings will be rejected. - A locale added to
routing.tsmakes its<locale>.jsonfiles required at build time — there is no silent fallback. - After
next build,apps/web/scripts/strip-default-locale-prefix.shstrips the/enprefix from the static export. Don't bypass it.
Code conventions
- Repo files are English only. Committed code, comments, docs, copy. Chat replies can be in any language; files cannot.
- Use British English for public paths and all user-facing copy. This includes route segments, slugs, content JSON values,
next-intlmessages, metadata, docs that quote public copy, and CMS seed fixtures. Prefer spellings such asdecentralised,centralised,programme,organise,organisation,neighbourhood, andlicencefor copy. Keep third-party API literals, official external URLs, schema.org types, package names, generated types, and internal field names unchanged when they require American spelling. - Icons are React SVG components exported from
@repo/ui(packages/ui/src/icons). If a designed icon is missing, stop and ask for a Figma export — don't substitute Unicode/text. - The λ brand mark is
<LogosMark />from@repo/ui. Size viasize, color via parenttext-*(the SVG usescurrentColor). Never<img src=".svg" />or<span>λ</span>. - Every clickable element gets
cursor-pointerin its Tailwind className: buttons,onClickhandlers, anchors, clickable cards. - Visuals match Figma 1:1. Pull the spec (font sizes, fills, gaps, padding) from Figma before implementing. See
docs/components.mdfor canonical node IDs anddocs/web-pages.mdfor per-page references. - Types on public APIs. Exported functions, shared utilities, component props. Use
interfacefor object shapes,typefor unions/intersections. Avoidany; useunknown+ narrowing for external input. - Keep reusable code out of generated or oversized files. If a type, constant list, validator, or UI helper is used in more than one place, move it into a focused module and import it. Do not let generated files or collection configs absorb long literal unions, large option arrays, or repeated validation logic.
- Split files by feature when they grow. Prefer small folders with focused files (types, constants, validators, components) over large catch-all modules. Create a feature folder once a file mixes multiple responsibilities or becomes hard to scan.
- Immutability. Spread/copy, never mutate.
Readonly<T>on inputs where it clarifies intent. - No
console.login committed code. - Follow existing file organization. Many small focused files over large ones. Routes live under
apps/web/app/[locale]/<route>/; section components underapps/web/components/sections/<section>/.
Content + CMS
Schemas: packages/content/src/schemas/ (Zod). Loaders: packages/content/src/loaders/ (typed, fail loudly on missing required locale files).
When changing a schema:
- Update the Zod schema in
packages/content/src/schemas/. - Update / add the corresponding loader.
- Update fixtures under
content/**. - If the CMS edits this collection, update the matching Payload collection in
apps/cms/src/collections/and the workflow service inapps/cms/src/services/content-workflow/. - Run
pnpm generate-types(CMS) andpnpm check-types.
Payload collections: Pages, Circles, Ideas, Rfps, ContentChangeRequests, Users. The first four mutate via the GitHub PR workflow (see save-as-pr.ts, save-idea-as-pr.ts, save-rfp-as-pr.ts).
Testing
- Vitest lives in
apps/web(pnpm testfrom root or app). Add tests beside the code in__tests__/folders. - Do not add UI implementation contract tests that assert Tailwind class strings, Figma measurements, layout spacing, motion details, hover treatment, or component source structure. UI changes are reviewed through browser verification, not brittle source-string tests.
- Playwright is the standard for E2E if/when added.
- For UI/frontend changes, start the dev server and verify in a browser before reporting done. Type-check passing ≠ feature working.
Git + PRs
- Never auto-commit or push without explicit instruction.
- Never add co-author /
Co-Authored-Bylines to commits in this repo. - Conventional commit prefixes:
feat,fix,refactor,docs,test,chore,perf,ci. - PR base branch is
develop.
Where to look first
docs/deployment.md— env vars, Vercel dev/staging vs self-hosted prod, Postgres setup, troubleshooting.docs/cms-github-content-plan.md— canonical schema + workflow design.docs/web-pages.md— per-page Figma references and requirements.docs/components.md— shared component specs (Nav, Footer, etc.) with Figma node IDs.docs/code-quality-followups.md— known gaps awaiting design/infra decisions.docs/seo.md— SEO/metadata expectations.docs/responsive-image-frame-fitting.md— techniques for making images fill a card or frame perfectly at every viewport (aspect-ratio, derived heights, object-fit, sizes). Read before implementing any full-bleed or frame-fitted image section.
Don't
- Don't introduce new top-level packages or apps without checking with the user.
- Don't add backwards-compat shims,
_unusedrenames, or "removed in vX" comments. Delete cleanly. - Don't add error-handling for cases that can't occur, or fallbacks that hide misconfiguration.
- Don't write comments that restate the code. Comments explain why, only when non-obvious.
- Don't create new
.mdfiles unless explicitly asked. - Don't run destructive git operations (
reset --hard, force push, branch delete) without confirmation.