Skip to content
4 min readSarvStack Team

What makes a codebase agent-ready

Conventions files, executable checks, and guardrails: the patterns that turn AI coding agents from a liability into a force multiplier.

ai-agentsconventionsdeveloper-experience

Coding agents are useful in inverse proportion to how much implicit knowledge your codebase carries. On a greenfield project with one file they're a rocket. On a mature codebase where the important rules live in your head — "we don't put roles on profiles", "every table needs GRANTs" — they're a slow-motion incident waiting for a Monday morning.

"Agent-ready" isn't a product feature. It's a small set of habits that make your project legible to a stateless collaborator that will read a handful of files and start writing. This is what we optimise for in this kit, and what we'd recommend for any codebase that's going to be touched by an agent regularly.

A single conventions file the agent will actually read

The most impactful thing you can do is write down what you already know and put it somewhere obvious. In this kit that file is CLAUDE.md at the repo root. It documents folder structure, naming, how to add a route, the RLS pattern for new tables, the billing flow end-to-end, the roles model, and the design system tokens. It's ~600 lines and it is the single most consulted document during any change.

A few rules that seem to matter:

  • Put it at the root and give it a canonical name. CLAUDE.md, AGENTS.md, .cursor/rules/*.mdc — whatever your primary agent expects. Every agent worth using will pick it up automatically.
  • Write it for someone who has never seen the project. Not for yourself six months from now, not for your teammate. For a stranger with no context and no ability to ask follow-up questions.
  • Show the pattern, then the exception. "Every new table needs GRANTs — here's the checklist. Exception: private helper tables that are only touched by the service role."
  • Link the file from your slash commands. In .claude/commands/ we ship new-table.md, new-page.md, new-server-fn.md — three markdown files that walk the agent through the exact steps from CLAUDE.md with concrete examples. Typing /new-table in Claude Code loads the recipe verbatim.

The point is not that documentation is nice-to-have. It's that documentation is the primary API you're exposing to your agent.

Executable checks as feedback loops

An agent is only as good as its ability to know when it's wrong. Every convention that matters should be enforceable by a script the agent can run and interpret without you. In this kit:

  • `bunx tsgo --noEmit` — TypeScript strict, no any, no @ts-ignore. The single fastest signal.
  • `bun run lint` — ESLint across src/. Catches unused imports, dead code, forbidden patterns.
  • `bun run check:i18n` — Custom script that diffs the English dictionary against DE and ES. Fails on missing keys, type mismatches, placeholder drift, and empty translations. Without this, agents silently ship broken translations; with it, they add the DE/ES entries in the same edit because the check fails otherwise.
  • `bun run test:unit` — Pure-logic tests for things like entitlement resolution and webhook signature verification. Small, fast, boring.
  • Playwright smoke. A few tests that hit the landing page, pricing, and login and assert the basics — the H1, the CTA, no runtime error. Catches "the whole page is white now" regressions that typechecks miss.

The full pipeline runs in CI on every PR. Locally an agent can run any of them individually to verify its work. The key property is that a failing check produces a precise, actionable error message — the agent reads it, fixes the underlying issue, and re-runs.

Two anti-patterns to avoid: checks that only fail some of the time (flaky Playwright specs), and checks whose output is a wall of text with no clear signal. Both teach the agent to ignore the check.

Guardrail patterns in code, not just docs

Some rules can't be captured by a linter. For those, encode them into patterns the agent can copy verbatim.

  • A canonical example for every recurring task. public.projects is our reference user-owned table. New table? Copy the migration, copy the RLS policies, copy the query helper, copy the CRUD component. The agent doesn't invent — it clones a working shape.
  • `SECURITY DEFINER` helpers instead of clever RLS. has_role() and has_org_role() exist so that role-based policies can never accidentally recurse. The agent doesn't need to reason about recursion because there's a helper to call.
  • Auth-gated server functions with `requireSupabaseAuth`. Every mutation on user-scoped data chains this middleware. A missing middleware is a bug that's easy to spot in review and easy to write a lint rule for; a missing auth check hidden inside 200 lines of handler code is not.
  • **Public API prefix (/api/public/*) for external callers only.** The prefix is the signal. If it's under /api/public/, it's unauthenticated by design and must verify the caller itself. If it isn't, it's app-internal and must be a server function or an authenticated route.

Patterns are cheaper than rules. A well-shaped example prevents ten categories of mistake without a single line of enforcement code.

The compound effect

None of this is fancy. A conventions file, half a dozen check scripts, and a handful of canonical patterns. What makes it work is that they compose: the docs point at the patterns, the checks enforce what the docs can't, and the patterns are small enough that the agent can copy one without misunderstanding it.

The result is that most feature work reduces to a repeatable loop: read CLAUDE.md, copy the closest pattern, run the checks, fix what they complain about, ship. That loop is what "agent-ready" actually means — and it happens to be a pretty good loop for humans too.