Repository navigation
fix(scheduler): honor final attempt permission in external worker - #7140
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; gpt-6-astra; OpenAI; runtime_reported; reasoning_effort=xhigh.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
未发现阻塞性问题。评审提交:0f2054ac7ffe1a6d7bb158ae25667ec440b4dfcc。这是执行端的契约对齐修复;批准结论不代表合并授权,也不代表长期实验效果已验证。
动机
使用外部定时 worker 的操作者,会在任务等待新证据时遇到无效模型唤醒。旧实现可能在最终契约要求等待、原始可运行提示仍为真时反复调用模型;修复后等待,收到新的执行义务再继续。可观察结果是等待阶段没有子进程,新的正常工作或修复工作能启动一次,完成后立即重读停止状态。本次不改变任务评分、模型提示、预算、状态存储或运行中实验。剩余问题是这项修复在真实长期任务中的成本收益尚未测量。
改动思路
最终交互契约由控制平面生成,外部 worker 只负责执行它。最小修复是在现有解码处读取 must_attempt,不把 action 文本、通知、delivery_allowed 或原始 should_run 重新拼成第二套决策。保留现有宿主执行器和退避状态,避免增加一次语义桥调用或新的可选策略。本 PR 的边界是修复 shell connector 对现有契约的消费。
严格布尔校验保护输入边界;只有整个契约不存在时才保留旧 CLI 的布尔接口。这个兼容分支服务于可独立配置的 --cli-bin,不是另一套调度策略。契约存在但畸形时明确拒绝唤醒,producer stop 仍先于报文校验。现有 #7100 已统一 producer 的 execution projection;它没有修复这个实际执行者,单改 producer 无法消除本次复现。
以下为源码导出的行为对照,表格不是执行证据;进程验证见下文。
| 输入 | 旧执行端 | 本次执行端 |
|---|---|---|
| 等待契约、原始提示为真 | 错误唤醒 | 按既有退避等待 |
| 修复义务、原始提示为假 | 错过执行 | 执行修复 |
| 畸形契约、原始提示为真 | 错误回退 | 拒绝唤醒 |
| producer 停止 | 停止 | 保持停止 |
具体改动
规范依据是 docs/development/testing-and-quality.md 的 Decision Replay And Issue #2191,按提交 831b6d33246d31586742cfefcdf464245b8f7f84 的文本评审。条款 final-interaction-authority 要求最终 interaction_contract 为调度权威,原始 should_run 和底层兼容字段不得覆盖它:本次已实现,验证覆盖等待和修复两个相反方向。条款 nonblocking-user-action 要求非阻塞用户动作不能满足 Todo 的阻塞决策范围:本次不改 Todo scope owner,属于已接受切片之外;通知本身不授权执行的消费边界已覆盖。
关键代码讲解
scripts/external_scheduler_worker.py::parse_tick在 producer stop 和失败结果检查之后,优先读取 interaction_contract.agent_channel.must_attempt。缺失或者非布尔字段不能通过 Python truthiness 授权;只有无契约的旧报文沿用原始严格布尔分支。scripts/external_scheduler_worker.py::_run_worker_locked是实际使用 TickDecision.should_run 启动命令的现有调用方。代码未改,但输入已改为最终权限,等待时可累计 unchanged_count,在达到上限前执行最后一次 quota 探测,发现新工作即可转入子进程执行。tests/test_external_scheduler_worker.py::test_typed_wait_backs_off_then_fresh_attempt_wakes_once经真实 TypeScript 仲裁、scheduler hint、quota 子进程和 wake 子进程,验证两次退避、第三次的最终探测、一次执行及立即读回停止。分别验证普通工作与 delivery 禁用的修复义务;通知方向刻意相反。test_malformed_contract_cannot_fall_back_to_legacy_wake对空、错类型和非布尔字段验证无子进程。停止报文同时带畸形权限,检验优先级而非只测孤立 happy path。
对主干的风险
主要风险是错把正常修复当成不可交付工作,从而只阻止循环却造成饥饿。本次反例用原始字段为假、must_attempt 为真、delivery_allowed 为假,最后探测仍启动一次命令。另一个风险是更新脚本后旧 CLI 不带契约;91 项原有外部 worker 用例在基线通过,在本次 161 项组合中也通过,宿主失败、锁、重启、超时和预算退出码的规则保持原状。动态 --cli-bin 和 benchmark/runtime/scheduler.py::worker_command 是实际可达入口,新增契约字段无需操作者手动维护。
本地验证:161 项外部 worker 与相邻 scheduler 测试通过;同一组 11 个新增反例在基线全部失败、修复后全部通过;标准 premerge 六个 canary 及直接检查通过。首次 premerge 因新工作树未安装 typescript 依赖失败,补齐 npm ci --ignore-scripts 后同一命令通过,保留最初失败记录。按当前 review 配置不查询远端 CI。这里模拟 quota 传输、替代模型为实际标记子进程,并使用临时时钟记录等待,因此证明执行边界,不证明真实模型质量或日级收益。
语义与 CI 对齐
复用现有 interaction_contract.agent_channel.must_attempt,没有新增协议、枚举或持久化字段。全量语义 canary 已通过;差异扫描不覆盖脚本中的所有语义形式,空扫描不是正确性证据。PR 明示旧行为与新行为;错误信息明确说明拒绝模型唤醒,通用契约未加入 benchmark 专属措辞。
我的整体评价
APPROVE,作为更大长期效率目标的 justified_increment。long_horizon 在本切片范围内 improved:连续等待不会执行,未来工作仍能恢复;user_experience preserved:启动参数、停止和错误恢复入口保持现状,没有额外设置或确认轮次。scripts/external_scheduler_worker.py 的命令行执行没有渲染界面变更,因此 visual_review 不适用;未验证范围是实际模型采用与长期成本收益。
改动量与问题相称,生产变更仅在已有解码分支,没有第二个 owner 或额外状态。保留兼容入口有明确独立 CLI 消费者;不为三行投影解码建立抽象框架。对比“不改”会保留已复现的错误唤醒;对比“只改 producer”无法修复忽略最终字段的消费者。后续由实验集成维护者在下一次版本冻结时采用并测量效果,当前实验无需热改。合并需独立的 maintainer 授权和即时 readiness。
English verdict: APPROVE at 0f2054a. The worker now obeys final attempt permission, with strict legacy decoding and producer-stop precedence preserved. 161 focused/adjacent tests and standard premerge passed; all 11 new regressions fail on the base. No blocking finding. Real-model adoption and long-horizon savings remain unqualified; this verdict grants no merge authority.
The external scheduler worker can launch a model while the final interaction contract requires an unchanged wait: it reads raw
should_runinstead ofinteraction_contract.agent_channel.must_attempt. The reverse mismatch can also suppress required repair work. This PR makes the shell connector consume the existing execution projection, keeping scheduling semantics in their current owner.A present malformed contract now fails closed. Packets without a contract retain strict boolean legacy behavior. Producer stop directives still win; notification and delivery permissions do not substitute for attempt permission. The synthetic process test crosses native TypeScript arbitration, scheduler projection, quota subprocess and actual wake subprocess boundaries, proving progressive waiting, final fresh-probe recovery, immediate post-wake recheck and stop.
Validation:
831b6d332and pass with the fix.Delivery boundary: a shell-connector correctness fix. Long-horizon cost savings and real model adoption remain unqualified; no claim of benchmark improvement follows from these synthetic process tests. The existing S7 scheduler acceptance remains open. Maintainer merge authorization is separate.