泳道(Lanes)
圣保罗一支支付团队的两位工程师,对同一个仓库各自跑同一个 Claude Code 会话,跑了四十分钟才有人发现。他们在不同分支、不同终端、同一栋楼的不同楼层。等他们对完账,agent 的 commit 已经被互相覆盖三次、又有两次新的彼此冲突,值班工程师被一个其实并不 flaky 的测试叫起来——那个测试是被一个谁都没 brief 过的模型悄悄改写了。
正是为了挡掉这种碰撞,lane 才存在。如果这一页你只记一件事:lane 是围栏。它告诉工作区、agent 和你的队友,这份工作、这个作用域、这一组权限,在它关闭之前属于这一次运行。两条 lane 不会重叠在同一组文件上。两个 agent 不会同时跑在同一条 lane 里。
观点:agent 工程的大多数混乱不是模型质量问题,而是缺一个”这是我的,别动它”的原语。lane 就是那个原语。
一条 lane 到底是什么
从存储角度看,一条 lane 是 lanes 表里的一行:UUID、一个 owner(人类身份或 agent 身份)、一组它已 claim 的路径、一条写在溯源台账上的签名起始条目,以及一个状态——open、paused、merged 或 abandoned。你通常不需要想那一行。你想的是 Lanes 面板里的那张卡片:标题、作用域、实时 diff,以及底部的 sign-off 槽。
从运行时角度看,一条 lane 是一道强制边界。当 lane A 内部的 agent 试图读或写 A 已 claim 作用域以外的文件时,工作区在文件系统被触及之前就返回一个 LANE_SCOPE_DENIED 错误。agent 在自己的 tool log 里看到错误,要么收窄工作,要么请求 lane owner 扩大 claim。
从审计角度看,一条 lane 是溯源台账上一段自包含的章节。lane 内部每一次 read、write、模型调用、命令执行、sign-off 都链回该 lane 的起始条目。当你为 SOC 2 或内部复盘导出证据时,你导出的是 lane,而不是 commit。
为什么这不是一个 branch
显而易见的反驳:“我已经有 git branch 了,为什么还要 lane?”
Branch 回答的是哪些文件跟 main 不一样?它不回答谁在做这个?、他们用什么工具?、过去一小时有没有别人动过同样的路径?、这份工作里 agent 能否调用生产数据库? Branch 是个快照原语。lane 是个会话原语。
实践中你常会得到 1:1 映射——一条 lane 产出一个 branch,merge 进一个 PR。但 lane 比 branch 活得久。branch 被删除之后,lane 仍然承载着模型日志、tool call、sign-off 和溯源。六个月后 branch 不在了,lane 还可查询。
密码学溯源
每条 lane 都带一条签名条目链——Ed25519 签名套在 BLAKE3 内容哈希之上,按只追加顺序排列。链头对前一个链头签名,因此篡改任何历史条目都会让之后的所有条目失效。这跟 federation root 签名方为工作区证据包所用的构造一样。
三把 key 重要:
| Key | 存放位置 | 用途 |
|---|---|---|
| Lane signing key | 工作区 HSM | 给 lane 内的 tool-call 条目签名 |
| Owner attestation key | 用户设备 | 给关闭 lane 的 sign-off 条目签名 |
| Federation root key | 仅工作区所有者 | 把已关闭的链头戳进证据包 |
普通 lane 里你不会手动管理这些 key。它们只在你接外部 HSM(BYOK)、为审计导出证据,或从工作区外验证某张 federation 收据时才会出现。
作用域 claim 与冲突
一次作用域 claim 就是一组 glob:apps/api/auth/**、infra/terraform/networking/*.tf、package.json。当第二条 lane 试图 claim 一个跟现有 open lane 重叠的 pattern 时,工作区会在两条 lane 的 UI 上把冲突浮出来,并拒绝启动新的那条。新 lane 的 owner 看到已有 owner 的名字,可以选择等、请对方释放冲突路径,或者收窄作用域。
值得知道的三个例外:
- 一条 lane 可以通过
Share path主动把某条路径共享给另一条 lane。极少用——主要用于两个并行 feature 都要碰的共享配置文件。共享后这条路径成为一个串行化资源:agent 在一次 tool call 期间拿锁。 - 工作区所有者可以从停滞的 lane 上force-claim 一条路径。这会写下一条显式标明覆盖的 sign-off 条目。
README.md、CHANGELOG.md,以及任何被.sprintloop/shared-paths匹配到的路径,永远是共享的。任何 lane 都能向其追加。
创建一条 lane
按自动化程度递增的三种方式:
-
从 Lanes 面板手动创建。 点 New lane,敲一行标题,选仓库,claim 你预计要碰的路径,把自己或 agent 指派为 owner。lane 以 Drafting 状态打开,台账上还没有条目。
-
从 Issue 创建。 当一个 SprintLoop Issue 被拖进 In progress,工作区会建议针对其关联仓库新开一条 lane,并把 Issue 的验收标准预载为 lane 的 brief。多数团队第一周就会落到这条路径上。
-
从 agent harness 创建。 跑在 SprintLoop 里的 Claude Code 或 Codex 会话在首次读文件时打开自己的 lane。agent 是 owner,发起会话的人是 sign-off 权限方。lane 的标题先按首条 prompt 自动生成,首次 commit 后被改名。
关闭一条 lane
一条 lane 在其 diff 被 merge、owner 显式放弃、或七天无活动(每个工作区可配)时关闭。关闭时,lane 台账的链头被 owner 签名后戳进工作区的 federation root。这一刻起,这份工作便归档可证:任何持有公开校验 key 的人,日后都能验证这一组变更、这个顺序、这位 owner、这些 tool call,最终落到这个 commit hash。
如果你只想再读一页,那就读 Harness 竞速——单条 lane 是如何被同时 dispatch 给两个或更多 agent 的。