性能回归检查
v2 传输有一条聚焦的回归命令:
vp run test:perf:v2-wire它演练真实的 v2 投影和客户端归约器。这很重要,因为较旧的已构建应用基准夹具播种的是 v1 编排事件;拿那些夹具去跑 v2 客户端,可能得到令人安心的计时,同时漏掉正在使用的传输路径。
v2 检查钉住这些不变量:
- 冷打开和历史页大约停在 1 MiB 的已编码行。一个容纳用户回合的窗口也会停在 10 个用户回合(较旧的页面是 20 个)或 500 行,因此普通回合保持完整。任何其他窗口,包括一个回合内部超过 500 行的部分,停在 75 行。快照查询在解码载荷之前应用同一条规则。客户端可以选择把大型工作组推迟到展开时。这些组之外的消息和条目仍留在普通的历史分页里。必需的控制记录和至少一行的规则可以超过字节目标;它不是硬上限。
- 有界快照不保留完整的重复对话消息表。
- 更早的活动按有界的 HTTP 页获取,合并时不扰动实时滚动窗口。
- 已确认历史分页的客户端会选择套接字快照回退,它使用同一个有界的近期历史窗口,并带上获取更早历史所需的游标。较旧的客户端和仅 WebSocket 的启动保留兼容的完整快照回退。
- 快照查询按每个分叉祖先限制时间线读取,而不是按总行数:依赖和控制记录仍然伴随窗口。仅仅更小的套接字帧并不能确立整台服务器的内存下降。
- 恢复追赶在解码之前检查原来的 1 MiB 已持久化载荷预算,然后最多重放 128 条对话线程事件和 1 MiB 的投影事件 JSON,之后回退到快照。原始 JSON 字节不是堆内存上限。仅仅因为输出投影很小就提高这个预算,会在重复的大型工具更新上增加服务器分配。
- 原始命令输出、动态工具结果正文和内联文件更改正文在线路边界被省略,包括很小的输出。显式失败标志和有界的结果 ID 保留状态和分组动作计数,而不运送那些正文。推理文本、命令行和搜索结果在它们的工作组加载时运送。子智能体提示和结果以及检查点控制记录仍然伴随初始窗口。已持久化的事件保持完整;现有的差异端点在被请求时仍然提供文件内容。显式的跨环境记录导出保留完整的工具正文,以便目标智能体能阅读上下文。导出在发送之前拒绝超过文件附件上限的时间线 JSON;客户端仍对完整文件应用它的附件上限。
- 实时命令输出只通过
orchestration.subscribeCommandOutput到达客户端,展开的命令行会随自己打开和关闭它。原始输出增量从不是编排事件:适配器在摄取之前把它们送到命令输出枢纽,它为每条正在运行的命令保留一段有界的尾部,并节流每个订阅者。不要把输出增量路由进turn_item.updated:一条忙碌的命令会淹没事件存储,并挡住其他每条对话线程的摄取。 - 初始 shell 只包含活动的导航行。归档行使用专门的归档查询,记录消息正文留在对话线程详情里,与消息大小无关。
- Shell 恢复发送的是增量加上紧凑的仓库充实元数据,而不是另一份完整的项目和对话线程快照。
- 自动、转向和重启发送在服务器的每条对话线程分发锁内,从权威状态解析投递。模型选择和已识别检查点的回滚,在服务器宣告支持时也不先获取完整的对话线程投影。较旧的服务器保留基于投影的校验。显式的开始发送已经跳过那次读取,因此这项节省并不适用于每条客户端发送路径。
更改投影模式、分页、shell 同步或对话线程状态时,把这条命令和聚焦的包类型检查,以及对每个受影响表面的真实客户端走查一起运行。传输性能夹具校验契约编码,并比较同一份合成工作负载在压缩前的 RPC JSON,同时单独保留历史上的应用对象计数。这些既不是压缩后的 WebSocket 捕获,也不是服务器 RSS 的测量,命令夹具排除无关的设置调用和订阅事件。载荷预算属于这些测试,这样回归会在本地失败。传输预算集成测试(apps/server/integration/transferBudgetV2.integration.test.ts)发送当前客户端发送的选择加入,并限制一个 900 行智能体回合的有界快照和一页更早内容的解码字节与行数,以及由已结束对话线程和空闲子智能体组成的侧栏在冷连接 shell 上的解码字节与行数。客户端新增一项选择加入时,把它加到测试的客户端查询里,并给夹具它节省下来的数据。
事件存储与启动
序列游标必须查找索引,而不是扫描保留的历史。应用序列索引包含项目事件和 v2 对话线程事件;每条对话线程的序列索引只包含 v2 对话线程事件。仅遗留和未知的对话线程必须返回零,而不扫描其他对话线程。追赶查询保持序列范围以及可选的对话线程或命令谓词可索引。
启动在读取完整对话线程投影之前,从当前投影状态选择恢复候选。候选选择包括已排队的运行、待处理的运行时请求、活动的提供方会话、后台工作,以及未完成的委托投递。需要恢复的地方,归档的对话线程仍然参与。已完成的对话线程历史不会仅仅为了发现没有剩余工作而被加载。
投影校验按有界页解码规范行,包括共享的提供方记录和分叉谱系。重建在它的事务内按固定顺序重放事件页;它不得在投影之前收集整个事件日志。
聚焦的回归覆盖位于 OrchestrationEventStore.sequence.test.ts、ProjectionRecovery.test.ts,以及 apps/server/src 下的提供方运行时恢复测试。
后台工作与导航
结算工作器从 ProjectionStore.getSettlementCandidates 读取活动的、未固定的、没有显式结算覆盖的对话线程。它读取最新运行和用户消息的时间戳,拒绝活动运行和待处理请求,并用与 shell 相同的推导检查后台工作。它不加载归档历史、分叉谱系、记录计数或提供方会话。编排器在应用结算之前仍然校验当前对话线程。
已读回执用 ProjectionStore.getThread 只加载规范的对话线程元数据。一次访问单调推进 lastVisitedAt,而不改变 updatedAt 或解码历史。Shell 查询用一个覆盖对话线程/运行的索引取得条目计数,并用一个部分索引取得非终态后台条目,包括空闲条目。最新用户消息的读取使用按更新时间排序的索引。
命令面板的结果把活动的版本控制和已链接拉取请求查询租给可见视口,包括一小段过扫描区域。租约释放期间,缓存的徽章仍然可用。本地 Git 状态读取跳过分歧计算;需要领先/落后计数的调用方保留完整状态路径。缓存的获取失败共享现有的退避,并且只记录真正失败的那次尝试。未改变的桌面环境引导读取保留它们的数组身份,这样轮询不会使订阅者失效。
本地跟踪记录和提供方事件日志使用异步、串行的文件写入和轮转。把同步追加批处理起来,在慢存储上仍然会堵住服务器事件循环,在 Windows 上尤其如此。跟踪写入变慢时,生产者最多保留一个跟踪文件的记录,因此给停顿计时的 span 能熬过它;只有磁盘一直卡住时记录才会丢弃。提供方日志保留背压。关闭时会在关闭它们的作用域之前排空已接受的写入。
中继感知为每条对话线程排队一次待发布,并在发送期间对话线程发生变化时保留一次后续发布。记录增量和工具输出不会把感知工作入队。运行、请求和相关的对话线程元数据事件会。配置在 shell 读取之前检查,连接变化会使已发布的身份失效,这样重新关联可以发布未改变的状态。失败的发布最多用指数延迟重试最新状态五次。新的相关事件重置预算;发布成功、禁用或解除关联会取消重试。预算耗尽之后,发布等待另一个相关事件,而不是在中断期间持续产生流量。
聚焦覆盖:ProjectionSettlement.test.ts、ThreadSettlementService.test.ts、runtimeLayer.test.ts、AgentAwarenessRelay.test.ts、GitVcsDriverCore.test.ts 和 desktopLocal.test.ts。
移动端循环动画
apps/mobile/package.json 设置 Reanimated 的 DISABLE_COMMIT_PAUSING_MECHANISM,使键盘避让在 iOS 上产生动画。Reanimated 把该标志记录为只有和 React Native 的 preventShadowTreeCommitExhaustion 一起才安全,而后者默认关闭,并且需要从源码构建 React Native,因此我们的构建没有它。Reanimated 把每一帧动画提交到影子树,没有提交暂停时,这些提交可能饿死 React 的提交:JS 线程不断重试它的提交,屏幕冻结,返回也没有作用。发布构建会永远重试;开发构建以 ShadowTree.cpp: assertion failed (attempts < 1024) 中止。在重内容渲染时仍留在屏幕上的循环动画,应该使用 React Native 的 Animated 和 useNativeDriver: true,它更新视图而不提交,而不是 Reanimated 的 withRepeat。thread-work-log.tsx 里正在运行的对话线程闪光就是例子。