Codex Misses Project-Specific Conventions: Fix With AGENTS.md

Codex writes `getUserById` when your codebase uses `findUserById`. Codify each convention as rule + a canonical example file in AGENTS.md, then verify it loaded.

Codex generates working code that doesn’t feel like your codebase. Naming uses get* when you use find*. Error handling throws strings when your code throws typed AppErrors. Tests use expect(x).toBe(y) when your style is assert.strictEqual(actual, expected). The code reviews badly — not because it’s wrong, but because it’s wrong-shaped.

Fastest fix: put each broken convention into AGENTS.md as a one-line rule plus a pointer to a real file that already follows it, then run /status to confirm the file actually loaded. Codex copies from the example file; the rule is just a label. A pointer to src/services/user/getUserById.ts beats any amount of prose.

Conventions don’t show up to a model from a few file reads. They have to be made visible — encoded as rules with concrete examples Codex can read at the start of the session. As of June 2026, Codex (the OpenAI coding agent) reads AGENTS.md files automatically: it concatenates your global ~/.codex/AGENTS.md plus every AGENTS.md from your git root down to the directory you’re editing, joined root-first and capped at 32 KiB (project_doc_max_bytes). AGENTS.md is the contract; pointing Codex at canonical example files is the enforcement.

Which bucket are you in

Run this 20-second triage before changing anything:

Symptom you seeMost likely causeJump to
Nobody can name a file where the rule livesConvention undocumentedCause 1
Rule is in STYLE.md / CONTRIBUTING.md (not AGENTS.md)Codex never read the docCause 2
Output matches a popular blog/Storybook pattern, not yoursLost to a training-data defaultCause 3
AGENTS.md has the rule but no code/example below itRule without an exampleCause 4
grep shows both spellings in src/Repo contradicts itselfCause 5
Lint fails on Codex’s file but the rule is in .eslintrcCodex skipped lintCause 6

A fast check that covers most of these: run /status inside the session to see which model and config are active, then ask Codex “List every instruction file you loaded, in order.” If your style doc isn’t in that list, you’re in Cause 1 or 2 — fix the file path first, before touching prompts.

Common causes

Ordered by hit rate, highest first.

1. Convention isn’t documented anywhere

The team agreed in chat last quarter to use find* for “may return null” and get* for “must exist or throw.” Codex can’t see chat — and the agreement was never written down. Codex picks whichever it saw first.

How to spot it: Ask your team: “Where is this convention documented?” If nobody can point to a file, Codex couldn’t either.

2. Convention is documented but Codex didn’t read the doc

STYLE.md exists, but Codex only auto-loads AGENTS.md files (global ~/.codex/AGENTS.md plus one per directory from git root down to the file you’re editing). A doc named STYLE.md, CONTRIBUTING.md, or docs/conventions.md is not pulled into context unless something points Codex at it. As of June 2026, Codex still treats README.md as an ordinary source file, not as instructions — only the AGENTS.md chain (and its AGENTS.override.md sibling) is auto-injected.

How to spot it: Run /status, then ask Codex “List the instruction files you loaded.” If STYLE.md isn’t in the list, it was never read.

How to fix it: Either merge the content into AGENTS.md (or scaffold one with the /init slash command and paste it in), or keep STYLE.md and reference it explicitly with /mention STYLE.md so it’s attached to the turn.

3. Codex defaulted to a common pattern instead of yours

The team uses kebab-case test IDs (data-testid="user-card-name"). Codex generates camelCase because camelCase is far more common in the public examples it trained on. Your convention loses to the statistical default.

How to spot it: New code matches a popular-blog or framework-docs pattern more than your existing code. Most common with test IDs, lint rules, test assertions, and naming.

4. AGENTS.md states the rule but provides no example

“Use the team’s error pattern.” What pattern? Codex needs a concrete example to copy from — a one-line rule isn’t enough.

How to spot it: AGENTS.md has a section heading but no code block or file pointer below it. Rules without examples often get ignored.

5. Conventions conflict within the repo

Half the codebase uses findUserById, half uses getUserById (legacy migration in progress). Codex picks whichever it reads first. No clear canonical means random output.

How to spot it: grep -c "findUserById" src/ && grep -c "getUserById" src/ — both nonzero means inconsistent. Codex can’t pick correctly because no winner has been declared.

6. Convention is enforced by linter but Codex didn’t run lint

The convention IS encoded — in .eslintrc rules like naming-convention: camelCase — but Codex generates code and skips running lint. The error only surfaces in CI.

How to spot it: Run pnpm lint path/to/new-file.ts after Codex’s patch. Lint errors = encoded convention Codex bypassed.

Shortest path to fix

Ordered by ROI. Steps 1–3 fix the most common cases.

Step 1: For each broken convention, add rule + example to AGENTS.md

Don’t write rules in isolation. Always pair them with a real file Codex can read:

## Naming conventions

- `find*` for nullable lookups (returns `T | null`).
  Example: `src/services/user/findUserById.ts`
- `get*` for required lookups (throws on missing).
  Example: `src/services/user/getUserById.ts`

## Error handling

All thrown errors must extend `AppError` from `src/lib/errors.ts`.
Example pattern: see `src/api/billing/handlers.ts:42`

## Test style

Use `vitest` with `assert` (NOT `expect`).
Canonical example: `src/services/user/user.test.ts`

The example file is what Codex actually copies from. The rule is just navigation.

Step 2: Point at the example in the task prompt

Add a new lookup for `getOrgById`.
Follow the exact pattern in `src/services/user/getUserById.ts`:
- Same function shape
- Same error throw style
- Same test layout (see `getUserById.test.ts`)

Generate the file + its test together.

A pointer to a real file beats any amount of prose convention.

Step 3: When divergent, reject + re-prompt referencing the canonical

If Codex generates the wrong shape:

Your output uses `expect(x).toBe(y)` — that's jest style.
This codebase uses `assert.strictEqual(actual, expected)`.
See `src/services/user/user.test.ts` for the canonical pattern.

Re-generate the test using the canonical style.

Each rejection trains the session. After 1–2 corrections, Codex stays on pattern for the rest.

Step 4: For visible markers, require them as outputs

For data-testid, ARIA attributes, custom decorators — list them as required outputs:

New components must include:
- `data-testid="<kebab-name>"` on every interactive element
- `aria-label` for icon-only buttons
- Storybook story file at `<Component>.stories.tsx`

Reject any component missing any of these.

Step 5: Run lint as part of the verifier

Lint encodes a lot of conventions Codex bypasses. Make it part of “done”:

After writing, run:
  pnpm eslint <new-files> --max-warnings 0

If any warning/error, fix it before saying done.

Pair this with a strict ESLint config (naming-convention, import/order, custom rules) and many conventions are enforced automatically. You can also wire the lint step into AGENTS.md itself so every session knows it’s part of “done”:

## Definition of done
- `pnpm eslint <changed-files> --max-warnings 0` passes
- `pnpm vitest run <changed-files>` passes

Step 6: For monorepos, per-package AGENTS.md

Different packages can have different conventions. Don’t force one root rulebook. Add a file per package:

AGENTS.md                 → repo-wide rules (root)
apps/web/AGENTS.md        → Next.js App Router conventions
packages/ui/AGENTS.md     → component library conventions
packages/db/AGENTS.md     → Prisma + repository pattern conventions

Codex concatenates the chain from git root down to the edited file, joined root-first. Files closer to the file being edited appear later in the combined prompt, so the nearest file wins on any rule that conflicts. Two gotchas as of June 2026:

  • The merged total is capped at 32 KiB (project_doc_max_bytes). A bloated root file can crowd out your per-package rules — keep the root lean.
  • A ~/.codex/AGENTS.override.md (or per-directory AGENTS.override.md) takes the place of the normal AGENTS.md at that level if present. Check for a stray override if a package suddenly stops following its rules.

How to confirm it’s fixed

  1. Confirm the file loaded. In the session run /status, then ask “List every instruction file you loaded, in order.” Your AGENTS.md (and any nested ones) should appear. If you want a logged record, start Codex with codex -c log_dir=./.codex-log and grep ./.codex-log/codex-tui.log for the file paths.
  2. Re-run the failing task. Ask Codex to generate the same kind of file that came out wrong before (e.g. a new get* lookup with its test).
  3. Diff against the canonical. The new file should match the function shape, error style, and test layout of the example file you pointed to.
  4. Run the verifier. pnpm eslint <new-file> --max-warnings 0 and your test command should both pass with no convention warnings.

If step 1 shows the file loaded but the output is still wrong-shaped, the problem is the rule itself — it almost certainly lacks a concrete example file (Cause 4). Add the pointer and re-run.

FAQ

Does Codex read CLAUDE.md or .cursorrules? Codex auto-loads the AGENTS.md chain. AGENTS.md is now an open standard (stewarded by the Agentic AI Foundation under the Linux Foundation) supported across Codex, Cursor, Gemini CLI, Windsurf, and Copilot, so a single AGENTS.md is the portable choice. If your repo still has a CLAUDE.md, the cleanest fix is a one-line AGENTS.md that says “Follow the conventions in CLAUDE.md” and /mention CLAUDE.md in the task, rather than relying on Codex to find it.

Why does Codex follow my rule for one file then drift on the next? Two usual causes: a rule with no example (the model has nothing to copy from — Cause 4), or a 32 KiB overflow where a large root AGENTS.md crowds out the per-package file. Check load order with /status and trim the root.

I added the rule but Codex still ignores it. Now what? Verify it actually loaded (step 1 above). The single most common miss is that the rule sits in STYLE.md/CONTRIBUTING.md, not AGENTS.md, so it’s never injected. Merge it into AGENTS.md or attach it with /mention.

Should I commit AGENTS.md to the repo? Yes — commit per-project AGENTS.md files so the whole team (and CI agents) share them. Keep machine-specific or personal preferences in your global ~/.codex/AGENTS.md, which is not committed.

Codex contradicts itself between packages. That’s the override or precedence rule biting. The file nearest the edited file wins; a stray AGENTS.override.md replaces the normal file at its level. Search the tree for override files and conflicting rules.

Prevention

  • Every convention in AGENTS.md pairs with a concrete example file — rules-only sections get ignored
  • Resolve in-repo inconsistencies first (declare a winner) before relying on Codex to follow the convention
  • Encode as much as possible in ESLint/Biome/Prettier so the linter enforces what AGENTS.md describes
  • Per-package AGENTS.md in monorepos — nearest file wins, and keep the root file small so it doesn’t blow the 32 KiB cap
  • When a divergent output ships, treat it as a documentation gap, not a Codex failure — add the rule + example
  • Quarterly: run Codex against the project’s style and check whether output drifts; if it does, the docs are stale

Tags: #Codex #Coding agent #Troubleshooting #Debug #Conventions