If you use AI coding agents daily, you already know the routine: you type "we use pnpm here" into a chat box for the third time this week. Then "never edit migrations directly". Then "tests go in `__tests__/`". The agent complies, forgets, and tomorrow you type it all again.

This post shows a different setup — one where you write each rule **once, into a file**, and the agent reads it before every session, on every surface, with every new model. The approach is called **context engineering**, and the practical implementation is a small set of files I call the **Context Filesystem**.

One thesis: *if you type the same instruction twice, it belongs in a file, not a chat box.*

## Why Context Engineering?

Prompt engineering optimizes what you type **once**. It doesn't compound. Context engineering optimizes what the model reads **every time** — and that compounds with every session you ever run.

The mental model that makes everything click:

- **AGENTS.md** = the **project layer**. Architecture, conventions, do-nots. Lives in the repo, belongs to the project.
- **SOUL.md** = the **identity layer**. Tone, style, how the agent talks to you. Lives in your Hermes home, follows you everywhere.

The dividing rule: if it belongs to a project, it's AGENTS.md. If it should follow you everywhere, it's SOUL.md. Mix them up and you get an agent reciting your database ports in repos that don't have a database.

One more thing worth knowing: context is an expensive room. Everything in the system prompt costs tokens on every single turn. So the goal is not "write down everything" — it's "write down the right things, in the right file, at the right directory level, so each byte earns its rent".

![Context Engineering](https://firebasestorage.googleapis.com/v0/b/kyzlab-blog.firebasestorage.app/o/posts%2Fagentic-ai%2Fcontext-engineering.jpg?alt=media&token=93fa7e5a-6654-47bc-b5db-44751095615d){:style="display: block; width: 100%; height: auto; aspect-ratio: 1536 / 864; object-fit: cover;"}

## How Discovery Actually Works

The most common mistake: dropping an `AGENTS.md` next to a `CLAUDE.md` and a `.cursorrules`, assuming the agent merges all three. It does not.

Per session, **ONE project context file is loaded per directory** — first match wins:

```
.hermes.md  →  AGENTS.md  →  CLAUDE.md  →  .cursorrules
```

Plus one global identity file, loaded independently:

- `SOUL.md` — only from `HERMES_HOME` (usually `~/.hermes/SOUL.md`). It is NOT probed from your project directory, on purpose: your personality shouldn't change because you `cd`'d into a different repo.

The three mechanics that matter in production:

1. **Startup** — the context file in your working directory is read, security-scanned for prompt injection, truncated if huge (floor 20k chars, scales up with the model's context window, ceiling 500k), and injected into the system prompt.
2. **Progressive subdirectory discovery** — when the agent reads files in a subdirectory mid-session, any `AGENTS.md`, `CLAUDE.md`, or `.cursorrules` there is discovered on the spot (walks up to 5 parents), scanned, capped at 8,000 chars, and appended to the tool result. Each directory is checked at most once per session.
3. **Existing conventions come free** — repo already has `CLAUDE.md` or `.cursorrules` and no `AGENTS.md`? Those are used. Migration is "rename or add", not "rewrite".

Why progressive discovery matters: a 40-package monorepo's worth of instructions would blow up the system prompt if loaded eagerly, and a bloated frozen prompt kills the prefix cache. Loading per-directory keeps the prompt small **and** the cache stable.

The injection scan is real, by the way: files containing "ignore previous instructions" patterns, hidden HTML comments, or `cat .env`-style exfiltration get blocked with a marker, not loaded. Your context files are an attack surface, and Hermes treats them that way.

## The Production AGENTS.md Template

The second common mistake: writing essays. The agent doesn't need prose — it needs a map, rules, and boundaries.

Here is the template I actually use, expanded from the official docs' best-practice list (structure with `##` headers, concrete examples, say what NOT to do, list key paths and ports):

```markdown
# Project Context

Next.js 14 frontend + FastAPI backend, deployed via Docker Compose
on a Hetzner VPS. Repo is a pnpm workspace.

## Architecture
- Frontend: Next.js 14 App Router in /frontend (port 3000)
- Backend: FastAPI + SQLAlchemy in /backend (port 8000)
- Database: PostgreSQL 16 (port 5432), Alembic migrations in /backend/alembic
- Infra: docker-compose.yml at root; staging deploys via ./scripts/deploy-staging.sh

## Conventions
- TypeScript strict mode; no `any` without a comment justifying it
- Python: PEP 8, type hints everywhere, Google-style docstrings
- API responses: {data, error, meta} shape — see backend/app/schemas/envelope.py
- Tests: frontend in __tests__/, backend in tests/ — run with pnpm test / pytest
- Commits: conventional commits, scope required (feat(api): ...)

## Do NOT
- Never edit files in backend/alembic/versions/ by hand — use `alembic revision`
- Never commit .env.local (contains real keys)
- Never add a dependency without noting it in this file's Architecture section
- Don't run the production compose file locally; use docker-compose.dev.yml

## Gotchas
- Frontend dev server needs `pnpm dev --hostname 0.0.0.0` inside Docker or
  HMR breaks
- Alembic autogenerate misses enum changes — always review generated migrations
```

Sizing guidance: keep it under ~2,000 words. If a section grows past that, the details belong in a nested AGENTS.md or in a skill — not the root file. Remember the rent: the agent reads this file every turn.

**Honesty moment**: my first AGENTS.md was 4,000 words of everything I knew about the repo. Response quality got *worse*, not better — the important rules drowned in trivia. The file started working when I deleted half of it. Curation is the skill, not writing.

![Context Ledger](https://firebasestorage.googleapis.com/v0/b/kyzlab-blog.firebasestorage.app/o/posts%2Fagentic-ai%2Fcontext-rent-ledger.jpg?alt=media&token=7e52dbd8-e1da-4d5c-8d1f-c4235fcd20b0){:style="display: block; width: 100%; height: auto; aspect-ratio: 1536 / 864; object-fit: cover;"}

## The Monorepo Pattern: Nested Context Files

Stuffing everything into the root file breaks the economics. So does writing nested files that repeat the root.

Put directory-specific rules in **nested AGENTS.md files**. They load only when the agent actually works in that subtree:

```
my-project/
├── AGENTS.md            # architecture, shared conventions (loaded at startup)
├── frontend/
│   └── AGENTS.md        # "use pnpm not npm; components in src/components/;
│                        #  Tailwind only, never inline styles; pnpm test"
├── backend/
│   └── AGENTS.md        # "poetry for deps; uvicorn main:app --reload;
│                        #  all endpoints need OpenAPI docstrings"
└── infra/
    └── AGENTS.md        # "terraform state is remote; never run apply locally"
```

The rules for nesting:

- **Root file**: what EVERY session needs — architecture map, shared conventions, global do-nots
- **Nested files**: only what's specific to that subtree
- **Never repeat root content in nested files** — both end up in context together when the subdirectory is discovered, so repetition pays the token tax twice
- **Keep nested files small**: the 8,000-char per-file cap truncates

The elegant part: discovery walks up to 5 parents, so reading `backend/src/main.py` finds `backend/AGENTS.md` even if `backend/src/` has nothing. You place files at the level where the rules apply, and the mechanism finds them.

## SOUL.md: The Global Identity Layer

One file, `$HERMES_HOME/SOUL.md`, injected on every session regardless of project. It sits in slot #1 of the system prompt — the agent's actual identity — and it's for cross-project voice, not project facts:

```markdown
Direct and concise. Lead with the answer, not a preamble.
When verifying work, state what was tested vs. what I should check myself.
Plain text over heavy markdown in chat.
```

The mechanics worth knowing:

- Hermes seeds a default SOUL.md if none exists; your file is never overwritten
- An empty file adds nothing to the prompt (the built-in identity takes over)
- It's loaded only from `HERMES_HOME` — never probed from your project directory
- `/personality` presets (concise, technical, teacher...) are session-level overlays on top of it; SOUL.md is the durable baseline

The dividing rule again: follows you everywhere → SOUL.md. Belongs to a project → AGENTS.md. That one sentence prevents 90% of the misconfiguration I see.

## Failure Modes (and the One Rule That Fixes Most of Them)

![Refusal Agent](https://firebasestorage.googleapis.com/v0/b/kyzlab-blog.firebasestorage.app/o/posts%2Fagentic-ai%2Frefusal-agent.jpg?alt=media&token=4e37e5e2-5ee6-4a06-8d21-4330e3dba9bb){:style="display: block; width: 100%; height: auto; aspect-ratio: 1536 / 864; object-fit: cover;"}

Writing the files is the easy part. Auditing what the agent actually does with them is where most setups quietly rot.

The failure table I wish someone had handed me:

| Symptom | Cause | Fix |
| :--- | :--- | :--- |
| Agent ignores a convention | It's in a nested file the agent never triggered | Move it to root, or make the agent work in that directory |
| Agent follows a stale rule | File says something the codebase stopped doing | Delete it — stale context is worse than none; review monthly |
| Context feels bloated, responses slow | Root file grew past usefulness | Split: root = map + rules, details → nested files or skills |
| Conventions conflict across tools | CLAUDE.md and AGENTS.md both exist with different rules | First-match-wins per directory — only one loads. Consolidate into AGENTS.md |
| Agent recites your rules but violates them in code | The rule is prose, not checkable | Rewrite it as a verification step |

That last row is the deepest cut, and it's the one rule that fixes most of the table:

**Rules the agent can verify get followed; rules it can't get aspirated.**

"use strict typing" is a wish. "run `tsc --noEmit` and show zero errors before claiming done" is a procedure. "write good tests" is a wish. "run `pnpm test`, paste the pass count" is a procedure. The best AGENTS.md files read like a pre-flight checklist, not a values statement.

Go read your current file. Every line that's a value, rewrite as a check.

## Why the Order Matters

Root before nested, rules before gotchas, checkable before aspirational.

The order matters because the startup file is the only guaranteed context — nested files load on demand, so anything that must always be true has to live at the root. And checkable rules come first because an uncheckable rule is just a suggestion the agent agrees with politely and then ignores under pressure.

The compounding is quiet but real: every convention you stop repeating is tokens saved every turn, every gotcha written down is a mistake made exactly once, and every project-layer file you write transfers across tools — AGENTS.md is a cross-tool standard (Claude Code and others read it too), so the work isn't locked to one agent.

You're not prompting anymore. You're building the environment the prompt lands in.

## The 15-Minute Setup

```bash
# 1. Draft the skeleton in your repo root
$EDITOR AGENTS.md        # architecture, conventions, do-nots, gotchas

# 2. Fold in existing .cursorrules / CLAUDE.md (only the first match loads —
#    don't leave parallel competing files)

# 3. One nested file for your most-edited subdirectory
$EDITOR frontend/AGENTS.md

# 4. Global tone (optional)
$EDITOR ~/.hermes/SOUL.md

# 5. Verify — from the file, not from you:
hermes chat -q "What conventions must you follow in this repo?"
```

Then run the four checks:

- [ ] New session in the repo: the agent states your conventions unprompted
- [ ] Ask for a change in a nested subdirectory — its rules show up in behavior
- [ ] Request something your "Do NOT" section forbids — the agent refuses or flags it
- [ ] `wc -c` your nested files — all under 8,000 chars

## The Playbook

- Type it twice → it belongs in a file, not a chat box
- One context file per directory, first match wins: `.hermes.md → AGENTS.md → CLAUDE.md → .cursorrules`
- Root AGENTS.md = map + rules + do-nots, under ~2,000 words
- Nested AGENTS.md = subtree rules only, under 8,000 chars, never repeat root
- SOUL.md = identity, `$HERMES_HOME` only, no project facts
- Every rule rewritten as a checkable verification step
- Stale context is worse than none — review monthly
- Existing CLAUDE.md/.cursorrules migrate by folding in, not competing

## Conclusion

The fork between the two versions of you — the one who keeps typing the same instructions into chat boxes, and the one whose agent starts every session already briefed — is about 15 minutes wide.

One action, tonight: open your most-active repo, create `AGENTS.md`, and write the five instructions you're most tired of repeating. Rewrite each one as something the agent can run and verify.

The filesystem is the prompt. Start editing it.

## References

- [Hermes Agent — Context Files](https://hermes-agent.nousresearch.com/docs/user-guide/features/context-files)
- [Hermes Agent — Personality / SOUL.md](https://hermes-agent.nousresearch.com/docs/user-guide/features/personality)
- [AGENTS.md — cross-tool standard](https://agents.md/)

---

*Setting up my first AGENTS.md took 15 minutes and permanently ended the "we use pnpm here" era of my life. What's the instruction you're most tired of repeating? Share with me on X!* 🚀
