Layering Agent Instructions: Personal, Project and Path-Scoped Rules
Project conventions living in someone's personal settings is why two engineers disagree about what the agent does to the same codebase — and why both of them are right.
Three layers, frequently confused
Instructions reach an agent through three distinct channels, and teams routinely put content in the wrong one. The result is rules that apply where they should not, or fail to apply where they should.
| Layer | Scope | Changes | Belongs there |
|---|---|---|---|
| Vendor system prompt | Every session, every project | Not yours to change | — |
| User instructions | Every session you run | Rarely | Personal preferences |
| Project rules | One repository | With the code | Everything about the code |
The test is ownership. If an instruction would still be true for a colleague working on the same repository, it is a project rule. If it would be false for them, it is a user instruction. Almost everything people put in user instructions fails this test.
What actually belongs in user instructions
A short list, and it should stay short:
- Response format. "Show the diff before applying it." "Explain before you change anything." Preferences about interaction, not about code.
- Verbosity. Some people want the reasoning; some want the patch.
- Personal tooling. "I use fish, not bash." "My editor is neovim."
Notice what is absent: nothing about code style, architecture, testing or project conventions. Those are properties of the repository, and putting them in your personal settings means your colleagues get different behaviour from the same codebase — which produces the confusing situation where two engineers disagree about what the agent does, and both are right.
Personal instructions that encode project conventions are invisible to everyone else. Reviewers start noticing that changes look different depending on who ran the agent, with no visible cause, because the cause is in a settings file on someone's laptop.
Rules that apply to some files only
Beyond repository-wide rules, several tools support path-scoped rules — instructions that activate only when files matching a pattern are in play. Where available, this is the mechanism that keeps the always-on instruction set small.
---
description: React component conventions
globs: ["src/components/**/*.tsx"]
alwaysApply: false
---
- Function components only. No classes anywhere in this directory.
- Props interfaces are named `Props` and exported.
- No inline styles. Use the token classes in `src/styles/tokens.css`.
- Data fetching does not belong here — components receive data as props.
The value is not organisational tidiness. It is that an agent editing a database migration never pays for the React rules, and vice versa. Every rule that is conditionally loaded is a rule that costs nothing on the sessions where it is irrelevant — which is most of them.
Structuring the always-on set
Whatever remains unconditional deserves ordering. Put the rules whose violation is most expensive first, because position affects influence: material at the start of a context is weighted more heavily than material buried in the middle.
1. Prohibitions with real consequences
- never commit secrets; never edit applied migrations
2. Commands
- how to build, test and verify; what "done" means
3. Boundaries
- which layers may import which
4. Conventions
- naming, formatting, file placement
Most people write these in the reverse order, because conventions are the easiest to articulate and prohibitions feel obvious. The obvious ones are exactly the ones an agent under pressure to complete a task will reason its way around.
Write prohibitions, not preferences
A pattern that shows up consistently when teams measure this: explicit negative instructions change behaviour more reliably than positive preferences.
| Weak | Strong |
|---|---|
| "Prefer composition over inheritance" | "Do not add new base classes. Use composition." |
| "Try to keep functions small" | "Over 60 lines, split it. No exceptions in src/domain/." |
| "Use the existing HTTP client" | "Do not import requests or httpx directly. Use src/adapters/http.py." |
| "Be careful with migrations" | "Never edit a file in migrations/ that is already committed." |
Two things distinguish the right column. Each names a concrete artifact — a module, a directory, a number — rather than a quality. And each is checkable, which means you could write a lint rule for it, which is the clearest signal that an instruction is specific enough to steer anything.
The escape hatch, again
Every prohibition needs a stated alternative. This bears repeating because it is the single most common defect in rule files:
Do not importrequestsdirectly. All outbound HTTP goes throughsrc/adapters/http.py, which handles retries and timeouts. If you need something it does not support, add it there rather than bypassing it.
Without the second sentence, an agent that needs an HTTP call the wrapper does not support has two options: violate the rule, or stall. Both are worse than the third option you failed to mention.
Testing that a scoped rule fires
Path-scoped rules fail silently. A glob that matches nothing produces no error — the rule simply never loads, and you carry on believing it is in force.
The check is the same marker trick that works for nested context files. Add a distinctive line to the scoped rule, open a session on a file inside the scope, and ask for it back:
---
globs: ["src/components/**/*.tsx"]
---
When asked which rules are loaded, reply exactly: COMPONENTS-RULE-ON
Then ask from a file outside the scope, where it must not appear.
Two checks, one minute, and they distinguish "the rule is working" from "the
glob does not match and nobody noticed". Globs are easy to get subtly wrong
— a missing **, a wrong extension, a leading slash —
and nothing in the tooling will tell you.
Keeping the layers honest
A periodic audit that takes ten minutes and consistently finds something:
- Open your personal instruction settings. For each line, ask whether it would be true for a colleague on the same repository. Move anything that would be into the project rules.
- Open the project rules. For each line, ask whether it could be path-scoped. Move what can be.
- Read what remains always-on, in order. If the most expensive prohibition is not near the top, reorder.
The first step is the one that surprises people. Personal instruction files accumulate project-specific rules because that is where you were when you noticed the problem, and nothing ever prompts a move.
Sort instructions by ownership: if a colleague on the same repo would need it too, it is a project rule, not a personal one. Path-scope everything you can, order the remainder by cost of violation, write prohibitions rather than preferences, and pair every prohibition with the alternative.