资讯动态

oh-my-openagent ulw-loop 未提交 Diff 抢救实录:CLI 批量 Steering 契约与 Validation-Batch 关闭语义的双缺陷修复

发布时间:2026/9/18 10:25:56 来源:尧图企业网站定制
oh-my-openagent ulw-loop 未提交 Diff 抢救实录CLI 批量 Steering 契约与 Validation-Batch 关闭语义的双缺陷修复【免费下载链接】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-openagentOmO仓库中.omo/evidence/20260721-ulw-loop-gajae-adoption/的证据目录完整还原了omo-codex插件下ulw-loop组件一次未提交变更抢救uncommitted diff salvage的全过程一个丢失的 worker 在 17 个文件里留下未提交的 tracked changes团队逐 hunk 审查后保留三块有效内容其中两块最终演化为真实缺陷修复——cli-steering.ts的批量 steeringsource默认值契约以及checkpoint.ts/validation-batch.ts的 validation-batch 关闭语义。文章将结合 cli-steering.ts、validation-batch.ts、checkpoint.ts 等源码讲清每个修复的根因、红绿测试流程、错误码语义与最终验证矩阵。读完你将掌握如何安全地从遗留 diff 中筛选范围外内容、如何用 TDD 固化两个并发语义缺陷、以及 ulw-loop 组件 checkpoint / steering / validation-batch 三条核心链路的底层实现。一、事件背景为什么会有需要抢救的未提交 diff该事件发生在 ulw-loop gajae-adoption 计划的 todo-6真实表面 QA 与全量门禁执行期间。根据 README.md 的记载该计划共 7 项验收内容checkpoint 自动推进auto-advance、原子化steer --proposals-json、validation-batch 模式、validation-batch 与 checkpoint/steering 的强制约束、docs/help 同步、全量门禁与内置 CLI QA、以及最终验证波次 F1–F4。前 5 项由历史提交完成todo-6 从持久状态恢复执行时发现丢失的 worker 在 17 个仅涉及 ulw-loop 的文件中留下了未提交的 tracked changes。抢救决策的核心纪律记录在原文档 Status 一节The lost worker left tracked changes in 17 ulw-loop-only files. Every hunk was inspected before commit. No out-of-scope tracked path from that diff was kept.即逐 hunk 人工审查凡不属于 ulw-loop 范围的 tracked path 一律不保留。这是一次典型的范围纪律演练——遗留 diff 中往往混有格式化噪音、无关重建产物与真实缺陷修复抢救者必须逐块分类而不是整包提交。二、抢救出的三类有效内容Kept hunks原文档将保留内容分为三类下面逐一结合源码展开。2.1 格式与导入顺序清理biome-ignore-all format与 import ordering第一类保留内容是 ulw-loop 源码与测试中的格式/导入顺序修复在已经处于紧凑/行数预算受限compact/line-budget-constrained的文件中补充biome-ignore-all format注释并调整 import 顺序。这类修改由npm run check即biome check .无修复通过与组件测试双重验证。为什么 ulw-loop 会有行数预算约束从源码可以直观看到cli-steering.ts、validation-batch.ts、checkpoint.ts、steering-batch.ts的文件首行都带有// biome-ignore-all format: ...注释例如 cli-steering.ts 第 1 行 注明keep this module under the mandated pure LOC budget。这意味着这些模块受纯逻辑行数上限约束无法按 biome 默认格式排版必须通过 ignore 注释豁免同时保持 import 顺序稳定。抢救中把这些注释与排序恢复到与既有约定一致属于低风险但必要的一致性维护。2.2 修复一批量 CLI Steering 的source默认值契约这是第一个真实缺陷由 todo-6 的 real-surface QA 发现当--proposals-json中的某个条目缺失source字段时会报ULW_LOOP_STEERING_SOURCE_INVALID错误。根因与修复契约如下。source的合法取值定义在 constants.ts 第 31 行export type UlwLoopSteeringSource user_prompt_submit | finding | cli;在单条 steering 的解析路径中parseSteeringSource 早已实现了缺省即 cli的语义export function parseSteeringSource(argv: readonly string[]): UlwLoopSteeringSource { const value readValue(argv, --source); if (value undefined) return cli; return isSource(value) ? value : fail(Invalid --source: ${value}., ULW_LOOP_STEERING_SOURCE_INVALID, { value, expected: SOURCES }); }而批量路径--proposals-json在 proposalFromObject 中虽然也写了const source readObject(value, source) ?? cli;但在 todo-6 的这批遗留改动之前该默认值并未真正生效——QA 实测一个不含source的条目直接以ULW_LOOP_STEERING_SOURCE_INVALID失败。修复方式是让批量条目先经过normalizeSteeringProposal({ source: cli, ...rawElement })契约归一化确保每个元素在进入后续校验前都已具备确定的source。normalizeSteeringProposalcli-steering.ts 第 98-104 行负责把 proposal 的所有文本字段统一 trim 并剔除空串、按需附加goalId/targetGoalId/criterionId/childGoals/pendingOrder等字段是批量与单条两条路径共享的归一化闸门。批量入口 parseSteeringProposals 的逻辑要点如下未传--proposals-json时退化为单条解析parseSteeringProposal--kind与--proposals-json互斥同时出现报ULW_LOOP_STEERING_BATCH_CONFLICT见 cli-steering.ts 第 109 行--proposals-json必须是非空 JSON 数组否则报ULW_LOOP_STEERING_BATCH_ARRAY_REQUIRED每个条目必须是对象kind必须在ULW_LOOP_STEERING_MUTATION_KINDS之内source必须在SOURCES之内。ULW_LOOP_STEERING_MUTATION_KINDS定义在 constants.ts 第 20-28 行共 7 种add_subgoal / split_subgoal / reorder_pending / revise_pending_wording / revise_criterion / annotate_ledger / mark_blocked_superseded对应的 CLI 必填参数在 cli-steering.ts 的 STEERING_KIND_HELP 中逐条列出如add_subgoal需要--title、--objective、--evidence、--rationalesplit_subgoal需要--goal-id、--children等字段缺失会抛出ULW_LOOP_STEERING_FIELD_REQUIRED或ULW_LOOP_STEERING_FIELD_EMPTY--goal-id缺失则是ULW_LOOP_GOAL_ID_REQUIRED。批量执行与原子性由 steering-batch.ts 的steerUlwLoopBatch承载所有提案先在内存中顺序准备prepareBatch期间每个提案会先按idempotencyKey ?? promptSignature查 ledger 去重命中则标记deduped再执行validateUlwLoopSteeringProposal不变式校验全部通过后逐个applySteeringMutation。只要有一个提案被拒绝整批失败并只在 ledger 写入一条steering_rejected审计rejectedLedgerEntry按索引汇总所有拒绝原因见 steering-batch.ts 第 106-109 行全部通过则以多条steering_acceptedrevise_criterion用criteria_revised审计落盘必要时附加一条batch_updated。这种全有或全无的设计正是原子化 steering 批次的底层保证。2.3 修复二aggregate-final checkpoint 关闭 validation-batch 的语义第二个真实缺陷同样来自 todo-6 生命周期实测当某个 goal证据记录中的G003**同时是 validation-batch 的最终成员batch-final和整个计划的最终成员run-final**时checkpoint 会在考虑当前这次完成之前就抛出ULW_LOOP_VALIDATION_BATCH_OPEN导致合法的最终完成被误拒绝。修复落在 checkpoint.ts 与 validation-batch.ts 两处。先看 validation-batch 的 schema 与约束。--validation-batch-json的解析与校验在 parseValidationBatches 与 validateBatches每个 batch 必须包含batchId、至少两个memberIds、finalGoalIdfinalGoalId必须是成员之一否则ULW_LOOP_VALIDATION_BATCH_FINAL_NOT_MEMBER成员必须是计划中已存在的 goalULW_LOOP_VALIDATION_BATCH_MEMBER_UNKNOWN同一 goal 不得出现在多个 batchULW_LOOP_VALIDATION_BATCH_OVERLAPbatchId 与 memberIds 均不得重复。关闭语义相关的关键函数有三个batchClosedBy判断某个 goal 是否为某 batch 的 final 成员requireBatchFinalReadybatch 最终成员 checkpoint 时若其他成员尚未 resolve抛ULW_LOOP_VALIDATION_BATCH_OPENfail-closedrequireAllValidationBatchesClosed计划级关闭检查其签名带closingGoalId?: string——正在完成的 goal 会被排除在未关闭判断之外这正是本次修复的核心此前未把当前完成即 batch-final这一情况纳入排除导致G003同时充当 batch-final 与 run-final 时先被ULW_LOOP_VALIDATION_BATCH_OPEN卡住。checkpoint 侧的完整完成分支checkpoint.ts 第 171-226 行如今呈现为const aggregate codexGoalMode(plan) aggregate; const final isFinalRunCompletionCandidate(plan, goal); const closesBatch batchClosedBy(plan, goal.id) ! undefined; if (final) { requireAllCriteriaPass(goal); requireAllPlanCriteriaPass(plan); requireAllValidationBatchesClosed(plan, goal.id); // 排除当前 goal } else if (aggregate) requireEssentialCriteriaPass(goal); else requireAllCriteriaPass(goal); // ... if (closesBatch) requireBatchFinalReady(plan, goal); if (closesBatch args.qualityGateJson undefined) throw new UlwLoopError(Validation batch final checkpoint requires --quality-gate-json., ULW_LOOP_VALIDATION_BATCH_GATE_REQUIRED); // ... requireBatchGate(plan, goal, qualityGate);要点解读closesBatch分支凡是要关闭 batch 的 goal 都必须先过requireBatchFinalReady即其余成员必须全部 resolve否则 fail-closed 拒绝。batch 最终成员必须携带质量门--quality-gate-json缺失时报ULW_LOOP_VALIDATION_BATCH_GATE_REQUIREDrequireBatchGate 还会核对所有成员的 success criteria 状态存在非 pass 项报ULW_LOOP_VALIDATION_BATCH_CRITERIA_PENDING覆盖率与成员 criteria 不匹配报ULW_LOOP_VALIDATION_BATCH_GATE_MISMATCH。成功关闭时写入审计checkpoint 提交时追加一条batch_closedledger entry见 checkpoint.ts 第 234-235 行kind: batch_closed也在 constants.ts 的 ledger 事件全集 中登记。此外updateBatchesAfterSupersedevalidation-batch.ts 第 67-76 行处理 supersede 场景下的 batch 成员替换被替换的 goal 位置由 replacementIds 平铺填充若被替换者恰好是 final 成员则新 final 取替换列表末位从而保证 batch 结构在 steering 后依然自洽。三、范围外副作用的识别与恢复Discarded/restored side effects原文档明确记录了一类必须丢弃并恢复的副作用bun run test:codex在运行过程中重建了packages/omo-codex/plugin/components/codegraph/dist/cli.js且使用了与预期不同的 bundle 注释前缀alternate bundle comment prefixes。这属于代码图组件的构建产物漂移与 ulw-loop 的改动范围无关属于out of scope。处理方式从 codegraph 组件的包目录package cwd重新构建一次该组件将其恢复为受控状态最终 tracked status 显示 codegraph 无任何 diff。这一步骤说明抢救遗留 diff 时不仅要决定留什么还要能识别哪些是测试/构建副作用造成的噪音并主动还原避免把无关重建产物混入提交。四、验证矩阵红测试 → 聚焦绿测试 → 全量门禁原文档的 Verification 一节给出了一条完整、可复现的 TDD 验证链这也是整篇证据最值得复用的部分。第一步先跑红Red证明两个缺陷真实存在npm test -- cli-steering-batch # 失败批量条目缺失 source npm test -- validation-batch-checkpoint # 失败batch-final 的 aggregate 完成被误拒绝对应的测试文件即 test/cli-steering-batch.test.ts 与 test/validation-batch-checkpoint.test.ts。第二步聚焦绿Green修复后只跑相关用例并做类型检查npm test -- cli-steering-batch steering-batch npm run typecheck npm test -- validation-batch-checkpoint checkpoint npm run typecheck第三步全量门禁npm run check npm test # biome check 组件全量测试 bun run test:codex # 仓库根级 Codex 兼容性门禁据 task-6.md 的记录组件全量测试从抢救前的 40 文件 / 412 测试增长到 reviewer-fix 后的 40 文件 / 418 测试根级 Codex 门禁 510 个测试 0 失败此外还构建了dist/cli.js并在隔离的临时 git 仓库中完成了 e2e 生命周期转录task-6-e2e-transcript.txt。真实~/.codex/config.toml的 SHA-256 在 QA 前后完全一致780c740e…61d5证明全程未污染真实 Codex 配置所有临时 QA 仓库也均被清理。五、e2e 生命周期解读三条特性在同一计划上的交汇task-6-e2e-transcript.txt 用一段可复现的转录完整演示了checkpoint 自动推进 原子化 steering 批次 validation-batch 关闭在同一计划上的串行交汇$ create-goals --validation-batch-json VB001 created goals3 batchVB001 version1 $ checkpoint G001 - complete自动推进 checkpointG001-goal-alpha-complete nextG002-goal-beta:in_progress $ steer --proposals-json 2 个不含 source 的条目CLI 默认 sourcecli steer acceptedtrue results2 acceptedItems2 $ checkpoint G002非 batch-final 成员- complete $ 记录 G003 剩余 criteria 与最终质量门工件coverage6/6 $ checkpoint G003 finalbatch 关闭 aggregate 完成 checkpointG003-goal-gamma-complete aggregatecomplete $ 最终 statusaggregatecomplete complete3/3 criteriaPass9/9 $ ledger 中出现 batch_closed 行 {kind:batch_closed,goalId:G003-goal-gamma,message:VB001}随后在同一临时仓库的隔离会话中验证 fail-closed 场景故意在 batch 仍有未 resolve 成员时对 final 成员执行 checkpoint得到{ok: false, error: {code: ULW_LOOP_VALIDATION_BATCH_OPEN, message: Validation batch has unresolved members., details: {batchId: VB001, open: [G001-goal-alpha]}}}这段转录是理解本文两个修复价值的直观入口G003同时充当 batch-final 与 run-final正是触发关闭语义缺陷的场景两个--proposals-json条目均未写source正是触发默认值契约缺陷的场景。两处修复合流后生命周期得以一路绿灯走到aggregatecomplete。六、工程启示从一次抢救中沉淀的四个原则范围纪律先行17 个文件逐 hunk 审查、非 ulw-loop 路径一律不保留是所有后续动作的前提遗留 diff 里的格式化噪音 构建副作用 真实修复必须分层处理。TDD 固化并发语义缺陷source默认值与 batch 关闭语义都是缺省行为/边界条件类缺陷最容易在真实 QA 中暴露先用cli-steering-batch、validation-batch-checkpoint两个用例复现红再修复转绿最后跑npm run check npm test与bun run test:codex全量回归形成闭环。fail-closed 胜过静默通过ULW_LOOP_VALIDATION_BATCH_OPEN、ULW_LOOP_VALIDATION_BATCH_GATE_REQUIRED、ULW_LOOP_STEERING_BATCH_CONFLICT等一批带ULW_LOOP_*前缀的类型化错误码让每一次拒绝都可被机器读取、可被 QA 断言而不是静默吞掉非法状态。副作用可还原构建产物漂移codegraphdist/cli.js通过从包目录重建即可还原且最终 tracked status 必须干净同时用配置哈希~/.codex/config.tomlSHA-256 前后一致证明 QA 过程不污染真实环境。如需继续深入可进一步阅读 ulw-loop 组件 README、steering.ts单条 steering 校验与变异应用、checkpoint-final.test.tsfinal 关闭路径的既有回归覆盖以及 reviewer-fix-report.md独立评审阶段的阻塞项修复记录从测试侧反向验证本文所述两条修复的边界。【免费下载链接】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),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价