其他语言: English, Español, Português, 日本語, Deutsch, Français, 한국어, Italiano, हिन्दी · 全部中文指南
声明:Carbon Forge 由 100monkeys.ai 开发,因此请把这篇指南当作开发方的说明来读。它由我们的一个 AI 智能体根据公开文档(英文)起草,主要是 Working in lanes 和 Code and files on the substrate,并经公司审阅;这个简体中文版是英文原版指南的译文。下面的每个调用都是文档自己的示例,引文保留英文原文,译文附在旁边。文档没有回答的问题,最后一节会说明。
为什么是一个 changeset
文档首页这样定义这个单位:"A changeset is many writes landed as one: every page, atom, code tree and library entry it names is written in one transaction under one intent, the changeset's intent, or nothing is written"(changeset 是作为一个整体落地的多次写入:它指定的每个页面、atom、代码树和库条目,都在同一个 intent,即 changeset 的 intent 下,在一个事务中写入,否则什么都不写入)(Welcome to Carbon)。代码和文字是同一份记录中的实体,所以读者不会在新代码旁边看到旧页面,回退也会把两者一起退回。
你需要一个能写 lane 和代码树的令牌;文档为这类工作指定的预设是 SCM agent。
第 1 步:弄清 trunk 是否受保护
workspaces.get_settings {}
→ { "workspaceId": "…", "trunkProtected": true, "trunkProtectedEntities": false } "With trunkProtected, code trees, library entries and changesets go through a lane; with trunkProtectedEntities as well, page and atom edits do too."(开启 trunkProtected 时,代码树、库条目和 changeset 要经过 lane;再开启 trunkProtectedEntities 时,页面和 atom 的编辑也一样。)无论哪种情况,这里用 lane 都合适:文档在 "a change should be seen whole before it lands"(一次修改应在落地前被完整看到)时使用 lane。
第 2 步:为这次修改开一条 lane
lanes.create { "label": "agent/7/fix-login" } 标签由斜杠分隔的小写段组成,在打开的 lane 中唯一。"A lane costs nothing to open and holds no copy, so one per task, or one per agent, is the expected use."(开一条 lane 没有成本,也不持有副本,所以每个任务或每个智能体一条,是预期的用法。)传入 owner,这条 lane 的写入、冲突解决和落地就只归你。
第 3 步:在 lane 上修改代码
trees.apply_patch { "path": "app", "operations": [ { "op": "patch", "entry_path": "src/login.ts", "old_string": "password", "new_string": "magicLink" } ], "intent": "fix: accept magic links", "lane": "agent/7/fix-login" } 把标签作为 lane 传入,"the write is held on the lane as a patch against the trunk"(写入作为针对 trunk 的补丁保存在 lane 上),"the trunk is not written"(trunk 不会被写入)。trees.apply_patch 只发送操作(文件内替换、写入、删除或移动);trees.put 发送代码树的完整条目列表。无论哪种,"the lane keeps only what differs."(lane 只保存不同的部分。)
第 4 步:在同一条 lane 上修改描述它的页面
pages.apply_patch { "pathOrId": "guides/login", "old_string": "Sign in with a password.", "new_string": "Sign in with a magic link.", "lane": "agent/7/fix-login" } 标签相同,所以页面编辑就在代码旁边等待。如果这次修改还要给页面改名,pages.move 也接受 lane,而且 "a rename belongs to the change that needs it."(改名属于需要它的那次修改。)截图也以同样方式,用 library.put_at_path 加上标签放进去。
第 5 步:读出这条 lane 会产生的结果
在读取时传入同一个 lane,"the answer is the trunk as it stands now with the lane's patches applied, composed on the call"(返回的是当前的 trunk 加上 lane 的补丁,在调用时组合而成):页面用 pages.read,代码用 trees.get、trees.diff 或 trees.grep。然后审阅整体:
lanes.diff { "label": "agent/7/fix-login" }
lanes.get { "label": "agent/7/fix-login" } lanes.diff 返回 "entity by entity, the trunk's section and the lane's"(逐个实体,trunk 的段落和 lane 的段落);lanes.get 返回补丁、冲突和 landable:"open, at least one live patch, no conflict."(处于打开状态,至少有一个有效补丁,没有冲突。)在应用中,/<workspace>/lanes/<label> 显示同样的内容,每个改动的文本文件以 unified diff 显示。读过 lanes.export 并在其 composition 哈希上批准的审阅者,"has approved exactly that change."(批准的正是那一次修改。)
第 6 步:回应冲突
如果这期间有人在 trunk 上改了页面或文件的同一行,补丁就处于冲突状态,而且 "nothing merges by itself."(没有任何东西会自行合并。)lanes.conflicts 给出补丁、原来的段落、trunk 现在的段落,以及改动它的 changeset。用 lanes.resolve 回应,或用 lanes.drop_patch 丢弃补丁。文件内的替换会 "replays on the trunk's current file, so two agents changing different parts of one file both land."(在 trunk 当前的文件上重放,所以修改同一文件不同部分的两个智能体都能落地。)
第 7 步:带着 intent 一次落地
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" } 先运行 dry run,再落地。"Every live patch is replayed as one changeset whose intent is lane agent/7/fix-login: fix: accept magic links"(每个有效补丁被重放为一个 intent 为 lane agent/7/fix-login: fix: accept magic links 的 changeset),在它自己的事务中再次检查,并且 "refused whole, with every conflict named, if anything moved."(只要有任何东西变动,就整体拒绝,并点名每个冲突。)让 intent 以 feat、fix、docs、chore 或 revert 开头,发布说明就会按这个词归类。发送 idempotency_key,超时后的重试会 "answers the first landing's result instead of landing twice."(返回第一次落地的结果,而不是落地两次。)
第 8 步:回读这次落地
打开 /<workspace>/changes/<id>:它显示完整的 intent、执行者、来源 lane、封印和各个条目,代码文件和页面在一起。改动行上的 blame 现在会给出你写的 intent:trees.blame 给出 "each line's revision, changeset, intent, actor and landing lane"(每一行的修订、changeset、intent、执行者和落地 lane)(Code and files)。要一次撤销两者,changesets.revert 会写入一个新的 changeset,恢复这次落地写过的每个实体。
文档没有回答的问题
- 不经 lane、一次调用完成的路径。lanes 指南展示了带
"targets": [ … ]的changesets.apply,但没有说明那里 target 的结构,所以本指南使用 lane。 - 哪个页面描述哪段代码。这里没有建立这种关联;声明它正是 governed seams 的用途(见下面的相关指南)。