Most teams are not bad at giving AI context.
They are bad at putting the right context in the right layer.
The common failure mode is not a lack of effort. It’s overloading one context mechanism with everything: architecture rules, coding style, test commands, security warnings, and one-off task instructions, all dumped into the same file. The assistant reads it all, can’t tell what matters most for the current task, and produces inconsistent output.
The fix is simpler than most teams expect. Current AI tooling already gives you three distinct layers to work with, and each one has a different purpose.
This guide walks through those three layers, explains what belongs in each one, and shows how they cooperate in a real monorepo. By the end, you’ll have a clear mental model and example files you can adapt to your own codebase.
The Three-Layer Model
Think of it like this:
| Layer | File(s) | Question it answers |
|---|---|---|
| Agent files | AGENTS.md, CLAUDE.md | How should the AI operate in this codebase before it starts the task? |
| Repository instructions | .github/copilot-instructions.md | What does any contributor need to know to make a correct change in this repo? |
| Path-specific instructions | .github/instructions/*.instructions.md | What is different in this part of the system? |
These are not three ways to say the same thing. They encode three fundamentally different kinds of knowledge: behavioral guidance, repository mechanics, and local architectural variation.
Layer 1: AGENTS.md / CLAUDE.md — Behavioral Guidance and Architectural Posture
What this layer is for
AGENTS.md and CLAUDE.md are where you put persistent guidance for how the assistant should operate in your project. Not what to build, but how the assistant should think and act before it even starts.
In practice, Codex reads AGENTS.md files along the directory path from the repo root down to the current working directory, with later files overriding broader ones. Claude Code walks the directory tree for CLAUDE.md and CLAUDE.local.md, and also loads subdirectory files when it enters those areas. This means guidance is scoped and composable by design.
⚠️ Worth knowing: Agents have specificity. A nested
AGENTS.mdwill override a root-level one for requests in its subtree but won’t use the one on the way back up. So maybe this is not the best place for project-conventions or architectural rules that should apply everywhere.
What could belong here
My approach… BEHAVIORAL GUIDENCE and ARCHITECTURAL POSTURE (If you don’t nest them).
This layer is about behavioral and architectural principles, not implementation detail:
- How cautious or proactive the agent should be
- Whether it should propose a plan before making edits
- Whether it should run tests automatically
- Whether it should prefer minimal diffs or broader refactors
- Architectural boundaries that matter everywhere
- ”Never do X without checking Y” rules
- Cross-cutting engineering values
- How to communicate findings and risks
# AGENTS.md
## Working style
- Prefer small, reviewable diffs.
- Before editing, identify the affected layer: UI, application, domain, or infrastructure.
- Do not introduce new dependencies unless clearly justified.
- When changing more than one abstraction layer, explain why.
## Safety rails
- Never change database schemas without checking migration conventions.
- Never bypass feature flags for production-facing work.
- For auth, payments, and PII-related code, be conservative and explain risks before acting.
- When a change has broad surface area, summarize what will be affected.
## ‼️ Architecture (In my experience in complex architectures this works good in a path-specific file too)
- Domain logic belongs in services/use-cases, not in controllers or UI components.
- API contracts are the source of truth for backend/frontend integration.
- Shared utilities must remain framework-agnostic.
- Adapters can depend inward. Domain logic cannot depend outward.
## Auto-invoked tools
‼️ Listing your auto-invoked tools here is a good idea, but remember to keep this section up to date as your toolset evolves. If you have a lot of tools, consider a separate `TOOLS.md` file and link to it from here.
SKILLS
| Path | use case | description |
|---|---|---|
| `@skills/plan.md` | Planning | Before making edits, create a plan for how to approach the task. |
INSTRUCTIONS
| Path | use case | description |
|---|---|---|
| `.github/copilot-instructions.md` | Repository instructions | Load repo-wide rules about how this codebase works and how to validate changes. |
|`.github/instructions/**/*.instructions.md` | Path-specific instructions | Load local rules that only apply to specific parts of the codebase. |
...
What should not go here
Don’t overload AGENTS.md with low-level mechanics:
- Exact test commands for every package
- File-pattern-specific style rules
- Framework-specific exceptions for one folder
- Temporary migration notes for this sprint
That content belongs in the other layers.
A nice nuance with CLAUDE.md
Claude’s model has an interesting property worth mentioning: CLAUDE.md can import additional files via @path/to/file syntax, letting you compose guidance from multiple sources. Claude also supports private CLAUDE.local.md files — committed to .gitignore — for personal working habits you don’t want to share with the team. That separation between shared team rules and personal preferences is genuinely useful in practice.
💡 Pro tip: Think of
AGENTS.md/CLAUDE.mdas the assistant’s briefing document. It should answer: How should the AI behave in this codebase before it even starts the task?
Layer 2: Repository Instructions — Shared Repo Mechanics
What this layer is for
My approach… CODE STANDARDS and PROJECT CONVENTIONS.
GitHub Copilot’s repository-wide instructions live in .github/copilot-instructions.md and are designed to provide context on how the project works and how to build, test, and validate changes. This layer is less about assistant personality and more about how this repository operates day-to-day.
What belongs here
Think of this as the answer to: what would every contributor need to know?
# .github/copilot-instructions.md
## Stack
- Frontend uses Next.js + TypeScript.
- Backend APIs are consumed through generated clients (never call raw endpoints).
- State management uses Zustand for client state; prefer server state via React Query.
## Validation
- Run `pnpm lint` and `pnpm test` before submitting changes.
- For UI work, also run `pnpm test:e2e` when routes or user flows change.
- Generated types come from OpenAPI and must not be manually edited.
## Implementation rules
- Prefer server components unless interactivity is required.
- Reuse design-system components from `packages/ui`.
- API calls must go through the typed client in `lib/api`.
- Database access must happen via repository adapters, never directly from controllers.
... and so on. This is not an exhaustive list, just examples of the kind of repo-wide rules that belong here.
What should not go here
Avoid adding things that only apply to one part of the repo:
- Conventions that apply only to part of the repo
- Every architecture detail in your entire organization
If repo-wide instructions become a kitchen sink, they add noise without adding clarity.
⚠️ Worth knowing: On GitHub Copilot, repository instructions and path-specific instructions can both apply to the same request — repo-wide instructions work best as the broadest baseline layer. Also, Copilot code review reads only the first 4,000 characters of a custom instruction file, even though that limit doesn’t apply to Copilot Chat or the coding agent. Write the most important rules first.
Layer 3: Path-Specific Instructions — Local Overrides for Bounded Contexts
What this layer is for
⚠️ Worth knowing: This is probably the most practically powerful layer, and the most underused.
My approach… SPECIFIC RULES THAT ONLY APPLY TO SPECIFIC FILES (ALWAYS!).
GitHub Copilot supports path-specific instruction files under .github/instructions/, with frontmatter using applyTo globs. When both repo-wide and path-specific instructions match, both apply — the repo-wide instructions form the baseline and the path-specific file adds (or tightens) rules for that context.
---
applyTo: "services/payments/**/*.ts"
---
Not every folder in a repo behaves the same way. Some parts of your system are:
- Legacy code that must change as little as possible 💡
- Highly regulated domains with audit and compliance requirements
- Performance-sensitive hot paths
- Generated output that should never be touched manually
- Framework-specific zones with different rules
- Migration boundaries where conventions are in transition
Path-specific instructions let you tell the AI: inside this part of the system, the local rules are different.
What belongs here
Good candidates — organized by concern:
Domain-critical zones:
---
applyTo: "services/payments/**/*.ts"
---
# Payments service instructions
- Be conservative with refactors. Prefer extensions over rewrites.
- Preserve audit logging behavior. Never remove or silence audit events.
- Never change idempotency or retry semantics without an explicit comment explaining why.
- Add tests for failure paths, not only happy paths.
- Describe risk in the PR when changing settlement, retries, or state transitions.
Frontend zones with RSC conventions:
---
applyTo: "apps/web/**/*.{ts,tsx}"
---
# Web app instructions
- Prefer React Server Components by default. Client components must justify local state.
- Fetch data via server actions or approved API utilities in `lib/api`.
- Reuse existing UI primitives from `packages/ui` before adding new variants.
- Client components must not import server-only modules.
Legacy zones that need stability:
---
applyTo: "legacy/**/*.js"
---
# Legacy zone
- Minimize refactors. Preserve existing public behavior above all else.
- Avoid syntax upgrades unless they are required by the task.
- Add characterization tests before changing risky logic.
- Do not introduce new abstractions — extend what is already there.
Generated code that must not be touched:
---
applyTo: "src/generated/**"
---
# Generated files
- These files are auto-generated from OpenAPI specs.
- Never edit manually. Regenerate with `pnpm generate:api`.
- If the types seem wrong, fix the spec, not this output.
Infrastructure with strict idempotency rules:
---
applyTo: "infra/**/*.tf"
---
# Infrastructure instructions
- All changes must preserve idempotency and environment parity.
- State changes require explicit justification.
- Do not destroy and recreate resources when an update is possible.
- Test changes against staging before proposing production changes.
💡 Pro tip: Path-specific instructions are where architecture stops being abstract and starts affecting code generation directly. This is the layer that makes AI context genuinely useful in heterogeneous codebases.
How They Work Together in a Real Monorepo
Here’s a concrete project structure showing all three layers in action:
repo/
AGENTS.md # Global engineering posture
.github/
copilot-instructions.md # Repo-wide mechanics and validation
instructions/
web.instructions.md # applyTo: apps/web/**
payments.instructions.md # applyTo: services/payments/**
legacy.instructions.md # applyTo: legacy/**
generated.instructions.md # applyTo: src/generated/**
apps/
web/
admin/
services/
auth/
payments/
packages/
ui/
contracts/
legacy/
Each layer handles a distinct concern:
AGENTS.md (root) — Cross-cutting architectural posture:
- Prefer minimal diffs
- Respect layer boundaries and dependency direction
- Avoid unnecessary dependencies
- Flag risk in auth and payments areas proactively
.github/copilot-instructions.md — Repo-wide operational rules:
- Use
pnpm - Run lint + tests after changes
- Generated clients are the source of truth for API types
- Design system is mandatory for UI work
- Database access happens through repository adapters
.github/instructions/payments.instructions.md — Domain-local constraints:
- Preserve audit behavior
- Test retry/idempotency paths
- Don’t alter settlement flows without explicit explanation
.github/instructions/web.instructions.md — Frontend-local constraints:
- Prefer server components
- Don’t bypass the typed API client
- Reuse UI primitives
Any request touching services/payments/ picks up repo-wide rules and the payments overlay. Any request in legacy/ picks up repo-wide rules and the conservative legacy rules. The layers compose cleanly.
Common Anti-Patterns
Anti-pattern 1: Putting everything in AGENTS.md
The result is a bloated file with poor relevance signal. The assistant can’t distinguish your critical architectural constraints from your formatting preferences. Everything gets treated as context, which means nothing gets priority.
Anti-pattern 2: Making repo-wide instructions too specific
When .github/copilot-instructions.md includes rules that only apply to one part of the codebase, every task carries irrelevant rules. The assistant overfits to constraints that don’t apply to what it’s currently doing.
Anti-pattern 3: No path-specific overrides in a heterogeneous repo
This is the most common gap. If your payments service and your marketing pages get the same instructions, the AI has no way to know that one deserves caution and the other is low-risk. Risky domains get treated like generic CRUD folders.
Anti-pattern 4: Duplicating rules across all three layers
Rules that appear in AGENTS.md, copilot-instructions.md, and several path-specific files will drift over time. One version will be stricter than another. The assistant will see contradictions and resolve them unpredictably.
Anti-pattern 5: Storing sprint tasks as persistent context
One-off task instructions — “for this sprint, we’re migrating from REST to GraphQL” — do not belong in persistent context files. When the sprint ends, that context becomes noise. Worse, it becomes misleading noise that looks like architectural guidance.
⚠️ Persistent context should contain stable truths, not sprint residue.
Final Takeaway
Context quality is mostly about placement, not volume.
The teams that get the most out of AI-assisted development are not writing the longest instruction files. They are thinking carefully about what kind of knowledge they are encoding and which layer is the right home for it.
- Use agent files for how the assistant should behave before it starts
- Use repository instructions for how this repo usually works
- Use path-specific instructions for what is different in this part of the system
That distinction sounds simple, but it changes everything about how consistently the AI performs across a real, heterogeneous codebase.
If you’ve been lumping everything into a single file and getting unpredictable results, this is worth trying. Start with the root AGENTS.md, add a solid .github/copilot-instructions.md, and identify the two or three subdomains in your codebase that most deserve their own local rules. That alone will give you noticeably better signal-to-noise in generated output.
If this was useful, you might also enjoy Agent Skills: Getting Started with SKILL.md and Agent Skills Part 2: Patterns, Architecture & Tooling.