AGENTS.md and CLAUDE.md are table stakes — enforcement is the moat
Every serious SaaS boilerplate now ships an AGENTS.md or a CLAUDE.md — a soft file the agent can quietly ignore by prompt 50. The differentiator is enforcement: ESLint-checked DDD boundaries and typed contracts that turn the build red when the agent crosses a line.
Open almost any serious SaaS boilerplate in 2026 and you'll find an AGENTS.md
or a CLAUDE.md at the repository root. It's the new README: a plain-language
brief that tells your coding agent how the codebase is laid out, which patterns
to follow, and which mistakes have already bitten someone. UseDeploy ships one
too — a long, opinionated CLAUDE.md. But if you're grading a boilerplate on
whether it "has an AGENTS.md," you're grading the wrong thing. That file is table
stakes. The moat is what happens when the agent ignores it.
The rules file is a suggestion, and the agent knows it
AGENTS.md is now a real ecosystem standard, not a Claude-specific quirk. As of
mid-2026 it's used across tens of thousands of open-source
repositories and read natively by 30-plus agent tools —
OpenAI Codex, Claude Code, GitHub Copilot, and Cursor among them. CLAUDE.md is
the Claude-specific sibling. Both do the same thing mechanically: the file is
loaded into the model's context at the start of a session and injected as a
prompt prefix. The agent tends to follow it.
"Tends to" is the whole problem. A markdown file — whatever its filename — is guidance, not a gate. It has no failure mode. When the agent follows it, great. When the agent drifts, nothing happens: the build still goes green, the PR still opens, and the violation ships until a human notices it in review.
And agents drift. The failure mode has a name now — practitioners call it the
"doom loop" or "context rot": the agent sees only a slice of your project each
session, so it edits confidently against a stale mental
model
and the bug rate climbs as sessions get longer. A rule the agent didn't load
this session is a rule that, for this session, does not exist. You can write the
most beautiful AGENTS.md in the world and the agent will still, by prompt 50,
confidently reintroduce the exact pattern you told it to avoid — because the file
is upstream of the model's attention, not downstream of the compiler.
This is why "AI-agent-ready" has quietly stopped being a differentiator. As of
July 2026 every serious competitor ships the file: supastarter, for instance,
markets an AGENTS.md optimized for Cursor and Claude Code, "designed to be
extended by AI coding agents." Shipping the soft file is now the price of entry.
It's necessary. It is nowhere near sufficient.
What UseDeploy's CLAUDE.md actually asks for
To be clear: the soft file earns its keep. UseDeploy's CLAUDE.md is where the
hard-won, human-only knowledge lives — the stuff no linter can express. A good
example is the rule about shared contracts, which exists because we shipped the
bug it prevents:
When a form, mutation, or query touches an HTTP endpoint, the client-side Zod schema must come from
@app/contracts— never re-define a server-known field. [...] If the form has its ownfullNamewhile the server expectsname, typecheck passes and the user gets a 400 in production. We have shipped this exact bug; do not repeat it.
That's real institutional memory, and it's exactly the kind of thing an agent
starting from a blank repo would get wrong on its first pass. But read it again:
it's still an ask. "Must come from @app/contracts" is a sentence. The agent
can nod at it and then inline the field anyway, and the file has no way to stop
it. Which is why the same concern shows up a second time in the repo — as
enforcement.
The moat: three walls the agent can't talk its way past
The market has already crystallized the winning frame for this. As of July 2026, one competitor landing page describes serious buyers as purchasing "the guardrails [the agent] can't write for itself: the rules that keep it consistent, and the walls that stop it breaking your login when it touches payments" — because otherwise "by prompt 50 it's spaghetti." That's the pitch. The question is whether a boilerplate actually builds those walls, or just writes them down.
UseDeploy builds them. There are three, and none of them are markdown.
1. DDD layering that fails lint, not review
The domain layer of every bounded context is forbidden — by ESLint, not by
convention — from importing a framework or reaching up the stack. This is the
literal config from packages/eslint-config/ddd.js:
{
files: ['**/modules/*/domain/**/*.ts'],
rules: {
'no-restricted-imports': ['error', {
patterns: [
{
group: ['*/application/*', '*/infrastructure/*', '*/interfaces/*'],
message:
'domain/ may not import from application, infrastructure, or interfaces. See ADR-001.',
},
{
group: ['express', 'prisma', '@prisma/client', 'better-auth'],
message: 'domain/ must remain framework-agnostic. See ADR-001.',
},
],
}],
},
}When the agent — trying to be helpful — imports @prisma/client into a domain
entity to "just fetch the record here," bun run lint exits non-zero with a
message that names the ADR. It's not a style nit that a reviewer might wave
through at 6pm. It's a red build. The bounded-contexts
doc explains the layering the rule
protects; the rule is what makes the doc true even when nobody's looking.
2. The wall between login and payments is a compiler error
Remember "the walls that stop it breaking your login when it touches payments"?
That's not a metaphor here — it's a second ESLint rule. A bounded context may
reference another context's runtime code only through import type (for
dependency-injection wiring) or its sanctioned HTTP middleware. A value import
across contexts is banned:
{
files: ['**/modules/*/**/*.ts'],
rules: {
'@typescript-eslint/no-restricted-imports': ['error', {
patterns: [{
group: ['ai', 'audit', 'billing', 'feature-flags', 'iam',
'notifications', 'platform', 'storage', 'tenancy',
'usage', 'webhooks'].flatMap((m) => [
`**/${m}/domain/**`,
`**/${m}/application/**`,
`**/${m}/infrastructure/**`,
]),
allowTypeImports: true,
message:
'Cross-context runtime import: a module may reference another bounded context only via `import type` or its HTTP middleware, never a value/runtime import. See ADR-001.',
}],
}],
},
}So when the agent is deep in modules/billing and decides the fastest way to
check a user's session is to import { SessionService } from '../iam/...', the
build breaks. Billing (payments) physically cannot reach into IAM (login) at
runtime by accident. The soft file could only ask the agent not to; this makes
the coupling impossible to introduce without turning the CI red first.
3. Typed contracts the schema itself can't drift from
The @app/contracts rule from CLAUDE.md has an enforcement twin. Whenever an
endpoint's shape changes, bun run generate:api regenerates the OpenAPI document
and the client's typed paths — and a CI job (api-types-fresh) fails the PR if
those artifacts are stale. This is the exact class of failure type systems are
built for: an agent that renames a field on the server but forgets the client now
gets a typecheck failure at the call site instead of a 400 in production. Layer a
golden-path Playwright suite gated in CI on top — a real, constrained smoke test
the change has to pass — and you have what the pro-dev crowd calls "harness
engineering": the
repo is an execution environment for the agent, with fast validation and a
bounded blast radius, not a trust exercise.
Why enforcement matters more with an agent than with a human
A human engineer reads CLAUDE.md once, internalizes it, and carries it across
every session for months. An agent re-derives its plan from a partial view of the
codebase every single time, and — as the "context rot" research keeps
documenting — converges on its own generic defaults as the session drags on. The
soft file fights that with persuasion. Enforcement fights it with a wall in the
code path.
That's the asymmetry that makes this the actual moat. A rules file scales with how reliably the agent reads and remembers it — which degrades exactly when you need it most, on long sessions in large codebases. A lint rule, a failing typecheck, a red E2E gate: those fire whether or not the agent read the doc, on turn 1 and on turn 500 identically. The wall is in the code, not in a prompt the agent might have evicted from its context ten tool-calls ago.
The honest version
UseDeploy ships a CLAUDE.md today, not yet an AGENTS.md — supporting the
cross-tool standard so Codex, Cursor, and Copilot users get the same brief is a
real gap and it's on the roadmap. But notice that admitting it costs the argument
nothing: even a perfect, cross-agent AGENTS.md would still be a suggestion. The
part that stops the agent from breaking login when it touches payments isn't the
file. It's the ESLint boundary, the typed contract, the CI gate — the layers that
turn "please don't" into "the build won't let you."
So when you compare boilerplates for agent-driven work, don't stop at "does it have an AGENTS.md." Everyone's file is fine. Ask the harder question: when the agent ignores that file — and it will — what actually catches it before it ships? That's the difference between a document and a moat.
If you're evaluating UseDeploy for exactly this — pointing a coding agent at a base it can't quietly rot — the AI coding agents page lays out the full set of walls, and the bounded-contexts doc shows the layering the ESLint rules above defend. The tenant-isolation guard is the same philosophy one layer down: a runtime wall that throws instead of leaking, because a guardrail an agent can't see is a guardrail that works.