Codex 不照你的项目结构来:6 个原因 + AGENTS.md 模板

新文件落到 `/src/` 但你的 repo 用 `/app/`;monorepo 里依赖被加到根 `package.json`;测试用了你没装的框架——用 AGENTS.md + canonical example 指针修。

你让 Codex 加一个 feature 模块。PR 把它放到了 /src/features/billing/,但你的 repo 用的是 /app/(dashboard)/billing/(Next.js App Router)。Diff 看着合理——只是和你的代码库完全不在一个频道。或者:pnpm workspace 项目,Codex 把 vitest 依赖加到根 package.json,每个包本来都有自己的 package.json。再或者:repo 全用 vitest,它写出来一份 jest 风格的测试。

Codex 不是故意瞎猜——是你的 repo 惯例对它不可见时,它就退回到通用模式。修法:AGENTS.md 给它一份目录地图 + canonical example 指针,并在 task 里显式指名目标路径。

常见原因

按命中率从高到低:

1. 没 AGENTS.md(或者过期了)

repo 根没 AGENTS.md,Codex 只能抽几个文件读、从中泛化——但几个文件读不出结构惯例。它落地的第一个目录就成了”项目布局”。

如何判断ls AGENTS.md——没有就是 agent 在猜。有的话 git log AGENTS.md 看上次更新——超过 6 个月而期间又加了很多 feature,就是过期了。

2. Task 没指定目标路径

「加一个 billing feature」由 Codex 自己挑。「在 apps/web/src/features/ 下加 billing feature,参照 apps/web/src/features/auth/ 的布局」就确定了。第一种是抛硬币,第二种是确定的。

如何判断:回看 prompt——没指明目录就是 Codex 自己选的。

3. Codex 把错的样本当 canonical

它先读到 legacy/v1/UserService.ts(按字母序遍历更靠前),判定”项目风格就是这样”,忽略了你现代的 src/services/UserService.ts。两个都存在,Codex 挑了错的。

如何判断:新代码的 style / import / 结构匹配某个 deprecated 区——Codex 读的是错的子树。

4. Monorepo 被当成单包

Codex 没意识到这是 pnpm/yarn workspace。依赖加到根 package.json、跨包 import 没用 workspace 别名、pnpm install 在错的层级跑。

如何判断:新 import 直接跨包边界(相对路径或顶级包名)。看 pnpm-workspace.yaml / lerna.json / nx.json——任何一个存在,Codex 都要尊重 workspace。

5. 测试框架 / lint 工具不匹配

Repo 用 vitest,Codex 写出 jest 风格——因为 jest 在它训练数据里更常见。测试看着对,但跑不起来,因为框架根本没装。

如何判断:新测试 import 一个 package.json 里没有的框架。跑 pnpm test path/to/new-file.test.ts——Cannot find module 'jest' 就是。

6. 同名不同大小写的文件夹

你的 repo 有 src/Components/(大写 C),Codex 建了 src/components/(小写)。case-insensitive 文件系统(macOS 默认)上看着重复;case-sensitive(Linux CI)上 import 莫名其妙就断了。

如何判断:两个只差大小写的目录。ls -la src/ | sort 一目了然。

最短修复路径

按收益从高到低,Step 1 一步去掉 70% 的结构不匹配。

Step 1:写 AGENTS.md,里面带目录地图

repo 根加:

# AGENTS.md

## 目录地图

- `apps/web/` — Next.js 14 App Router(**不是** pages router)
  - `apps/web/src/features/` — feature 模块,一个 feature 一个目录
  - `apps/web/src/components/` — 共享 UI 原语(小写!)
  - `apps/web/app/` — 路由,分组目录用 `(name)` 语法
- `packages/ui/` — 共享组件库,tsup build
- `packages/db/` — Prisma client + migration
- `scripts/` — Node CLI 工具

## Canonical examples

- Feature 模块布局:见 `apps/web/src/features/auth/`
- 共享组件:见 `packages/ui/src/button/`
- API route 约定:见 `apps/web/app/api/users/route.ts`

## 约定

- 包管理器:**pnpm** workspace。不要用 npm 或 yarn。
- 测试框架:**vitest** + `@testing-library/react`。不要 jest。
- Lint:ESLint config 在 `packages/eslint-config-acme/`
- Import:用 workspace 别名(`@acme/ui`),不要相对路径 `../../`

## 禁止

-`src/` 下直接建文件夹——feature 进 `apps/web/src/features/`
- 依赖加到根 `package.json`——加到用它的那个包里。
- 同名不同大小写文件夹(`Components/` vs `components/`)——统一小写。

Codex 每个 task 都读这个,绝大多数结构错都消失了。

Step 2:在 task 里指一个 canonical example

新 feature:

加 billing feature。结构照 `apps/web/src/features/auth/`:
- index.ts (公开 API)
- types.ts
- components/
- hooks/
- billing.test.tsx (vitest,不是 jest)

新文件放 `apps/web/src/features/billing/`。

指一个真实样本比任何文字描述都管用。

Step 3:monorepo 里指定 working package

Working directory: apps/web

- 本次 task 所有路径都相对 apps/web。
- 加依赖用 `pnpm --filter=@acme/web add <pkg>`。
- import 共享组件用 workspace 别名 `@acme/ui`。
- 不要动根 `package.json` 或其他 package。

Step 4:结构错了立刻拒收 + 重 prompt

Codex 把目录建错了,不要手动改——删错的,重 prompt:

你上一个 patch 把文件建在 `src/billing/`,错了——见 AGENTS.md。
正确路径:`apps/web/src/features/billing/`。

删 `src/billing/`。在正确路径下重写,照 `apps/web/src/features/auth/` 的样子。

这是给本次 session 训练,不只是修一次错。

Step 5:大 repo 里钉住 Codex 的”第一视野”

顶层目录多的 repo,Codex 最先读到的几个文件决定了它的心智模型。提前钉死:

写任何代码前先读这三个文件:
1. apps/web/src/features/auth/index.ts
2. apps/web/src/features/auth/billing.test.tsx
3. apps/web/src/features/auth/components/LoginForm.tsx

这些是本代码库的 canonical pattern。新 feature 照这个来。

Step 6:大 monorepo 每个包一份 AGENTS.md

30 个包的 repo 一份根 AGENTS.md 装不下所有人的约定。加 apps/web/AGENTS.mdpackages/ui/AGENTS.md,每份写本包专属规则——Codex 取最近的那份。

apps/web/AGENTS.md
└── "本包用 App Router,默认 Server Component。"

packages/ui/AGENTS.md
└── "所有组件都是 client component,文件首行 'use client'。"

预防建议

  • AGENTS.md 当活文档维护——架构变了立刻更新
  • 永远带「canonical examples」一节,指向 Codex 能读的真实目录
  • Task prompt 里显式指名目标路径;“放对位置”从来不管用
  • Monorepo 每个 prompt 都指定 working directory 或 filter
  • 大 repo 每个 package 一份 AGENTS.md——最近的那份生效
  • 季度审一次同名不同大小写的文件夹,统一掉

相关阅读

标签: #Codex #Coding Agent #排查 #排查 #项目结构