— 04 / 第四章
记忆 · 从零写一份 CLAUDE.md.
同一件事你跟它说了十遍。这一章从零写一份真的 CLAUDE.md,并且用一组前后对照证明它确实改变了行为 —— 不是「感觉好点了」,是同一句提示、同一个仓库,做出来的东西不一样。顺带把四层记忆的位置与优先级、@import、rules 目录的路径限定,和一条容易踩的时间差讲清:它在会话开始读进内存,你中途改了不生效。
— I
第一步:先拍一张「没有记忆」的底片.
提示词底片提示 · 故意不给任何约定
在这个仓库里加一个 <最小的、你们有特殊做法的东西>,照你觉得合适的方式做。 做完告诉我三件事: 1. 你把文件放在了哪里、为什么选这个位置; 2. 你跑了什么命令来验证,输出是什么; 3. 你对这个项目的约定做了哪些假设。
在这个仓库里加一个 带单元测试的日期格式化工具函数,照你觉得合适的方式做。 做完告诉我三件事: 1. 你把文件放在了哪里、为什么选这个位置; 2. 你跑了什么命令来验证,输出是什么; 3. 你对这个项目的约定做了哪些假设。
— II
第二步:写那份文件,一行一个否决问题.
/init,它会分析代码库,把探出来的构建命令、测试方式、项目惯例生成一份初稿;已经有 CLAUDE.md 的话,它提改进而不是覆盖。然后你做减法。
CLAUDE.md 的敌人不是太短,是太长:它整份进入每次会话的上下文,臃肿的文件会让真正要紧的规则被淹没 —— 官方直说,写得太满,Claude 反而开始忽略你的指令。2注 2Claude Code Docs · Best practices —— 写 CLAUDE.md 的取舍表:收录它猜不到的构建命令、和默认不同的风格规则、测试跑法、仓库礼仪、项目特有的架构决定与坑;排除读代码就能推断的、语言通识、频繁变动的信息、逐文件的代码库描述。每一行问一句「删掉它会不会导致出错」,不会就删;臃肿的 CLAUDE.md 会让真正的规则被忽略。截至 2026-08-05。纪律是对每一行问同一个问题:删掉这行,它会不会因此犯错?不会,就删。2注 2Claude Code Docs · Best practices —— 写 CLAUDE.md 的取舍表:收录它猜不到的构建命令、和默认不同的风格规则、测试跑法、仓库礼仪、项目特有的架构决定与坑;排除读代码就能推断的、语言通识、频繁变动的信息、逐文件的代码库描述。每一行问一句「删掉它会不会导致出错」,不会就删;臃肿的 CLAUDE.md 会让真正的规则被忽略。截至 2026-08-05。
| 值得一行 | 不值得 |
|---|---|
| 它猜不到的构建 / 测试命令 | 读代码就能推断的结构 |
| 和默认不同的风格规则 | 语言的通行惯例 |
| 禁区目录、仓库礼仪、环境怪癖 | 经常变动的信息 |
| 踩过的坑和非直觉行为 | 逐文件的代码库导览 |
# 这个仓库的规矩
## 命令
- 测试:`npm test`(不是 npx jest —— 我们的 jest 配置只在这个脚本里生效)
- 单文件测试:`npm test -- <路径>`
- 构建前必须先 `npm run codegen`,否则类型对不上
## 约定
- 组件放 `src/components/<名字>/`,一个目录含组件、测试、样式三个文件
- 日期一律用 `src/lib/date.ts` 里的工具,不要直接 new Date()
- 新增依赖前先问我 —— 这个项目对包体积有硬预算
## 禁区
- 不要改 `src/db/migrations/`,那是线上迁移历史
- 不要碰 `.env*`
- 不要在提交信息里加 co-author 尾注
## 坑
- `src/legacy/` 下的代码没有测试覆盖,改那里要手动验证并说明怎么验的一份真实形状的项目记忆:每一行都通过了「删掉会不会出错」
— III
第三步:重跑同一句,看差别.
/clear 开一个新会话(记住 —— 中途改文件不生效,下一节讲),把第 I 步一字不改的那句提示再发一次。
没有 CLAUDE.md
文件落在
src/utils/(它的通用直觉),直接用了 new Date(),顺手 npx jest 跑测试 —— 报了个配置错,它又装了一个依赖去绕。交回来的东西能跑,但三条项目约定全踩了,而它列出的「假设」正好就是这三条。有 CLAUDE.md
文件落在
src/components/ 的目录约定里,日期走 src/lib/date.ts,验证用的是 npm test 并贴了输出;想加依赖时它停下来问你。没有一次纠正,因为不需要纠正 —— 这就是那份文件替你说的十遍话。— IV
放哪一层,以及两个时间差.
| 层级 | 位置 | 放什么 |
|---|---|---|
| 组织 | managed policy(各系统固定路径) | 全公司的编码与合规规范 |
| 用户 | ~/.claude/CLAUDE.md | 你个人的偏好,跟你走所有项目 |
| 项目 | ./CLAUDE.md 或 ./.claude/CLAUDE.md | 团队共享:命令、架构、惯例,进版本库 |
| 项目 · 个人 | ./CLAUDE.local.md | 你的沙盒地址、测试数据,进 .gitignore |
@path 把别的文件拼进来,最多递归四层,相对路径按「含这条导入的文件」解析而不是工作目录 —— 这一条踩错过很多人。1注 1Claude Code Docs · How Claude remembers your project —— 四层 CLAUDE.md 按加载顺序从宽到窄:managed policy(macOS /Library/Application Support/ClaudeCode/CLAUDE.md、Linux 与 WSL /etc/claude-code/CLAUDE.md、Windows C:\Program Files\ClaudeCode\CLAUDE.md)、用户 ~/.claude/CLAUDE.md、项目 ./CLAUDE.md 或 ./.claude/CLAUDE.md、本地 ./CLAUDE.local.md;项目层出现在用户层之后。查找方式是从当前工作目录向上逐级走,沿途每一层都查 CLAUDE.md 与 CLAUDE.local.md;当前目录之下的子目录 CLAUDE.md 不在启动时加载,而是 Claude 读到那些子目录里的文件时才带进来。@path 导入相对路径按「含该导入的文件」解析而非工作目录,最多递归四层;导入解析会跳过 markdown 行内代码与围栏代码块,想提路径又不想导入就用反引号包住。项目文件里的导入有信任对话框保护(防别人往共享仓库里塞),用户层导入是你自己写的,不弹。Claude Code 读 CLAUDE.md 而不是 AGENTS.md,官方给的桥接写法是在 CLAUDE.md 里写 @AGENTS.md 或做符号链接。两套记忆都在每次会话开始加载,并且「Claude treats them as context, not enforced configuration」—— 要不论模型怎么决定都拦住某个动作,用 PreToolUse hook。auto memory 每次会话加载 MEMORY.md 的前 200 行或 25KB。截至 2026-08-05。另一个是 .claude/rules/,按主题拆文件,还能用 paths 限定只在 Claude 碰到匹配文件时加载。1注 1Claude Code Docs · How Claude remembers your project —— 四层 CLAUDE.md 按加载顺序从宽到窄:managed policy(macOS /Library/Application Support/ClaudeCode/CLAUDE.md、Linux 与 WSL /etc/claude-code/CLAUDE.md、Windows C:\Program Files\ClaudeCode\CLAUDE.md)、用户 ~/.claude/CLAUDE.md、项目 ./CLAUDE.md 或 ./.claude/CLAUDE.md、本地 ./CLAUDE.local.md;项目层出现在用户层之后。查找方式是从当前工作目录向上逐级走,沿途每一层都查 CLAUDE.md 与 CLAUDE.local.md;当前目录之下的子目录 CLAUDE.md 不在启动时加载,而是 Claude 读到那些子目录里的文件时才带进来。@path 导入相对路径按「含该导入的文件」解析而非工作目录,最多递归四层;导入解析会跳过 markdown 行内代码与围栏代码块,想提路径又不想导入就用反引号包住。项目文件里的导入有信任对话框保护(防别人往共享仓库里塞),用户层导入是你自己写的,不弹。Claude Code 读 CLAUDE.md 而不是 AGENTS.md,官方给的桥接写法是在 CLAUDE.md 里写 @AGENTS.md 或做符号链接。两套记忆都在每次会话开始加载,并且「Claude treats them as context, not enforced configuration」—— 要不论模型怎么决定都拦住某个动作,用 PreToolUse hook。auto memory 每次会话加载 MEMORY.md 的前 200 行或 25KB。截至 2026-08-05。
两个时间差别踩。其一:CLAUDE.md 在会话开始读入内存,中途改了不生效 —— 新内容要等下次 /clear、/compact 或重启才加载。4注 4Claude Code Docs · How Claude Code uses prompt caching —— 项目根与用户级 CLAUDE.md 在会话开始读入内存,中途编辑不废缓存、但也不生效,下次 /clear、/compact 或重启才加载新内容。截至 2026-08-05。改完发现它还按旧规矩走,不是它叛逆,是你还没翻页。其二:除了你写的记忆,它也在自己记 —— auto memory 把「下次会用上」的东西写进项目记忆目录,每次会话加载 MEMORY.md 的前 200 行或 25KB。1注 1Claude Code Docs · How Claude remembers your project —— 四层 CLAUDE.md 按加载顺序从宽到窄:managed policy(macOS /Library/Application Support/ClaudeCode/CLAUDE.md、Linux 与 WSL /etc/claude-code/CLAUDE.md、Windows C:\Program Files\ClaudeCode\CLAUDE.md)、用户 ~/.claude/CLAUDE.md、项目 ./CLAUDE.md 或 ./.claude/CLAUDE.md、本地 ./CLAUDE.local.md;项目层出现在用户层之后。查找方式是从当前工作目录向上逐级走,沿途每一层都查 CLAUDE.md 与 CLAUDE.local.md;当前目录之下的子目录 CLAUDE.md 不在启动时加载,而是 Claude 读到那些子目录里的文件时才带进来。@path 导入相对路径按「含该导入的文件」解析而非工作目录,最多递归四层;导入解析会跳过 markdown 行内代码与围栏代码块,想提路径又不想导入就用反引号包住。项目文件里的导入有信任对话框保护(防别人往共享仓库里塞),用户层导入是你自己写的,不弹。Claude Code 读 CLAUDE.md 而不是 AGENTS.md,官方给的桥接写法是在 CLAUDE.md 里写 @AGENTS.md 或做符号链接。两套记忆都在每次会话开始加载,并且「Claude treats them as context, not enforced configuration」—— 要不论模型怎么决定都拦住某个动作,用 PreToolUse hook。auto memory 每次会话加载 MEMORY.md 的前 200 行或 25KB。截至 2026-08-05。/memory 能看到两套记忆的全部文件:定期翻一翻,删掉过时的 —— 记忆也会腐烂,烂记忆比没记忆更误事。
本章验收 · 这份文件立起来了吗:
- 拍过底片:同一句提示在没有 CLAUDE.md 时跑过一次,它列出的「假设」你留着。
- 写好了文件:跑过 /init 打底,并用「删掉会不会出错」的尺子至少删掉三行。
- 做过对照:/clear 后一字不改地重跑同一句,亲眼看见差别 —— 这是你说服自己和队友的证据。
- 分清了层:个人偏好在 ~/.claude/,团队规矩进版本库,私人地址进 CLAUDE.local.md。
同一句话说第三遍之前, 把它变成一行记忆.
Before you say it a third time, make it one line of memory.
Aklman Library
— 讨论
讨论.
评论区初始化中…