设计理念
贯穿每个技能、每份参考资料、每个转换器的共性规则。读一次;当任何发现看起来奇怪时翻一翻。
无论技能是否被调用都成立的三条规则:
- 陈述条件,而非流程或情形。当一段文字在不断吸收「把我们刚发现的 case 也加上」时 — 无论是在编写、评审回合还是你为一条发现所做的修复 — 这种表述就是错的。删掉那些补充并重新陈述目标,然后针对原表述服务的每条路径重新验证;一次无法再命名任何路径的重述是一个新的缺陷,而不是简化。
- 仅在你拥有的层级规定机制。委托型技能陈述条件、安全失败方向以及不可推导的被调用者事实,绝不复述被调用者的命令。
- 把你触碰的段落提升到标准;保留未触动的段落不动,并把那些段落记为后续工作。技能早于标准存在,会逐步演进到该标准。
十条贯穿主题
这些主题来自对 105 份参考文件的完整阅读。它们与版本无关 — 新技能加入时,同样的形态会再次出现。
1. 稳定的 ID 契约
统一计划格式使用一套小巧、固定的稳定标识符,下游消费者据此索引:
- R-ID(需求)
- 产品需求的稳定 ID。会被实现单元和测试场景引用。
- U-ID(实现单元)
- 工作单元的稳定 ID。实现提交可在标题后追加
(U3)。 - KTD(关键技术决策)
- 规划阶段做出的决策,当由用户指示或用户批准时,标注
session-settled:来源类别。 - A / F / AE
- 锚点:Assumption(假设) / Fact(事实) / Architectural Element(架构元素)。计划章节的引用标签。
这些 ID 永不重写、永不重新编号。消费者可以放心地认为 (U3) 在跨会话和提交中指向同一个单元。
2. session-settled: 来源
计划中的决策分三类来源:
| 类别 | 含义 | 示例 |
|---|---|---|
session-settled: user-directed | 用户在规划对话中明确指示了该决策。 | 「用 SQLite,因为我们生产里已经在跑它。」 |
session-settled: user-approved | 用户批准了规划者抛出的某个选项。 | 「在规划者提出 3 次重试后,用户表示同意。」 |
| (未标注) | Agent 自身的建议。不能约束用户。 | 规划者基于证据的最佳猜测。 |
只有前两类可以钉住后续决策。Agent 绝不自己把自己的建议定为定论。
3. 三层模型架构
每个调度型技能都使用三层能力阶梯。选择按任务形态,而非模型名称。
- 抽取层
- 能完成工作的最便宜模型。用于检索-引用类任务:
work-recap-scout、pr-snapshot快照器、learnings-researcher。 - 生成层
- 用于起草的能力型模型:
ce-code-review的人格、调度工作者、构思框架。 - 顶层
- 编排器的主上下文。绝不调度。负责判断、综合、调和与最终成稿。
4. 带 independence_verified 的跨模型同行
当改动风险足够大时,ce-code-review 和 ce-doc-review 会在同一份产物上调用一个同行模型。同行的发现只在「如果让一个独立评审者重新看也会得出相同结论」时才会被合入;综合阶段会在每条合格发现上盖上 independence_verified: true 的戳。
只用锚定式评分。只有 0 / 25 / 50 / 75 / 100 这四个级别。连续的浮点数会引入虚假的精度;请使用锚。
5. 防「狼来了」收敛(ce-babysit-pr)
babysit 循环是「看门狗喊狼来了」最容易发生的地方。设计编码了三条防护:
- 看轨迹,不看断言。快照会输出
check_recur_max、recurring_checks、unresolved_trend、new_threads_this_tick、heads_since_progress。委托的叶子节点据此判断是否真的收敛。 - 触发条件透传给叶子。当
invariant_rounds[].rounds >= 2或其他触发条件被跨越时,编排器必须把轨迹透传给ce-resolve-pr-feedback或ce-debug— 绝不自己宣告不收敛。 - 有界停止。
max-runtime限定运行;「看起来就绪」闸门检查 7 个条件;未解决的残留会阻塞「就绪」声明但不会停止循环。
6. docs_root 纪律
docs_root 是 .compound-engineering/config.yaml 中唯一只从 config.yaml 读取的键 — 永远不会从 config.local.yaml 读取。原因:docs_root 是一条物理机器路径;本地覆盖会把用户的机器路径泄漏到 CI。完整的分层规则见 配置系统。
7. 通过建目录实现原子写
多步写入采用「先建目录,再写文件」的模式,避免半写状态。父层写入器先调用 ensureDir(path);文件随后写入已存在的目录。代价是多一次 syscall;收益是中途崩溃时不会留下孤立文件。src/utils/files.ts 中的 writeTextSecure / writeJsonSecure 变体实现了该模式。
8. 共享工作区的 wave 契约
当多个子 Agent 需要写入同一份检出(ce-work 中的常见情况),它们共享工作区但通过文件所有权来协调:
| 约束 | 规则 |
|---|---|
| 1 | 按不相交的文件所有权进行扇出,而非按条目。 |
| 2 | 条目会跨文件;共享同一文件的 Agent 会丢失彼此的改动。 |
| 3 | class 条目要预先枚举每个具体位置。 |
| 4 | 工作者集成是规范的;由编排器提交。 |
| 5 | 工作区隔离是升级手段,而非入门券。 |
9. 每条想法的输出契约(ce-ideate)
ce-ideate 的发散阶段生成的每条想法都必须携带一份固定契约:
- title
- 短名称;在本次运行内唯一。
- summary
- 一段话 — 这条想法是什么意思。
- axis
- 它针对的是分解出的 3–5 个维度中的哪一个。
- basis
- 想法的依据 — 仓库 / 外部 / 非软件。
- why_it_matters
- 它所交换的价值或风险。
- meeting_test
- 能证实这条想法值得推进的单一可观测事实。
存活下来的想法必须解释每个字段;被拒绝的候选只需要一行拒绝理由。这就是大量生成、全部批判、只解释存活者。
10. 信心锚(0 / 25 / 50 / 75 / 100)
发现、决策和评分使用 5 个锚定等级,而非连续浮点数。原因如下:
- 锚迫模型表态;浮点让模型对冲。
- 锚可在评审者之间聚合;浮点不能。
- 锚与闸门行为匹配(75 配跨角色一致 → 应用;50 有锚且无一致 → 不应用)。
「信心 0.73」看起来精确但毫无意义 — 发出该评分的模型也无法为 0.71 与 0.75 之间的取舍辩护。而 Confidence 75 (cross-persona agreement noted in the report) 则把闸门需要的信息讲清楚了。
AGENTS.md 三条规则的实践
陈述条件,而非流程
当你发现自己往规则里加「并且在 X 时也这样」时,请说出 X 所代理的那个条件并直接陈述它。一条必须枚举情形的规则,本身就陈述错了。
具体地说:ce-babysit-pr 的「看起来可以合并」闸门并不是说「没有失败检查时就绪」。它说的是「mergeability_certain + MERGEABLE + CLEAN + 没有 base_ref_blocker + checks_terminal + 积压为零 + branch_currency_blocker == null + open_needs_human == 0 + settle 已过 + review-still-coming 干净 时就绪」。情形由合取涌现;条件就是这个合取,不是「情形 X:就绪」。
仅在拥有的层级规定机制
委托型技能陈述条件、安全方向以及不可推导的被调用者事实。它不复述被调用者的命令。
具体地说:ce-lfg 第 5 步说「应用并持久化评审修复」 — 并指向 references/review-followup.md 中的四项条件过滤。ce-lfg 不会说「对每条发现,检查 suggested_fix 是否存在,再检查 confidence」。那是 ce-code-review 的机制。lfg 陈述策略(「仅当…时在工作区中应用一条发现」),由参考资料拥有该规则。
把你触碰的段落提升到标准
如果你在编辑段落 X,就把 X 修到符合标准。段落 Y 留着不动 — 并把 Y 列为后续工作。
具体地说:当 ce-skill-work 的评审模式发现一个缺口时,修复应当是重写该段,而不是再添一个 case。如果同一段在第二轮又拿到发现,把所有内容合并成一次重述,永远不要再二次修补情形列表。
编码了这些规则的 15 份技能设计解决方案
docs/solutions/skill-design/ 收录了 44 份沉淀的学习。上面 10 条主题是元规则;这 44 份文件是逐个 case 的编码。以下这些反复出现于评审发现:
| 解决方案文档 | 重复出现的模式 |
|---|---|
skill-gates-state-conditions-not-prescribed-git-commands.md | 同一发布闸门改了六版;修复是陈述条件。 |
state-the-condition-not-a-placement-absolute.md | 代理规则(条件 + 放置绝对项)一旦重复就会开始禁止其条件所要求的做法。 |
subordinate-the-failing-shape-to-the-condition.md | 在无法实例化条件的宿主上如何保持条件可读。 |
strong-models-mask-defensive-skill-fixes.md | 带技能 vs 基线的平局并不能证明修复有效。 |
size-driven-skill-restructure.md | ce-babysit-pr 从 90KB → 8KB;如何在不丢失不变量的情况下瘦身。 |
bound-contradiction-checks-to-named-guidance.md | ce-compound-refresh 与学习命名的指导文件作对比。 |
dispatch-script-failure-degrade-outcome-not-boundary.md | 脚本守住边界;失败时退化的是结果,不是边界。 |
liveness-judgment-belongs-to-the-agent.md | ce-babysit-pr 在收尾后多挂了 15–30 分钟,因为活性检测器把评审状态建错了模。 |
watch-loops-need-a-blocked-external-terminal-state.md | watch 循环需要第三个终态:blocked-external。 |
named-frameworks-with-detection-conditions-for-review-personas.md | 引用 OWASP / Fowler smells / Release It!;而不是「以 X 视角评审」。 |
context-absent-skill-handoff-needs-pinned-invocation.md | 为什么 Skill 工具在跨技能交接时胜过散文。 |
invocation-opt-out-flags-block-sibling-skill-invocation.md | disable-model-invocation 会在宿主之间阻断兄弟技能调用。 |
inline-callee-side-channel-must-name-where-it-may-not-land.md | 调用方的侧信道规则必须说明它「不能」落在哪里。 |
workspace-isolation-is-escalation-not-entry-fee.md | 隔离是给必须提交的工作者用的;不是并行的默认项。 |
post-menu-routing-belongs-inline.md | 不要把菜单路由放在模型不会打开的参考资料里。 |
每一条都在 解决方案库 中有专属页面;本表只是一个主索引。