cairn(垒石)—— repo 内项目知识与进度管理约定
> 山路石标:每个任务收尾垒一行索引。石标不指挥你怎么走,只告诉后来者"有人来过、这条路通"。
快速上手(先会用,再懂为什么)
速查——日常就这三条命令:
| 想干什么 | 敲什么 |
|---|---|
| ① 起个新任务 | cd "$(./.cairn/scripts/mktmp.sh <主题>)" |
| ② 查以前弄过没 | grep -i <关键词> .cairn/tasks/INDEX.md |
| ③ 看现在有什么在途 | grep "^- 🚧\|^- ⏸" .cairn/tasks/INDEX.md |
三个典型场景,照着走(完整多文件样例在 assets/examples/sample-trail-zh/):
场景 A — 起任务前先查历史(最常见)
> 用户:"帮我定位下利宝附件同步不过来的问题。"
- 主题似曾相识,先查:grep -i 附件 .cairn/tasks/INDEX.md
- 命中一行 - 06-16 附件下载 query_date 窗口修复 …[附件][下载] → Read 那个任务的总结,修法可复用,省掉重新定位;
- 没命中才起新任务:cd "$(./.cairn/scripts/mktmp.sh libao-attach-sync)",调试脚本、SQL、截图全丢进当前 scratch/(往根目录写会被 hook 拦)。
场景 B — 干完收尾(总量 ≤2 分钟)
> 用户:"差不多了,收尾吧。"
- 先过门禁:以后的任务会因为这一行做得更好吗?——否 → 删掉占位行就完事(日期目录本身就是留痕)。是 → 先 grep INDEX 有无同主题旧行,再在顶部垒/并一行,写结论不写流水账:
- 07-15-libao-attach-sync — download 漏传 query_date 致老案永收不到,改走全量队列 [附件][下载]
- 三问自检:①以后还会做吗?这类补数第二次了 → 抽进 sop/补利宝附件.md(顺带想:能不能做成 hook/lint 自动拦住?)。②半年后有人问为什么这么改吗?不涉架构选型 → 不登记决策。③这次召回的哪条真用上了、哪条误导了?→ 给对应行升/降 [用N] 计数。
- 踩了坑顺手记一行 spec/pitfalls.md,触发条件 → 正确做法 →(一句对比为什么老做法不行):- 2026-07-15 query_date 冻结在首次入库日,老案补材料永远滑出窗口 → download 不传 date 走全量队列(按窗口拉取永远漏老案)[附件]
场景 C — 跨会话续做
> 用户(隔天新会话):"那个附件的事做到哪了?"
- 看在途:grep "^- 🚧\|^- ⏸" .cairn/tasks/INDEX.md → 命中 🚧 07-15-libao-attach-sync
- Read .cairn/tasks/07-15-libao-attach-sync/progress.md 的"下一步/卡点"直接接上,不翻会话历史。
0. 六条核心理念(为什么长这样)
从 5 个月 / 148 任务的重型框架尸检提炼:需要纪律喂养的机器(JSONL 注入、agent 流水线、.current-task 指针)全烂掉,活下来的只有目录约定 + 纯 markdown。六条:①留痕在 repo 且分个人层/团队层 ②任务=日期文件夹 ③脏净分离(scratch + 根目录 hook)④索引+详情两层(grep 索引再读详情)⑤按需检索不全量注入(实证:22KB/会话没人读)⑥约定优先于机器、状态长在任务身上。展开见 docs/philosophy.md。
1. 六类内容 × 统一布局
| 类型 | 回答的问题 | 位置 | 索引 | 更新时机 |
|---|---|---|---|---|
| 任务留痕 | 做过什么、结论是什么 | tasks/MM-DD-<topic>/ |
tasks/INDEX.md |
收尾时 |
| 进度 | 现在做到哪、卡在哪 | 长任务的 progress.md + INDEX 状态标记 |
tasks/INDEX.md(🚧 标记) |
每次会话结束 |
| SOP | 怎么做 X(可重复步骤) | sop/<动词-主题>.md |
sop/目录.md |
第二次做同一件事时 |
| 决策 | 为什么系统长这样、为什么否了 B | docs/decisions/NNN-<主题>.md |
文件名顺序编号自成索引 | 收尾自检命中时 |
| 坑与规约 | 什么会翻车、什么必须遵守 | spec/pitfalls.md(坑账)+ spec/<主题>.md(详细规约);硬规则升级进 CLAUDE.md |
自身 / CLAUDE.md 指针行 | 踩到当场记 / 规约成形时 |
| 沉淀文档 | 方案、文章、对外材料 | docs/ |
docs/目录.md(可选) |
写完归档 |
2. 任务生命周期(起 → 干 → 记进度 → 收尾)
先定位项目(多项目规则):从当前工作目录向上找最近的 .cairn/ 或 .trellis/,任务就记进那个项目——与 .git 同构,每项目一份互不污染。Trellis 迁移提醒:只找到 .trellis/ 时本会话提醒一次可迁移到 cairn(步骤见 assets/setup-and-recovery.md);不同意就以 .trellis 为 base 正常干活,不再重复问。父子仓(如 fn-agent-manager + 8 子仓):跨仓任务记父仓;单仓任务记该子仓(若已落地 cairn),子仓没落地则向上落到父仓。一个都找不到 → 提示"落地 cairn"。🔴 CHECKPOINT:同一会话跨多项目操作时,任何写入前必须先报一句目标项目再动手——石标垒错项目比没垒更糟。
起:cd "$(./.cairn/scripts/mktmp.sh <topic>)"(建目录+scratch/,输出 scratch 路径)。它还会在 INDEX 顶部幂等垒一行 🚧 <slug> — 待补结论 占位——忘了收尾就变成看得见的欠账(doctor 揪得出),而不是静默丢失;收尾时把这行改写成真结论即可。起之前若主题似曾相识,先按 §6 查历史。
干:临时产物全进 scratch/;往根目录写会被 block-root-scratch.py 拦下并提示正确位置。
记进度(用户说"记进度/今天到这",或长任务会话自然结束时):
- 首次:从 assets/progress.template.md 建 progress.md(目标 / 已完成 / 下一步 / 卡点·等待,均带日期)。
- 之后:只增量更新"已完成/下一步/卡点"三节,旧内容不删(是日志不是看板)。
- 同时确保 INDEX.md 里该任务行首有 🚧。
收尾(用户说"收尾/归档/差不多了",总量 ≤2 分钟):
1. 写入门禁(入口闸,不是可选项):以后的任务会因为这一行做得更好吗?——否 → 删掉占位行即收尾完成,日期目录本身就是留痕、自然老化。(不过滤的积累会实测拖垮检索:门槛是必需品,不是优化项。)
2. 写前对质:先 grep INDEX 有无同主题旧行。命中 → 三选一:就地改写旧行 / 旧行标 [失效:MM-DD 原因] 再垒新行 / 不写(旧行已覆盖)。
⚠️ 矛盾不等于过时:两条结论看着打架,可能只是适用条件不同(不同分支/环境/数据规模下都曾是对的)。分不清是"旧的错了"还是"场景变了"时——两条都留,各自补上适用条件,别强行合并成一条;机制只负责把冲突摆到台面上,裁决权在人(合并掉的那半,半年后翻案时找不回来)。未命中 → 把 mktmp 垒的 🚧 待补结论占位行改写成结论并去掉标记(没跑 mktmp 就在顶部新垒一行):
- MM-DD-<topic> — 一句话结论(查到什么/修了什么,不是主题复述)[关键词1][关键词2]
写成可被搜到的:用你以后会 grep 的词——既有用户的说法、也有技术锚点(报错串/表名/函数名),近义词和别名塞进 [关键词](grep 只认你真写下的字面)。
if-fail:占位行不在 → 顶部新垒;同 slug 已有结论行 → 就地改写禁止新旧并存;任务目录不见了 → 先回"先定位项目"核对目标仓。
3. 有复用价值 → 任务顶层写总结 md(个人层)。🔴 CHECKPOINT:个人层文件(tasks/、INDEX、pitfalls)要 git add -f 入共享库前,先跑 doctor.sh --strict 过一遍密钥形状扫描,再向用户确认"这是个人层留痕,确定进团队 git 吗"——进了共享历史就不可逆,默认不入。
4. 🔴 三问自检——收尾最容易被跳过的一步,必须逐问作答(反馈钩子,对应 SRE"每个复盘至少更新一篇 runbook"):
- "这次的操作以后还会做吗?" 第二次做同类操作 → 按 §4 沉淀/更新 SOP;踩了坑 → 按 §5 记一行——顺带问一句:能做成 hook/lint 自动拦截的,就别再垒行,直接做成自动化。
- "这次做的选择,半年后有人会问为什么吗?" 会 → 按 §4 决策记录登记进 docs/decisions/。
- "这次召回的哪条真用上了、哪条误导了?" 用上 → 该行尾计数 [用N] +1(首次确认从 [用2] 起);误导 → -1,反复误导 → 标 [失效:MM-DD 原因]。这是记忆的用进废退回路——没有它,索引只进不出。
5. 影响检查(seek.sh --inflight 看族内在途)——两类,时机不同:
- 状态型(改名/迁移/删除/schema/配置/索引重建等改了共享可变状态):改完立刻往 tasks/ENV-LOG.md 记一行、改名必带 ID,不等收尾(等收尾对方已在错误前提上跑了几小时)。🔴 反过来:指标变差/东西不见了时,先查 ENV-LOG 和族内在途,再怀疑自己的改动。
- 结论型(我的结论会改变你的判断):收尾时通知——往对方任务 progress.md「卡点·等待」追加一行 - MM-DD 受 <仓>/<本slug> 影响:<一句>,详情 Read <路径>;对方会话还开着可再发一行会话消息(只给指针不贴正文)。无影响不推,定向不广播。
- 池是分布式的:每个会话注册在自己最近的 .cairn,全局视图靠 seek 现算。细则(ENV-LOG 格式/诊断纪律/实战案例)见 assets/cross-repo.md §5。
3. 进度管理(回答"现在做到哪了")
- 看全局在途:速查表③(锚定行首,避开表头文字)——这就是项目进度面板,零维护成本。
🚧进行中⏸挂起等外部(等发版/等回复) 无标记 = 已完结- 续做某任务:读该任务
progress.md的"下一步/卡点"即可接上,不需要翻会话历史。 - 防烂机制:状态标记只存在于 INDEX 一个文件里,一次 grep 全暴露;发现过期 🚧 顺手改掉。禁止再造
.current-task式的独立指针。 - 主动自检:
./.cairn/scripts/doctor.sh(零依赖只读)揪陈旧标记(🚧 14/⏸ 60 天)、悬空、重复 slug、索引死链、INDEX 超 200 行容量硬顶。故意不进 SessionStart hook,要查才跑。--strict加占位未改写 + 密钥形状扫描(分享前必跑);--orphans加游离目录;--review除时间维度外还给内容维度过期信号(结论行引用的仓内路径已不存在)与增长度量(对比.snapshots/出净变化与"行/周"斜率——压垮长寿记忆的是斜率不是体积;回顾后仍净增="删除优先"没落实)。 - 图形面板(想"看"而不是 grep 时):
./.cairn/scripts/panel.sh生成.cairn/panel.html(零依赖 bash 现算,单文件深色 HTML:容量仪表/在途年龄/回顾候选/可搜索任务流/SOP/坑账/决策;只读快照,个人层不进 git,重跑即刷新)。 - 回顾仪式(每 2–4 周一次):🔴 触发时先读
assets/review-ritual.md再动手。入口:单仓doctor.sh --review/ 族内seek.sh --review(列已完结、无有效[用N]、超 30 天无活动的条目)。本质=给人审的提案 diff,删除与精简优先于新增;[失效]满一周期在此物理删除(平时不删的衔接点)。
4. SOP(一等公民)
定位:SOP = 可重复执行的步骤序列,是 agent 可靠性的核心(GenericAgent/MetaGPT 一类框架的本质就是 SOP 驱动)。声明式规约管"不能做什么",SOP 管"怎么把一件事做对"。
每篇 SOP 的结构(模板 assets/sop.template.md):
1. 何时用(触发条件,一两句)
2. 前置检查(环境/权限/数据前提,可执行的检查命令)
3. 编号步骤(每步 = 动作/命令 + 预期输出;agent 可直接照跑)
4. 验证(怎么确认做成了)
5. 失败处置/回滚
6. 文末:来源任务链接(从哪次实战沉淀的)
沉淀时机:第一次做完是任务总结;第二次做同一件事时把它抽成 SOP(一次性的事不配 SOP)。已有 SOP 走完发现步骤变了,当场更新——过期 SOP 比没有更危险。
升级链(知识的生命周期):
scratch 里的操作记录 → 任务总结 md → sop/<x>.md(项目级) → 个人 skill(跨项目通用时)
坑同理:pitfalls.md 一行 → 反复踩则升级为 CLAUDE.md 硬规则或 SOP 里的前置检查。
执行约定:做一件事之前先 ls .cairn/sop/ 或查 sop/目录.md,有 SOP 就照跑别重新探索(实证教训:Mage 运维每次重新探索 API 浪费了大量会话)。
决策记录(decisions/)——升级链的另一支
定位:SOP 沉淀"怎么做",决策记录沉淀"为什么系统长这样"。决策的内容在 task 里诞生,但 task 是冻结的个人档案;决策是活的团队资产——会被后来的决策取代,必须有"现行/superseded"状态和固定检索入口(半年后的查询入口是"系统现状",不是"某天的事件")。
- 位置:
docs/decisions/NNN-<主题>.md,顺序编号,文件名自成索引;团队层进 git。模板assets/decision.template.md(状态/日期/来源任务/背景/候选含被否原因/理由/后果),一页以内。 - 登记门槛(窄):影响未来多个任务的选择、被否但以后还会有人再提的候选、跨仓库约定。一次性"这个 bug 用哪种修法"不配。
- 被推翻不改旧文:新开一篇,旧篇状态改
superseded by NNN并链过去——"现行策略是什么"永远只有一个答案(对比任务档案:两个任务各说各话没人裁决)。
5. 坑与规约
- 坑账
spec/pitfalls.md:一行一坑,三段式:- YYYY-MM-DD <触发条件> → <正确做法>(<一句对比:老做法为什么不行>)[关键词]——成败对比一起蒸馏,比只记对策召回更准。踩到当场记,别攒。append-only 只针对新坑:同一个坑的对策变了就地改原行(行尾加修订:MM-DD),禁止新旧两行并存让人猜哪条现行;坑不再成立 → 标[失效:MM-DD 原因]不删行(保考古)。 - 分级——常驻/按需的线要划硬:偶发坑留在坑账(按需层);反复踩/一踩就重伤的升级为 CLAUDE.md 硬规则(每会话强制加载)或写进相关 SOP 的前置检查。必守规约必须常驻——别指望 agent 主动来 grep(实测:常驻遵守率 ~100%,靠按需检索只有 ~53%,败因是 agent 根本不发起检索)。CLAUDE.md 保持 200 行内——它只放"每次都必须遵守"的,不是知识库。
- 详细规约
spec/<主题>.md:编码规范、领域约定、接口契约这类太长放不进 CLAUDE.md 的 reference(团队层,进 git)。CLAUDE.md 只放一行指针("改 X 前先读 spec/x.md"),agent 按需读全文——继承 Trellis spec 层的分层理念,去掉自动注入。
6. 历史检索(回答"以前处理过没")
- 速查表②——一份文件永远在预算内。
- 没命中别停——扩词再搜,不是加机器(查询时临时扩,不维护同义词库):把要找的东西拆成 4–6 个变体——现象、业务名词、技术标识/报错串、中英各一——OR 起来搜:
grep -iE '超时|timeout|429|重试风暴' .cairn/tasks/INDEX.md。grep 只认你真敲的字面,所以近义词得自己敲全(收尾行在[关键词]里带别名就是为这个)。 - 同时查
sop/目录.md和spec/pitfalls.md(可能已升级成 SOP/坑)。 - 命中 → Read 对应任务的总结 md / progress.md(别进 scratch/ 翻垃圾)。
- INDEX 没有 ≠ 没做过(存量不回填):
ls .cairn/tasks/ | grep <关键词>按目录名兜底。 - 仓族检索:
seek.sh <词1> <词2>(默认全族、多词 OR、就近优先)。模式--inflight/--review/--links/--repos× 作用域--scope self|up|pair|family(up查规约、pair前后端联调);跨族加CAIRN_ROOTS。别的仓可能早就踩过你正要踩的坑。
6.5 仓族:跨项目借鉴与去重
根仓 + 子仓 = 仓族(fn-agent-manager + weknora/hunter-*…;study + blog/Nowledge…)。三条铁律:
- 借鉴上溯:动手前 seek.sh <词>(默认族内,就近优先打标);父仓/兄弟仓的 SOP、坑账可直接复用。
- 一件事只在一个仓记账:跨仓任务记父仓,另一仓写指针行 [→<仓>/<slug>],不双写。
- 冲突就近者胜(同 CLAUDE.md 祖先链);父仓规约对子仓不是全盘继承。
🔴 涉及父仓/兄弟仓、关联登记、跨仓去重、状态型影响时,先读 assets/cross-repo.md(作用域表 / 分类继承表 / 关联仓渐进登记 / ENV-LOG 细则)。
7. 记录分流表("这个该记到哪")
| 内容 | 去处 | 进 git |
|---|---|---|
| 一次性调试脚本/数据/截图 | tasks/<slug>/scratch/ |
❌ 用完即弃 |
| 本任务结论 | 任务总结 md + INDEX 一行 | ❌ 个人层;值得团队看时 git add -f |
| 跨会话任务的进度/下一步 | 任务 progress.md + INDEX 🚧 |
❌ 个人层 |
| 可重复的操作步骤 | sop/ + 目录.md 挂行 |
✅ 团队资产 |
| 踩的坑 | spec/pitfalls.md 一行 |
❌ 个人层;团队级的升级进 SOP 前置检查或 CLAUDE.md |
| 详细规约(编码规范/领域约定/接口契约) | spec/<主题>.md + CLAUDE.md 一行指针 |
✅ 团队资产 |
| 每次会话必须遵守的规则 | CLAUDE.md(200 行内) | ✅ |
| 方案/文章/对外材料 | docs/ |
✅ |
| 个人跨项目小事实 | Claude auto-memory | ❌ 单机 |
| 团队要看的定位修复全记录 | 飞书 wiki(规范 .agents/skills/issue-archive/SKILL.md,codex skill 需 Read 不能 Skill()) |
— |
8. 落地新仓库 & 异常兜底 —— 按需附页
以下两类场景不常发生,细节全挪到 assets/setup-and-recovery.md(省掉每次触发都载入的 token)。命中时先读那个附页再动手:
- 落地/安装 cairn("落地 cairn"、"初始化工作目录约定"):首选一条命令
bash "<本 skill>/assets/install.sh" <目标仓库>(幂等);附页含铺完两步收尾、🔴 settings 只追加合并、手动兜底、以及存量老仓 🛑 不搬家的规则。 - cairn 出故障排查(mktmp/INDEX/hook 没拦/面板混入/误提交/settings 覆盖 等 if-fail):处置对照表在附页。
9. 硬规则
- 根目录零临时文件;scratch/ 与 >1MB 数据文件永不进 git。
- 个人层(tasks/、INDEX、pitfalls)默认不进 git;要共享必须
git add -f显式声明,禁止整目录批量入库。 - INDEX 一行 ≤120 字符、写结论不写流水账、带 2–4 个
[关键词]、新条目在顶部;总量 ≤200 任务行(doctor 强制——超限去合并/归档,不是调高阈值)。 - 只做增量:收尾只允许 append 或就地改自己名下的行,禁止"顺手整理"全量重写 INDEX / pitfalls(LLM 全量重写会把记录改塌)。合并与精简只发生在回顾仪式里,以人审 diff 落盘。
- 批量改动前必须快照:个人层默认不进 git(很多仓根本没有有效 git),成批删改没有回滚手段。回顾仪式/跨仓去重前先
cp INDEX.md .cairn/tasks/.snapshots/INDEX-$(date +%Y%m%d-%H%M).md(留最近 5 份)。单行收尾不需要。 - 过期行标
[失效:MM-DD 原因]不直接删——保留决策考古;真正的删除是回顾仪式里的裁决。 - 不回填存量空任务目录;不做半途而废的搬家。
- 禁止引入独立状态指针文件(.current-task/task.json 式)——状态只长在 INDEX 标记和任务自身 progress.md 上。
- 过期即更新:走 SOP 发现步骤变了当场改;看到过期 🚧 当场摘。
- 收尾必做的只有 INDEX 一行(或门禁判"否"删占位);其余按价值判断;任何维护动作 >2 分钟即设计错误。