Mert Özen Further With Every Line
Artificial Intelligence AI-Assisted Development — #02

Giving Agents the Right Context: AGENTS.md, copilot-instructions.md, CLAUDE.md and Path-Specific Instructions

Mert Özen Nov 14, 2026 13 min 9 views
Giving Agents the Right Context: AGENTS.md, copilot-instructions.md, CLAUDE.md and Path-Specific Instructions

The tool-neutral way to describe a repository to a coding agent is a short AGENTS.md at the root: build and test commands, folder layout, team rules and topics to ask about first. We cover which tool reads which file, nested and path-specific instructions, a real AGENTS.md for Bookstore, the results of the same task with three different contexts, and a check that catches stale instruction files.

Version note: The code in this post was built and run with .NET 10; model outputs come from gemma3:12b on a local Ollama 0.34.4. Which tool reads which file is taken from official documentation as of November 2026.

Guess First

In the first post we asked gemma3:12b to add an endpoint to Bookstore that returns the books of one category. Without seeing the repository, the model proposed an MVC controller, a Startup.cs, a second Book class and a hard-coded list of four books. In this post we ask the same question twice more, with the same model and the same seed: first adding only a 1.6 KB AGENTS.md, then adding the relevant source file as well.

What is your guess? Is adding only an instructions file enough to get code that compiles and follows the project's rules? If not, what is still missing: the rules or knowledge of the code? The answer is in the third section, with real output.

Instruction Files: Which Tool Reads What

Every coding agent reads an instructions file from the repository at the start of a session and adds it to its context. The problem is that tools picked different file names for years. As of November 2026, official documentation says this:

FileLocationRead byNote
AGENTS.mdRepository root and subfoldersOpenAI Codex, GitHub Copilot cloud agent and code review, Cursor, Gemini CLI, JetBrains Junie, VS Code and the others listed on agents.md; Claude Code too when there is no CLAUDE.mdThe file closest to the edited file wins
.github/copilot-instructions.md.githubGitHub CopilotRepository-wide instructions
.github/instructions/*.instructions.md.github/instructionsCopilot cloud agent and code review on GitHub.comBound to paths with applyTo
CLAUDE.mdRepository root or .claude/, subfoldersClaude CodeCan import @AGENTS.md
AGENTS.override.mdRoot and subfoldersOpenAI CodexReplaces the AGENTS.md in the same folder

The first row is the main recommendation of this post: write the team's rules once, in AGENTS.md. AGENTS.md is an open format started by OpenAI and handed over to the Agentic AI Foundation, founded under the Linux Foundation in December 2025. Anthropic's MCP and Block's goose agent were contributed to the same foundation. The format imposes no schema; it is plain Markdown with whatever headings you like. As of November 2026 the agents.md site says more than 60,000 open-source projects use the file.

Tools follow different rules when combining files, and knowing them makes it easier to see why conflicting instructions give inconsistent results. Codex takes at most one file per folder from the repository root down to your working folder and concatenates them from the root down; the last one, the file closest to you, overrides earlier ones, and the total size is capped at 32 KiB by default. Claude Code reads only CLAUDE.md if there is one in the same folder chain, and AGENTS.md otherwise. In Copilot, personal instructions come before repository instructions and repository instructions before organization instructions, and all of them reach the model together; GitHub therefore recommends not writing conflicting instructions. Tool-specific details are in the Claude Code and OpenAI Codex series; here we stay tool-neutral.

An AGENTS.md for Bookstore

This is the file at the root of Bookstore. It is not a long wiki; it is what you would tell a new developer out loud on their first day:

# AGENTS.md — Bookstore

Online bookstore: ASP.NET Core 10 Web API, EF Core 10 on SQL Server, Angular 22 client.

## Commands
- Build: `dotnet build` (warnings are errors, see `Directory.Build.props`)
- Test: `dotnet test` (needs SQL Server and `ConnectionStrings__BookstoreDb`; tests create `<db>_Tests`)
- Run the API: `dotnet run --project src/Bookstore.Api` (http://localhost:5080)
- Angular client: see `web/bookstore-web/AGENTS.md`

## Layout
- `src/Bookstore.Api/<Module>/`: one folder per module (Catalog, Ordering, Inventory).
  `<Module>Module.cs` holds `Add<Module>Module()` and `Map<Module>Endpoints()`.
- `src/Bookstore.Api/Data/`: `BookstoreContext` and migrations.
- `tests/Bookstore.Api.Tests/`: integration tests, xUnit v3 + `WebApplicationFactory`.

## Rules
- Minimal APIs with route groups and `TypedResults`. No MVC controllers, no `Startup.cs`.
- A module reads another module's data only through its interface (`ICatalogService`, `IInventoryService`).
- Money is `decimal` in TRY; times are UTC and come from `TimeProvider`.
- Schema change: edit the entity and its configuration, then
  `dotnet ef migrations add <Name> --project src/Bookstore.Api`. Never edit an existing migration.
- New behaviour comes with an integration test. Do not delete, skip or weaken an existing test.
- Do not add NuGet or npm packages without asking. Versions live in `Directory.Packages.props`.
- No secrets in files. Connection strings come from user-secrets or environment variables.

## Ask first
Changing a public endpoint contract, touching migrations or CI files, anything you are not sure about.

Each section answers one question. Commands: how does the agent verify its work? If you do not write the build and test commands, the agent guesses and often tries to run a script that does not exist. Layout: where is the code, and where does a new file go? Rules: the decisions that set this repository apart from similar ones. "Use Minimal APIs, no controllers" is the direct answer to the Startup.cs suggestion in the first post. Ask first: the places where the agent should not decide on its own.

Two of the rules exist specifically for AI. "Do not delete, skip or weaken an existing test" targets the agent's tendency, shown in the seventh post of the TDD series, to silence a red test with Skip. "Ask before adding packages" targets the made-up package risk we discuss in the fifth post. Both should also be enforced with a hook or a CI check; an instructions file is a request, not a hard rule.

The Same Task with Three Different Contexts

I gave the prompt from the first post to gemma3:12b again, this time with three different contexts; seed and temperature were fixed. The prompt token counts returned by Ollama also show what the context costs:

ContextPrompt tokensResult
Prompt only61MVC controller, Startup.cs, hard-coded list, no tests
Prompt + AGENTS.md498Right file and Minimal APIs, but invented APIs and an unapproved package
Prompt + AGENTS.md + CatalogModule.cs1,442Compiles, matches the repository's style, no tests, one behavior bug

In the second row the model tried to follow the rules: it put the code in src/Bookstore.Api/Catalog/CatalogModule.cs, used Minimal APIs and, because the rule said so, wrote an integration test. But since it had not seen the code, it made up the gaps:

namespace Bookstore.Api.Catalog;

public static class CatalogModule
{
    public static void AddCatalogModule(WebApplication app)
    {
        app.MapGroup<CatalogModule>(app.Services.CreateScope().ServiceProvider);
    }

    public static void MapCatalogEndpoints(WebApplication app)
    {
        app.MapGet("api/categories/{categoryName}/books", GetBooksByCategory);
    }

The namespace is wrong (Bookstore uses Bookstore.Catalog), there is no MapGroup<T> overload, the existing ICatalogService is redefined in another folder, and the Book.Category navigation property is compared with a string. The test contains response.StatusCode.Should().Be(...), which means the FluentAssertions package, while AGENTS.md said "ask before adding packages". The model did not even mention that it added the package; you would have noticed when the code failed to compile. That is how far rules go when they are applied without seeing the code.

In the third row I also passed the contents of CatalogModule.cs. This time the model wrote the whole file back; according to git only 21 lines changed. The route and the handler it added are these:

catalog.MapGet("/categories/{categorySlug}/books", GetBooksByCategory); // New Endpoint

private static async Task<Ok<PagedResult<BookSummary>>> GetBooksByCategory(
    BookstoreContext db, string categorySlug, int page = 1, int pageSize = 12, CancellationToken ct = default)
{
    page = Math.Max(page, 1);
    pageSize = Math.Clamp(pageSize, 1, 50);

    IQueryable<Book> query = db.Books.AsNoTracking();
    query = query.Where(b => b.Category.Slug == categorySlug);

    int total = await query.CountAsync(ct);
    List<BookSummary> items = await query
        .OrderBy(b => b.Title)
        .Skip((page - 1) * pageSize)
        .Take(pageSize)
        .Select(b => new BookSummary(b.Isbn, b.Title, b.Author.Name, b.Category.Name, b.Price, b.PublishedYear))
        .ToListAsync(ct);

    return TypedResults.Ok(new PagedResult<BookSummary>(items, total, page, pageSize));
}

It compiles, uses the slug, and its TypedResults and paging match the rest of the repository. This time the model wrote no tests and only reminded me that I "must" write an integration test. I put the code on a trial branch and wrote two tests (output shortened):

[Fact]
public async Task Returns_only_books_of_the_category()
{
    PagedResult<BookSummary>? result =
        await client.GetFromJsonAsync<PagedResult<BookSummary>>("/api/categories/dystopian/books", ct);

    Assert.NotNull(result);
    Assert.NotEmpty(result.Items);
    Assert.All(result.Items, b => Assert.Equal("Dystopian", b.Category));
}

[Fact]
public async Task Unknown_category_returns_404()
{
    HttpResponseMessage response = await client.GetAsync("/api/categories/no-such-category/books", ct);

    Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
> dotnet test
failed Bookstore.Api.Tests.CategoryBooksTests.Unknown_category_returns_404 (34ms)
  Assert.Equal() Failure: Values differ
  Expected: NotFound
  Actual:   OK
Test run summary: Failed!
  total: 12
  failed: 1
  succeeded: 11
  skipped: 0
  duration: 3s 134ms

A category that does not exist returns 200 with an empty list. The model had named this risk in its answer but had not handled it in the code. So the answer to the guess: an instructions file gives the agent the rules, not the code. Real agents fill that gap by reading the relevant files themselves; the "explore first" step in the third post is exactly that. The job of the instructions file is to tell the agent where to look and what not to do.

Nested and Path-Specific Instructions

Bookstore has two different worlds: the .NET API and the Angular client. Putting the Angular rules in the root file would waste tokens in every session spent on the API. Instead I added a second AGENTS.md to web/bookstore-web; when the agent works in that folder, this is the closest file:

# AGENTS.md — bookstore-web

- Angular 22, standalone components, signals for state. No NgModules.
- TypeScript 6.0 (Angular 22.2 needs >=6.0 <6.1); do not upgrade it.
- Use `inject()`, not constructor parameters.
- Pages read data with `httpResource`; writes go through a store such as `src/app/cart/cart-store.ts`.
- API types live in `src/app/core/api-models.ts` and mirror the C# records.
- Tests: `npm test` (Vitest). Format: `npx prettier --write .`
- `/api` is proxied to the API (`proxy.conf.json`); never hard-code http://localhost:5080.

For Copilot, path-specific instructions come in a separate file format. The file name ends in .instructions.md, and the applyTo glob at the top says which files it applies to. In Bookstore I put the rules for writing tests there:

---
applyTo: "tests/**/*.cs"
---
- Test names describe behaviour with underscores: `Placing_an_order_reserves_stock_and_removes_the_cart`.
- Use the shared `BookstoreApiFactory`; do not create a second host or database.
- Assert on HTTP status and on the response body; use ISBNs from the seed (`9780261102217` is The Hobbit).
- Pass `TestContext.Current.CancellationToken` to every async call.

If you add excludeAgent: "code-review" or excludeAgent: "cloud-agent" to this file, only the other agent uses it. As of November 2026, on GitHub.com path-specific files are supported only by Copilot cloud agent and code review; check the documentation for your version for IDE support. I kept the root .github/copilot-instructions.md short and wrote only what is specific to Copilot; the shared rules are already in AGENTS.md:

Project rules are in AGENTS.md (root) and web/bookstore-web/AGENTS.md. Copilot-specific notes:

- When reviewing a pull request, check that every new endpoint has an integration test
  and that no existing test was deleted, skipped or loosened.
- Flag new entries in Directory.Packages.props or package.json; they need a reason in the PR description.

If you use Claude Code, you may not need a separate CLAUDE.md; Claude Code reads AGENTS.md when it finds no CLAUDE.md. If you need to write something specific to Claude, import the shared file by writing @AGENTS.md on the first line of CLAUDE.md; keeping the same rule in two files means that one day one gets updated and the other is forgotten. Instructions can also be set at organization level (organization custom instructions in Copilot, managed settings in Claude Code); those are for company-wide rules, not repository-specific knowledge.

What to Write and What Not to Write

A good instructions file holds what the agent cannot work out on its own: build and test commands, environment requirements (such as tests needing SQL Server), the logic behind the folder layout, deliberate team decisions that the code does not reveal, and places that must not be touched. In Bookstore, "never edit an existing migration" is an example; someone reading the code cannot learn it.

What you leave out matters just as much. Do not repeat style rules the formatter and analyzers already enforce; indentation, naming and var usage are protected by .editorconfig and build warnings (the second post of the Coding Conventions series). The same rule in the instructions file is tokens the agent reads for nothing in every session. Do not paste the whole wiki, the architecture document or the ADRs; give their paths if needed and let the agent read them when it has to. GitHub's documentation also notes that Copilot code review does not follow external links, so needed content has to be copied into the file. Vague requests such as "be more careful" do not work either. Secrets, connection strings and customer data never go into this file; it travels with the repository to everyone.

If the agent needs up-to-date library documentation, giving it access through an MCP server is healthier than copying the docs into the instructions file; how that works is covered in the AI Agents and MCP series.

Length and Staleness

The instructions file enters the context at the start of every session. The 1.6 KB AGENTS.md in the experiment took about 440 tokens with gemma3's tokenizer, and the agent processes them again on every request. Tool vendors also recommend keeping it short: Claude Code's documentation suggests aiming for under 200 lines per CLAUDE.md, GitHub's suggests keeping a single instructions file to about 1,000 lines, and Codex takes no more than 32 KiB in total by default. Older sources say Copilot code review reads only the first 4,000 characters of an instructions file; GitHub removed that limit on 12 June 2026. Still, a short file is both cheaper and followed more consistently.

The second risk is staleness. A folder is renamed, a command changes, the file stays as it was and sends the agent to a path that no longer exists. Instead of leaving this to human attention, I made it a check. The file-based app below finds relative paths written inside `...` in AGENTS.md files and checks that they exist:

#:property PublishAot=false
using System.Text.RegularExpressions;

// Finds paths written as `code` in every AGENTS.md and checks that they still exist.
string root = Directory.GetCurrentDirectory();
Regex codeSpan = new(@"`([^`\s]+)`");
int missing = 0;

foreach (string file in Directory.EnumerateFiles(root, "AGENTS.md", SearchOption.AllDirectories)
             .Where(f => !f.Contains("node_modules")))
{
    string dir = Path.GetDirectoryName(file)!;
    foreach (Match m in codeSpan.Matches(File.ReadAllText(file)))
    {
        string candidate = m.Groups[1].Value.TrimEnd('/');
        // Only relative paths such as `src/Bookstore.Api/Data/`; routes (/api) and placeholders (<Name>) are skipped
        bool looksLikePath = candidate.Contains('/') && !candidate.StartsWith('/');
        if (!looksLikePath || candidate.Contains('<') || candidate.Contains("://"))
        {
            continue;
        }
        bool exists = new[] { Path.Combine(dir, candidate), Path.Combine(root, candidate) }
            .Any(p => File.Exists(p) || Directory.Exists(p));
        if (!exists)
        {
            missing++;
            Console.WriteLine($"MISSING {Path.GetRelativePath(root, file)}: {candidate}");
        }
    }
}

Console.WriteLine(missing == 0 ? "All paths in AGENTS.md files exist." : $"{missing} stale path(s).");
return missing == 0 ? 0 : 1;
> dotnet run eng/check-agents-md.cs
All paths in AGENTS.md files exist.

> git mv src/Bookstore.Api/Data src/Bookstore.Api/Persistence
> dotnet run eng/check-agents-md.cs
MISSING AGENTS.md: src/Bookstore.Api/Data
1 stale path(s).

The script returns exit code 1, so it can be added to CI as a step. The simplest way to check the commands is for CI to run the same commands that AGENTS.md lists: if dotnet build and dotnet test break in CI, the file is stale too. The rest is habit: when the agent makes the same mistake a second time, or review turns up something the agent should have known, add a line to the file; and delete a line as soon as you notice it is wrong.

In Production

Manage the instructions file like code: change it through pull requests, review it, and set its owner with CODEOWNERS. Copilot code review reads instructions from the pull request's head branch, so you can see the effect of an instruction change in the same pull request, but a malicious instruction change arrives the same way. Read instruction file changes carefully in review.

Measure the effect. Giving the same task with and without instructions, as in this post, is a cheap experiment that shows whether the file actually changes anything. In a team you can keep a few typical tasks and rerun them whenever the instructions file changes significantly.

Finally, remember that the file guarantees nothing. Claude Code's documentation states plainly that CLAUDE.md is context, not configuration, and that a hook is needed to block an action for certain. "Never edit an existing migration" is a request; catching a pull request that touches the migrations folder in CI is a control.

Common Pitfalls

  • Pasting the whole wiki into the instructions file: it costs tokens every session and the important rule gets lost in the crowd.
  • Keeping several files that contradict each other: when CLAUDE.md says one thing and copilot-instructions.md another, the result depends on the tool.
  • Repeating style rules the formatter already enforces.
  • Writing the file once and forgetting it: folders and commands change, and the instructions keep pointing to the old path.
  • Treating instructions as hard rules: critical prohibitions should also be protected by hooks, branch protection and CI.
  • Putting secrets, internal system addresses or customer data in the instructions file.

A Small Experiment

★☆☆ If your project has no AGENTS.md, write one of no more than 30 lines with Commands, Layout, Rules and Ask first sections. Ask your agent "How do I build and test this repository?"; does its answer match the commands in the file?

★★☆ Add the GetBooksByCategory method above and the two tests to Bookstore, then fix the method: return 404 when the category does not exist, so that Unknown_category_returns_404 turns green. Then decide whether to move the query code duplicated with SearchBooks into a shared method, and write your decision and its reason in the commit message.

★★★ Extend check-agents-md.cs so that it also finds the dotnet and npm commands in AGENTS.md and actually runs them in a CI step. Think about how you would mark commands that cannot run in CI, such as those that need a database.

The instructions file told the agent the rules, but if you skip the "read the code, then plan" step, knowledge of the code is still missing, as in the third row. In the third post we add a wishlist feature to Bookstore, split the work into explore, plan, implement and verify, and see where a person gives approval at each step.