oh-my-openagent 遥测 Schema 注册的对抗式验证:parallelism_summary事件契约的完整审计
【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent
本文围绕 oh-my-openagent 仓库中telemetry-parallel-latency-v2工作流的第 5 号任务(todo 5:为parallelism_summary事件注册遥测 schema)展开,完整呈现一份独立验证者对提交de7416776所做的对抗式(adversarial)审计过程与结论。读者将从中掌握该项目的遥测事件契约如何被精确约束——15 个属性的冻结 schema、文档字节级一致性门禁、直方图的 64 字符隐私截断防线、以及 allowlist 在传输边界的强制执行——并可直接复现全部验证命令。
验证背景与隔离方法
被验证的提交是de7416776(feat(omo-senpi): register parallelism_summary event schema),对应任务文档 task-5.md。验证者独立于实现者,全程不参与 todo 5 的编码,仅以"黑盒 + 探针"方式检验"完成声明"是否成立。
关键的方法学要点是验证与实现彻底隔离:
- 在
git worktree add --detach /tmp/vt5-scratch de7416776创建的分离式 scratch 工作树中运行全部探针,HEAD 精确钉在de7416776; node_modules从父工作树符号链接(symlink)而来,不执行bun install,避免依赖漂移;- 活动工作树中零跟踪文件被修改——所有变更探针都在 scratch 副本上执行,验证结束前恢复。
$ git worktree add --detach /tmp/vt5-scratch de7416776 HEAD is now at de7416776 feat(omo-senpi): register parallelism_summary event schema这种"分离工作树 + 符号链接依赖 + 变更后恢复"的组合,是该仓库证据链(evidence)体系的标准做法,保证验证结论可审计、可复现,且不污染主线工作区。
注册的属性契约:恰好 15 个属性,不多不少
验证的第一项核心工作,是从冻结对象(frozen object)中直接读取运行时 schema,而不是信任代码注释或文档叙述。在 scratch 工作树中导入product-identity.ts并 dumpOMO_NATIVE_EVENT_SCHEMAS.parallelism_summary,得到与规格逐条相等的 15 个键:
| 属性 | 类型 | 说明 |
|---|---|---|
$session_id | {"type":"string"} | 会话标识(哈希后) |
clock_anomalies | {"type":"number"} | 时钟异常计数 |
eval_only_duration_ms | {"type":"number"} | 纯 eval 桶耗时(毫秒) |
eval_only_waves | {"type":"number"} | 纯 eval 桶波数 |
incomplete_calls | {"type":"number"} | 未配对调用数(数据质量计数) |
measured_turn_duration_ms_total | {"type":"number"} | 实测回合总耗时 |
mixed_waves | {"type":"number"} | 混合桶波数 |
modeled_wallclock_saved_ms | {"type":"number"} | 建模节省的墙钟时间 |
non_eval_joined_calls | {"type":"number"} | 非 eval 桶加入的调用数 |
non_eval_saved_round_trips | {"type":"number"} | 非 eval 桶节省的往返次数 |
non_eval_wave_size_histogram | {"type":"string"} | 波大小直方图(位置编码、无标签) |
non_eval_waves_multi | {"type":"number"} | 非 eval 桶多波数 |
non_eval_waves_total | {"type":"number"} | 非 eval 桶总波数 |
schema_kind | {"type":"string","values":["parallelism_v1"]} | 单值枚举,schema 版本标识 |
upper_bound_saved_ms | {"type":"number"} | 诚实标注的上界节省值 |
从实现侧看,该条目由 parallelism-schema.ts 导出PARALLELISM_SUMMARY_SCHEMA,并在 event-schemas.ts 中以parallelism_summary: PARALLELISM_SUMMARY_SCHEMA挂入OMO_NATIVE_EVENT_SCHEMAS。schema 对象统一使用共享的BOOLEAN_PROPERTY/NUMBER_PROPERTY/STRING_PROPERTY冻结常量和enumProperty([...] as const)枚举构造器,并整体套Object.freeze({ ... })——这意味着属性集合在运行时不可被任何调用方悄悄扩缩。
类型正确性(验收标准 1b)
运行时 dump 显示:$session_id与non_eval_wave_size_histogram为{"type":"string"};其余 12 个计数/时延属性全部为{"type":"number"};schema_kind为{"type":"string","values":["parallelism_v1"]}(单值)。与任务规格逐项吻合。
non_eval_前缀纪律(验收标准 1c)
规格要求"所有由工具调用(tool-call)派生的 COUNT 必须携带non_eval_前缀"。验证者逐一核对四个波/调用计数:non_eval_waves_total、non_eval_waves_multi、non_eval_joined_calls、non_eval_saved_round_trips均符合;而eval_only_waves、mixed_waves属于计划中明确分离的 eval 桶,incomplete_calls、clock_anomalies属于数据质量计数,不属于 non_eval 聚合域。结论:不存在漏掉前缀的 non_eval 计数。
文档门禁(Doc Gate):让文档漂移变成测试失败
遥测 schema 的参考文档位于 docs/reference/senpi-telemetry.md,其中夹着一段由<!-- BEGIN GENERATED SCHEMA -->与<!-- END GENERATED SCHEMA -->两个哨兵(sentinel)包围的生成块。该文档块不是手写的,而是由生成器generateTelemetrySchemaBlock()(位于 script/telemetry-schema-block.mjs,被 schema-doc.test.ts 动态 import)按 schema 对象逐行生成。
门禁如何工作
schema-doc.test.ts 包含两个测试:
- 字节级一致性测试:读取
docs/reference/senpi-telemetry.md,用indexOf精确提取两个哨兵之间的文本,与generateTelemetrySchemaBlock()的输出做===全等比较。不等则抛出Telemetry schema documentation drifted.并把应粘贴的完整生成块打印出来; - 空 schema 拒绝测试:对空事件
{}调用生成器必须抛错(Telemetry event empty_event must contain at least one property schema),防止生成损坏的空块。
// schema-doc.test.ts 中的关键校验 const expected = generateTelemetrySchemaBlock() const actual = extractGeneratedBlock(await readFile(DOC_PATH, "utf8")) if (actual !== expected) { throw new Error([ "Telemetry schema documentation drifted.", "Paste this exact generated block into docs/reference/senpi-telemetry.md:", expected, ].join("\n\n")) }探针 A:门禁真实触发
验证者在 scratch 副本的OMO_NATIVE_EVENT_SCHEMAS.parallelism_summary中插入一个多余属性zz_probe_property: NUMBER_PROPERTY,不触碰docs/reference/senpi-telemetry.md。结果:
$ bun test packages/omo-senpi/src/components/telemetry/schema-doc.test.ts error: Telemetry schema documentation drifted. (fail) ... #then the generated schema block is byte exact [16.09ms] 1 pass 1 fail恢复文件后回到2 pass / 0 fail。这证明文档门禁不是装饰性的——它会在 schema 与文档失同步的瞬间亮红灯。
探针 B:生成块确实是"生成"的
独立对比generateTelemetrySchemaBlock()的输出与哨兵之间的既有文本:actual bytes: 5921 expected bytes: 5921,byte-exact identical: true。零字节差异,说明文档块从未被手工编辑过。
RED / GREEN 开发流程
任务文档 task-5.md 还记录了实现者的标准流程:先加 schema(RED,门禁失败并打印期望块)→ 用生成器脚本重新拼接文档(GREEN)→ 恢复2 pass / 0 fail。重新生成的命令可以直接复用:
bun -e ' const fs = require("node:fs"); const { generateTelemetrySchemaBlock } = await import("./script/telemetry-schema-block.mjs"); const path = "docs/reference/senpi-telemetry.md"; const doc = fs.readFileSync(path, "utf8"); const B = "<!-- BEGIN GENERATED SCHEMA -->", E = "<!-- END GENERATED SCHEMA -->"; const b = doc.indexOf(B), e = doc.indexOf(E); fs.writeFileSync(path, doc.slice(0, b) + generateTelemetrySchemaBlock() + doc.slice(e + E.length)); console.log("replaced", b, e); '直方图编码:位置编码如何躲过 64 字符隐私截断
non_eval_wave_size_histogram是该 schema 中最"刁钻"的属性:它是一条字符串直方图,而传输层会对所有字符串属性做 64 字符截断(见下文)。验证者独立测量了两种编码在真实上界MAX_TRACKED_CALLS = 2000下的长度:
| 编码 | MAX_TRACKED_CALLS = 2000时的字符串 | 长度 | 能否安全通过 64 字符截断 |
|---|---|---|---|
| 位置编码(已发布的契约) | 2000:2000:2000:2000:2000:2000:2000:2000 | 39 | 能——原样保留,余 25 字符余量 |
| 带标签编码(被禁止) | 1=2000:2=2000:3=2000:4=2000:5_8=2000:9_16=2000:17_32=2000:33plus=2000 | 69 | 否——被截断为...:33plus,最后一个桶的值被破坏 |
39 = 8×4 + 7,由"8 个桶 × 每桶最多 4 位数字 + 7 个分隔冒号"构成。上界来源是 wave-assembler.ts 中导出的MAX_TRACKED_CALLS = 2000:波组装器最多追踪 2000 个调用,因此任何一个桶的计数都不可能超过 4 位——这是位置编码始终不超限的根本前提。
验证者还通过真实的捕获路径(而非单纯slice())观察到了 69 → 64 的截断:labelled input length: 69 captured length: 64、silently truncated to exactly 64: true。这正是位置编码要规避的"损坏机制":带标签形式被截断后,33plus=2000变成...:33plus,被截断的半截 token 仍能解析成看似合理的数字,属于静默数据损坏。
防护测试非空:变异探针(Mutation Probe)
光有约定不够,还要证明"守卫测试本身不是空转的"。验证者在 scratch 副本中删除 schema 里的non_eval_wave_size_histogram: STRING_PROPERTY一行,然后运行 product-identity.test.ts:
(fail) OmO Native product identity > #given the tracked-call cap #when the widest wave histogram is encoded #then it stays inside the 64 character privacy limit [0.20ms] 8 pass 1 fail8 pass / 1 fail,失败的正是一个新加的直方图守卫测试。恢复后git diff --stat为空。测试的断言逻辑见 product-identity.test.ts:从真实导出的MAX_TRACKED_CALLS推导最宽桶值(String(MAX_TRACKED_CALLS)长度为 4),构造最坏情况直方图,断言toHaveLength(bucketCount * 4 + (bucketCount - 1))(即 39)且<= 64,并同时断言 schema 中该属性的类型与 allowlist 成员身份——它耦合的是真实 schema 对象,属性缺席时必然失败,不可能空洞通过。
Allowlist 强制执行:传输边界的最终防线
机制:投影而非信任
遥测客户端 telemetry-core/src/events.ts 的createEventTelemetryClient在captureEvent中对每个属性执行三层过滤:
- 禁止键检查(
isForbiddenKey):$ip永远拒绝;字符串值且键名以_text/_path/_prompt结尾(FORBIDDEN_SUFFIX = /_(?:text|path|prompt)$/)直接拒绝;$前缀键只允许$os、$os_version、$process_person_profile、$session_id; - allowlist 检查:键不在该事件的允许集合内 → 丢弃并上报
telemetry_event_property_dropped; - 值类型检查:只接受 string / boolean / finite number;字符串进一步
value.slice(0, 64)静默截断到 64 字符。
allowlist 本身不是手写的,而是由 event-schemas.ts 从OMO_NATIVE_EVENT_SCHEMAS的键集合自动派生(OMO_NATIVE_PROPERTY_ALLOWLISTS),schema 与 allowlist 因此不存在双份漂移的可能。
探针:真实客户端 + 真实记录器
验证者用真实的createEventTelemetryClient(携带OMO_NATIVE_PROPERTY_ALLOWLISTS)配合仓库的createTransportRecorder()发出一个parallelism_summary事件,同时塞入三个非 allowlisted 的"入侵者"属性:median_wave_size、eval_cell_source、prompt_text。结果:
allowlisted count: 15 every allowlisted arrived intact: true missing allowlisted: [] intruders present: [] # 三个入侵者全部被丢弃 labelled input length: 69 captured length: 64 silently truncated to exactly 64: true15 个 allowlisted 属性全部原样到达传输层;三个入侵者(包括一个带_text后缀、会被FORBIDDEN_SUFFIX结构性拦截的prompt_text)全部被丢弃。任务文档中的手工 QA 还展示了完整 payload:除 15 个业务属性外,platform、product_name、package_version、schema_version、$process_person_profile是客户端包装层在投影之后追加的共享属性,不是调用方数据。
必须不存在项(Must-NOT-Have)检查
对抗式验证同样关心"不该动的有没有动":
turn_completed未受影响:git show de7416776 -- 'packages/omo-senpi/**' | grep '^-'为空——整个提交是纯新增(pure additions),没有任何 schema 条目被修改;diff 中仅有的turn_completed字样来自task-5.md的叙述性文字;- 无 median / 均值 / 比率属性:15 键 dump 中没有任何属性名含
median、avg、average、ratio;upper_bound_saved_ms携带了规格要求的_upper_bound式诚实标注; - 无
_text/_path/_prompt后缀:15 个键无一命中,且在 events.ts 的FORBIDDEN_SUFFIX层面被结构性禁止; - 禁止路径零改动:
git show --name-only --format="" de7416776只列出 4 个文件(task-5.md、docs/reference/senpi-telemetry.md、product-identity.test.ts、product-identity.ts),对telemetry-core/、omo-codex/、omo-opencode/、plugin/extensions/的 grep 计数为 0。
验收标准全表
以下为验证者依据探针结果逐项裁决的验收标准:
| # | 标准 | 结果 | 决定性观察 |
|---|---|---|---|
| 1 | Schema 恰好包含 15 个指定属性,不多不少 | PASS | 运行时 dumpOMO_NATIVE_EVENT_SCHEMAS.parallelism_summary:恰好 15 键,与规格列表集合相等 |
| 1b | 类型正确($session_idstring;12 个 number;直方图 string;schema_kind枚举) | PASS | dump 显示 string/number/单值枚举均与规格一致 |
| 1c | 每个工具调用派生的 COUNT 都带non_eval_前缀 | PASS | 四个波/调用计数均符合;其余为计划中独立域(eval 桶、数据质量计数) |
| 2a | 文档门禁真实触发 | PASS | 插入zz_probe_property后schema-doc.test.ts从 2/0 变为 1/1,报Telemetry schema documentation drifted.;恢复后回到 2/0 |
| 2b | 文档块是生成的而非手写 | PASS | generateTelemetrySchemaBlock()与哨兵间文本均为 5921 字节,===为 true,零字节差异 |
| 3a | 位置编码在真实上界内成立 | PASS | 独立复现:8 个2000以:连接 = 39 字符 = 8×4+7,64 字符切片后原样保留 |
| 3b | 带标签形式会被截断 | PASS | 独立复现:69 字符;64 切片裁到...:33plus,丢失末桶值 |
| 3c | 测试断言诚实的 39 字符上界而非虚构最坏情况 | PASS | 测试从真实导出的MAX_TRACKED_CALLS(= 2000)推导宽度,断言8*4+7= 39 且<= 64,未硬编码膨胀位数 |
| 4a | 15 个 allowlisted 属性全部原样到达 | PASS | 真实客户端 + 仓库createTransportRecorder():捕获的非共享键集合恰好 15 个,every allowlisted arrived intact: true |
| 4b | 非 allowlisted 属性被丢弃 | PASS | 混入median_wave_size、eval_cell_source、prompt_text后intruders present: [] |
| 4c | 超过 64 字符的字符串被截断到恰好 64 | PASS | 69 字符带标签字符串进、64 字符出,silently truncated to exactly 64: true |
| 5 | 变异探针:守卫测试非空转 | PASS | 删除non_eval_wave_size_histogram后product-identity.test.ts变 8/1,失败的正是新直方图守卫 |
| 6a | turn_completedschema 未变 | PASS | diff 为纯新增,唯一提及在task-5.md叙述文字中 |
| 6b | 无 median / 均值 / 比率属性 | PASS | 15 键无命中;upper_bound_saved_ms带诚实上界标注 |
| 6c | 无_text/_path/_prompt后缀 | PASS | 15 键无命中,且被FORBIDDEN_SUFFIX结构性拦截 |
| 6d | telemetry-core/omo-codex/omo-opencode/plugin/extensions零改动 | PASS | git show --name-only仅 4 文件;禁止路径 grep 计数 0 |
| 7a | given/when/then 约定 | PASS | 新增测试使用行内// given:/// when:/// then:注释(项目 AGENTS.md 明确允许),并与文件既有 8 个测试的#given/#when/#then标题约定一致 |
| 7b | 无as any、无@ts-ignore | PASS | 对新增源码行 grep 各 0 命中;enumProperty([...] as const)是 const 断言而非as any |
| 7c | 新增源码无 emoji、无 em dash | PASS | grep + perl Unicode 扫描 0 命中(em dash 仅存在于任务文档叙述中) |
| 7d | 文件低于 250 纯 LOC 上限 | PASS | product-identity.ts220→237;product-identity.test.ts114→125,均在 250 以内 |
| 8 | 证据数字可复现 | PASS | 9 / 2 / 116-0 / exit-0 全部精确复现 |
证据复现与类型检查
验证者在 scratch 工作树中逐项复现任务文档声明的数字:
$ bun test packages/omo-senpi/src/components/telemetry/product-identity.test.ts 9 pass / 0 fail / 160 expect() calls (claim: 9 pass) MATCH $ bun test packages/omo-senpi/src/components/telemetry/schema-doc.test.ts 2 pass / 0 fail (claim: 2 pass) MATCH $ bun test packages/omo-senpi/src/components/telemetry/ 116 pass / 0 fail / 457 expect() calls across 14 files (claim: 116/0) MATCH $ bun run --cwd packages/omo-senpi typecheck # 在活动工作树中 $ tsgo --noEmit -p tsconfig.json EXITCODE=0 (claim: exit 0, no diagnostics) MATCH未能攻破的尝试
对抗式验证的价值在于"试图失败而失败不了"。验证者明确记录了五类失败的攻击:
- 寻找缺少
non_eval_前缀的工具调用派生计数——唯一的无前缀计数是计划中明确分离的 eval 桶与数据质量计数器; - 让文档门禁变成空操作——第一次变异就触发了失败;
- 删除守卫测试断言的对象使其空洞通过——测试大声失败(8/1)而非静默通过;
- 把非 allowlisted 与禁止后缀属性偷渡进真实传输通道——三个入侵者全部被丢弃;
- 寻找对
turn_completed或禁止包的隐藏修改——diff 只有 4 个文件且为纯新增。
已知非阻塞观察
验证者同时诚实记录了三个不构成缺陷的观察项:
- Scratch 专属的类型检查噪音:在 scratch 工作树中运行 typecheck 时,
packages/utils/src/runtime/file.ts第 35 行出现一个TS2322。这是node_modules符号链接将该包解析回父工作树路径(重复 lib 解析)造成的伪影,该文件未被此提交触碰,真实工作树中同一命令以 0 诊断退出,因此不可归因于 todo 5; - 软 LOC 限制:
product-identity.ts位于 237 纯 LOC,超过项目 AGENTS.md 的 200 LOC 软限制,但提交前已是 220(+17 是注册该 schema 所需的最小增量),且在本验证适用的 250 硬上限之内,仅作信息提示; - 跨泳道耦合:守卫测试从
wave-assembler.ts(todo 1 所属)导入MAX_TRACKED_CALLS。验证者核对了在途泳道对该文件的 diff:常量保持 2000 不变,4 位数字假设依然成立。若后续泳道把该常量提升到 9999 以上,守卫会正确地失败而非静默发出可截断的字符串——这是特性而非隐患。
清理凭证(Cleanup Receipt)
验证结束后的环境是干净的:
- scratch 工作树
/tmp/vt5-scratch:先移除所有node_modules符号链接,再git worktree remove --force /tmp/vt5-scratch;移除前 scratch 内git status --short为空(两次变异探针均已恢复); - 临时文件
/tmp/vt5-pi.bak、/tmp/vt5-probe.ts、/tmp/vt5-tc-real.txt全部删除,ls /tmp/vt5-*返回No such file or directory; - 活动工作树:无跟踪文件被本验证修改。最终
git status --short仅显示?? omo-native-parallel.test.ts与?? omo-native-parallel.ts——那是另一位协作者在途 todo 4 的文件,与本验证无关。
结论:confirmed
parallelism_summaryschema 注册的完成声明经受住了全部探针。属性集合恰好是指定的 15 项且类型正确;文档块是受门禁守护的字节级生成器产物(门禁被证明真实触发);直方图守卫非空转,并在真实的 64 字符截断阈值下断言诚实的 39 字符上界;allowlist 在丢弃每一个入侵者的同时完整放行 15 个属性;所有 must-NOT-have 均未违反;每一条声明的数字都精确复现。验证者给出的置信度为 0.95,未发现缺陷。
补充一点时间维度的事实:上述验证钉死在提交de7416776的 15 属性契约上。在当前仓库中,该 schema 已随后续任务继续演进——parallelism-schema.ts 现包含eval_execution_*、eval_nested_tool_call_*等更多计数器,schema_kind枚举扩展为parallelism_v1 | parallelism_v2,senpi-telemetry.md 的生成块也随之增长。但本文审计的方法论——冻结 schema 精确断言、字节级文档门禁、上界驱动的编码设计、传输边界 allowlist、变异探针防空转——正是这一整套演进能够持续安全进行的制度保障,也是本仓库遥测工程质量的核心所在。
【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考