Agent'a Doğru Context Vermek: AGENTS.md, copilot-instructions.md, CLAUDE.md ve Path-specific Talimatlar
Coding agent'a repo'yu anlatmanın araçtan bağımsız yolu kökte kısa bir AGENTS.md yazmaktır: build ve test komutları, klasör yapısı, ekip kuralları ve önce sorulacak konular. Hangi aracın hangi dosyayı okuduğunu, nested ve path-specific talimatları, Bookstore için gerçek bir AGENTS.md'yi, aynı görevin üç farklı context'le verdiği sonuçları ve talimat dosyasının bayatlamasını yakalayan bir kontrolü ele alıyoruz.
Sürüm notu: Bu yazıdaki kodlar .NET 10 ile derlenip denenmiştir; model çıktıları yerel Ollama 0.34.4 üzerinde gemma3:12b ile alınmıştır. Araçların hangi dosyayı okuduğu bilgisi 2026-11 itibarıyla resmî dokümanlardan alınmıştır.
Önce Tahmin Et
İlk yazıda gemma3:12b'den Bookstore'a "bir kategorinin kitaplarını dönen endpoint" eklemesini istemiştik. Repo'yu görmeyen model MVC (Model-View-Controller; controller class'larıyla çalışan klasik yapı) controller'ı, Startup.cs, ikinci bir Book class'ı ve dört kitaplık sabit bir liste önerdi. Bu yazıda aynı soruyu, aynı model ve aynı seed ile iki kez daha soracağız: önce yalnız 1,6 KB'lık bir AGENTS.md ekleyerek, sonra buna ilgili dosyayı da ekleyerek.
Tahminin ne? Yalnız talimat dosyası eklemek, derlenen ve projenin kurallarına uyan bir kod çıkarmaya yeter mi? Yetmezse eksik kalan ne olur: kurallar mı, kod bilgisi mi? Cevap üçüncü bölümde, gerçek çıktılarla.
Talimat Dosyaları: Hangi Araç Neyi Okuyor
Her coding agent, oturum başında repo'dan bir talimat dosyası okuyup context'ine ekler. Sorun, araçların yıllarca farklı dosya adları seçmiş olması. 2026-11 itibarıyla resmî dokümanlardaki durum şu:
| Dosya | Konum | Okuyan araçlar | Not |
|---|---|---|---|
AGENTS.md | Repo kökü ve alt klasörler | OpenAI Codex, GitHub Copilot cloud agent ve code review, Cursor, Gemini CLI, JetBrains Junie, Visual Studio Code ve agents.md sitesinde listelenen diğerleri; Claude Code da CLAUDE.md yoksa okur | Düzenlenen dosyaya en yakın olan önceliklidir |
.github/copilot-instructions.md | .github | GitHub Copilot | Repo geneli talimat |
.github/instructions/*.instructions.md | .github/instructions | GitHub.com'da Copilot cloud agent ve code review | applyTo ile path'e bağlanır |
CLAUDE.md | Repo kökü ya da .claude/, alt klasörler | Claude Code | @AGENTS.md ile import edebilir |
AGENTS.override.md | Kök ve alt klasörler | OpenAI Codex | Aynı klasördeki AGENTS.md'nin yerine geçer |
Tablonun ilk satırı bu yazının ana önerisi: ekibin kurallarını bir kez, AGENTS.md'ye yaz. AGENTS.md, OpenAI'ın başlattığı ve Aralık 2025'te Linux Foundation altında kurulan Agentic AI Foundation'a devredilen açık bir format. Aynı vakfa Anthropic'in MCP'si (Model Context Protocol; agent'ların dış araç ve verilere bağlandığı açık protokol) ve Block'un goose agent'ı da devredildi. Format bir şema dayatmıyor; düz Markdown, istediğin başlıkları kullanırsın. agents.md sitesi 2026-11 itibarıyla 60 binden fazla open-source projenin bu dosyayı kullandığını söylüyor.
Araçlar dosyaları birleştirirken farklı kurallar izliyor; bunu bilmek, çelişkili talimatların neden tutarsız sonuç verdiğini anlamayı kolaylaştırıyor. Codex, repo kökünden çalıştığın klasöre kadar her klasörde en fazla bir dosya alıp kökten aşağı doğru birleştiriyor; sona gelen, yani sana en yakın olan dosya öncekileri geçersiz kılıyor ve toplam boyut varsayılan olarak 32 KiB ile sınırlı. Claude Code, aynı klasör zincirinde CLAUDE.md varsa yalnız onu okuyor; yoksa AGENTS.md'yi okuyor. Copilot'ta ise kişisel talimatlar repo talimatlarından, repo talimatları organization talimatlarından önce geliyor ve hepsi birlikte modele gidiyor; GitHub bu yüzden çelişen talimat yazmamanı öneriyor. Araçlara özel ayrıntılar Claude Code ve OpenAI Codex serilerinde; burada araçtan bağımsız kalıyoruz.
Bookstore için AGENTS.md
Bookstore'un kökündeki dosya şu. Uzun bir wiki değil; yeni katılan bir geliştiriciye ilk gün sözlü anlatacağın şeyler:
# 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.
Dosyanın her bölümü bir soruya cevap veriyor. Commands: agent işini nasıl doğrular? Build ve test komutu yazmazsan agent tahmin eder, çoğu zaman da var olmayan bir script'i çalıştırmaya çalışır. Layout: kod nerede, yeni dosya nereye gider? Rules: bu repo'yu benzerlerinden ayıran kararlar. "Minimal API kullan, controller yazma" cümlesi, ilk yazıdaki Startup.cs önerisinin doğrudan cevabı. Ask first: agent'ın kendi başına karar vermemesi gereken yerler.
Kuralların ikisi özellikle AI için var. "Var olan bir testi silme, atlama ya da gevşetme" kuralı, TDD (Test-Driven Development; önce testi yazıp sonra kodu yazma yaklaşımı) serisinin 7. yazısında gördüğümüz, agent'ın kırmızı testi Skip ile susturma eğilimine karşı. "Paket eklemeden önce sor" kuralı da beşinci yazıda konuşacağımız uydurma paket riskine karşı. İkisi de bir hook ya da CI kontrolüyle de korunmalı; talimat dosyası bir istek, zorunlu bir kural değil.
Aynı Görev, Üç Farklı Context
İlk yazıdaki prompt'u gemma3:12b'ye bu sefer üç farklı context'le verdim; seed ve temperature sabit. Ollama'nın döndürdüğü prompt token sayıları context'in maliyetini de gösteriyor:
| Context | Prompt token | Sonuç |
|---|---|---|
| Yalnız prompt | 61 | MVC controller, Startup.cs, sabit liste, test yok |
Prompt + AGENTS.md | 498 | Doğru dosya ve Minimal API, ama uydurma API'ler ve onaysız paket |
Prompt + AGENTS.md + CatalogModule.cs | 1.442 | Derleniyor, repo'nun stiline uyuyor, test yok, bir davranış hatası var |
İkinci satırda model kurallara uymaya çalıştı: kodu src/Bookstore.Api/Catalog/CatalogModule.cs'ye koydu, Minimal API kullandı ve kuralda yazdığı için integration test de yazdı. Ama kodu görmediği için boşlukları uydurdu:
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);
}
Namespace yanlış (Bookstore'da Bookstore.Catalog), MapGroup<T> diye bir overload yok, var olan ICatalogService başka bir klasörde yeniden tanımlanmış ve Book.Category navigation property'si bir string'le karşılaştırılmış. Testte response.StatusCode.Should().Be(...) var; bu FluentAssertions (test assertion'larını akıcı bir syntax ile yazdıran bir library) paketi demek ve AGENTS.md "paket eklemeden önce sor" diyordu. Model paketi eklediğini söylemedi bile; kod derlenmeyince fark edecektin. Kurallar, kodu görmeden uygulanınca bu kadar ileri gidebiliyor.
Üçüncü satırda CatalogModule.cs'nin içeriğini de verdim. Model bu sefer dosyanın tamamını geri yazdı; git'e göre değişen yalnız 21 satır. Eklediği route ve handler şu:
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));
}
Derleniyor, slug kullanıyor, TypedResults ve sayfalama repo'nun geri kalanıyla aynı. Model bu sefer test yazmadı, yalnız "integration test yazmalısın" diye hatırlattı. Kodu bir deneme branch'ine alıp iki test yazdım (çıktı kısaltıldı):
[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
Var olmayan bir kategori için 200 ve boş liste dönüyor. Model bu riski cevabında kendisi yazmıştı ama kodda çözmemişti. Tahminin cevabı şu: talimat dosyası agent'a kuralları veriyor, kodu vermiyor. Gerçek agent'lar ilgili dosyaları kendileri okuyarak bu boşluğu dolduruyor; üçüncü yazıdaki "önce keşfet" adımı tam olarak bu. Talimat dosyasının işi, agent'ın neye bakacağını ve neyi yapmayacağını söylemek.
Nested ve Path-specific Talimatlar
Bookstore'da iki farklı dünya var: .NET API ve Angular istemcisi. Angular kurallarını kökteki dosyaya koymak, API üzerinde çalışan her oturumda gereksiz token harcatır. Bunun yerine web/bookstore-web klasörüne ikinci bir AGENTS.md koydum; agent o klasörde çalışırken en yakın dosya bu oluyor:
# 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.
Copilot için path-specific talimatlar ayrı bir dosya biçimiyle geliyor. Dosya adı .instructions.md ile bitiyor ve başındaki applyTo glob'u hangi dosyalarda geçerli olduğunu söylüyor. Bookstore'da test yazma kurallarını buraya koydum:
---
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.
Bu dosyaya excludeAgent: "code-review" ya da excludeAgent: "cloud-agent" eklersen yalnız diğer agent onu kullanır. GitHub.com'da path-specific dosyalar 2026-11 itibarıyla yalnız Copilot cloud agent ve code review tarafından destekleniyor; IDE tarafındaki desteği kullandığın sürümün dokümanından kontrol et. Kökteki .github/copilot-instructions.md'yi ise kısa tutup yalnız Copilot'a özel olanı yazdım; ortak kurallar zaten AGENTS.md'de:
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.
Claude Code kullanıyorsan ayrı bir CLAUDE.md açmana gerek olmayabilir; Claude Code, CLAUDE.md bulamadığında AGENTS.md'yi okuyor. Claude'a özel bir şey yazman gerekirse CLAUDE.md'nin ilk satırına @AGENTS.md yazarak ortak dosyayı import et; iki dosyada aynı kuralı ayrı ayrı tutmak, bir gün birinin güncellenip diğerinin unutulması demek. Organization seviyesinde de talimat verilebiliyor (Copilot'ta organization custom instructions, Claude Code'da yönetilen ayarlar); bunlar şirket genelindeki kurallar için, repo'ya özel bilgi için değil.
Ne Yazmalı, Ne Yazmamalı
İyi bir talimat dosyası, agent'ın tek başına çıkaramayacağı bilgiyi içerir: build ve test komutları, ortam gereksinimleri (testin SQL Server istemesi gibi), klasör yapısının mantığı, ekibin bilinçli verdiği ve koddan anlaşılmayan kararlar, dokunulmaması gereken yerler. Bookstore'da "migration'ı elle düzenleme" kuralı bunun örneği; koda bakan biri bunu öğrenemez.
Yazmaman gerekenler de bir o kadar önemli. Formatter'ın ve analyzer'ın zaten zorladığı stil kurallarını yazma; girinti, naming ve var kullanımı .editorconfig ve build'deki uyarılarla korunuyor (Coding Conventions serisinin 2. yazısı). Talimat dosyasında aynı kural, agent'ın her oturumda boşuna okuduğu token demek. Tüm wiki'yi, mimari dokümanını ya da ADR'leri (Architecture Decision Record; mimari kararların gerekçesiyle yazıldığı kısa dokümanlar) yapıştırma; gerekirse yolunu ver, agent ihtiyaç duyduğunda okusun. GitHub'ın dokümanı, Copilot code review'un dış link'leri takip etmediğini, gereken içeriğin dosyaya kopyalanması gerektiğini ayrıca belirtiyor. "Daha dikkatli ol" gibi belirsiz istekler de işe yaramıyor. Secret, connection string ve müşteri verisi ise hiçbir koşulda bu dosyaya girmez; dosya repo'yla birlikte herkese gidiyor.
Güncel bir library dokümanına ihtiyaç varsa, onu talimat dosyasına kopyalamak yerine bir MCP server'ı üzerinden agent'a erişim vermek daha sağlıklı; bunun nasıl çalıştığı AI Agent'ları ve MCP serisinde anlatılıyor.
Uzunluk ve Bayatlama
Talimat dosyası her oturumun başında context'e giriyor. Deneydeki 1,6 KB'lık AGENTS.md, gemma3'ün tokenizer'ında yaklaşık 440 token tuttu; agent her istekte bunu yeniden işliyor. Araçların önerileri de kısa tutmak yönünde: Claude Code'un dokümanı CLAUDE.md için 200 satırın altını hedeflemeyi, GitHub'ınki tek bir talimat dosyasını yaklaşık 1.000 satırla sınırlamayı öneriyor; Codex varsayılan olarak toplam 32 KiB'tan fazlasını almıyor. Eski kaynaklarda Copilot code review'un talimat dosyasının yalnız ilk 4.000 karakterini okuduğu yazar; GitHub bu sınırı 12 Haziran 2026'da kaldırdı. Yine de kısa dosya hem daha ucuz hem daha tutarlı uygulanıyor.
İkinci risk bayatlama. Klasör adı değişir, komut değişir, dosya ise eski hâliyle kalır ve agent'ı artık var olmayan bir yola yönlendirir. Bunu insan dikkatine bırakmak yerine kontrol ettim. Aşağıdaki file-based app, AGENTS.md dosyalarında `...` içinde yazılmış göreli yolları bulup var olup olmadıklarına bakıyor:
#:property PublishAot=false
using System.Text.RegularExpressions;
// Her AGENTS.md'de `code` olarak yazılmış yolları bulur ve hâlâ var olup olmadıklarına bakar.
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('/');
// Yalnız `src/Bookstore.Api/Data/` gibi göreli yollar; route'lar (/api) ve yer tutucular (<Name>) atlanır
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).
Script çıkış kodu 1 döndürdüğü için CI'a bir adım olarak eklenebilir. Komutları kontrol etmenin en basit yolu da CI'ın AGENTS.md'deki komutların aynısını çalıştırması: dotnet build ve dotnet test CI'da kırılırsa dosya da eskimiş demektir. Kalan kısım alışkanlık: agent aynı hatayı ikinci kez yaptığında ya da review'da "agent bunu bilmeliydi" dediğin bir şey çıktığında dosyaya bir satır ekle; yanlışlığı fark ettiğin satırı da hemen sil.
Üretimde Dikkat
Talimat dosyasını kod gibi yönet: pull request ile değiştir, review et, CODEOWNERS (bir dosya ya da klasör değiştiğinde review'u zorunlu olan kişileri tanımlayan GitHub dosyası) ile sahibini belirle. Copilot code review talimatları pull request'in head branch'inden okuyor; yani talimat değişikliğinin etkisini aynı pull request'te görebilirsin, ama kötü niyetli bir talimat değişikliği de aynı yoldan gelir. Talimat dosyasındaki değişiklikleri review'da ayrıca oku.
Etkisini ölç. Bu yazıdaki gibi aynı görevi talimatlı ve talimatsız vermek, dosyanın gerçekten bir şey değiştirip değiştirmediğini gösteren ucuz bir deney. Ekipte birkaç tipik görevi saklayıp talimat dosyası büyük ölçüde değiştiğinde tekrar çalıştırabilirsin.
Son olarak, dosyanın garanti vermediğini unutma. Claude Code'un dokümanı da CLAUDE.md'nin bir configuration değil context olduğunu, bir eylemi kesin engellemek için hook gerektiğini açıkça yazıyor. "Migration'ı elle düzenleme" bir istek; migration klasörüne dokunan pull request'i CI'da yakalamak ise bir kontrol.
Sık Düşülen Tuzaklar
- Tüm wiki'yi talimat dosyasına yapıştırmak: her oturumda token harcar, önemli kural kalabalıkta kaybolur.
- Birbiriyle çelişen birden çok dosya tutmak:
CLAUDE.mdbir şey,copilot-instructions.mdbaşka bir şey söylediğinde sonuç araca göre değişir. - Formatter'ın zaten zorladığı stil kurallarını tekrar yazmak.
- Dosyayı bir kez yazıp unutmak: klasör ve komut değişir, talimat eski yolu göstermeye devam eder.
- Talimatı zorunlu bir kural sanmak: kritik yasaklar hook, branch protection ve CI ile de korunmalı.
- Talimat dosyasına secret, iç sistem adresi ya da müşteri verisi koymak.
Küçük Bir Deneme
★☆☆ Kendi projende AGENTS.md yoksa 30 satırı geçmeyen bir tane yaz: Commands, Layout, Rules ve Ask first bölümleriyle. Kullandığın agent'a "Bu repo'yu nasıl build edip test ederim?" diye sor; cevap dosyadaki komutlarla aynı mı?
★★☆ Yukarıdaki GetBooksByCategory'yi ve iki testi Bookstore'a ekle, sonra method'u düzelt: kategori yoksa 404 dönsün ve Unknown_category_returns_404 yeşile dönsün. Sonra SearchBooks ile tekrar eden sorgu kodunu ortak bir method'a taşıyıp taşımamaya karar ver ve kararını commit mesajında gerekçesiyle yaz.
★★★ check-agents-md.cs'yi genişlet: AGENTS.md'deki dotnet ve npm komutlarını da bulsun ve bir CI adımında gerçekten çalıştırsın. Hangi komutların CI'da çalıştırılamayacağını (veritabanı isteyenler gibi) nasıl işaretleyeceğini de düşün.
Talimat dosyası agent'a kuralları anlattı ama "kodu oku, sonra planla" adımını atlarsan üçüncü satırdaki gibi kod bilgisi yine eksik kalıyor. Üçüncü yazıda Bookstore'a istek listesi özelliğini ekleyeceğiz ve işi keşfet, planla, uygula, doğrula diye adımlara bölüp her adımda insanın nerede onay vereceğini göreceğiz.