A repository can now carry a great deal of instruction for AI tooling: always-on conventions, path-scoped rules, cross-tool agent guidance, reusable prompts, loadable skills, agent personas, external tool configuration and lifecycle hooks.
Each exists for a reason. Used interchangeably they produce a configuration nobody understands, where behaviour seems intermittent because a rule is in the wrong mechanism.
This lesson is the decision.
The organising question
Section titled “The organising question”Every mechanism differs on one axis: when is it loaded?
Always Repository instructions, AGENTS.mdWhen a path matches Path-specific instructionsWhen judged relevant Agent skillsWhen you invoke it Prompt filesWhen selected Custom agentsWhen configured MCP serversAt lifecycle events HooksEverything else follows. Always-on content costs context on every request, which caps how much of it there can be. Conditionally-loaded content can be long, because it is absent when irrelevant. Invoked content is under your control. Enforced content — hooks — is the only category that can actually stop something.
The decision table
Section titled “The decision table”| Need | Mechanism |
|---|---|
| Always-on repository guidance | Repository custom instructions |
| Rules only for specific paths | Path-specific instructions |
| Cross-tool agent guidance | AGENTS.md, where supported |
| A repeatable prompt you invoke | Prompt file |
| Specialised task knowledge and workflow | Agent skill |
| A specialised persona with restricted tools | Custom agent |
| Access to external tools or data | MCP server |
| Deterministic lifecycle validation or control | Hooks |
| A distributable bundle of the above | Plugin |
| Explanation for humans | A document — README, ADR, onboarding guide |
The last row matters. Not everything belongs in AI configuration. A model does not need your architectural history; a new engineer does. Putting human documentation into instructions wastes context and produces a document that is bad for both readers.
Worked decisions
Section titled “Worked decisions”The table is abstract. These are the calls that actually come up.
“We always use pytest, never unittest.” Always true, short, universal. → Repository instructions.
“Migrations must be reversible and must not drop a column in the same release.”
Only relevant to migration files, and specific. → Path-specific instructions scoped to
migrations/**.
“Here is our full security review procedure — twelve checks, with examples of each.” Long, and only relevant when reviewing. Putting it in always-on instructions would cost that context on every request. → Agent skill.
“Generate a test suite the way we like them.” A task, invoked deliberately, in an IDE. → Prompt file. If it also needs to work for the cloud agent or code review, → skill instead.
“Reviews of Terraform should be done by something that knows Terraform and cannot edit files.” A persona with restricted tools. → Custom agent.
“The agent should be able to read our issue tracker.” External data. → MCP server, read-only.
“No agent may ever commit directly to main.” Must be enforced, not requested. → Not a mechanism on this page. That is a ruleset, and branch protection is what makes it true.
“Run our secret scanner before any commit the agent makes.”
Deterministic, at a lifecycle point, and it must be able to block. → Hook, on preToolUse.
“Why is the scheduler written this way?” Human context. → A document, or a commit message body.
An example repository layout
Section titled “An example repository layout”Verified against the current documented locations.
.github/├── copilot-instructions.md # always on, repository-wide│├── instructions/ # loaded when matching paths are in scope│ ├── migrations.instructions.md│ ├── terraform.instructions.md│ └── frontend.instructions.md│├── prompts/ # invoked deliberately (IDE only, preview)│ ├── security-review.prompt.md│ └── release-notes.prompt.md│├── skills/ # loaded when judged relevant│ ├── security-review/│ │ └── SKILL.md│ └── release-notes/│ └── SKILL.md│├── agents/ # selectable personas with tool restrictions│ ├── terraform-reviewer.md│ └── docs-agent.md│└── hooks/ # deterministic lifecycle control └── validation.json
AGENTS.md # cross-tool agent guidance, repository rootNotes on the details, because these are the things that go wrong:
Custom agent files are NAME.md or NAME.agent.md in .github/agents/. Both extensions are
recognised; the name minus the extension is used for deduplication between levels.
Skills are directories, each containing a SKILL.md. The directory name should be lowercase with
hyphens. Skills can also live in .claude/skills/, .agents/skills/, and personal locations such as
~/.copilot/skills/.
Hooks are JSON in .github/hooks/, with a version of 1.
AGENTS.md is at the repository root, not in .github/.
What belongs in each
Section titled “What belongs in each”Repository instructions — a page at most:
- Stack and versions, where they change the answer
- Conventions not inferable from code
- Constraints: frozen directories, mandatory wrappers
- Things you do not want done: no unrequested reformatting, no silent dependency additions
Path-specific instructions — rules that differ by area:
- Migration safety rules
- Infrastructure conventions
- Accessibility requirements for UI code
- Anything where “it depends where you are” is the honest answer
AGENTS.md — guidance for agents generally, portable across tools. In a repository where
contributors use different assistants, this is the shared file. Avoid duplicating it into
copilot-instructions.md; use a file reference where the surface supports one.
Skills — long, conditional procedures:
- A full review checklist with examples
- A release process with steps and scripts
- A domain-specific workflow with supporting files
Custom agents — role plus tool restrictions. The mechanism for “this one reviews and cannot edit”.
MCP — external systems. Read-only unless there is a reason.
Hooks — the only enforcement in this list.
Instructions versus documentation, again
Section titled “Instructions versus documentation, again”The boundary that most often gets blurred, and worth a rule.
Documentation explains. It has context, reasoning, examples and prose. It is read once by a human who then understands something.
Instructions direct. They are terse, imperative, and applied on every request without being read.
The failure in both directions:
README content in instructions — an architectural overview, a history of the system, an explanation of why a decision was made. It costs context on every request and a model does not need the reasoning to follow the rule.
Instructions in a README — “always use the shared logger”, buried in a document nobody reads after their first week. The convention does not get applied, and the person who wrote it believes it is documented.
The rule that separates them: if the reader needs to understand it, it is documentation. If the reader needs to comply with it, it is an instruction. The same fact can appear in both — the README explains why all HTTP goes through one wrapper; the instruction says that it does — and the split is deliberate rather than duplication.
AI repository documentation covers the human side, including the observation that the two are different documents for different readers and should not be merged.
The context budget
Section titled “The context budget”The concept that makes the always-on versus conditional distinction concrete.
Every request has a finite context window, shared between: your prompt, the code the model needs to see, the conversation so far, and all always-on configuration. That is a budget, and configuration spends from it before your code gets any.
The practical arithmetic:
A short instructions file — say thirty lines — is a small, worthwhile spend. It buys correct conventions on every request.
A five-hundred-line instructions file is a large spend on every request, including the ones where none of it is relevant. It crowds out the files the model needs, and it dilutes each individual rule.
A five-hundred-line skill costs nothing on unrelated requests and is fully available when relevant. Same content, dramatically different economics.
This is the whole argument for the conditional mechanisms, and it explains a pattern people find confusing: adding more guidance can make results worse. Past a point, the additional rules displace the code and dilute the rules that were working.
The heuristic: if it is longer than a screen and not relevant to most requests, it is a skill. If it is short and always relevant, it is an instruction. If it is long and always relevant, something is wrong with the framing — that combination rarely exists genuinely.
Multiple repositories
Section titled “Multiple repositories”At organisation scale the question changes from “what goes where” to “what is shared”.
Organisation-level instructions apply across repositories. Right for genuinely universal standards — a security requirement, a language policy — and wrong for anything that differs between teams, because no repository-level file cleanly overrides them.
Custom agents can live at organisation or enterprise level, in an agents/ directory in the
organisation’s .github or .github-private repository. That makes a well-built reviewer agent
available everywhere without copying.
Skills and instructions are per repository. Sharing them means copying, which means divergence — or a generation step that syncs them, which is an ordinary tooling problem.
The pattern worth adopting: universal standards at the organisation, everything specific in the repository. The test is the same as always — is this true everywhere? An organisation instruction that is wrong for one team is wrong on every request that team makes, and they cannot fix it.
Hooks deserve their own paragraph
Section titled “Hooks deserve their own paragraph”Everything else on this page influences. Hooks control.
A hook is a shell command, HTTP call or prompt that runs at a defined point in an agent’s lifecycle.
The events include sessionStart, sessionEnd, userPromptSubmitted, preToolUse, postToolUse
and others.
preToolUse is the significant one: it runs before the agent uses any tool, and it can deny the
call. A hook returning a deny decision blocks the tool use, and for command hooks an exit code of 2
is treated as a deny.
{ "version": 1, "hooks": { "preToolUse": [ { "type": "command", "bash": "scripts/check-tool-use.sh", "timeoutSec": 10 } ] }}That is the difference between “please do not modify production configuration” and a mechanism that prevents it. For anything that must not happen, hooks are where the control belongs — and they are covered in full in Building Repository AI Agents.
Support is narrower than for instructions: available in Copilot CLI and on GitHub, in preview in VS Code, and not available in several IDEs.
Security of the configuration itself
Section titled “Security of the configuration itself”These files change how AI tooling behaves in your repository, which makes them a security-relevant surface rather than a preferences file.
They are code, and should be reviewed as such. A pull request modifying
.github/copilot-instructions.md, a skill or a hook is changing what happens on every subsequent
interaction. A CODEOWNERS entry on .github/ covers this and the
workflow files at the same time.
Hooks execute. A hook is a shell command that runs at a lifecycle point. Adding one to a repository is adding code that runs on contributors’ machines when they use an agent. That is the same trust question as a Git hook or a build script, and it deserves the same scrutiny.
MCP configuration grants access. A server added to repository configuration is a set of tools agents can invoke. Read-only versus write-capable is a security decision, not a convenience one.
Instructions can be influenced by a pull request. This matters most for code review, where customisation is read from the branch being reviewed — so a contributor can modify the instructions shaping the review of their own change. Human review of the diff, including its configuration files, is the control.
Nothing sensitive belongs in these files. They are in the repository and their contents reach a model on every request. An internal hostname, a credential format, a description of an unpatched weakness — all disclosure.
The general framing: this directory is now part of your repository’s attack surface. It is a small part, and treating it as configuration rather than as code is how it stops being small.
Avoiding the common failures
Section titled “Avoiding the common failures”Do not duplicate across files. AGENTS.md and copilot-instructions.md containing the same rules
is two things to update and two to diverge. Pick a primary and reference it.
Do not put long content in always-on files. If it is long, it is a skill.
Do not use instructions where enforcement is needed. Instructions are followed probabilistically.
Do not build on a mechanism your team cannot use. Check the support matrix against the surfaces your team actually uses.
Do not let it grow unowned. This is now a configuration surface with several files. Somebody should
own it, and it should be reviewed like code — a CODEOWNERS entry
on .github/ is reasonable, and it also covers the workflow files, which is
a separate good idea.
Keeping the configuration honest
Section titled “Keeping the configuration honest”A configuration surface with eight mechanisms decays in specific ways. Three checks that catch it.
Does each file still describe the repository? The failure mode is a rule for a convention you migrated away from, which does not become inert — it actively steers output toward the old pattern. Reviewing the instructions when you change a convention is the cheap version; a quarterly read is the fallback.
Is anything configured for a surface nobody uses? Prompt files in a team that works mostly in the terminal. Hooks in a team on an IDE that does not support them. Configuration that applies nowhere is maintenance without benefit, and it is invisible because nothing errors.
Is anything being ignored? The hardest to detect. A rule buried in a long file, or a skill whose description does not match how tasks are actually phrased, simply does not fire. The symptom is that you are still making the correction the rule was written to prevent.
The test for that last one is direct: take a rule and deliberately ask for something that violates it. If the output complies, the rule is working. If not, the rule is either in the wrong mechanism, in a file the surface does not read, or drowned in a file that is too long.
Running that check on three rules takes five minutes and is the only positive evidence available that any of this configuration does anything.
Starting from nothing
Section titled “Starting from nothing”The order that produces value fastest.
-
Repository instructions, ten lines, from corrections you actually make. Universal support, and the largest single improvement.
-
Path-specific instructions for the two or three areas with genuinely different rules.
-
AGENTS.mdif your team uses more than one assistant, referencing rather than duplicating. -
A skill for the first long procedure you find yourself explaining repeatedly.
-
A custom agent for the first role where tool restriction matters — usually a reviewer.
-
MCP, read-only, for the first external system you keep pasting from.
-
Hooks for the first thing that must not happen.
-
Review the whole thing quarterly, and delete what is not being used.
Steps 1 and 2 deliver most of the benefit. Everything after is worth doing when a specific need appears and not before — speculative configuration is maintenance with no payoff.
A monorepo example
Section titled “A monorepo example”Where the mechanisms stop being optional, because a single set of rules cannot describe several projects.
.github/├── copilot-instructions.md # only what is true everywhere├── instructions/│ ├── api.instructions.md # applyTo: "services/api/**"│ ├── worker.instructions.md # applyTo: "services/worker/**"│ ├── web.instructions.md # applyTo: "apps/web/**"│ ├── infra.instructions.md # applyTo: "infra/**"│ └── migrations.instructions.md # applyTo: "**/migrations/**"└── skills/ ├── add-service/ └── release-service/The repository-wide file becomes very short — the shared build tooling, the commit convention, the directories that are frozen — because almost nothing else is true across a Python service, a TypeScript front-end and a Terraform stack.
Two monorepo-specific points.
Path patterns need to be right, and are easy to get subtly wrong. services/api/** and
**/services/api/** behave differently in a nested layout, and the failure is silent: the instructions
apply to nothing and the model behaves as though the file does not exist. Test by asking for something
inside and outside the path.
Skills are how you encode cross-cutting procedures. “Add a new service” touches the service directory, the CI configuration, the infrastructure and the deployment manifest. That is a long procedure, only relevant occasionally, and it is exactly the shape a skill fits.
The general lesson from a monorepo is that it makes the always-on budget problem obvious: there is simply not enough that is universally true to fill a large instructions file, and attempting one produces rules that are wrong in half the repository.
Common mistakes
Section titled “Common mistakes”Putting everything in one instructions file. Length degrades reliability and costs context.
Confusing skills with instructions. Skills are conditional and can be long; instructions are always on and must be short.
Confusing prompt files with skills. You invoke a prompt file; an agent selects a skill.
Expecting instructions to enforce. Hooks and rulesets enforce.
Duplicating between AGENTS.md and copilot-instructions.md. They diverge silently.
Configuring for surfaces your team does not use. Check the matrix.
Never pruning. Stale rules actively steer the wrong way.
Treating this as documentation. Human context belongs in documents; this is configuration.
What this buys you
Section titled “What this buys you”Worth being concrete, because the configuration is a real cost and the benefit is easy to assert vaguely.
Consistency across contributors. Everybody’s tooling applies the same conventions, including people who joined last week and do not know them yet. That is the same value as a linter, obtained earlier in the loop.
Delegation becomes viable. A cloud agent working unsupervised has only the repository to go on. The difference between a repository with good instructions and one without is the difference between delegation producing usable work and producing generically-reasonable work that does not fit.
Review gets cheaper. Conventions applied in the first draft are conventions nobody comments on.
Knowledge outlives people. The engineer who knows why all HTTP goes through one wrapper will leave. The instruction stays, and — unlike a wiki page — it is applied rather than read.
The exercise itself is valuable. Writing down what a new engineer needs told reveals disagreements and undocumented conventions. Several teams get more from that conversation than from the resulting file.
What it does not buy: correctness. Everything in this pillar’s verification chain still applies, and a well-instructed model produces well-formatted code that can be wrong in exactly the ways the hub describes. Instructions improve fit, not truth.
Mental model
Section titled “Mental model”Think of it as a briefing pack for somebody joining for a day. The standing brief is short and always read. The detailed procedures sit on a shelf and get taken down when relevant. The rules that cannot be broken are not in the pack at all — they are the locks on the doors.
What you learned
Section titled “What you learned”- Every mechanism differs on when it loads, and everything else follows from that
- Always-on content is charged per request, which caps how much there can be
- Conditional content — skills — can be long because it is absent when irrelevant
- Custom agents are
NAME.mdorNAME.agent.mdin.github/agents/ - Skills are directories containing
SKILL.md, in several supported locations AGENTS.mdlives at the repository root and is the cross-tool convention- Hooks are the only mechanism that enforces;
preToolUsecan deny a tool call - Human documentation belongs in documents, not in AI configuration
- Start with instructions and path-specific rules; add the rest when a need appears
Exercise
Section titled “Exercise”Use a repository you work in.
-
List five things you would tell somebody joining the project. For each, decide which mechanism it belongs in using the table above.
-
Write the repository instructions for the ones that are always true and short.
-
Take the longest one and write it as a skill instead. Predict: does the always-on file get noticeably shorter?
-
Add a path-specific instruction file for one area. Test that it applies inside and not outside.
-
Take a rule that must not be violated. Predict: which mechanism enforces it? Write it as a ruleset or a hook rather than an instruction.
-
Check each mechanism against the surfaces your team uses. Predict: does anything you configured apply nowhere?
-
Delete anything from the list that turned out to be documentation rather than configuration.