oh-my-openagent:task_send always-steer 变更的 HEAVY 自审——证据标准、11 项编程复盘与源码级印证
【免费下载链接】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 仓库中一份真实的变更自审记录(.omo/evidence/20260727-senpi-task-always-steer/06-self-review.md)为主体,完整解读其"HEAVY 级"自审是如何组织的:从成功标准的逐条证据核验、11 项编程后写复盘、check-no-excuse-rules规模检查,到"评审驱动清理"的落地方式,并结合senpi-task的现行源码(schema、路由、渲染器、steering 引擎)印证每个结论的实际实现位置,帮助读者掌握一次"公共工具接口收缩"变更的完整自审方法论。
背景:这次改动的内容与验证等级
自审记录对应的变更是:从task_send的公共输入面中移除deliver_as投递模式选项,使普通文本消息无条件以 steer 方式送达运行中的子任务。变更元数据记录在同目录的证据清单 README.md 中:
- 验证等级(Tier):HEAVY——因为改动触及"公共
task_sendschema 与任务会话消息投递语义",属于接口级变更; - 分支:
fix/senpi-task-always-steer; - 基线:
origin/dev的f2ae25041。
四条成功标准(成功判据全部继承自证据 README):
task_send不暴露任何deliver_as属性,且普通子任务消息无条件请求 steer;- 运行中子任务与已退出常驻任务(finished-resident)的行为,通过真实任务面(real task surface)在不带投递选项的情况下工作正常;
- 聚焦测试、senpi 兼容性、typecheck、build 与变更文件诊断全部干净;
- Reviewer 批准且 PR 以仓库要求的 merge commit 合入。
从当前仓库源码看,这条公共契约已经稳定存在。send-schema.ts 定义的TaskSendParams只有五个字段,其中没有任何投递模式:
export const TaskSendParams = Type.Object({ to: Recipient, message: Type.Optional(Type.Union([PlainMessage, StructuredMessage])), team_run_id: Type.Optional(Type.String({ description: "Team run id for lead-to-member messages or shutdown messages." })), summary: Summary, all_scope: Type.Optional( Type.Boolean({ description: "Allow messaging a child owned by another session. Off by default." }), ), })参数语义对照:
| 参数 | 类型 | 说明 |
|---|---|---|
to | string | 子任务 id/name 或团队成员名;*表示广播(lead-only) |
message | string 或结构化对象 | 普通文本,或{type:'shutdown_request'}/{type:'shutdown_response', approve, reason?} |
team_run_id | string(可选) | lead 发给成员、或 shutdown 消息使用的 team run id |
summary | string(可选) | 团队消息的单行摘要 |
all_scope | boolean(可选,默认关) | 允许给其他会话拥有的子任务发消息 |
而在路由层 send.ts,普通字符串消息被硬编码为 steer 投递:
const outcome = await manager.sendToTask({ idOrName: params.to, message: params.message, deliverAs: "steer", // 公共面不再有选择权:一律 steer ...(callerSessionId !== undefined ? { callerSessionId } : {}), ...(params.all_scope === true ? { allScope: true } : {}), })工具描述(send.ts 的DESCRIPTION)也与之对齐:"Plain-text messages always steer a running child immediately."。这就是自审中"行为敏感证据"所锚定的公共契约。
Criterion review:四条标准的证据逐条核验
自审文档的第一部分(06-self-review.md)逐条回应成功标准,每条都给出"证据具体到行为"而非"测试通过了"这类模糊表述:
Criterion 1:schema、runtime、renderer 三层均有 RED→GREEN 证据,且证据是"specific and behavior-sensitive"(具体且对行为敏感的)。对应的就是 send-always-steer.test.ts 中的三个测试:
- 公共契约测试:检查
tool.parameters.properties的键名不含deliver_as,且工具描述中不含deliver_as、followUp、interrupt字样; - 路由测试:mock 一个
SendManager,断言runTaskSend传给引擎的SendInput严格等于{ idOrName, message, deliverAs: "steer", callerSessionId },且返回details为{ kind: "steered", delivered: "steer" }; - 渲染测试:
renderTaskSendCall输出的行包含task_send to:st_1与消息摘要,但不包含deliver:前缀。
这三个断言分别钉死 schema、runtime、renderer 三层——任何一层回退都会让对应测试失败,这正是"行为敏感"的含义。
- 公共契约测试:检查
Criterion 2:运行中子任务的 steer 走真实 Senpi RPC 验证通道(live running-child steer passed through the Senpi RPC harness);常驻任务复活走直接的 changed-library driver(resident revival passed through a direct changed-library driver)。同时如实披露:一个更宽范围的 mock-provider E2E 失败发生在
task_send之前,与本次变更无关但被记录在案。Criterion 3:package 测试、兼容性、typecheck、build 与 diff 检查全部通过;LSP 当时不可用,这一缺口同样被显式披露而非掩盖。
Criterion 4:评审/合并状态为"pending PR/reviewer/merge"——自审记录的是当时的真实状态,不越权声明已完成。
这种"通过项 + 披露项 + 待办项"三分法,是证据型自审区别于普通 checklist 的关键:不完美之处也进入记录。
Programming post-write review:11 项编程后写复盘
自审的第二部分按 11 项标准逐一给出 PASS/FAIL 判定,每项都附带了可核验的理由。以下完整继承原文结论,并逐项给出当前仓库中的源码印证:
- 单一职责(Single responsibility):PASS。
send-schema.ts拥有公共输入 schema;send.ts拥有task_send路由;renderers.ts拥有控制类工具渲染;新增的测试拥有 always-steer 公共契约。从源码结构看,这四个文件确实各守一职:send-schema.ts 只导出 schema 与类型守卫isStructuredMessage,renderers.ts 只产出渲染组件,不触碰任何状态。 - 边界纯度(Boundary purity):PASS。TypeBox 保持边界解析器角色;移除
deliver_as之后,非法投递模式在TaskSendInput类型中不可表示(unrepresentable)。这正是"用类型消除非法状态"的实例——不需要运行时校验来拒绝一个在类型层面就不存在的值。 - 变体判别(Variant discrimination):PASS。结构化消息与结果变体仍用
assertNever做穷举 switch。当前 renderers.ts 的taskSendResultRow对SendResultDetails的 15 个变体逐一case,末尾default: return assertNever(details)——新增任何结果变体而忘记渲染,会直接编译/运行时报错。 - 逃生舱(Escape hatches):PASS。没有新增
any、类型抑制、非空断言或被忽略的诊断。 - 防御层(Defensive layer):PASS。补丁是移除过时的投递校验分支,而不是添加冗余检查。这与 send.ts 的现状吻合:
validateParams现在只校验"message 必填"和"shutdown 拒绝必须带 reason"两条业务规则。 - 一次性助手(One-off helpers):PASS。没有引入新的生产环境辅助函数。
- 测试:PASS。回退 schema/runtime/renderer 的改动,能复现捕获到的三个 RED 失败。这与证据目录中
01-red-focused-tests.txt/02-green-focused-tests.txt两份原始测试输出配套(见 证据目录)。 - 参数膨胀(Parameter bloat):PASS。没有函数新增参数;
validateParams从三个参数简化到一个参数。当前源码 send.ts 中function validateParams(params: TaskSendInput)恰为单参数签名。 - 冗余验证(Redundant verification):PASS。运行时清理检查是 QA 收据(QA receipts),不是生产环境 setter/getter 的重复。
- 负面命名(Negative naming):PASS。没有新增否定式命名的生产符号。
- 日志(Logging):PASS。日志行为零变化。
值得注意的是第 2 项与第 5 项的组合逻辑:先让非法状态不可表示,再删掉为它服务的校验——收缩型变更的正确顺序,反过来做(先删校验)会留下类型谎言。
Size check:check-no-excuse-rules 规模检查
自审第三部分记录了针对变更文件的"规模检查",命令与结果原样继承:
bun run packages/omo-senpi/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts <14 changed TypeScript files>结果:No violations in 14 file(s).
这个脚本的用途是静态扫描变更后的 TypeScript 文件,拦截"借口型"代码(违反既定编码纪律的写法)。需要说明一个仓库现状差异:自审记录引用的是packages/omo-senpi/plugin/...下的脚本路径,而从当前仓库结构看,该脚本现在位于 check-no-excuse-rules.ts(shared-skills包内,且有配套的 check-no-excuse-rules.test.ts),即技能脚本在后续版本中从 omo-senpi 插件目录迁移到了共享技能包。复现此检查时应以当前仓库的实际路径为准。
"14 个变更文件"与第 8 项复盘(无新增参数)共同构成规模约束:变更面被压缩到 schema、路由、渲染、类型、引擎清理与测试的闭包之内。
Review-driven cleanup:评审驱动的死代码清理
自审最后一部分记录了一个由评审过程发现的清理项:
公共选项移除后,控制层的
SendManager、SendResultDetails与渲染器中仍残留不可达的 interrupt/no-op 变体。这些死掉的公共控制变体被移除,同时保留了底层 manager/steering 的 interrupt API,供内部生命周期与对抗性测试使用。
这句话对应源码中的两处现状:
- 公共面已收缩:types.ts 中的
SendResultDetails结果联合类型里,steered变体的delivered字段类型是字面量"steer"({ readonly kind: "steered"; ...; readonly delivered: "steer" })——公共结果面同样只有一种投递形态。 - 底层 API 保留:steering/types.ts 的引擎级
SendInput仍然带有deliverAs?: SendDelivery可选字段;steering/engine.ts 中const deliverAs = input.deliverAs ?? DEFAULT_SEND_DELIVERY表明引擎层默认值仍是followUp,内部排队/生命周期路径(如 pending 任务的enqueuePending、steerRunning)继续使用该能力,并有引擎级测试固定这一行为(如engine-run-epoch.test.ts断言排队 payload 中deliverAs: "followUp")。
这正是"公共契约收缩、内部能力保留"的分层手法:对外删除选项以简化心智模型与错误面,对内保留低层 API 以服务生命周期与对抗性测试——清理的对象是"公共控制层",而不是引擎本身。
可复现的验证入口
结合证据目录 README.md 与自审记录,聚焦回归的最小命令为:
bun test packages/senpi-task/src/tools/control/send-always-steer.test.ts packages/senpi-task/src/tools/control/renderers.test.ts判据与自审一致:实现之后 exit code 0;若回退生产代码,则因deliver_as重新出现、默认发送在引擎侧落成followUp、或渲染器重新打印deliver:而 RED。真实任务面的端到端场景(运行中子任务 steer、finished-resident 复活)的完整调用序列同样记录在证据 README 的 "Planned exact scenarios" 中,可作为复现实验的操作脚本直接参考。
小结
这份 HEAVY 自审样本展示了接口收缩类变更的完整验证闭环:标准逐条对证(每条评价标准都落到具体测试与行为断言)、11 项编程后写复盘(类型层消除非法状态优先于删除校验)、规模静态检查(check-no-excuse-rules零违规)、以及评审驱动清理(只删公共层死变体、保留引擎层 interrupt API)。四条成功标准之外的失败(mock-provider E2E)与工具缺口(LSP 不可用)被显式披露,保证了证据链的可信度——这套"标准、复盘、规模、清理、披露"的组织方式,可直接迁移到任何触碰公共 schema 与消息投递语义的变更自审中。
【免费下载链接】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),仅供参考