All Field Notes

Your App Needs a Constitution: Constraints for AI Coding Agents

A practical way to turn architectural decisions, naming conventions, data rules, and forbidden patterns into durable project constraints.

AI-assisted developmentArchitectureCI

AI coding agents do not need perfect architecture to be useful. What they need is a clear design space: which files are safe to change, which commands prove the change still works, which shortcuts are forbidden, and which decisions belong to a human.

When those rules live in five different places, the agent follows whichever one it happens to see first. One codebase we reviewed carried five, and each claimed something different:

  • Memory note: the database had become the real source of truth.
  • System document: a markdown-to-database sync convention was the source of truth instead.
  • Skill file: one particular queue status model was current.
  • Scheduled deployment wrapper: still invoked the agent using older status names, and continued past non-critical failures instead of stopping.
  • Separate automation path: could commit and push generated output directly from a server-side job.

None of this is carelessness; it is structural. Agent sessions don't share memory across runs, so each one re-derives policy from whatever file it opens first. Each tool reads its own instruction file — a memory note, a skill file, a deploy script — with nothing tying them together. And reconciling them is nobody's job: each file was written by someone solving a narrower problem, and no one owns keeping them in agreement afterward.

The problem was constitutional: the repo had plenty of local instructions, but no single rulebook for source of truth, deployment authority, test evidence, or where agents were allowed to add new scripts.

A Constitution Is a Constraint Set

Think of a project constitution as a short, versioned set of constraints that tells humans and agents what must stay true while the product changes. Not an architecture manifesto. Not a design system. Just the rules that actually matter every week.

For an AI-built app, it should answer questions like:

  • What product behavior must not break?
  • Which modules own which business rules?
  • Which files define source-of-truth data shapes?
  • Which commands must pass before a PR is ready?
  • Which patterns are banned, even if they appear elsewhere in the repo?
  • Which security, privacy, and deployment decisions require human approval?
  • How does the team record exceptions when reality changes?

That last question is easy to skip, but it is the one that keeps the constitution alive. If the database replaces markdown as the source of truth, write that down, update the importers, update the skills, and remove or mark the stale path. If a background job is allowed to create reviewable artifacts, say whether it may push directly, open a PR, or only write to a staging table.

The document itself is not the point. The point is that the next agent — or the next teammate — does not have to infer policy from a stale note, a hopeful README, and a thousand lines of accidental precedent.

README Is Not Enough

A README still matters. It explains what the project does, why it exists, how to get started, and who maintains it. That context helps agents orient themselves just as it helps people.

But a README describes the project. It does not control the work.

Different files should carry different responsibilities. Architecture Decision Records capture a single decision and its rationale, including trade-offs and consequences — they matter even more once a codebase is big enough that agents can no longer infer the design from nearby files, a failure mode we cover in Why Large Vibe-Coded Apps Become Incoherent. A SECURITY.md file tells contributors how to report vulnerabilities and what security support expectations apply. Contribution docs describe review norms. Agent instruction files turn the parts that affect everyday edits into operational constraints.

The major AI coding tools already assume this layering. Codex reads AGENTS.md, supporting root-level instructions and nested overrides for specific directories. Claude Code reads CLAUDE.md and supports importing AGENTS.md when a repo already uses that shared file. Cursor reads .cursor/rules. Each tool has its own format, but the underlying idea is the same: a committed file that tells the agent how to behave in this repo.

The practical rule that falls out of this: keep your canonical repo policy in one committed place, then bridge tool-specific files to it instead of maintaining parallel copies of the same rules.

What Goes in AGENTS.md

The first version should be boring and specific. A useful AGENTS.md can fit on one screen:

# Project Instructions
 
## Product Context
 
- This app manages customer onboarding workflows for internal operators.
- Preserve existing customer records and audit history during refactors.
 
## Architecture Boundaries
 
- App Router pages may compose UI and call server actions.
- Domain rules live in `src/domain/`.
- Database access goes through `src/server/db/`.
- Background jobs live in `src/jobs/` and must be idempotent.
 
## Source of Truth
 
- Database schema types are generated from migrations.
- API request bodies are validated at the route boundary.
- Docs describe the contract; generated types and tests enforce it.
 
## Forbidden Patterns
 
- Do not add new top-level one-off scripts without an owner and runbook.
- Do not push from production automation.
- Do not bypass typed helpers with raw SQL in route handlers.
- Do not store secrets, tokens, personal emails, or hostnames in docs.
 
## Verification
 
- Run `pnpm run check` before handoff.
- Run `pnpm run test` for runtime behavior changes.
- Run `pnpm run build` for routing, rendering, dependency, or config changes.
 
## Security and Privacy
 
- Ask before changing auth, authorization, encryption, data retention, or
  production deployment behavior.
- Redact customer data in examples and fixtures.
 
## Exceptions
 
- If a rule blocks necessary work, open or update an ADR explaining the context,
  decision, alternatives considered, and rollback plan.

This will not govern a large product forever. But it prevents the most expensive category of agent mistake: confidently extending the wrong pattern because the repo never named the right one.

Nested instruction files should be rare and justified. Add them where the rules genuinely change: a payments service with different approval requirements, an infra directory with a different formatter and deployment model, or a generated client directory that agents should not edit by hand. If every folder gets its own manifesto, the hierarchy becomes another source-of-truth problem.

Turn Advisory Rules into Enforced Rules

Instruction files tell agents what to do. They cannot stop an agent from doing the wrong thing the way a type error or a failing CI check can.

That is why the constitution should track which rules are enforced by tooling and which still rely on review. Source-of-truth rules can become generated types, runtime validators, schema migration checks, or import-boundary lint rules. Forbidden direct pushes can become branch protection. "Run the tests" can become required CI. "Do not invent data" can become nullable fields, provenance columns, and assertions that fail when an agent fills unknown values with placeholder text. We walk through the release-gate half of this in CI/CD Is the Adult Supervision Your Vibe-Coded App Needs, and the incident-to-guardrail loop that keeps rules current in Why Your AI Keeps Reintroducing the Same Bugs.

Here is the difference between advice and a constraint.

Before: the README says "prefer repository helpers," a runbook says "routes should not query the database directly," and a reviewer sometimes comments when a route imports the SQL client. The policy exists, but an agent can miss it and still get a green build.

After: the root AGENTS.md names the canonical rule:

## Architecture boundaries
 
- Route handlers and server actions must call repositories in `src/server/db/`.
- Do not import database clients or `drizzle-orm` directly from `src/app/**`.

Then the same rule becomes a lint check:

// eslint.config.ts
export default [
  {
    files: ['src/app/**/*.{ts,tsx}'],
    rules: {
      'no-restricted-imports': [
        'error',
        {
          patterns: [
            {
              group: ['@/server/db/client', 'drizzle-orm', 'drizzle-orm/*'],
              message: 'Call repository functions from src/server/db/ instead.',
            },
          ],
        },
      ],
    },
  },
];

Now the handoff instruction and CI agree: pnpm run lint fails before review when an agent reaches across the boundary. The constitution still explains the why; the toolchain catches the repeat violation.

Go back to the codebase from the opening. Each file — skill, system doc, memory note, deploy script — carried a piece of policy that nothing in the build or test pipeline actually checked. A constitution would not have magically fixed the architecture, but it would have given the team a concrete review checklist:

  • Which document wins when docs, skills, memory, and code disagree?
  • Which scripts are production entrypoints versus historical artifacts?
  • Which automations may write data, commit, push, or deploy?
  • Which queue statuses are current, and where are they enforced?
  • Which checks prove an agent changed the right layer?

That kind of checklist turns a vague "the repo feels messy" concern into a hardening plan you can actually execute.

How We Score This in a Diagnostic

In a Post Code Labs diagnostic, we do not score the constitution by whether the repo has a pretty instructions file. We score it by evidence. This is also the one category you can have scored today without booking anything: the free repo audit checks exactly this — committed agent instructions, constraints, and whether an agent can navigate the repo — and the full consultation extends the same evidence-based scoring across all twelve categories.

Clear means the repository has committed instructions, documented architecture decisions, known verification commands, security boundaries, and a small number of source-of-truth documents that match the running code.

Partial means the right ideas exist but are scattered: a README here, a memory note there, a skill file with better rules than the production script, tests that only cover one path, or deploy rules that humans know but agents cannot discover.

Gap means the agent has to infer policy from precedent. That is where repeated bugs, direct production edits, unsafe scripts, stale docs, and sprawling helper files tend to cluster.

The output is concrete: keep these rules, delete or archive these stale instructions, add these enforcement checks, and record these decisions as ADRs.

What to Do Next Monday

You can start in an hour.

  1. Inventory every instruction source: README, AGENTS.md, CLAUDE.md, .cursor/rules, skills, memory files, runbooks, deploy scripts, CI files, and ADRs.
  2. Pick one source of truth for each critical domain: product behavior, data model, deployment, background jobs, security, and verification.
  3. Write a first AGENTS.md with product context, boundaries, forbidden patterns, verification commands, and exception rules.
  4. Delete, import, or clearly mark duplicated instruction files so tools do not read conflicting policies.
  5. Convert one recurring rule into enforcement: a lint rule, regression test, schema check, CI requirement, or branch protection.
  6. Add an ADR template for changes that alter architecture, data ownership, security posture, or deployment authority.

Do not try to document the whole company in one pass. Write the rules that would have prevented the last three expensive mistakes, and give your agents fewer wrong places to go.

References