- 人工智能
- AI Agent
- 代码智能体
- 多智能体
- MCP Clients
- Agent 编排
【免费下载链接】oh-my-openagent
OmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.
本篇指南围绕 OmO(oh-my-openagent)omo-senpi 遥测管线中一次真实的缺陷修复与对抗性复核展开:并发波组装器wave-assembler.ts的跟踪上限缺陷从发现、修复到独立验证的完整闭环。读者将掌握该模块的区间图波组装原理、六类计数器语义、paired.length + pending.size双结构闸门的正确性论证,以及一套可复用的对抗性验证方法论——包括 RED 重构、变异测试(mutation test)、随机交错扫描与会计恒等式(accounting invariant)审计。
一、背景:为什么遥测需要"并发波"而非"回合"
om -senpi 的并行度遥测要回答一个核心问题:一个会话里有多少工具调用是真正并行执行的,以及并行执行节省了多少墙钟时间。为此,todo 1 在 wave-assembler.ts 中实现了纯函数assembleWaves,把成对的tool_execution_start/tool_execution_end观测按toolCallId配对,再按时间区间重叠关系(区间图连通分量)聚合成"并发波"。
模块头注释(wave-assembler.ts)明确了三个关键设计决策:
- 波是区间图连通分量,不是回合:一个调用只要其
[startMs, endMs]区间与波内任一调用重叠就加入该波,因此链式执行 A(0-5)、B(4-9)、C(8-12) 会聚成一个波——A 与 C 从不直接重叠,但通过 B 传递连接。 spanMs = maxEnd - minStart而非最长单次时长:下游的 savings 公式需要真实的消逝窗口,max(duration)在链式波上会高估。这正是验证文档中的核心守卫之一(详见"变异测试"一节)。- 驻留明细(resident detail)存在两处:等待 end 的
pendingMap 与已完成配对的paired数组,二者之和才是真正的内存占用,也因此成为容量闸门(cap gate)的管控对象。
1.1 模块输入:为什么观测必须自带时间戳
task-1 证据文档(task-1.md)特别指出:senpi 事件ToolExecutionStartEvent/ToolExecutionEndEvent本身不带时间戳字段,因此 todo 4 的订阅者必须在到达时打上时间戳,本模块只接受已打戳的ToolExecutionObservation记录:
export type ToolExecutionObservation = { readonly kind: "start" | "end" readonly toolCallId: string readonly toolName: string readonly atMs: number }1.2 核心数据结构与输出
export const MAX_TRACKED_CALLS = 2000WaveCounters是全部会计出口(wave-assembler.ts):
| 计数器 | 语义 |
|---|---|
observedCalls | 每个格式良好的 start 观测 +1(永不饱和) |
pairedCalls | 成功配对的调用数 |
incomplete | 组装结束时仍驻留在pending的 start 数(=pending.size) |
clockAnomalies | end 时间早于 start 时间的调用数(第四会计出口) |
droppedCalls | 超过容量闸门被拒绝的 start 数 |
malformed | 结构上无法解析的输入数 |
输出WaveAssembly携带waves: ConcurrencyWave[](每个波含calls、spanMs、maxConcurrency)与上述计数器。
二、原始缺陷:只拦paired.length的闸门形同虚设
2.1 缺陷定位
初始提交b8078d13a的容量闸门只检查已完成配对数组的长度:
// b8078d13a: wave-assembler.ts:69(缺陷版本) if (paired.length >= MAX_TRACKED_CALLS) { counters.droppedCalls += 1 continue }但调用明细首先累积在pendingMap 中——一个 start 只有等 end 到达后才会变成paired条目。因此对"5000 个 start 全部先到、5000 个 end 全部后到"的全并行到达顺序,paired.length全程为 0,pending无界增长,闸门从未触发。独立对抗性验证(verify-t1-t3.md)实测:5000 starts + 5000 ends 得到tracked=5000, dropped=0,而声明的上限是 2000。
2.2 为什么原测试漏掉了它
原 case (f) 夹具是严格交错的[start, end, start, end, ...]——恰好是paired.length闸门碰巧有效的唯一到达顺序,因为每个 end 都在下一个 start 到来前清空了pending。而全 start 后全 end 恰恰是本遥测存在的意义所在(测量全并行批处理),计划文档的 MUST-NOT("배열을 무한히 키우지 말 것",即数组不得无限增长)在最关键处未被强制执行。
三、修复方案:对两个驻留结构之和设闸
3.1 一行代码的修复
修复提交791437517将闸门改为对两个驻留结构之和施限(wave-assembler.ts):
if (paired.length + pending.size >= MAX_TRACKED_CALLS) { counters.droppedCalls += 1 continue }正确性论证:一个 start 在配对时会从pending迁移到paired,迁移前后paired.length + pending.size之和不变,因此该和在整个组装过程中单调有界——无论到达顺序如何交错,驻留明细都不会超过 2000。
3.2 计数器语义不变
observedCalls仍然统计每个格式良好的 start,droppedCalls统计超过上限被拒绝的 start。修复提交总 diff 仅 8 行:6 行模块注释 + 1 行闸门变更 + 1 行删除,纯逻辑改动是单行。
四、独立复核:三个声明形状全部精确重现
独立验证者(未参与实现,verify-t1-repair.md)从头编写了/tmp/vt1/probe.ts,直接导入导出的assembleWaves与MAX_TRACKED_CALLS,作者脚本被删除且未被复用:
SHAPE1 5000-starts-then-5000-ends tracked=2000 paired=2000 dropped=3000 observed=5000 incomplete=0 anomalies=0 malformed=0 accounted(paired+incomplete+dropped)=5000 residentDetail=2000 CAP=2000 INVARIANT_OK=true BOUND_OK=true SHAPE2 2500-starts-no-ends tracked=0 paired=0 dropped=500 observed=2500 incomplete=2000 anomalies=0 malformed=0 accounted(paired+incomplete+dropped)=2500 residentDetail=2000 CAP=2000 INVARIANT_OK=true BOUND_OK=true SHAPE3 2010-interleaved-pairs tracked=2000 paired=2000 dropped=10 observed=2010 incomplete=0 anomalies=0 malformed=0 accounted(paired+incomplete+dropped)=2010 residentDetail=2000 CAP=2000 INVARIANT_OK=true BOUND_OK=true三个形状与作者声明逐位一致,原始缺陷(tracked=5000 dropped=0、2500 驻留)确认关闭。
五、攻击不变式:六种对抗到达顺序 + 400 次随机扫描
验证者用resident = trackedDetail + incomplete(paired 数组 + pending Map)作为真实内存占用,构造了两种极端到达顺序都未覆盖的攻击集(/tmp/vt1/probe2.ts):
| 探针 | 场景 | 结果 |
|---|---|---|
| (a) | 3000 starts → 1500 ends → 1500 更多 starts(部分排空后重入) | resident=2000,BOUND_OK |
| (b) | 3 starts : 1 end 比例 × 2000 轮(pending 持续非零且 paired 增长) | resident=2000,BOUND_OK |
| (c) | 2500 starts 后 2500 ends,含被拒 starts 的 ends | resident=2000,无损坏、无复活 |
| (c2) | 2100 starts,ends 仅针对 100 个被拒 starts | resident=2000,dropped=100 |
| (d) | 边界 n=1999/2000/2001,两种到达顺序 | n=2000 全收、n=2001 恰好拒 1,>=比较符正确 |
| (e) | 时钟异常 + 正常配对 | anomalies=1,第四会计出口生效 |
5.1 关键发现一:被拒 start 的 end 静默丢弃,无复活
探针 (c)/(c2) 验证:end 的 start 若已被拒绝,命中pending.get(...) === undefined(wave-assembler.ts)后被静默丢弃——既不增加pairedCalls,也不重新接纳明细,且不计入malformed。这与既有文档化的孤儿 end 语义一致(task-1 判定记录 3 曾专门澄清:nan/negative的 end 是格式良好的观测,其 start 被拒绝,故按孤儿 end 处理而非 malformed 输入)。
5.2 关键发现二:作者的不变式缺了第四出口
探针 (e) 证明作者的paired + incomplete + dropped == observed并非普遍成立:时钟异常调用被从pending移除、永不进入paired、只落入clockAnomalies。正确的完整恒等式为:
paired + incomplete + dropped + clockAnomalies == observed这是b8078d13a就存在的既有行为(本次修复未改变),且每个调用都落在某个计数器中,无静默丢失。验证者将其记录为对作者声明的一次精度修正,而非修复本身的缺陷。
5.3 随机化扫描:构造不出反例
/tmp/vt1/probe3.ts使用确定性 LCG 生成 400 次交错(每次 2500-4500 条观测,62% start 偏置,乱序 ends):
400 randomized interleavings: worstResident=2000 CAP=2000 boundBreaks=0 invariantBreaks=0作者的正确性论证——start 从pending迁移到paired时二者之和不变、和单调有界——在攻击下成立,验证者无法构造出反例。
六、RED 重构与 GREEN:修复是真实的,不是伪造的
6.1 RED 重构(针对旧闸门)
验证者独立把修复前模块从 git 取出,让当前测试文件指向它,作者工件未复用:
$ git show b8078d13a:packages/omo-senpi/src/components/telemetry/wave-assembler.ts > /tmp/vt1/oldgate/wave-assembler.ts $ bun test /tmp/vt1/oldgate/wave-assembler.test.ts (fail) #given every start arriving before any end beyond the tracking cap ... expect(trackedCalls).toBe(MAX_TRACKED_CALLS) Expected: 2000 Received: 2500 (fail) #given unmatched starts beyond the tracking cap ... expect(result.counters.incomplete).toBe(MAX_TRACKED_CALLS) Expected: 2000 Received: 2500 11 pass 2 fail与作者声称的 RED 完全一致(11 pass / 2 fail,incompleteExpected 2000 Received 2500)——两条新测试确实钉住了缺陷,RED 捕获非伪造。
6.2 GREEN(已发布代码)
$ bun test packages/omo-senpi/src/components/telemetry/wave-assembler.test.ts 13 pass 0 fail 35 expect() calls6.3 变异检查:span 守卫仍然非重言式
把当前(修复后)模块复制到/tmp/vt1/mutant/,将spanMs: maxEnd - minStart换成spanMs: max(endMs - startMs)(即计划明令禁止的max(d)基准):
(fail) #given tool executions that overlap in time ... #then all three calls join one wave carrying a span (fail) #given a chained wave where the first and last calls never overlap ... #then one wave reports the full span and a concurrency of two expect(result.waves[0]?.spanMs).toBe(12) Expected: 12 Received: 5 11 pass 2 failmax(d)变异体仍被两条测试捕获(而非一条)——修复没有削弱 span 守卫。这印证了 task-1 的原始回归守卫:链式 A(0-5) B(4-9) C(8-12) 中 A 与 C 从不重叠,朴素"最长时长"会报 5,朴素"波大小"并发会报 3,实测是 12 和 2。
6.4 指标逻辑零回归
链式波用例不受修复影响:1 个波、span=12、maxConcurrency=2;spans、波划分、incomplete、clockAnomalies全部与先前确认值一致。8 行 diff(6 行注释 + 1 行闸门 + 1 删除)不存在扰动 span/波/并发计算的可能,测量也证实未扰动。
七、被点名的遗留问题:incomplete少报与 schema 缺口
7.1 模块内可接受,线上不可审计
一旦触顶,被拒 starts 落入droppedCalls而非incomplete。2500-starts 用例上报incomplete=2000,但实际有 2500 个 start 未完成——incomplete在MAX_TRACKED_CALLS处饱和,少报了被拒数量。
模块层面可接受的理由:incomplete定义为残差 pending 明细,计划明确要求超限时"카운터만 유지하고 상세는 버림"(只保留计数器、丢弃明细);observedCalls精确且不饱和,droppedCalls精确承载亏空,完整会计恒等式在全部 400 个随机形状与每个手工形状上成立。持有全部五个计数器的消费者总能还原真相,没有调用被静默丢失。
7.2 真正的风险:parallelism_summary不带dropped_calls
验证者点名 omo-native-parallel-summary.ts:buildParallelismSummary在事件里暴露了incomplete_calls和clock_anomalies,也暴露了dropped_calls——但验证文档写作时点所引用的注册 schema(parallelism-schema.ts)尚未携带dropped_calls属性(该文件现已包含dropped_calls: NUMBER_PROPERTY,对应 todo 5 的演进)。读取该事件的仪表盘若只看到incomplete_calls = 2000,将无从得知另有 500 个 starts 被拒绝、也无从判断该值是被截断的天花板而非测量值——"会让仪表盘读到的指标静默损坏"的失效模式只是被推迟了一层,并未消除。
验证者对 todo 6 的建议(非 todo 1 的阻塞项):要么为parallelism_summary增加dropped_calls数值属性使恒等式可在线上重建,要么发出饱和标志(saturation flag)让读者区分真实的incomplete_calls = 2000与被截断的值。schema 文件归 todo 5 所有,本修复正确地未触碰。
八、范围纪律与约定合规
$ git show --stat 791437517 .omo/evidence/telemetry-parallel-latency-v2/task-1.md | 121 +++++++++++++++ packages/omo-senpi/src/components/telemetry/wave-assembler.test.ts | 40 ++++ packages/omo-senpi/src/components/telemetry/wave-assembler.ts | 8 +-提交恰好只触碰三个许可路径;savings-math.ts、eval-classifier.ts、product-identity.ts、product-identity.test.ts、senpi-telemetry.md、packages/telemetry-core/、packages/omo-codex/、packages/omo-opencode/、plugin/extensions/、index.ts均未被该提交触碰(范围 diff 中出现的其他文件来自中间提交d6dd78b4f与de7416776,属其他 worker 的 todo 2/5)。
交错夹具被保留:git diff b8078d13a 791437517 -- .../wave-assembler.test.ts的删除行计数为 0——原 case (f) 夹具逐字节未动,未被重写来迁就修复。两条新用例纯为新增。这是正确之举:保留严格交错夹具即保留了旧闸门唯一能处理的到达顺序,测试套件现在同时覆盖两种顺序。
约定检查(verify-t1-repair.md 第 7 节):
- given/when/then:两条新用例均用嵌套
describe("#given ...") > describe("#when ...") > test("#then ..."),符合 AGENTS.md 约定; - 代码卫生:新增行中
as any0 处、@ts-ignore0 处、em dash 0 处、非 ASCII/emoji 0 处; - 纯 LOC:wave-assembler.ts 157 行、测试 204 行,均低于 250 上限;
- 测试数据完全字面化:无
Date.now()、无定时器、无 sleep、无 async——两条新用例不可能靠时序运气通过。
九、套件健康度
$ bun test packages/omo-senpi/src/components/telemetry/ 133 pass 0 fail 491 expect() calls Ran 133 tests across 15 files. [2.67s] $ bun run --cwd packages/omo-senpi typecheck $ tsgo --noEmit -p tsconfig.json TYPECHECK_EXIT=00 fail。133 而非作者记录的 118,是因为其他 worker 并发落地了 todo 2/5 的提交及未跟踪的 todo-4 文件;这些进行中的工作不在本次范围内且均为绿色。
十、方法论沉淀:对抗性验证的可复用清单
从这份验证实录可以提炼出一套可复用的复核流程:
- 独立复现声明数字:从头编写探针直接导入被测模块,不复用作者脚本,逐一核对每个声明值(本文三个 SHAPE);
- 攻击不变式:构造两种极端到达顺序都覆盖不到的场景(部分排空后重入、被拒 start 的 end、纯被拒尾部、精确边界 n=1999/2000/2001、时钟异常);
- 随机化扫描:用确定性 PRNG 生成数百次任意交错,验证最坏驻留与恒等式零破坏;
- RED 重构:
git show取出修复前模块,指向当前测试文件,验证 RED 捕获真实(11 pass / 2 fail,Expected 2000 Received 2500); - 变异测试:对守卫本身施加禁止的变异(如
max(d)span),确认测试仍能击杀、守卫非重言式; - 会计恒等式审计:
paired + incomplete + dropped + clockAnomalies == observed在每个形状上成立,任何调用都不静默丢失; - 范围与约定检查:提交只触碰许可路径、夹具未被重写、约定扫描干净、套件零失败。
结论
verdict: confirmed。修复真实且完整:闸门现在约束paired.length + pending.size;三个声明复现数字集全部精确重现;上界在六种手工对抗到达顺序与 400 次随机交错下成立(最坏驻留 2000,零越界);对旧闸门的 RED 精确重现(11 pass / 2 fail);交错夹具被保留而非重写;指标逻辑未受扰动且其 span 守卫在变异下仍非重言式;范围恰为三个许可文件;约定干净;套件 0 fail。
两条带出的非阻塞修正:
- 作者不变式应写作
paired + incomplete + dropped + clockAnomalies == observed——clockAnomalies是第四个会计出口(既有行为,非本次修复引入); incomplete_calls在上限处饱和,schema 是否携带dropped_calls决定了该权衡背后的恒等式能否被仪表盘重建——留待 todo 6(该属性现已在 parallelism-schema.ts 中注册,属 todo 5 的后续演进)。
想深入底层实现的读者可直接阅读 wave-assembler.ts(区间图分组、sweepline 最大并发、解析边界拒绝)与其测试套件 wave-assembler.test.ts(13 条 given/when/then 用例),以及上游证据 task-1.md(初版实现与缺陷复盘)和 verify-t1-t3.md(首次对抗性验证,7/7 变异击杀矩阵)。
- 人工智能
- AI Agent
- 代码智能体
- 多智能体
- MCP Clients
- Agent 编排
【免费下载链接】oh-my-openagent
OmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.
相关推荐
omo-senpi 并行度遥测的对抗性验证实战:从 wave 装配、注入时钟到会话状态泄漏的边界探测
omo senpi 并行度遥测的对抗性验证实战:从 wave 装配、注入时钟到会话状态泄漏的边界探测 导读 本文基于 .omo/evidence/telemet
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排OmO ulw-loop 批量 steering 独立评审修复实录:审计持久化、CLI 冲突编码与验证批次错误码
OmO ulw loop 批量 steering 独立评审修复实录:审计持久化、CLI 冲突编码与验证批次错误码 本文基于 oh my openagent(Om
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排OmO 发布门禁修复实录:beta.8 release-state 双根因定位与 RED/GREEN 验证方法论
OmO 发布门禁修复实录:beta.8 release state 双根因定位与 RED/GREEN 验证方法论 本文以 oh my openagent 仓库中
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考