Separate discovery from admission in the context pipeline
A file can exist, be discovered and admitted, and never reach the model — see who owns each of the four steps with a temporary fixture tree.
“the model read my AGENTS.md” hides four facts: the file exists, Pi discovered it, something admitted it, and it is in the request the model receives. Each fails on its own and each has a different owner. The excerpts below ran as the Chapter 28 tests against a temporary tree — default loader, no model.
Discovery: a walk up, not a scan down
Starting Pi in repo/src/billing, discovery walks up from the working
directory to the agent directory — never sideways or down, so
repo/docs/AGENTS.md does not apply to work started in repo/src/billing
even though both are in one repository:
started in repo/src/billing
agent/AGENTS.md discovered — agent directory
repo/AGENTS.md discovered — a parent
repo/src/billing/AGENTS.md discovered — working directory
repo/docs/AGENTS.md not discovered — a siblingThe test asserts the walk-up rows:
const loader = new DefaultResourceLoader({ cwd: billing, agentDir });
await loader.reload();
const got = names(loader.getAgentsFiles().agentsFiles);
assert.ok(got.includes("AGENTS.md=user rules"), "the agent directory applies everywhere");
assert.ok(got.includes("AGENTS.md=repo rules"), "a parent directory is inherited downward");
assert.ok(!got.some((g) => g.includes("docs rules")), "a sibling directory is never discovered");Admitted: an override replaces only what is beside it
An AGENTS.override.md replaces the AGENTS.md or CLAUDE.md in the same
directory and only that one. It is a sibling replacement, not a global kill
switch — the repo-root and agent-directory files survive it. The cited test
asserts both halves.
The host owns admission
In an embedded application the files the model sees are whatever the resource
loader returns. agentsFilesOverride receives what was found and returns what
to use — here the host drops the user file and adds a virtual one:
const loader = new DefaultResourceLoader({
cwd: billing,
agentDir,
agentsFilesOverride: (current) => ({
agentsFiles: [
...current.agentsFiles.filter((f) => !f.content.includes("user rules")), // never admit the user's own file
{ path: "/virtual/AGENTS.md", content: "policy supplied by the host" },
],
}),
});Expected outcome
| Step | Owner | Fixture result |
|---|---|---|
| Exists | whoever wrote the repository | all five files are on disk |
| Discovered | Pi’s directory walk | agent, repo and billing files; docs never |
| Admitted | Pi’s rules, you, an extension, or the host | override replaces only billing’s AGENTS.md; the host swaps the user file for a virtual one |
| In context | the request builder | the loader’s list, on every request |
The working directory chooses which rules exist, and the default loader
caches what it reads until reload(); a custom ResourceLoader decides for
itself.
Mechanism and limitations
Documented: configuration.md pins discovery (the agent directory, the
working directory and its parents), the accepted names, the local override
rule and --no-context-files; sdk.md pins DefaultResourceLoader and
agentsFilesOverride (1.0.4). Observed: the three tests of
admission.test.ts passed against the real loader in a temporary tree with
no model: siblings are never discovered, an override replaces only the file
beside it, and a host override both drops and adds files. Proposed: the
four-step split and admission as application policy are this book’s framing.
This is not a security boundary: context files load without project trust, so
an unfamiliar repository’s AGENTS.md is admitted before anything is
approved. If the instructions matter, the host must admit them itself; no
model is involved here.
Understand this example
Copy this prompt into your AI tool. No code runs here.
Apply this example
Copy this prompt into your AI tool. No code runs here.