


让AI编程代理遵守分支和提交规则,不破坏发布
在有版本控制的代码仓库里让AI编程代理工作时使用。规定如何创建分支、运行测试、写提交、存放临时文件,以及如何编写在不同AI工具中都能用的代理指令,避免破坏发布。
要使用这个AI规则
## 只保留一个代理指令文件,并确认该改仓库的哪个部分
- 只保留一份权威的代理指令文件。当某个运行环境要求使用不同的文件名时,用符号链接指向权威文件,而不是复制内容:第二份真实文件会随时间产生分歧,还可能让把备用路径当作不同产物类型的校验工具出错。保留符号链接,不要让它被还原成普通文件。
- 一次改动可能同时涉及多个界面——已发布的产品内容、目录或配置元数据、构建或CLI代码。在确认每个被修改文件归属于哪个界面之前,不要想当然地认为某次改动"只是代码"或"只是文档",要修改真正拥有该行为的界面,而不是与之相邻的界面。
- 把仓库自身的指令文件当作仓库的编写上下文来看待。任何必须在运行时影响已发布产物的规则,应该写在该产物内部,而不是写在仓库的指令文件里,因为最终用户是依据自己的指令来运行该产物的。
## 在功能分支上工作,只通过拉取请求合并
- 对任何非琐碎的改动都要创建功能分支。如果当前已经在该任务对应的正确分支上,就继续使用它;除非明确要求,否则不要额外创建分支或工作树。
- 所有对默认分支的改动都要通过拉取请求(pull request)进行。直接推送或合并会绕过校验和发布检查,导致版本偏移,之后需要多个步骤才能恢复;应依靠分支保护机制来强制执行这一点,而不是想办法绕开它。
- 把发布工作交给发布自动化流程处理。在日常工作中不要手动修改由发布流程管理的版本号,也不要手动编写发布说明,应使用符合规范的提交标题,以便自动化流程能够识别改动意图。
## 改动后运行全部测试,并区分自动测试和评估
- 任何影响解析、转换、输出或机械性约定的改动之后,都要运行完整测试套件。本地套件与CI运行的是同一套,并不存在可以跳过的"仅限本地"通道。
- 按性质区分检查方式。确定性的不变量——如模式(schema)和前置元数据(frontmatter)、路径安全性、输出结构、可被grep检索的约定——放在自动化套件中,必须在CI中通过。而像判断力和克制力这类非确定性的行为,应通过评估来验证,不要伪装成脆弱的字符串测试。
- 当审阅者发现套件遗漏了某个可被grep检索的不变量时,优先收紧现有的防护措施,而不是新增一整套测试;并且要锁定能被证伪的最小单元——即一个在退化改动上会失败的令牌、路径或测试样本——而不是对整个文件内容做快照。
## 决定临时文件放在哪里,计划和文档留在仓库中
- 默认情况下,把用完即弃的临时文件放到操作系统的temp目录:这是一个按每次运行划分、由操作系统自动回收的临时目录,适合只使用一次就丢弃的文件。
- 对于需要跨多次调用保留的临时文件,应使用归当前用户所有的固定前缀路径,而不是随机的temp目录,这样后续运行才能找到它。创建该根目录时要设为私有(仅所有者可访问),如果它是符号链接或归属于其他用户则应拒绝使用,并且每次运行保持独立目录,除非后续运行确实需要枚举同级的最终产物。
- 只有当产物确实与当前仓库或分支密不可分、需要用户直接整理维护,或者相对于仓库的路径本身就是输出的核心内容时,才把临时文件绑定在工作仓库内。
- 持久化输出——计划、规格说明、文档、最终交付物——应放在受仓库版本控制的位置,绝不能放在上述任何一层临时空间中。
## 名称只用ASCII,Markdown表格用竖线分隔
- 标识符——文件名、命令名、代理名称——一律只使用ASCII字符,因为转换工具和正则表达式依赖于这一点。
- 编写Markdown表格时使用竖线分隔的行,绝不使用制表符绘制字符。
- 正文中可以使用Unicode字符;但在代码块和终端示例中优先使用ASCII箭头(-> 和 <-),以确保复制出来的命令具有可移植性。
## 按改动实际做的事选择fix或feature提交前缀
- 选择规范化提交前缀时,要依据改动做了什么,而不是依据文件类型。位于docs或skills目录下的内容仍然可能是产品代码;只有当文件的唯一用途就是文档时,才使用docs前缀。
- 当fix和feature前缀似乎都合适时,默认选择fix:修复损坏或缺失的行为属于fix,即使实现方式是新增代码也是如此。feature前缀只保留给那些此前用户无法实现、且原本并未损坏的能力。经验法则:如果今天写的回归测试放在改动之前会失败,那这次改动就是fix。
- 提交范围要写得尽可能窄且有用——技能、模块或区域名称——绝不要把提交范围定为整个项目。未经用户明确确认,绝不添加破坏性变更(breaking-change)标记,因为它会触发自动的主版本号提升,而用户可能并不希望如此。
## 使用相对路径,让每个发布单元在任何AI工具中都能运行
- 保持每个可发布单元自成体系:只引用自身目录树内的文件,并使用相对路径。绝不要跨越到同级单元中引用文件,也不要硬编码绝对安装路径——安装位置是带版本且不可预测的,打包过程会将每个单元各自独立地复制。共享文件应在每个单元中各自复制一份,而不是跨目录依赖。
- 不要在没有兜底方案的情况下依赖特定于宿主环境的变量——例如skill-directory、session-id或plugin-root这类替换变量——要确保在值为空或未解析时也有可用的兜底方案;其中不少变量只在某一种运行环境中会被赋值,在其他环境中则会悄无声息地为空。
- 对于运行内置脚本的shell命令,要让路径解析变得确定:在同一条命令中直接内联设置一个绝对基准目录(因为shell状态不会在多次调用之间保留),并基于该目录引用脚本,而不要使用裸相对路径——裸相对路径会相对于用户的项目目录解析,而不是相对于该单元本身。
## 代理指令只写可检验的规则,先给审查意见分类再修改
- 只有当一行内容陈述了一个可被证伪的约束、纠正了某种已知的默认倾向或已观察到的走捷径行为,或者提供了能改变决策的领域知识时,才允许保留它。删除诸如"要做到彻底"这类模糊的努力或质量类措辞;要么替换成可观察的规则,要么直接删掉。不要给一条已经能够独立成立的指令附加激励性的说明理由。
- 只在已证实存在偏移的位置重复某条指令——即该指令所处的位置确实会决定它是否生效的地方,并且要用一致性测试来保护那些真正必要的重复内容。
- 把应用审阅反馈当作一次实质性的修订来对待:将每一条意见分类为"需修改(Change)"、"需核实(Verify)"或"可考虑(Consider)",只对"需修改"的条目进行编辑;在每条意见所属的层级上,用最小的手段进行修复;然后重新通读该段落,删除因这次改动而变得重复或过时的文字。审阅者的措辞只是关于如何修复的一种假设,而不是对修复方式的最终裁定。
## 在已加载的上下文中查找项目约定,不点名指令文件
- 当某个单元需要探知项目约定——如问题追踪系统、提交格式、lint命令、范围限制——应描述在代理已加载的上下文中要寻找的内容,而不是指定要打开哪个文件。在读取路径中不要指名带点前缀的指令文件:宿主环境已经会注入根指令文件,其文件名在不同运行环境中各不相同,而让代理去打开指名的指令文件,正是提示词注入防护机制会标记的那种形式。
- 只有在上下文引用无法表达需求的情况下才指名具体文件——例如需要将某项约定写回目标文件,或者需要读取一个全新子代理确实无法继承的内容。
- 按类别来描述某项能力(例如"该项目的问题追踪系统,如GitHub Issues、Linear或Jira"以及"无论它暴露出什么样的接口"),绝不要把缺少某个可执行文件、环境变量或连接器,当作该能力不可用的证据。
## 比较文件内容而不是版本号,确认会话是否读到最新修改
- 修改了一个长时间运行的会话在启动时加载过的内容之后,应假定该会话仍在使用修改前的旧版本。缓存捕获的是加载那一刻的状态,而同一发布版本内的修改并不会改变版本号,所以版本号一致只能证明缓存来自这个发布版本,并不能证明它捕获到了你最新的修改。
- 要确认哪个版本正在生效,应该把已加载文件的内容与你的工作副本进行比对,而不是查看版本号字符串。内容一致,说明正在运行的版本是最新且可信的;内容不一致,则应先重启或使用重新加载的路径,之后才能信任行为测试的结果。
- 机械性的代码路径——脚本、解析器,以及测试套件所执行的任何内容——始终运行的是当前源代码。只有在启动时加载的指令与文字说明这一层,才会存在这种滞后性(过期)问题。
