Spec file
Codex AGENTS.md discovery chain: check global, project and override files
Check which AGENTS.md files Codex reads, in what order and under which size limit, with an offline model of the documented rules.
When Codex starts it builds an instruction chain in a fixed order: a global file from the Codex home directory first, then files from the project root down to the current directory. When an edit to AGENTS.md has no effect, the usual causes are an AGENTS.override.md in the same directory, an empty file, a combined size over the limit, or a CODEX_HOME that points somewhere else. This guide turns the documented rules into an offline script and uses throwaway directories to check which files enter the chain and in what order. The expected result is a load list for your own repository. The script models the documented rules only; Codex was not started and no model was called.
- Verified
- · Not tested by us
- Applies to
- Codex Applies to Codex. The page states no version floor; rely on the 2026-11-05 check and the behavior on your machine.[1]
- Tools
File locations
| Path | Scope | How it is read |
|---|---|---|
~/.codex/AGENTS.override.md | Temporary global override; when present and non-empty, AGENTS.md at the same level is not read. | Read at startup; the Codex home defaults to ~/.codex and CODEX_HOME changes it.[1] |
~/.codex/AGENTS.md | Global default instructions for every repository. | Read when there is no non-empty AGENTS.override.md.[1] |
./AGENTS.md | Project instructions at the repository root. | Checked at each directory from the project root to the current directory, at most one file per directory.[1] |
./services/payments/AGENTS.override.md | A subdirectory override; in the official example it makes AGENTS.md in the same directory ignored. | The closer to the current directory, the later it appears in the combined prompt and the higher its precedence.[1] |
~/.codex/config.toml | Holds project_doc_fallback_filenames and project_doc_max_bytes. | Restart Codex or start a new run after changing it.[1][2] |
When to use
- You edited AGENTS.md and Codex behavior did not change, and you need to know which files entered the chain.[1]
When not to use
- You need instruction loading for Claude Code or Cursor, or intend to use an instruction file as an enforced permission control.[1]
Documented rules
- Global: in the Codex home directory (default ~/.codex, changed by CODEX_HOME) look for AGENTS.override.md first, otherwise AGENTS.md, and use only the first non-empty file.
- Project: walk from the project root (typically the Git root) down to the current directory; with no project root only the current directory is checked. In each directory check AGENTS.override.md, then AGENTS.md, then the names in project_doc_fallback_filenames, and include at most one file per directory.
- Merge: files are joined from the root down with blank lines, and files nearer the current directory appear later and therefore take precedence. The docs also say empty files are skipped and that nothing more is added once the combined size reaches project_doc_max_bytes (32 KiB by default).
- Rebuild: the chain is rebuilt at the start of every run (usually once per TUI session), so there is no cache to clear by hand.
Model the order with a script
The script below does not depend on Codex. It takes a project root, a current directory and an optional byte limit, and prints whether each file enters the chain under the rules above. The Codex home comes from CODEX_HOME and fallback names from the FALLBACKS environment variable (comma separated); the real config.toml is not read. It needs Node.js, and is intended to run against a throwaway tree you build yourself.[1]
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const [root, cwd, limitArg = "32768"] = process.argv.slice(2);
const limit = Number(limitArg);
const fallbacks = (process.env.FALLBACKS ?? "").split(",").filter(Boolean);
const home = process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
const firstNonEmpty = (dir, names) => {
for (const name of names) {
const file = path.join(dir, name);
if (fs.existsSync(file) && fs.statSync(file).size > 0) return file;
}
return null;
};
const dirs = [];
for (let dir = path.resolve(cwd); ; dir = path.dirname(dir)) {
dirs.unshift(dir);
if (dir === path.resolve(root)) break;
if (dir === path.dirname(dir)) throw new Error("cwd is not inside root");
}
const chain = [firstNonEmpty(home, ["AGENTS.override.md", "AGENTS.md"])];
for (const dir of dirs) chain.push(firstNonEmpty(dir, ["AGENTS.override.md", "AGENTS.md", ...fallbacks]));
let total = 0;
for (const file of chain.filter(Boolean)) {
if (total >= limit) { console.log("not added (limit reached):", file); continue; }
total += fs.statSync(file).size;
console.log(total > limit ? "added, crosses limit:" : "added:", file, total);
}
The official example has a global file, a root AGENTS.md, and services/payments with both AGENTS.md and AGENTS.override.md. I built the same tree in a throwaway directory and ran four cases. The global AGENTS.md contains "# global" (9 bytes), the root AGENTS.md contains "# root" (7 bytes), and the override in payments contains "# payments override" (20 bytes), with the AGENTS.md next to it expected to be ignored. A search directory has an empty override, the fourth case removes the root AGENTS.md and models a fallback name with FALLBACKS=TEAM_GUIDE.md, and the third case sets the limit to 20 bytes. The number at the end of each line is the running total in bytes, not a single file size, and the throwaway path is replaced by TMP. The docs do not say whether the file that crosses the limit is truncated or dropped, so the script only marks it as crossing.[1]
== case 1: payments, no fallbacks
added: TMP/codex-home/AGENTS.md 9
added: TMP/repo/AGENTS.md 16
added: TMP/repo/services/payments/AGENTS.override.md 36
== case 2: search (empty override skipped by script assumption)
added: TMP/codex-home/AGENTS.md 9
added: TMP/repo/AGENTS.md 16
added: TMP/repo/services/search/AGENTS.md 30
== case 3: limit 20 bytes
added: TMP/codex-home/AGENTS.md 9
added: TMP/repo/AGENTS.md 16
added, crosses limit: TMP/repo/services/payments/AGENTS.override.md 36
== case 4: only fallback name at root, FALLBACKS=TEAM_GUIDE.md, root without AGENTS.md
added: TMP/codex-home/AGENTS.md 9
added: TMP/repo/TEAM_GUIDE.md 22
added: TMP/repo/services/payments/AGENTS.override.md 42
What is not covered
- Whether an empty AGENTS.override.md lets that directory fall back to AGENTS.md: the docs say empty files are skipped and that the global scope uses the first non-empty file. The script lets an empty override fall through on that basis. This is an inference to confirm against the real Codex.
- How the size limit is worded: the AGENTS.md guide says nothing more is added once the combined size reaches the limit, while the Advanced Configuration page describes project_doc_max_bytes as how much is read from each AGENTS.md file. The two wordings do not fully agree. The script follows the guide's combined-size wording, so do not use it to decide where truncation happens.
- Project root detection: the docs say only "typically the Git root". The script takes the root as an argument and detects nothing, and it ignores trust state and profiles.
- The script does not parse config.toml. After changing project_doc_fallback_filenames, restart Codex and check the real loading result.
Verify in the product and stop conditions
One verification method in the docs is to run codex --ask-for-approval never "Summarize the current instructions." from the repository root and see whether the answer cites the global and project files in precedence order. For "wrong guidance appears", look first for an override file in the Codex home or a higher directory; for "nothing loads", confirm the file is non-empty and you are in the intended repository. This guide did not run those commands. When the load list differs from expectations, rule out causes in this order: override files, empty files, CODEX_HOME, fallback name typos, size limit, and only then change the instruction text; do not keep lengthening the root file to force one rule to apply. Only the official description was checked here: Codex was not started, no real instruction file was read and no model was called.[1]
Sources
All links are official pages; verification dates are listed per source, and links point to the version checked.