Skip to content

Anthropic built Scion for itself. Carbon Forge is yours →

Carbon Forge
Menu

指南

在 Carbon Forge 中回答 lane 冲突

两个智能体编辑同一个页面,其中一个先落地。Carbon Forge 不会去猜第二个想做什么:它写明冲突,展示双方,并等待一个绑定到回答者实际读过的 trunk 的回答。

其他语言: English, Español, Português, 日本語, Deutsch, Français, 한국어, Italiano, हिन्दी · 全部中文指南

声明:Carbon Forge 由 100monkeys.ai 开发,因此请把这篇指南当作开发方的说明来读。它由我们的一个 AI 智能体根据公开文档(英文)起草,主要是 Working in lanes 的第 6 到第 8 步和 Lanes 目录页,并经公司审阅;这个简体中文版是英文原版指南的译文。每个调用都是文档自己的示例,引文保留英文原文,译文附在旁边。文档没有回答的问题,最后一节会说明。

了解什么算冲突

lane 是覆盖在 trunk 之上的一组补丁,在它上面的每次读取,都是把 trunk 的现状和这些补丁组合起来。补丁处于冲突,是指 "when it no longer applies to the trunk: its old text no longer matches or matches twice"(它不再能应用到 trunk 上:它的旧文本不再匹配,或者匹配了两次),或者 trunk 改动了补丁所替换的树条目或库条目。仍然能应用的补丁不算冲突,即使 trunk 改了同一节;读取会把它报告为一个移动过的节,"worth a reviewer's look, not a conflict."(值得审阅者看一眼,但不是冲突。)两个智能体修改同一个文件的不同部分,两者都会落地。本指南其余部分遵循的规则是:"Nothing merges by itself."(没有什么会自己合并。)

发现某条 lane 处于冲突中

lanes.get 返回 landable,只有当 lane 是打开的、至少有一个有效补丁且没有冲突时它才为 true。在应用中,lane 的页面 /<workspace>/lanes/<label> 有一个 Conflicts 标签页。不想轮询的话,当 trunk 上的一次落地首次让 lane 进入冲突时,lane.conflict webhook 事件会写明这条 lane 以及它冲突行的路径。

阅读冲突

lanes.conflicts { "label": "agent/7/fix-login" }

每一行都带着合并者需要的东西:补丁本身;补丁生成时该节的样子;trunk 上该节现在的样子;lane 组合后的视图;以及移动了它的 changeset,每个都带有 intent、执行者和落地它的 lane。对代码树,该行还会附上每个冲突文件的统一 diff;对有语法的语言,还会附上一份不同的顶层定义的参考列表,它 "decides nothing"(什么也不决定)。这个工具只读,只需要 viewer 角色。在应用中,Conflicts 标签页显示 trunk 文本和 lane 文本,以及回答每一项的确切调用,"ready to copy, while the page itself decides nothing."(可以直接复制,而页面本身什么也不决定。)

回答文本冲突

lanes.resolve { "patch_id": "…", "note": "kept both sign-in options", "old_string": "…", "new_string": "…" }

回答是一个新补丁,附带 1 到 1,024 个字符的说明,写明原因。它 "matched against the trunk as it stands now and bound to the hash of that state."(与 trunk 的现状匹配,并绑定到该状态的哈希。)旧补丁作为历史保留,标记为 superseded。树冲突用同样的方式回答,使用 upserts、removals 或 patches;库条目则用 sha256。按顺序回答:同一实体上较晚的补丁要等较早的那个解决之后。

或者丢弃补丁

lanes.drop_patch { "patch_id": "…" }

当 trunk 已经做了补丁想做的事,或者补丁不再需要时,就把它丢弃。如果补丁所针对的实体已经不存在,文档的拒绝信息会让你丢弃补丁,而不是解决它。

回答移动冲突

lanes.resolve { "patch_id": "…", "note": "kept the trunk's rename and moved on from it", "to_path": "guides/sign-in" }

重命名页面的 lane 会在以下情况下冲突:trunk 之后也移动了同一个页面,trunk 现在占用了新路径,或者页面已不存在。用 to_path 回答,标题需要改变时再加 title。解决方案绑定到 trunk 现在的路径,所以之后 trunk 的移动也会让它过时。

回答组装工作中的冲突

lanes.merge { "from": "agent/8/tests", "into": "agent/7/fix-login", "intent": "assemble the login fix" }

主导智能体在落地前把几个智能体的 lane 汇集到一条里。以这种方式带过来的补丁,在接收方 lane 改动了它所改动的内容时会冲突,lanes.conflicts 会以 moved_by 写明它现在所依赖的接收方 lane 的较早补丁。它的解决方案回答的是接收方 lane 的视图,而不只是 trunk。

落地,先做一次 dry run

lanes.land { "label": "agent/7/fix-login", "intent": "fix: accept magic links", "dry_run": true }
lanes.land { "label": "agent/7/fix-login", "intent": "fix: accept magic links" }

落地会在它自己的事务中再次检查,只要有任何东西移动过,就整体拒绝,并写明每个冲突。一个解决方案回答的是 trunk 的一个确切状态;如果 trunk 在落地前移动了那一节,解决方案就会过时,落地会被拒绝,信息是:

resolution <id> was resolved against <hash>; the trunk's section is now <hash>

这时重新读取 lanes.conflicts,回答新的状态。在带有落地规则的受保护 trunk 上,记住批准之后的任何解决方案都会让那些批准 "count for nothing"(全部作废),所以批准要在最后一次回答之后。带上 idempotency_key 发送落地请求,超时后的重试会返回第一次落地的结果,而不会落地两次。

如果一条 lane 同时带着代码修改和它的文档,请阅读我们的指南 把一次代码修改和它的文档放进同一个 changeset 落地。

文档没有回答的问题

没有静默合并。每个回答都附有说明。