oh-my-openagent 纯结构重构 PR 的三层验证策略:以 delegate-task 常量拆分为例
本文以 oh-my-openagent 仓库中一份真实的 PR 验证策略文档为主体,完整还原“本地预检 → CI 门禁 → 多智能体评审 → 外部 Bot”的四步验证链路,并以 delegate-task/constants.ts 的模块化拆分为具体案例,讲解每个门禁要检查什么、失败时如何定位,以及“纯重构不改变运行时行为”这一声明如何被逐项证明。
背景:为什么“纯重构”也需要专门的验证策略
该策略文档(verification-strategy.md)服务于一次结构性重构:将 oh-my-openagent 中 654 行、承载 4 种职责的 constants.ts 拆分为聚焦单一职责的小模块。其执行计划(execution-plan.md)给出的拆分方案是:
| 新文件 | 职责 | 约 LOC |
|---|---|---|
default-categories.ts |
DEFAULT_CATEGORIES、CATEGORY_DESCRIPTIONS |
~40 |
category-prompt-appends.ts |
8 个 *_CATEGORY_PROMPT_APPEND 常量 + CATEGORY_PROMPT_APPENDS 记录 |
~300(提示词文本,豁免 LOC 限制) |
plan-agent-prompt.ts |
Plan agent 系统提示词常量 + 构造函数 | ~250(提示词文本,豁免) |
plan-agent-names.ts |
PLAN_AGENT_NAMES、isPlanAgent、PLAN_FAMILY_NAMES、isPlanFamily |
~30 |
constants.ts(更新) |
从 4 个新文件 re-export,保持向后兼容 | ~5 |
向后兼容通过两条路径保证:constants.ts re-export 全部 4 个新文件的内容;index.ts 已有的 export * from "./constants" 保持不变。从当前仓库源码看,该策略的向后兼容思路已体现在 index.ts 中:
export * from "./constants"
而当前的 constants.ts 顶部已采用“re-export barrel”形态,把 BUILTIN_CATEGORY_REQUIRES_MODEL、CATEGORY_DESCRIPTIONS、CATEGORY_PROMPT_APPENDS、CATEGORY_PROMPT_APPEND_RESOLVERS、DEFAULT_CATEGORIES 统一从 ./builtin-categories 转出。从源码结构看,实际落地的文件命名(如 builtin-categories.ts)与计划稿中的 4 文件方案存在演进差异,但“原路径继续可用、新增模块只负责实现”的兼容策略是一致的。
纯重构的验证核心难题在于:“无行为变更”是一个声明,不是事实。策略文档因此把验证拆成逐层递进的门禁,每层只回答一个问题:编译是否通过?现有消费者是否被破坏?结构是否满足拆分目标?外部视角有无遗漏?
第一层:Pre-Gate 本地验证(Push 之前)
策略要求在推送前于 worktree 内跑完与 CI 相同的关键检查,外加一条专门针对 re-export 完整性的动态导出检查:
# In worktree
bun run typecheck
bun test src/tools/delegate-task/
bun run build
# Verify re-exports are complete
bun -e "import * as c from './src/tools/delegate-task/constants'; console.log(Object.keys(c).sort().join('\n'))"
其中 bun -e 一行是本次策略最有价值的细节:它把“barrel 是否完整”从主观判断变成了可核对的导出清单比对。文档给出的期望导出清单为(原文标注 "13 total",实际列出 19 个符号——这个计数偏差本身就是评审时 QA 代理应抓住的小坑):
ARTISTRY_CATEGORY_PROMPT_APPENDCATEGORY_DESCRIPTIONSCATEGORY_PROMPT_APPENDSDEFAULT_CATEGORIESDEEP_CATEGORY_PROMPT_APPENDPLAN_AGENT_NAMESPLAN_AGENT_SYSTEM_PREPEND_STATIC_AFTER_SKILLSPLAN_AGENT_SYSTEM_PREPEND_STATIC_BEFORE_SKILLSPLAN_FAMILY_NAMESQUICK_CATEGORY_PROMPT_APPENDULTRABRAIN_CATEGORY_PROMPT_APPENDUNSPECIFIED_HIGH_CATEGORY_PROMPT_APPENDUNSPECIFIED_LOW_CATEGORY_PROMPT_APPENDVISUAL_CATEGORY_PROMPT_APPENDWRITING_CATEGORY_PROMPT_APPENDbuildPlanAgentSkillsSectionbuildPlanAgentSystemPrependisPlanAgentisPlanFamily
对照当前仓库可以验证这条检查为何必要:tools.test.ts 从 barrel 一次性导入 DEFAULT_CATEGORIES、CATEGORY_DESCRIPTIONS、isPlanAgent、PLAN_AGENT_NAMES、isPlanFamily、PLAN_FAMILY_NAMES 等符号——任何遗漏的 re-export 都会直接让这份测试在 import 阶段失败。而 buildPlanAgentSystemPrepend 这类函数在当前源码中仍是 barrel 的核心成员(constants.ts),它把“技能注入前的静态提示词 + 动态生成的类别/技能表格 + 技能注入后的静态提示词”三段拼成 plan agent 的完整系统提示词。
Gate A:CI(阻塞门禁)
CI 是唯一的硬阻塞门禁,用一条命令订阅其结果:
gh pr checks --watch
策略文档依据 ci.yml 列出预期通过的 4 个 CI 项:
- Tests (split):mock 密集型隔离测试 + 批量
bun test - Typecheck:
bun run typecheck(即tsc --noEmit) - Build:
bun run build - Schema auto-commit:仅在检测到 schema 变更时触发
文档对失败点的预判非常明确:没有预期失败点,因为这是纯 re-export 重构、无运行时行为变化。同时它预先写好了两类失败模式的标准排查路径,避免门禁失败后临时猜测:
- Typecheck 报错 → 缺少 re-export 或引入 import 环。修复位置在新模块内,amend 提交后重推。
- 测试报错 → 典型根因是
tools.test.ts这类“从./constants导入全部符号”的测试文件,要求 re-export barrel 必须完整。
这个预判与仓库现状互相印证:当前 constants.ts 顶部仅 3 行 import 类型/工具函数后即进入 re-export 块,没有任何指向新模块的循环引用——“无 import 环”这一 CI 风险点被结构本身消解。
Gate B:review-work 五代理并行评审
CI 通过后调用 /review-work,由 5 个并行代理从不同视角验证同一份改动。这份策略文档对每个代理的具体核查项都给出了可执行的断言,而非泛泛的“检查一下代码”:
- Oracle(目标/约束视角):验证“向后兼容”声明成立;逐一确认 13 条外部导入路径仍能解析。执行计划中为此附了完整的外部导入映射表(
src/agents/atlas/prompt-section-builder.ts、src/agents/builtin-agents.ts、src/plugin/available-categories.ts、src/plugin-handlers/category-config-resolver.ts、src/shared/merge-categories.ts及其测试均从../tools/delegate-task/constants导入),使核查项可以逐条打勾。 - Oracle(代码质量视角):验证每文件单一职责、LOC 上限、无 catch-all 模块违规——这正是拆分
constants.ts的原始动机,评审需要确认拆分结果确实满足目标。 - Oracle(安全视角):确认该重构无安全影响(提示词常量与名称判断逻辑不涉及权限边界)。
- QA(实机执行视角):实际运行
bun test src/tools/delegate-task/并确认全绿,而非仅阅读 diff。 - Context miner(上下文视角):确认没有相关未关闭的 issue/PR 与本次拆分冲突。
预期结论(Expected verdict)为 Pass:纯结构重构、无行为变化,五个视角均无实质风险。
Gate C:Cubic 外部 Bot 审查
第三个门禁是等待外部审查机器人 cubic-dev-ai[bot] 在 PR 上发布 “No issues found”。策略文档对此门禁的预期管理很务实:
- 若 Cubic 提出问题,大概率是误报——典型场景是对“新增文件数量多”这一客观事实的机械告警(本次拆分恰好新增 4 个文件);
- 处理方式是在 PR 评论中解释,而非盲目按 Bot 意见回改代码。
仓库自带的 PR 生命周期技能 work-with-pr 对 Cubic 门禁有更细的信号判别规范:批准信号是最新 Cubic 评论同时含 **No issues found** 与置信度 **5/5**;唯一允许“跳过”该门禁的情形是配额耗尽(Bot 发布配额/用量提示,或有界等待内始终未出现新评审),且必须显式记录为 SKIPPED 而非静默略过。发现问题从来不是跳过的理由。
Merge 策略与一处值得注意的差异
策略文档给出的收尾动作是:
gh pr merge --squash --delete-branch
git worktree remove ../omo-wt/refactor-delegate-task-constants
即 squash merge 将 2 个原子提交(“提取类别默认值与提示词常量”、“提取 plan agent 提示词与名称”)折叠为 dev 分支上的 1 个干净提交,随后移除任务专用 worktree 并清理。
需要注意一个仓库规则层面的差异:oh-my-openagent 自身的 work-with-pr 技能 明确写道“This repository requires merge commits. Never use --squash or --rebase”,并给出默认命令 gh pr merge "$PR_NUMBER" --merge --auto --delete-branch(自动合并,门禁全绿后由 GitHub 落地)。因此,若在当前仓库实际执行该验证策略,合并方式应服从仓库的 merge commit 规则,而策略文档中“两个原子提交折叠为一个”的叙述可替换为“保留原子提交历史、以 merge commit 并入 dev”。worktree 清理、失败时保留 worktree 供人工检查等配套纪律则不受影响。
小结:可复用的验证策略骨架
把这份文档抽象出来,它对“纯结构重构类 PR”给出了一个可直接复用的四段式骨架:
- 本地预检:typecheck + 定向测试 + build 之外,加一条
bun -e动态导出比对,把 barrel 完整性从主观判断变成清单核对; - CI 门禁:事先列出预期通过的 CI 项与最可能的两类失败模式,让失败后的定位成本趋近于零;
- 多代理评审:每个评审代理绑定一条可执行断言(导入路径数、LOC 上限、实机测试命令、冲突 issue 扫描),避免“看起来没问题”式的空洞评审;
- 外部 Bot:预先区分真问题与误报,并把“配额耗尽→SKIPPED”与“发现问题→必须修复”两种终态严格分开。
这套策略的价值不在于命令本身,而在于它把“验证”从 PR 合并前的被动等待,变成了一组在 push 之前就已写死、可逐项核对的断言——对任何“无行为变更”声明型改动,这都是比多跑一轮测试更有说服力的证明方式。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00