Reference — How Six AI Coding Harnesses Assemble Context
What is in the model's context window at the start of a turn, where each piece comes from, and what silently fails. Claude Code, Claude Desktop, Cursor, opencode, Codex CLI, Gemini CLI.
This page is the ground truth behind every module prompt that says "find the always-on instruction file this harness loads every session." It answers that for six harnesses, with real paths.
Researched 2026-08-30 against primary sources. Every claim is marked CONFIRMED with the page or source file it came from, or UNCONFIRMED. Where a doc contradicts the shipped source, the source wins and the contradiction is recorded — four such cases appear below, and they are the most valuable rows on the page.
Read the marks. A gap named is more useful than a plausible guess, because this file is used to write commands that must actually work.
Before you trust any of it: three doc trees moved in 2026
Older guides point at URLs that now 404 or redirect. If your agent fetches those, it gets nothing and says so, or gets a redirect and does not notice.
| Harness | Old location | 2026 location |
|---|---|---|
| Claude Code | code.claude.com/docs/en/docs/claude-code/<page> (404s) | code.claude.com/docs/en/<page> · index at /docs/llms.txt |
| Codex CLI | developers.openai.com/codex/* (308s) · repo docs/ now stubs | learn.chatgpt.com/docs/* |
| Gemini CLI | docs/cli/configuration.md, docs/core/memport.md, docs/tools/memory.md (404) | docs/{cli,core,reference,tools,hooks,extensions}/ |
| Cursor | /docs/context/* (301s) | /docs/rules, /docs/mcp, /docs/agent/tools/search |
1. Claude Code (CLI)
Measured against v2.1.251 on Linux/WSL2 in addition to the docs.
A. Context assembly
Load order, broadest to most specific (CONFIRMED, /docs/en/memory):
| Scope | Path |
|---|---|
| Managed policy | macOS /Library/Application Support/ClaudeCode/CLAUDE.md · Linux+WSL /etc/claude-code/CLAUDE.md · Windows C:\Program Files\ClaudeCode\CLAUDE.md |
| User | ~/.claude/CLAUDE.md |
| Project | ./CLAUDE.md or ./.claude/CLAUDE.md |
| Local | ./CLAUDE.local.md |
These concatenate, they do not override — "All discovered files are concatenated into context
rather than overriding each other… ordered from the filesystem root down to your working directory."
CLAUDE.local.md is appended after CLAUDE.md within each directory and is not deprecated
(CONFIRMED — it is still documented as live).
CLAUDE.md is delivered as a user message after the system prompt, not as part of it (CONFIRMED). Output styles are the thing that modifies the system prompt.
Upward walk, lazy downward. It loads from cwd "and every directory above it." Subdirectory files
load only when Claude reads a file in that directory with the Read tool — not at launch, and
not when writing or creating files there (CONFIRMED, /docs/en/debug-your-config).
@-imports: @path/to/import, max four hops, relative paths resolve against the file
containing the import, @~/.claude/foo.md works. Imports expand at launch — they do not save
context.
.claude/rules/*.md (2026 mechanism): without paths: frontmatter they load at launch at the
same priority as .claude/CLAUDE.md, user rules before project rules. With paths: they load only
when a matching file is read. Brace-expansion budget is 1,000 patterns / 4 MiB; over-budget patterns
are used unexpanded and match nothing.
Always vs on-trigger (CONFIRMED, /docs/en/context-window — token counts are the doc's own):
system prompt (~4,200) · auto memory MEMORY.md (~680) · environment info (~280) · MCP tool names
only (~120, schemas deferred) · skill descriptions only · user then project CLAUDE.md. Full
skill bodies load only on use, and once loaded stay in the conversation across later turns.
Documented limits:
| Limit | Value |
|---|---|
| CLAUDE.md hard limit | 4 MiB — a larger file is skipped entirely |
| CLAUDE.md advisory | under 200 lines |
Auto memory MEMORY.md | first 200 lines or 25 KB |
| Skill description in listing | truncated at 1,536 chars |
| Skill listing budget | 1% of context window, fallback 8,000 chars |
| MCP tool description | 2 KB each |
| Number of skills | no documented cap — the budget is the constraint |
Compaction. Re-injected from disk: project CLAUDE.md, unscoped rules, auto memory, the plan.
Files read come back for up to five files. Invoked skill bodies return at 5,000 tokens each,
25,000 total. Hook-added context is summarized away. The skill listing is the exception nobody
expects: it is not re-injected after /compact, so afterwards Claude can auto-invoke only skills it
already used this session (CONFIRMED). "Microcompact" appears in no page fetched — UNCONFIRMED.
Showing what is loaded: /context [all] is the answer — a colored grid breaking out system
prompt, MCP tools, subagents with the source each loaded from, memory files and skills. /memory
lists locations (including files that do not exist yet), not what loaded. /status shows active
settings sources. /doctor is now a bundled skill, not a screen.
B. I/O surface
Permission modes (CONFIRMED against the binary): default (alias manual) · acceptEdits ·
plan · auto · dontAsk · bypassPermissions. Evaluation is deny → ask → allow, first match
wins; specificity does not reorder it, so a broad Bash(aws *) deny beats a narrow
Bash(aws s3 ls) allow.
Two syntax traps worth memorizing: Bash(ls *) with a space does not match lsof, while
Bash(ls*) does. And in Read/Edit rules /path is relative to the settings source, not the
filesystem root — //path is the absolute form.
Sandboxing exists on macOS (Seatbelt), Linux and WSL2 (bubblewrap); native Windows and WSL1 are
unsupported. Default write is cwd plus --add-dir; default read is the entire computer except
denied paths, which still allows ~/.aws/credentials and ~/.ssh/.
MCP scopes: local (default, in ~/.claude.json) · project (.mcp.json at the repo root)
· user. Transports http / sse (deprecated) / stdio / ws. Project servers require a one-time
approval per machine.
Subagents: .claude/agents/ and ~/.claude/agents/, each with its own context window. Note
the inversion — for skills, personal overrides project; for subagents, project outranks user.
Built-in Explore and Plan skip CLAUDE.md and git status; custom subagents load it normally.
Hooks: 33 events (CONFIRMED, /docs/en/hooks), including InstructionsLoaded, which fires when
a CLAUDE.md or rules file loads and is the documented way to "log exactly which instruction files are
loaded, when they load, and why." Exit 2 is an absolute block that overrides JSON and cannot be
overridden even by permissionDecision: allow; stderr on exit 0 goes to the debug log only, never
to Claude. PreToolUse fires before any permission check in every mode including
bypassPermissions — but a hook allow cannot loosen a settings deny.
Headless: claude -p, --output-format text|json|stream-json, --json-schema for structured
output, exit 0 / non-zero / 143 on SIGTERM. --bare skips discovery of hooks, skills, commands,
subagents, plugins, MCP and CLAUDE.md, and never reads the keychain; it "will become the default for
-p in a future release." SDK packages are @anthropic-ai/claude-agent-sdk and claude-agent-sdk
— not @anthropic-ai/claude-code.
C. State on disk
Verified directly on a live install as well as in the docs.
~/.claude/CLAUDE.md user memory
~/.claude/settings.json permissions, hooks, env, model
~/.claude/settings.local.json written BY Claude Code on "don't ask again"
~/.claude/.credentials.json mode 0600 — Linux/WSL/Windows (macOS uses Keychain)
~/.claude/projects/<munged-cwd>/<uuid>.jsonl full transcript: every message, tool call, result
~/.claude/projects/<project>/memory/MEMORY.md auto memory, keyed by GIT REPO (worktrees share it)
~/.claude/history.jsonl every prompt ever typed, with timestamps
~/.claude/{skills,agents,plugins,shell-snapshots,file-history,state}/
~/.claude.json MCP servers, trust decisions, OAuth account — NOT settings~/.claude/todos/, statsig/ and logs/ are legacy and no longer written — tooling that reads
them is reading a vestige. CLAUDE_CONFIG_DIR relocates all of it.
Never commit: .claude/settings.local.json, CLAUDE.local.md, .credentials.json, anything
under ~/.claude/. Note the sharp edge: Claude Code adds settings.local.json to your global git
excludes, not the repo's .gitignore — a hand-created file is your problem. And transcripts are
not encrypted at rest: if a tool reads a .env, that value is now in the session .jsonl.
D. The most common silent misconfiguration
A skill whose description names a topic rather than an occasion never fires. It appears in
/skills, is loaded, listed, and inert, with zero error output. The fix per the docs is to include
"keywords users would naturally say."
Four more that fail just as quietly:
- Skill descriptions are silently truncated — or dropped. When the listing overflows its 1% budget, Claude Code "drops descriptions starting with the skills you invoke least." A skill that worked can stop working purely because you added other skills.
- Malformed skill frontmatter loads the body with empty metadata —
/skill-namestill works, auto-invocation is dead. ~/.claude.jsonis not a settings file.permissions,hooksandenvput there are simply never read.- In
-pand the SDK the trust gate fails open in the wrong direction: the repo'spermissions.allowis silently not applied, while its hooks,env, and.mcp.jsonservers all run.
2. Claude Desktop / claude.ai with connectors
The odd one out: no filesystem of its own, no headless mode, and no way to see what is in context.
A. Context assembly
Three personalization layers, not four (CONFIRMED, support.claude.com): Instructions for Claude (account-wide) · Project instructions (per project) · Skills.
Styles is retired. The article 404s and it is absent from the 79-article collection index; Skills is the successor. Any guide describing Styles is pre-2026.
Precedence between the three is NOT DOCUMENTED (CONFIRMED-NEGATIVE). The personalization page says only that you "can use these features independently or in combination" and never names a conflict winner. Any hierarchy you have seen asserted is inference.
Memory is persistent and stored as individual topics, not a file you can edit — global plus a per-project space. On by default for Free/Pro/Max, off by default for Team/Enterprise. Export is copy/paste text, not a download. Incognito chats write nothing to history or memory.
Projects silently switch to RAG. Retrieval "automatically activates when your project approaches or exceeds the context window limits" — no manual step, up to 10x capacity. The mechanism changes underneath you: instead of whole files in context, Claude uses a project knowledge search tool. There is a binary indicator; a percentage meter is not documented (UNCONFIRMED).
Context window, 2026: Opus 5 and Sonnet 5 run 1M tokens on paid plans; Opus 4.8/4.7/4.6 and Sonnet 4.6 run 500K; others 200K. Free tier is unpublished.
Long conversations now summarize rather than hard-stop — "allows conversations to continue indefinitely in most cases." This is a change from the old behavior, and it is gated on code execution being enabled. Without it you get the old hard error instead.
There is no /context equivalent, no token indicator, no context inspector (CONFIRMED-NEGATIVE).
The only signals are the transient "organizing its thoughts" state and the binary RAG indicator.
B. I/O surface
Connectors are remote MCP servers that run from Anthropic's cloud — "your server must be reachable over the public internet," so a firewalled server will never connect. Local servers come in as Desktop Extensions, which are Desktop and Claude Code only — not web, not mobile.
Connection is account-global but loading is per-conversation, with three modes: Auto (default, Claude decides), Always available, On demand. Guidance is under 10 connectors → Always, 10–30 → Auto, 30+ → On demand. "Each conversation setting is independent."
.dxt is now .mcpb and the repo moved out of Anthropic's org — github.com/anthropics/mcpb
301s to modelcontextprotocol/mcpb. The rename date is unpublished, and the on-disk install path
for a .mcpb is not documented anywhere (UNCONFIRMED).
macOS ~/Library/Application Support/Claude/claude_desktop_config.json
Windows %APPDATA%\Claude\claude_desktop_config.json
Linux NOT DOCUMENTED — though a Linux beta exists (apt, binary `claude-desktop`)Hooks exist in the extensibility model but are inert in chat. Verbatim: "Hooks and sub-agents run only in Cowork, so they appear grayed out in chat."
There is no headless mode (CONFIRMED-NEGATIVE). The programmatic path is the Agent SDK or
claude -p.
C. State on disk
Conversations are server-side on consumer claude.ai and Desktop. Logs are at
~/Library/Logs/Claude/mcp*.log (macOS) or %APPDATA%\Claude\logs. Org plugins live in
/Library/Application Support/Claude/org-plugins/ and C:\Program Files\Claude\org-plugins\.
Beware paths beginning Claude-3p/ in the docs — those are the third-party/BYO-inference build, not
consumer Desktop.
D. The most common silent misconfiguration
Editing claude_desktop_config.json without fully quitting the app. Verbatim: "fully quit and
reopen; closing the window is not enough." Closing the window looks identical to a restart and loads
nothing.
The 2026 runner-up has inverted: Desktop now ships its own Node.js, so a bundled extension can
work while a hand-configured stdio server on the same machine fails for PATH reasons — stdio servers
"inherit only a limited subset of environment variables," and a GUI app has no shell profile. Use
absolute paths and an explicit env block.
Third: project knowledge silently switching to RAG, which degrades any question needing whole-document reasoning without ever raising an error.
3. Cursor (and Cursor CLI)
Three 2026 changes invalidate most existing guides.
A. Context assembly
The CLI binary is agent, not cursor-agent (CONFIRMED — cursor-agent appears zero times in
the current CLI docs; the installed build on this machine still uses the old name, so verify with
agent --version).
Rules live in .cursor/rules/*.mdc. A plain .md file there is silently ignored because it
has no frontmatter — the docs' own example labels it # Ignored (wrong extension).
Three frontmatter keys only — description, globs, alwaysApply:
alwaysApply | description | globs | Behavior |
|---|---|---|---|
true | — | — | Always included. Globs and description are ignored. |
false | — | provided | Auto-attached when a matching file is in context |
false | provided | — | Agent reads description, pulls it in when relevant |
false | — | — | Only via @-mention |
Globs are comma-separated. Precedence is Team → Project → User, all merged, "earlier sources take precedence when guidance conflicts." Size guidance is explicit: keep rules under 500 lines.
AGENTS.md is supported at root and nested. CLAUDE.md is read by the CLI and applied as rules.
Embeddings are gone. codebase-indexing now redirects to agent/tools/search: discovery is
Instant Grep, "a custom search engine that outperforms ripgrep," plus an Explore subagent with
its own context window. The word "embedding" appears nowhere in current docs.
Memories is retired — /docs/context/memories redirects to /docs/rules. Do not plan around it.
.cursorrules status: UNCONFIRMED as formally deprecated, on absence-only evidence — zero
occurrences across every current page, and not among the four rule types. Treat it as unsupported.
Context usage is visible via the CLI's /context; manual compaction is /summarize (/compact and
/compress are aliases). The preCompact hook receives exact numbers but is observational only.
B. I/O surface
🔴 .cursorignore is not a security boundary. Verbatim: "The terminal and MCP server tools used
by Agent cannot block access to code governed by .cursorignore." It blocks Agent/Tab/Inline Edit
and @-mentions; it does not block cat, grep, any shell command, or any MCP tool. Real
enforcement is permissions.json plus the sandbox.
MCP at .cursor/mcp.json and ~/.cursor/mcp.json; stdio, SSE and Streamable HTTP; Roots,
Elicitation and Apps all supported; ${env:NAME} interpolation for secrets. The historical ~40-tool
cap has no mention in current docs — UNCONFIRMED whether raised, removed, or merely undocumented. Do
not assume it is gone; verify with agent mcp list-tools <id>.
Subagents: built-in Explore, Bash and Browser, plus custom ones in .cursor/agents/ — and it also
reads .claude/agents/ and .codex/agents/, with .cursor/ winning.
Hooks in .cursor/hooks.json, auto-reloaded, with 18 agent events plus Tab and workspace events.
Blocking is {"permission": "deny"} or exit code 2 ("This matches Claude Code behavior").
🔴 Hooks fail OPEN by default — a crash, timeout or invalid JSON lets the action through unless
failClosed: true.
Headless: agent -p, --output-format text|json|stream-json.
🔴 -p alone does not write files — "Without --force, changes are only proposed, not applied."
Permission tokens are Shell(cmd), Read(glob), Write(glob), WebFetch(domain),
Mcp(server:tool); deny beats allow. Exit codes beyond zero/non-zero are UNCONFIRMED.
C. State on disk
~/.cursor/ holds cli-config.json, mcp.json, hooks.json, permissions.json, sandbox.json,
rules (machine-local, not synced), agents/, skills/, worktrees/. Per-OS chat history and
credential cache paths are UNCONFIRMED — no such table exists in current docs.
Never commit .cursor/mcp.json with hardcoded secrets, or .cursor/keys. Note that
.cursor/cli.json and .cursor/permissions.json are meant to be committed.
D. The most common silent misconfiguration
Omitting alwaysApply turns the rule into a manual-only rule. With no alwaysApply, no
description and no globs, it is "included only when you @-mention the rule." The file exists, is
valid, shows up in the UI, and never loads.
Then the same failure Claude Code has: an "Apply Intelligently" rule whose description names a topic rather than an occasion is never selected. The docs' own good example is occasion-shaped — "Security specialist. Use when implementing auth, payments, or handling sensitive data."
Third, and purely mechanical: globs: *.ts matches only root-level .ts files, not src/foo.ts.
You need **/*.ts.
4. opencode
Open source, so the source is authoritative — and it contradicts its own published docs in four
places. Findings below are from sst/opencode@dev, v1.18.25 (2026-08-28).
A. Context assembly
Resolution order from packages/opencode/src/session/instruction.ts:
- Global:
~/.config/opencode/AGENTS.md, else~/.claude/CLAUDE.md— it breaks on the first that exists, so the two are mutually exclusive. - Project: for each of
AGENTS.md,CLAUDE.md,CONTEXT.mdin order,findUpfrom cwd to the git worktree root; on the first name with any match it adds all matches and stops. The source comment: "The first project-level match wins so we don't stack AGENTS.md/CLAUDE.md from every ancestor." Name precedence beats proximity. - The
instructionsconfig array, appended after.
🔴 DOCS-WRONG #1. opencode.ai/docs/rules says opencode "does not automatically scan
subdirectory AGENTS.md files." The source implements lazy nested loading: when the read tool
reads a file, it walks upward from that file's directory and attaches any instruction file found,
deduped per assistant message. Subdirectory AGENTS.md is read — on trigger, not always.
Config merges global (config.json → opencode.json → opencode.jsonc) then project files
walked up and reversed, so the config nearest cwd wins. XDG variables are honored. macOS MDM
managed preferences override everything.
Auto-compaction is on by default with no percentage threshold — it fires when tokens reach
usable(), computed from the model's limits minus a reserve. Constants are explicit in
compaction.ts (PRUNE_MINIMUM 20,000; preserve-recent clamped 2,000–15,000).
No TUI token indicator and no "show loaded context" command established — UNCONFIRMED.
B. I/O surface
Permission keys are read, edit, glob, grep, list, bash, task, external_directory,
todowrite, question, webfetch, websearch, lsp, doom_loop, skill — plus arbitrary keys,
since the schema carries a rest-record. Values are ask / allow / deny.
🔴 Matching uses findLast — the LAST matching rule wins, so catch-alls must come first. Config
parsing preserves key order specifically for this.
🔴 DOCS-WRONG #2. The docs say "most permissions default to allow" and that .env reads are
denied. The evaluator's fallback is ask; the permissiveness comes from a built-in defaults
ruleset ("*": "allow"), and .env reads are ask, not deny — anyone relying on the docs for
secret hygiene is unprotected.
🔴 DOCS-WRONG #3. The docs list a built-in Scout subagent. It does not exist in dev source.
🔴 DOCS-WRONG #4. Agent and command directories accept both singular and plural
({agent,agents}, {command,commands}, {plugin,plugins}), and nested subdirectories are scanned.
There is no URL allowlist. webfetch is typed as an action-only permission that does not even
accept patterns. Remote instructions URLs bypass the tool permission system entirely.
Plugins are .ts or .js in any config dir, with ~20 hooks including permission.ask (plugins
can auto-approve) and tool (plugins can define custom tools). Note the docs' event list
(session.created, file.edited) is a different axis from the hook names.
Headless: opencode run "prompt", --format default|json where JSON is a raw event stream,
not one document. Exit codes are only 0 and 1 — no per-failure-class codes, confirmed by
exhaustive grep. opencode serve exposes an OpenAPI 3.1 HTTP server on port 4096; the TUI itself is a
client of it.
C. State on disk
~/.local/share/opencode/auth.json credentials — NEVER COMMIT
~/.local/share/opencode/{log,project,plans,repos}/
~/.config/opencode/{opencode.json,AGENTS.md,agent[s]/,command[s]/,plugin[s]/,skills/}
~/.local/state/opencode/ lock state (in source, NOT documented)opencode auto-creates a .gitignore inside .opencode/ for plugin scaffolding only — your
opencode.json, agents, commands and plans there are meant to be committed.
D. The most common silent misconfiguration
Unknown and typo'd config keys are silently absorbed, not rejected. The schema is
StructWithRest(Struct({...}), [Record(String, Any)]), and normalize() sweeps every unrecognized
key into agent.options. A typo'd descripton: or permissions: (the correct key is singular,
permission) does not error, does not warn — it becomes an inert provider option. $schema gives
editor completion only; it is not enforced at load.
Two more, both pure silence: an AGENTS.md anywhere up-tree suppresses your CLAUDE.md entirely
(the loop breaks on the first name), and relative instructions paths are glob-walked up from
cwd, so they resolve differently depending on which subdirectory you launched from — a miss returns
empty, and a remote URL fails silently after a 5-second timeout.
5. Codex CLI (OpenAI)
The repo docs/ directory is now stubs — docs/config.md is 726 bytes, docs/sandbox.md 150.
The root AGENTS.md states the rule: "Do not add general product or user-facing documentation to the
docs/ folder." Canonical docs are at learn.chatgpt.com/docs/; findings below are cross-checked
against codex-rs source.
A. Context assembly
Load order (codex-rs/core/src/agents_md.rs):
- Global
~/.codex/AGENTS.override.md, else~/.codex/AGENTS.md— first non-empty only.AGENTS.override.mdis new in 2026. - Project, walking up from cwd to the project root (detected by
project_root_markers, default[".git"]), then concatenated root-first, cwd-last — so nearer files win by position in the prompt, not by replacing. - At most one file per directory.
🔴 An untrusted project loads no project AGENTS.md at all — the loader returns before reading
any project file, and .codex/config.toml and project hooks are skipped too. The session looks
completely normal.
~/.codex/instructions.md is confirmed REMOVED — zero hits in source as a user instruction file.
project_doc_max_bytes is 32768 (confirmed three ways: defaults.toml, config.schema.json,
DEFAULT_PROJECT_DOC_MAX_BYTES: usize = 32 * 1024).
Config precedence, highest first: CLI -c flags → project .codex/config.toml (trusted only) →
profile file $CODEX_HOME/<name>.config.toml → ~/.codex/config.toml → /etc/codex/config.toml →
defaults. Project configs are deliberately powerless over approval_policy, sandbox_mode,
model_provider, notify and otel.
Skills exist in 2026 and are not under .codex — they live at .agents/skills,
$REPO_ROOT/.agents/skills, $HOME/.agents/skills and /etc/codex/skills, with a description budget
of "at most 2% of the model's context window, or 8,000 characters." Explicit invocation is $skill.
/status, /context and /compact exist; the auto-compaction key is
model_auto_compact_token_limit.
B. I/O surface
sandbox_mode: read-only · workspace-write · danger-full-access. codex exec defaults to
read-only. approval_policy: untrusted · on-request · never — on-failure is gone,
replaced by an optional granular table.
🔴 The platform mechanism is not what older guides say. macOS uses Seatbelt; Linux and WSL2 use
bubblewrap, not Landlock+seccomp; Windows uses native Windows Sandbox. The old claim that WSL has
no sandbox is wrong — but if bwrap is not on PATH, sandboxing cannot be enforced.
Network is disabled by default in workspace-write (serde default false). Enable via
[sandbox_workspace_write] network_access = true. Sandboxed children get
CODEX_SANDBOX_NETWORK_DISABLED=1, which is the reliable in-process detector.
MCP under [mcp_servers.<id>], stdio or HTTP, startup_timeout_sec 10 and tool_timeout_sec 60.
Codex as an MCP server is UNCONFIRMED.
Web search is no longer a boolean: [tools] web_search = "cached" by default, from
disabled | cached | indexed | live.
Subagents exist — [features] multi_agent defaults true, with /agent and /subagents and
DEFAULT_AGENT_MAX_THREADS = Some(6).
Hooks are real in 2026 (not merely notify): hooks.json or inline [hooks] with
[[hooks.PreToolUse]] matchers, gated by [features] hooks.
Headless: codex exec "prompt" — progress to stderr, final message to stdout. --json emits
JSONL (thread.started, item.*, turn.completed); --output-last-message, --output-schema,
--ephemeral, and codex exec resume. Numbered exit codes are UNCONFIRMED. SDKs:
@openai/codex-sdk and, new, Python openai-codex.
C. State on disk
~/.codex/config.toml
~/.codex/auth.json credentials — NEVER COMMIT
~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl transcripts
~/.codex/history.jsonl
~/.codex/prompts/*.md custom /name prompts
$HOME/.agents/skills skills — OUTSIDE CODEX_HOME🔴 ~/.codex/log/codex-tui.log does not exist by default in 2026 — "the TUI records diagnostics
in bounded local stores by default. Set log_dir explicitly to enable a plaintext TUI log." Windows
native paths are UNCONFIRMED.
D. The most common silent misconfiguration
AGENTS.md truncation, and it is worse than a clean skip. The 32,768-byte budget is a shared
running total consumed root-first, and overflow is a mid-byte data.truncate():
- A large repo-root
AGENTS.mdstarves the cwd-level file that was supposed to override it — the precedence model inverts exactly where you rely on it. - The cut lands mid-sentence, so the model reads a syntactically valid, semantically corrupted file.
- The only signal is a
tracing::warn!, and the TUI writes no plaintext log by default, so you cannot see the warning without first settinglog_dir.
Runner-up: the untrusted-project silent drop described in section A. Third:
allow_managed_hooks_only works only in requirements.toml — set in config.toml it silently
does nothing, so an admin believes hooks are locked down when they are not.
6. Gemini CLI (Google)
In 2026 this has hooks, subagents, skills and plan mode — capabilities most guides still treat as absent.
A. Context assembly
Three tiers, in load order (docs/cli/gemini-md.md): global ~/.gemini/GEMINI.md → workspace
GEMINI.md in configured dirs and their parents → just-in-time GEMINI.md discovered when
tools touch a file, scanning up to a trusted root. The old "eagerly walk down every subdirectory"
model is superseded. Bounded by context.discoveryMaxDirs (default 200).
context.fileName takes a string or an array and is nested:
{ "context": { "fileName": ["AGENTS.md", "CONTEXT.md", "GEMINI.md"] } }.
Imports use @./x.md with circular detection and max depth 5. The context.importFormat
enum values are UNCONFIRMED.
Settings precedence, lowest to highest: defaults → system-defaults.json → ~/.gemini/settings.json
→ .gemini/settings.json → system settings file → env / .env → CLI args.
🔴 Note the trap: the system settings file overrides both user and project, the reverse of the
usual intuition.
| OS | System settings |
|---|---|
| Linux | /etc/gemini-cli/settings.json |
| Windows | C:\ProgramData\gemini-cli\settings.json |
| macOS | /Library/Application Support/GeminiCli/settings.json |
The schema is nested v3 (general.*, context.*, tools.*, security.*, hooksConfig.*), and
migration is active — deprecated flat keys are rewritten where writable, warned where the config
is read-only.
/memory has exactly four subcommands: show, reload, list, inbox (read from
memoryCommand.ts). There is no /memory add and no /memory refresh — reload is the name.
Compression is /compress, auto-triggered at model.compressionThreshold (default 0.5), with a
PreCompress hook. The footer context percentage is hidden by default
(ui.footer.hideContextPercentage defaults true). What survives compression is UNCONFIRMED.
.geminiignore requires a session restart to take effect.
B. I/O surface
Tools are read_file, write_file, replace, glob, grep_search, read_many_files,
run_shell_command, web_fetch, google_web_search, write_todos.
Sandbox via GEMINI_SANDBOX = true|docker|podman|sandbox-exec|runsc|lxc, image
ghcr.io/google/gemini-cli:latest, six macOS Seatbelt profiles. tools.sandboxNetworkAccess defaults
false.
MCP under mcpServers with command/args/env/cwd/timeout/trust/url/httpUrl/
headers/includeTools/excludeTools. excludeTools takes precedence over includeTools.
web_fetch handles up to 20 URLs per prompt. No proxy key found — UNCONFIRMED.
Subagents exist: .gemini/agents/*.md and ~/.gemini/agents/*.md, required frontmatter name,
description, kind, plus tools, model, max_turns. Invoked automatically or via
@agent_name. Subagents cannot call other subagents.
Hooks: eleven events — SessionStart, SessionEnd, BeforeAgent, AfterAgent, BeforeModel,
AfterModel, BeforeToolSelection, BeforeTool, AfterTool, PreCompress, Notification. The
config shape mirrors Claude Code's, and exit 2 is a hard block. stdout must carry nothing but
the final JSON object — debug output goes to stderr or it corrupts the contract. It even sets a
CLAUDE_PROJECT_DIR compatibility alias.
Headless: gemini -p, -o/--output-format text|json|stream-json,
--approval-mode default|auto_edit|yolo|plan. --yolo and --allowed-tools are deprecated, and
--checkpointing was removed in v0.11.0. Exit codes are enumerated — the only harness here that
does: 0 ok, 1 general, 41 auth, 42 input validation, 44 sandboxing, 52 config,
53 turn limit.
C. State on disk
~/.gemini/settings.json ~/.gemini/GEMINI.md
~/.gemini/oauth_creds.json SECRET — never commit
~/.gemini/mcp-oauth-tokens.json SECRET
~/.gemini/a2a-oauth-tokens.json SECRET
~/.gemini/trustedFolders.json ~/.gemini/{commands,skills,agents,extensions}/
~/.gemini/tmp/<id>/ checkpoints, shell_history, chats/, memory
~/.gemini/history/<id> a SHADOW GIT REPO used for checkpointing<id> is now a short id from a ProjectRegistry, not the raw sha256 — storage.ts carries
migration code from the old hash. The user directory is ~/.gemini on all platforms; only the
system config directory differs.
D. The most common silent misconfiguration
Setting context.fileName replaces the default rather than extending it. Set it to "AGENTS.md"
and every GEMINI.md in the tree stops loading, with no warning. Use the array form to keep both.
Close second: folder trust silently disables nearly everything. In an untrusted folder the
workspace .gemini/settings.json is ignored, .env is ignored, MCP servers do not connect,
custom commands are not loaded and memory is not auto-loaded. The project simply behaves as though
none of its configuration exists. Note the docs disagree with themselves on the default here — the
generated settings table says security.folderTrust.enabled defaults true while the trusted-folders
page presents it as opt-in; treat the generated table as authoritative.
Third: "trust": true on an MCP server bypasses every confirmation for that server's tools.
Comparison
| Always-loaded file | On-trigger mechanism | Tool protocol | Headless | Hooks | |
|---|---|---|---|---|---|
| Claude Code | ~/.claude/CLAUDE.md, ./CLAUDE.md, ./CLAUDE.local.md, .claude/rules/*.md (concatenated) | Skills (description → body); rules with paths:; subdir CLAUDE.md on Read | MCP (stdio/http/sse/ws), 3 scopes | claude -p, text/json/stream-json, --json-schema, exit 143 on SIGTERM | 33 events, exit 2 = absolute block |
| Claude Desktop | Instructions for Claude · Project instructions · Memory topics | Skills (zip upload); connectors per-conversation (Auto/Always/On demand); RAG auto-switch | MCP connectors (cloud-run) + .mcpb extensions | None | Present but inert in chat ("run only in Cowork") |
| Cursor | .cursor/rules/*.mdc with alwaysApply: true; AGENTS.md; User/Team rules | Auto Attached (globs); Agent Requested (description); @-mention | MCP (stdio/SSE/HTTP), .cursor/mcp.json | agent -p — needs --force to write | ~21 events, .cursor/hooks.json, fail-open |
| opencode | ~/.config/opencode/AGENTS.md or ~/.claude/CLAUDE.md; first project-level name match | Nested AGENTS.md on read; skills via skill tool | MCP local/remote in opencode.json | opencode run, --format json (event stream), exit 0/1 only | ~20 plugin hooks incl. permission.ask, custom tools |
| Codex CLI | ~/.codex/AGENTS.md; project AGENTS.md root-first (32 KB shared budget) | Skills at .agents/skills ($skill); ~/.codex/prompts/*.md | MCP [mcp_servers.*] in config.toml | codex exec, --json JSONL, exit codes UNCONFIRMED | Real in 2026, hooks.json + [hooks], plus notify |
| Gemini CLI | ~/.gemini/GEMINI.md + workspace/parents | JIT GEMINI.md on tool touch; skills; .gemini/commands/*.toml | MCP mcpServers (stdio/SSE/HTTP) | gemini -p, --output-format json, enumerated exit codes | 11 events, exit 2 = hard block |
The one-line version of each silent failure
| Harness | What looks fine and is not |
|---|---|
| Claude Code | A skill description naming a topic not an occasion never fires — and descriptions get dropped when you add more skills |
| Claude Desktop | Config edited without fully quitting; project knowledge silently switching to RAG |
| Cursor | alwaysApply omitted makes the rule manual-only; -p without --force writes nothing and exits 0 |
| opencode | Typo'd config keys are silently absorbed into an inert options bag; $schema is not enforced |
| Codex CLI | AGENTS.md truncated mid-sentence against a shared 32 KB budget, warned only to a log that does not exist by default |
| Gemini CLI | context.fileName replaces the default, so every GEMINI.md silently stops loading |
What this page does not establish
Named explicitly, because a gap is more useful than a guess:
- Whether Claude Code's CLAUDE.md walk stops at the repo root or continues to
/; "microcompact" appears in no current doc. - Claude Desktop: the
.mcpbon-disk install path, the Linux config path, free-plan context size, and whether Claude is told it is in RAG mode. - Cursor: the current MCP tool cap, an explicit
.cursorrulesdeprecation statement, per-OS history paths, and an exit-code table. - opencode: whether the TUI shows a token indicator, and the exact env-var literals behind its project-config kill switch.
- Codex CLI: numbered exit codes, Windows-native
CODEX_HOME, and whetherexperimental_instructions_filestill exists. - Gemini CLI:
context.importFormatvalues, a proxy key, and what survives/compress.