← Pi Agents

Context Admission Is Application Policy

A file existing, being discovered, being admitted and reaching the model are four different facts; this chapter separates them, shows which are Pi's rules and which belong to the application, and runs the discovery rules.

Compaction summarized away the schema you explained, and the model asks you to restate it. You will not restate it. The fix is a file, because loaded instructions travel with every request while a conversation gets compressed. Chapter 23 ended there.

But “put it in a file” is the start of the question, not the end. A file on disk can be ignored, replaced, filtered out, or supplied by someone other than you. This chapter is about the four separate facts hiding in the sentence the model read my AGENTS.md, and about who decides each one.

Four facts, not one

EXISTS       the file is on disk, somewhere
DISCOVERED   Pi found it, by walking directories
ADMITTED     something decided it should be used
IN CONTEXT   it is in the request the model actually receives

For a disk-backed file, these are four separate questions, not four guarantees. They are not even mandatory sequential stages: an SDK application can supply a virtual instruction file that was never on disk and never found by directory discovery. The default file-discovery path and the host-controlled path need to be distinguished.

Step Decided by Rule
Exists whoever created the file applies to disk-backed files, not virtual resources
Discovered the configured resource loader Pi’s default loader checks the agent directory and the working directory’s ancestry
Admitted loader configuration and file-selection rules same-directory overrides, --no-context-files, or a host-provided override
In context the request-building pipeline admitted instructions are incorporated into the request unless a later customization changes them

The usual debugging mistake is to check the first step and assume the other three. A file that exists is not necessarily one the model has read.

Discovered: a walk up, not a scan down

configuration.md: Pi loads context files from “the agent directory, the working directory, and its parent directories”, and a context file “applies whenever Pi runs in its directory or anywhere below it”. The accepted names are AGENTS.override.md, AGENTS.md, AGENTS.MD, CLAUDE.md and CLAUDE.MD. This lets Pi discover instruction files originally written for another tool, but discovery alone does not establish that their contents or tool-specific conventions work unchanged.

Notice the direction of the project-directory search: it walks up from Pi’s working directory, not recursively into sibling or child directories. The agent directory is a separate, explicitly checked location. Thus a rule in repo/docs/AGENTS.md does not apply to a run started in repo/src/billing/, even though both directories are in the same repository. The fixture below tests that distinction:

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");

The same fixture tree, started from repo/src/billing, discovers the user’s agent-directory file and the repository root’s, and does not discover the sibling docs file. The practical consequence is about where you start Pi: the working directory chooses which rules exist for the run.

Admitted: overrides are local, and filtering is somebody’s decision

Once discovered, a file can still be replaced. configuration.md has the narrow rule: “An AGENTS.override.md replaces AGENTS.md or CLAUDE.md only in the same directory. It does not suppress context files from the agent directory or other directories.”

    flowchart TD
    R["repo AGENTS.md"] -->|inherited below| M
    S["repo/src AGENTS.md"] -->|inherited below| M
    O["repo/src/billing AGENTS.override.md"] -->|replaces the AGENTS.md beside it only| M
    A["agent dir AGENTS.md"] -->|applies to every working directory| M
        M["context files admitted by the default loader"]
  

The test confirms both halves: the override is admitted, the AGENTS.md beside it is not, and the repository root’s file still is. An override is a sibling replacement, not a global kill switch.

Beyond Pi’s own rules, three other parties can decide admission:

  • You, at the command line. -nc / --no-context-files turns discovery off for one invocation. It is the only documented switch for it.
  • An extension, at request construction. before_agent_start can add, replace, or delete prompt sections, as chapter 18’s run_checks example does. That is downstream prompt customization, not the same operation as selecting which AGENTS.md files the resource loader admits.
  • The host, if the host is an SDK application. This is the one that changes the picture, and it is the reason for this chapter’s title.

In an application, the host owns admission

When you embed Pi with createAgentSession() (chapter 34), the files that reach the model are whatever the resource loader returns. DefaultResourceLoader gives you Pi’s standard discovery, with an override hook: agentsFilesOverride receives what was found and returns what to use. The coding agent’s own SDK example adds a virtual file that is not on disk at all.

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" },
	],
}),

In this test fixture the override removed the entry whose content contained user rules and supplied a virtual file. That establishes what the loader returns; the three loader tests described below do not inspect a provider request. The content-substring filter is useful for a fixture, but would be a fragile production policy: unrelated files containing that phrase would also disappear. Use an explicit path-based selection or allowlist for real admission decisions. For a multi-user host, the important design choice is to control the resource loader rather than inherit arbitrary directory contents. sdk.md draws the matching line: use DefaultResourceLoader for standard discovery with selected overrides, and a custom ResourceLoader when the application owns resource storage and discovery.

That is why admission is an application policy. In the terminal, Pi’s configured loader normally selects the context files. In an embedded agent, the host can keep or replace those discovery rules and choose the returned resources. The same disk file can exist, be discovered, and still be excluded. This selection is not a complete security boundary: other prompt inputs, extensions, tool results, and user messages can still influence what the model receives.

The system-prompt files are a different mechanism

SYSTEM.md replaces Pi’s default system prompt and APPEND_SYSTEM.md adds to it, each at user level (<agent-dir>) and project level (.pi/). Two rules from configuration.md set them apart from context files:

For SYSTEM.md and APPEND_SYSTEM.md, the trusted project file takes precedence over the corresponding agent-directory file. Files with the same name are not combined.

Not combined: a project APPEND_SYSTEM.md replaces the personal one. If you want both, say so in one file. And these files are part of the project-trust gate while context files are not. That asymmetry, what it admits and what it does not, is chapter 8’s subject and is not repeated here.

In context: the part nobody checks

The last step is the one you must verify separately. In Pi’s default request-building path, admitted context instructions form part of the system prompt, independently of conversation history. They therefore survive ordinary history compaction, but a host or extension can still customize a later request. Whenever the instructions are included, they consume context-window tokens; prompt caching may change the billing cost, not the context they occupy. A large AGENTS.md remains a context cost that compaction does not remove. See chapter 4.

“Every request” does not mean “re-read every request”

The distinction is worth stating precisely, because the wrong version of it is a common belief and this chapter previously asserted it. The default resource loader reads discovered files when it loads, and getAgentsFiles() returns that cached list. It is not re-reading the file for each request.

Observed on 1.0.4: start a real session with AGENTS.md reading REVIEW_CONTEXT_BEFORE, send a request, overwrite the file with REVIEW_CONTEXT_AFTER, then send another request with no reload. The second provider request still contained REVIEW_CONTEXT_BEFORE and did not contain the new text. DefaultResourceLoader.getAgentsFiles() returned the cached list throughout; reload() is what loads from disk and assigns a new one.

So the two facts are:

survives compaction      yes. Compaction rewrites conversation history, not the
                         instructions the loader is holding.
sees your latest edit     in an existing default-loader session, after /reload
                          or a host reload; a fresh session loads current files.

Both statements are consistent. “A file is read on every request” is wrong, and the error is easy to make because the practical advice — put it in a file, not in the conversation — is right for the reason given in the first row.

This describes DefaultResourceLoader. A host that implements ResourceLoader itself decides when to re-read, and an SDK application free to do so can make the stronger promise true.

To inspect admission, use the startup header’s resource list or, in an SDK host, inspect loader.getAgentsFiles(). An existing default-loader session needs /reload to pick up edits. Neither the header nor the loader list proves what a particular provider request contained after prompt construction and extension hooks. For that fourth fact, capture or inspect the actual request sent to the provider.

Choosing where a fact belongs

Four destinations, and they are not interchangeable.

A context file when the fact is true of the code: “this service uses integer cents, never floats”. When discovered and admitted, it survives compaction and is reviewable in version control. It will not load in every session regardless of working directory, loader policy, or --no-context-files.

A skill when the instructions apply only to particular tasks. A discovered, enabled skill’s description is advertised to the model; its body is loaded if the model selects it or the user explicitly invokes it. Selection is not guaranteed (chapter 13).

An extension when the fact must be computed, such as a prompt section derived from which tools are active (chapter 18).

A prompt template when it is something you say once, on request.

The mistake to avoid is relying solely on the first user message for a project-wide invariant. Compaction may preserve that rule in its summary, paraphrase it, or omit it. Keeping the authoritative rule in a reviewed, admitted context file avoids depending on the summarizer to retain it.

A boundary it is not

A context file is not a security boundary. Declining project trust is still meaningful: it can prevent project-local executable configuration from loading, but it does not prevent the default loader from reading context files. security.md warns that files, comments, instructions, command output, and model responses can steer the model through prompt injection; granting trust does not make their contents safe. Running Pi in an unfamiliar repository may therefore introduce its AGENTS.md into the prompt even before project trust is granted. An embedding host should explicitly select its admitted instructions, while enforcing sensitive actions outside the prompt itself.

What you can and cannot claim

Documented: discovery from the agent directory and the working directory’s parents, the accepted file names, the override rule, SYSTEM.md and APPEND_SYSTEM.md precedence, --no-context-files, and agentsFilesOverride and the loader distinction (configuration.md, sdk.md, and the SDK examples, 1.0.4).

Observed in the cited loader tests: three tests in examples/ch28-admission/ against pi-coding-agent 1.0.4 and a temporary directory tree: a sibling directory is not discovered; an override replaces only a same-directory file; and a host resource override removes and adds entries. These tests use the default loader without a model, so they establish resource selection, not provider-visible context. The earlier two-request observation of REVIEW_CONTEXT_BEFORE surviving an on-disk edit is a separate session-level claim and should have its own reproducible test cited before being treated as part of this suite.

Proposed: the four-fact split, and the framing of admission as application policy.

Next

A file can be correct, discovered and admitted, and the run can still fail, because the other half of an agent’s reliability is not what it was told but what happens when something goes wrong underneath it.

Chapter 29 is about failures: retried, recovered and stopped.