# Agent-readable repository structure: organize a codebase for safe AI changes

> A practical guide to agent-readable repository structure: boundaries, entry points, tests, docs, and conventions that help AI coding agents extend production apps.
> By Dave · 2026-09-03
> Source: https://otf-kit.dev/blog/agent-readable-repository-structure

An agent-readable repository structure makes the next change easier to scope, review, and verify. The goal is not to arrange files for a model’s convenience. It is to make the product’s boundaries visible to any contributor: where requests enter, where data access lives, which code is shared, which files are protected, and which checks prove a change is complete.

Start with a map an agent can read in a few minutes. Name the main application surfaces, keep tests close to the behavior they protect, and put project instructions where the coding tool already looks for them. A clear tree reduces guessing before the first edit.

## What makes a repository readable to an agent?

An agent-readable repository answers five questions without a long search:

1. Where does a user request enter the system?
2. Where are authorization and data boundaries enforced?
3. Which module owns the behavior being changed?
4. Which tests and commands verify the change?
5. Which files or interfaces must not change without review?

A useful starting tree might look like this:

```text
project/
├── README.md
├── CLAUDE.md
├── .cursor/
│   └── rules/
├── apps/
│   ├── web/
│   └── mobile/
├── packages/
│   ├── ui/
│   ├── data/
│   └── config/
├── server/
│   ├── routes/
│   ├── services/
│   └── jobs/
├── tests/
│   ├── contract/
│   └── fixtures/
└── docs/
    ├── architecture/
    └── decisions/
```

The names are less important than the ownership implied by them. If a route contains database queries, billing decisions, and response formatting, an agent has no obvious boundary to preserve. If each concern has a named home, the task can be smaller and the review can be sharper.

Do not create directories only to make the tree look organized. Every directory should answer a recurring question. If nobody can explain what belongs in `services/` rather than `routes/`, the extra layer adds vocabulary without adding a rule.

## Put the entry points near the top

The root README should explain how to run the application, how to run the checks, and where the main entry points are. It should not attempt to describe every module.

A good first page includes:

- The supported runtime and package manager.
- The install, development, test, lint, and build commands.
- The local configuration keys by name, without secret values.
- The applications and packages in the repository.
- The location of architecture decisions.
- The deployment smoke checks.
- A short list of protected interfaces.

Write commands that a contributor can run as written. If a command requires a service, fixture, or environment setting, state that next to the command. “Run the tests” is not an instruction if the repository has four test suites with different setup.

Keep the root README stable and link deeper documentation for details. The first screen an agent reads should orient it, not bury it under a history of every decision the team has made.

## Separate request handling from business rules

A common source of agent confusion is a route that does everything. The handler validates an input, checks access, queries several tables, calls a provider, formats a response, and sends an email. Any change to one step becomes a change to the whole path.

Prefer named boundaries:

```ts
// routes/invoices.ts
export async function createInvoice(request: Request) {
  const actor = await requireActor(request)
  const input = createInvoiceInput.parse(await request.json())
  const account = await requireAccountAccess(actor, input.accountId)

  const invoice = await invoiceService.create({
    accountId: account.id,
    items: input.items,
  })

  return Response.json(toInvoiceResponse(invoice), { status: 201 })
}
```

The route owns transport concerns. The access function owns authorization. The service owns the product operation. The response mapper owns the public shape. An agent asked to change invoice calculation now has a clear place to begin and a smaller surface to inspect.

This is not a demand for a particular architecture. It is a naming rule: each boundary should have one reason to change and a test that describes its contract.

## Make data ownership explicit

An agent should not infer tenancy from a table name or a comment. Put ownership checks next to data access and use names that expose the relationship.

```ts
export async function findAccountProject(
  actorId: string,
  projectId: string,
) {
  return db.project.findFirst({
    where: {
      id: projectId,
      members: { some: { actorId } },
    },
  })
}
```

Then test the failure path:

```ts
it("does not return a project from another account", async () => {
  const project = await findAccountProject("actor-a", "project-owned-by-b")
  expect(project).toBeNull()
})
```

A function named `findProject` leaves ownership ambiguous. `findAccountProject` tells the next contributor which rule is part of the operation. Use names that carry the constraint instead of relying on an agent to reconstruct it from a distant policy document.

Mark server-only modules and protected interfaces clearly. Keep secrets out of source and logs. Put configuration names in documentation, but never paste values into examples that might be copied into a commit.

## Use project instructions for durable decisions

Repository structure becomes agent-readable when the rules that govern changes are persistent. Claude’s official [memory documentation](https://code.claude.com/docs/en/memory) describes `CLAUDE.md` files as project instructions for coding standards, workflows, architecture, and commands. It also distinguishes those instructions from auto memory, which stores learnings and patterns.

Use project instructions for facts that should apply every time:

- Which command verifies a production build.
- Which directories contain generated files.
- Which modules own authentication and data access.
- Which public interfaces require a migration note.
- Which checks must pass before a pull request is ready.

Keep the file concise. A rule that applies only to one directory belongs closer to that directory or in a path-scoped rule. A multi-step procedure belongs in a document the task can link to. The point is to put durable context at the scope where it is needed, not to create one enormous instruction file.

Cursor’s [official documentation](https://cursor.com/docs) describes rules as part of its customization surface alongside plugins, skills, and other project context. Whether your team uses one coding agent or several, keep the intent consistent: rules should explain decisions, boundaries, and verification, not repeat generic programming advice.

For an existing example of this problem, read [why repository conventions matter for AI coding agents](/blog/ai-coding-agent-repo-conventions). The practical lesson is simple: a nearby, explicit convention beats a vague request to “follow the style.”

## Keep tests close to the contract

An agent can find a test more easily when the test name and location match the behavior. Use unit tests for local rules, integration tests for boundaries, and a small number of end-to-end checks for the paths users actually depend on.

Name tests after decisions, not implementation details:

```text
server/
├── billing/
│   ├── entitlement-service.ts
│   └── entitlement-service.test.ts
└── routes/
    ├── checkout.ts
    └── checkout.test.ts
```

The checkout tests should establish authentication, account ownership, valid plan selection, and the response contract. The entitlement tests should establish which billing states grant access. Neither test should require the other module’s entire implementation to understand its own rule.

Add fixtures for repeated boundary cases: an unauthorized actor, an expired entitlement, a duplicate event, malformed input, and a provider timeout. Redacted fixtures give an agent concrete examples without exposing customer data.

The test command should make failures actionable. If the repository has separate checks for type safety, database migrations, and UI behavior, document them separately and say which one is required for each kind of change.

## Record decisions where the code cannot explain them

Some choices are not obvious from the implementation. Why is a provider adapter shaped this way? Why is a table migration staged? Why is a mobile navigation pattern allowed to vary from the web layout? Put those answers in short decision records.

A useful decision file has four parts:

```text
Decision: Keep provider-specific options behind the adapter.

Context: Different providers expose different response and tool controls.

Choice: Normalize product behavior, preserve provider details in an explicit escape hatch.

Trade-off: Callers cannot use every provider feature without opting in.
```

Do not turn decisions into essays. Record the choice, the reason, and the consequence. Link to the affected module or test. When the decision changes, add a new record rather than silently rewriting history.

For visual and interaction rules, a similar contract helps across devices. [This cross-platform UI contract](/blog/cross-platform-ui-contract-agents) covers shared behavior, states, tokens, accessibility, and intentional platform differences.

## Give every change a completion protocol

An agent-readable repository tells an agent how to finish. Put the acceptance sequence in the project instructions and repeat the relevant part in the task brief:

1. Inspect the nearest module, test, and decision record.
2. State the intended change and the files it should touch.
3. Implement the smallest change that satisfies the contract.
4. Run focused tests first.
5. Run the repository checks required for the changed surface.
6. Review the diff for unrelated edits, secret exposure, and interface changes.
7. Report checks, assumptions, new files, and remaining risks.

This sequence creates evidence without requiring a special tool. It also gives reviewers a stable shape for evaluating agent work. A good completion note says which existing pattern was reused, which tests ran, and which boundary was intentionally left unchanged.

Protect the final step with repository automation where possible. Instructions guide behavior; tests and checks provide evidence. If a rule must block an action, enforce it at the relevant tool or server boundary rather than trusting prose alone.

## Treat readability as an operating cost

A repository is not agent-readable because it has more documentation. It is readable when the common path is easier to discover than the exception path.

Review the tree after a feature ships. Can a new contributor find the route, service, data rule, test, and deployment check? Can an agent tell which files are generated? Can it distinguish a reusable component from a one-off screen? If not, improve the boundary that caused the search.

OTF’s free MIT SDK is one example of making a repeated boundary explicit: the same component name, props, and look are intended across web, iOS, and Android, with design tokens providing one theme across platforms. You can inspect the current options on the [OTF templates page](https://otf-kit.dev/templates).

An agent-readable repository is a form of maintenance work that pays back on every feature. Keep entry points visible, make ownership part of names, put durable rules at the right scope, keep tests beside contracts, and require evidence before declaring a change finished. The agent still needs judgment. It simply has fewer important decisions to invent.

## Sources

- [Claude Code memory and project instructions](https://code.claude.com/docs/en/memory)
- [Cursor documentation](https://cursor.com/docs)
- [React Native accessibility documentation](https://reactnative.dev/docs/accessibility)
- [OTF templates](https://otf-kit.dev/templates)