Loop Engineering: How To Stop The "You're Absolutely Right" Sycophancy
The Problem of Sycophancy
“You’re absolutely right!” The agent says it before it has looked at anything. You point out a bug, it tells you you’re right. You suggest the opposite of what you asked for a minute ago, it tells you you’re right again. That reflexive agreement, the little affirmation stapled to the front of every reply, is one of the most complained-about tics coding agents have right now, and once you notice it you cannot stop noticing it.
The obvious fix is to tell it to stop. Add a line to your instructions: do not open with placation, do not tell the user they are right before you have checked. Reasonable. But that line now rides in the model’s context on every single turn, read again on the turns the agent was going to agree politely and the turns it was going to do real work, paid for whether or not it was ever about to placate. So the real question the title asks is how you actually stop it, and the answer turns out not to be an instruction at all.
Two Kinds of Rules
Notice something about that line, though. It is not about any particular file. “Do not open with placation” applies whenever the agent is about to speak, which is every turn, in every file and in none of them. Set it beside a different rule you might add: production code under src/ must not import a mock library. That one has a home. It is about src/ , a path you can name.
Two kinds of rule end up on an instruction surface: the kind you can pin to a file path and the kind you cannot, because they are about behavior rather than location. Which kind a rule is decides where it can live and what it costs to keep it there. That split is what this piece is about.
Both kinds pile up regardless. Your CLAUDE.md started at nine lines, and six months later it is three hundred. The no‑placation rule joins the mock‑import rule from the last two pieces, the payments‑module invariant, the frontend spacing scale, the deploy checklist, the API‑versioning policy, and a paragraph on how to phrase migration names. This morning you asked the agent to fix a typo in the README, and every one of those three hundred lines rode into its context alongside the request. None of them had anything to do with the README.
The Context Surface
The series opener named the loop that runs generate, check, steer, retry, stop, and posed three questions about the parts it turns on. Two pieces answered the first two: the check that decides good enough, stop, and the gate that refuses a bad write before it lands. This one takes the third question the opener posed, the part underneath both of the others: the context surface. It is the next arm on the bench, with more still to come.
Every check and every gate is fed by rules the agent carries as loaded text, and that text is paid for on every turn whether it applies to the turn or not. So the question the opener left last is a cost question. What does an instruction actually cost to keep loaded, and what changes when you load it only where it applies. The answer runs straight through the split above: one kind of rule you can teach the harness to load only where it belongs, and the other kind, the placation rule among them, you handle a different way entirely.
What the surface is here
The surface is everything the model sees before it reads your request. The system prompt, the tool definitions, whatever documents got retrieved, and the instruction files (CLAUDE.md, AGENTS.md, and their nested siblings) that ride along on every turn. Prompt engineering tuned the request. Context engineering curated this surface. The loop reuses it: the surface is the state the loop carries between iterations, re‑sent each time the model generates.
“Re‑sent each time” is the property this piece turns on. A conversation is not cumulative from the model’s side. Each turn, the whole surface is assembled and handed over again from the top. The three‑hundred‑line instruction file is not loaded once at the start of the session and remembered. It is loaded on turn one, and again on turn two, and again on the turn where you were editing a README. Whatever sits on the surface is paid for at every one of those turns, in full.
What Loaded Costs, Part One: Tokens
Put arithmetic on it, as a worked illustration and not a measurement. Say the instruction file has grown to a few thousand tokens, which three hundred lines of prose comfortably will. A working session runs dozens of turns. At forty turns, the file is assembled into the context window forty times. If two thirds of it is rules that only matter in one corner of the codebase, you have paid to keep a few thousand tokens of mostly‑irrelevant text in the window on every one of those forty turns, and two thirds of that spend bought nothing on the turn it rode along on. The numbers are illustrative; the shape is not. An always‑loaded instruction costs its length times the number of turns it survives, because the window is rebuilt every turn, so a rule you write once and never delete is one you pay for thousands of times over a session, whether or not any given turn had a use for it.
What Loaded Costs, Part Two: Attention
Tokens are the measurable half. The other half is what the extra text does to the model reading past it. The State of AI Instruction Quality pointed a deterministic analyzer at 28,721 repositories and found the median instruction file carries 50 content items and 12 actual directives. The other 38 items are headings, restated context, and structure the model is free to ignore. Every turn, the model has to find the twelve rules that bind inside the fifty items that are loaded, and the thirty‑eight that do not bind are still text it reads on the way to the work.
Adding a rule that does not apply to the current task costs its tokens and widens the pile the relevant rules are hiding in. There is a measured effect under that pile. A vaguely‑phrased instruction is followed less reliably as more unrelated instructions load beside it on the same turn: the same rule that the model largely honors when it is one of a few gets superficially acknowledged and then ignored once it is one of many, and the reporails corpus measures that decline rising with the number of competing topics. The rule did not change. The company it was loaded with did.
The lever it points at is fewer rules loaded at once, with the relevant one present on the turn it applies rather than on every turn. This is the surface reporails reads. It points the same deterministic analysis at your own instruction files and tells you, with measured evidence, which instructions couple to behavior and which are text the model can ignore, so you can see what you are actually paying to keep loaded instead of guessing at it. It does not load your context or trim it at runtime. It measures the surface you wrote down and reports where the spend is not buying compliance. Knowing which of your fifty items are the twelve that bind is the precondition for the next move, which is deciding where each of them should live.
The Rules You Can Pin to a Path
Start with the first kind, the rules that have a path. For a located rule the move is to load it only on the turns it applies to instead of on every turn, and the mechanism is older than agents: a thin index at the top that routes to depth, and depth that loads on match.
Claude Code ships exactly this affordance, and it is worth being concrete about it because the build below leans on it. A CLAUDE.md at the project root loads at the start of every session, so it is the always‑on surface. A CLAUDE.md placed inside a subdirectory does not: the harness loads it on demand, when the agent reads or edits a file in that subtree, and not before (per the Claude Code memory docs). A rule that only governs src/payments/ can live in src/payments/CLAUDE.md, and it reaches the model on the turns the agent works in src/payments/, on those turns only. The root file keeps the handful of rules that apply everywhere. Everything path‑specific moves down to the path it belongs to.
That subdirectory file is the simplest form of path‑scoped loading. The frontmatter‑scoped form is more precise, and it does not need a file parked in the directory at all. Claude Code also reads .claude/rules/*.md, and a rule file carrying a paths: glob in its frontmatter loads only for matching files. The docs state it plainly: “Path‑scoped rules trigger when Claude reads files matching the pattern, not on every tool use.” Tag a rule paths: ["src/**/*.py"] and it rides the surface when the agent touches Python under src/ and is absent otherwise.
The same primitive ships across every major agent tool, each with its own frontmatter key. Cursor scopes a .cursor/rules/*.mdc file with a globs: field, auto‑attaching the rule “when a matching file is in context.” GitHub Copilot scopes a .github/instructions/*.instructions.md file with an applyTo: glob, applying it to “requests made in the context of files that match a specified path.” Three vendors, one idea: attach a rule to a file pattern, and pay for the rule only when a matching file is in play.
Reporails canonizes that cross‑vendor mapping and checks for it. Its path-scope-declared rule (CORE:S:0038) verifies that a path‑scoped instruction file actually declares the scope it governs, and the Claude paths, Cursor globs, and Copilot applyTo equivalence is the exact thing the rule holds constant across agents. The full public rule set lives at reporails.com/rules.
That is the whole idea. The always‑on surface carries what is always relevant, and a rule that is relevant only sometimes loads only then.
This scales past a handful of nested files. The steering setup I work in runs its whole rule corpus this way: each rule is subscribed to the transition it governs, and its full body loads only when that transition fires. The system can report what actually loaded, and on a working session the report runs lopsided: a single steering rule loaded its full body, a handful more stayed one‑line pointers, and a dozen‑plus subscribed rules never loaded at all, because nothing in the session’s work touched their triggers. That is the shape you are after: the rules that applied were in context when they applied, and the rules that did not apply cost a pointer or nothing, instead of a full body on every turn.
One caveat the same receipt discipline demands stating: a pointer delivered is not a rule followed, so a rule that must hold every time cannot lean on being loaded only when it happens to apply. It also gets a deterministic floor, the cheapest place of all to keep a rule, which the placation case below turns out to need.
Build It: Split the File That Grew
Take the running example the series has used throughout. The no‑mocks rule from the check and the gate pieces says production code under src/ must not import a mock library. In the three‑hundred‑line file, it sits in the always‑on surface, loaded on every turn, including the turn you spent editing the README where it could not possibly apply.
The split that follows is itself something reporails measures. Its modular-file-organization rule (CORE:S:0010) checks whether an instruction surface distributes its rules across scoped files or piles them into one monolith, which is the exact shape a grown three‑hundred‑line CLAUDE.md has taken. The build below is what passing that check looks like on your own repo.
Start by sorting the file by scope, not by topic. For each rule, ask one question: does this apply to every turn, or only to turns that touch a particular path or task?
- The no‑mocks rule applies only when the agent is writing Python under
src/. - The commit‑message convention applies only when the agent is composing a commit.
- The frontend spacing scale applies only under
web/ortheme/. - The “which test runner” note genuinely does apply everywhere, so it stays.
Then move each rule to where its scope is. The root CLAUDE.md shrinks to the always‑relevant minimum:
# Project instructions (root, always loaded)
- Test runner is `pytest`; never edit files under `generated/`.
- Commit style and per‑area rules load from the CLAUDE.md nearest the file you are editing.
The no‑mocks rule moves down to src/CLAUDE.md, beside the code it governs:
# src/ instructions (loaded when the agent works under src/)
- Production code uses a real dependency or a constructor‑injected fake; do not import a mock library.
no-mocks.sh and the PreToolUse gate both enforce this; see the earlier pieces in this series.
Now the README turn loads the root file and nothing under src/, because the agent never opened anything under src/. The no‑mocks rule, the payments invariant, the frontend scale: none of them ride along, because none of their paths were in play. When the agent does start editing src/payments/charge.py, src/CLAUDE.md loads on that turn, carrying the no‑mocks rule to exactly the moment it applies. It is present on every turn it could bind and absent on every turn it could not, and the always‑on surface dropped from three hundred lines to a handful.
One discipline keeps the split honest: the nested src/CLAUDE.md extends the root and stays consistent with it. The root names the test runner and points to the nearest file; src/ adds the no‑mocks constraint on top and overrides nothing above it. Reporails checks exactly this as child-nested-instructions (CORE:S:0011): a nested instruction file has to build on its parent and hold consistent with what the parent already says, so a subtree file that quietly reverses a root rule is a defect the check catches. Progressive disclosure changes when the rule loads while leaving what the rule says untouched. The strictness is intact; only the standing cost falls. The check and the gate from the earlier pieces still sit beside the rule, unaltered, refusing to pay for it on the turns it has nothing to say.
The Rule With No Path
Every mechanism in the last two sections scopes a rule by path: the nested CLAUDE.md, the paths: glo
Comments
No comments yet. Start the discussion.