Repository navigation
fix(scheduler): reset backoff when deadline buckets shrink - #6787
Conversation
Signed-off-by: Lihua <1017343802@qq.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max
APPROVE:没有发现当前未解决的代码阻断项。 Exact head: 6787@403519cea8a116426093104f292e0f9e6278df9a;base: aeeab9214f8a85ee2fce5a840ee66077e5d7821f。批准针对下面的调度修复;质量凭据与合并权限单独保留。
动机
使用渐进退避心跳的用户,在待办进入截止窗口或等待本人操作时,需要调度器及时收紧下一次检查间隔。
原先允许间隔从 15/30 分钟收紧到 15 分钟时,旧索引仍被当作同一轮退避,确认新间隔会被误拒为倒退;修复后使用新的退避轮次并从首档继续。
这会让真正需要更早唤醒的监控不断遇到确认冲突,人工重复操作也不能改变错误的退避身份。真实 CLI 在 File、SQLite 及两种截止等待状态下都能确认收紧后的间隔,重复确认不再写状态,普通工作分支保持原决定。
本 PR 仅修复退避身份与最终间隔列表的一致性,不更改用户设定的间隔下限、执行准入、配额或工作内容,也不宣称整个调度系统已完成验收。
改动思路
最终可用间隔已经由现有调度器计算,直接把它加入退避身份的输入即可;TypeScript 的提交状态机继续独占顺序、并发和重放规则,不需要第二套重置条件。
当前 PR 边界是让真实心跳确认在间隔列表收紧后恢复正常推进,并保留同一列表内的防倒退与幂等约束。
现有代码先根据截止时间和间隔下限计算最终可用档位,再生成重置身份;这次把前一步的完整结果带入后一步。它没有另写一个“截止窗口允许倒退”的例外,因此原生提交仍能拒绝真正的同轮倒退。下面是从当前源码推导的说明表,运行证据另列;未核验 Mermaid 渲染,使用表格。
| 触发 | 基线 | 当前 head |
|---|---|---|
| 15/30 分钟已到第二档,截止时间收紧为 15 | 身份未变,首档被误拒 | 身份改变,首档正常提交 |
| 档位列表保持相同 | 保持现有轮次 | 同样保持,不反复重置 |
| 相同确认再次送达 | 原生幂等重放 | 原生幂等重放,无新写入 |
| 同身份倒退、旧策略、旧 CAS 或错误宿主 | 拒绝 | 继续拒绝 |
具体改动
完整差异是 6 个路径、263 行增加和 4 行删除;运行时只调整现有 profile 的组装,其他是说明及跨 Python/TypeScript 的回归覆盖。没有新增状态账本、公共命令、配置项或执行 owner。
关键代码讲解
_SchedulerHintBuilder.build在最终约束计算之后,把app_cadence_progression写入reset_profile_snapshot。已有 override 被保留,最终列表覆盖旧快照中不完整的列表;同样列表得到稳定身份。build_app_automation_scheduler_ack_hint继续生成绑定原 registry、runtime、Turn 的确认命令。修改的是身份输入,没有把 ACK 变成执行任务或扣配额的入口。evaluateSchedulerHeartbeatCommit是未改动的 TypeScript 状态机:不同身份必须从初始档开始,相同身份不能倒退;旧 CAS、策略、宿主或有冲突的重放均不能借新 hash 绕过。
规范基准 docs/quota-allocation.md,spec_revision=aeeab9214f8a85ee2fce5a840ee66077e5d7821f(固定版本)是在读实现前核对的既有约定:reset_policy.reset_token 对应有效 profile 变化后的首档重置;progression_minutes 对应同轮渐进及稳定性;app_automation.ack_hint.cli_args 对应原宿主与当前调用的确认范围。三项均由上述调用链和真实原生命令验证,未从新增测试倒推规范。
对主干的风险
最强反例是把监控每轮都当作新 profile,或者为修复误拒而放宽所有倒退。当前完整列表是确定性输入;列表不变的稳定性回归、同身份倒退与旧策略/CAS/宿主负例均通过。已有监控在首次纳入有效档位后可能重置一次,这是身份修正的迁移成本;没有承诺既有 hash 逐字节不变,也没有更改用户下限。
当前独立验证:28 个 Python 调度用例、22 个 TypeScript 原生提交用例、类型检查,以及标准 premerge 的 6 个直接检查和 19 个风险选择检查全部通过,包含语义与公开边界检查。使用同一脚本与相同 fixture,对不可变基线和当前 head 分别运行真实 File/SQLite CLI:两种截止等待状态共 4 组,基线全部报 progression_regression,当前全部提交首档;随后原样重放均 written=false,单独读回确认状态未变。另 12 个普通状态的决定、诊断和权限保持一致,只有预期监控 reset hash 变化。早期私有探针存在隔离环境和上下文取样错误,已保留且不计作有效红绿证据;这里采用修正后证明子进程源码来源的匹配结果。
托管 premerge 的总体结果仍为 false,唯一剩余项是当前范围 quality receipt missing;实际检查没有失败。这是独立合并资格限制,由原生质量流程补齐,评审没有创建凭据或放宽门槛。没有查询、等待或触发 GitHub CI。真实宿主长期运行未做,本轮验证没有改写运行中的自动化。
我的整体评价
这是针对已复现缺陷的完整有界修复。长期运行维度 improved:截止窗口能从错误拒绝恢复到原生确认和后续幂等,不靠反复重试;用户体验 improved:原有自动确认旅程恢复,不增加设置、复制参数或额外授权轮次。普通工作、准入和结算语义保持原样。
未来可维护性检查已应用在同一 owner 边界:复用已有最终 profile,保留 TypeScript 独占状态转换,避免新增 clamp 标记、平行 Python 判断或一行用途的框架。当前无需额外重构。批准仅对此 exact head 的代码与约定;缺少质量凭据和维护者合并授权的限制仍然存在。
English verdict: APPROVE at 403519cea8a116426093104f292e0f9e6278df9a. The matched real CLI counterexample fails on the base and succeeds on this head without weakening native monotonicity or replay guards. Exact-scope quality qualification remains a separate merge hold.
A monitor deadline can shorten the allowed heartbeat progression from
[15, 30]to[15]after the 30-minute/index-one ACK. The generated 15-minute/index-zero ACK previously reused the old reset token, so the native commit rejected it withprogression_regressioneven after the host applied the requested cadence.Bind the effective, post-constraint progression buckets into the existing reset-profile digest. Bucket changes start a fresh identity at index zero; elapsed time within the same buckets stays stable. This reuses the current Python projection adapter and TypeScript commit owner, preserves ordinary non-monitor identity behavior, and adds no capability, setting, CLI flag or second state owner. The native monotonic, host-readback, receipt-freshness, CAS and replay guards remain unchanged.
Validation:
progression_regression; both new initial regression cases failed before the repair.python -I -m loopx.cliagainst real disposable File/SQLite Goals: generated should-run → host manifest readback → exact ACK → replay. Each commits 15-minute/index-zero state, preserves canonical Todos, and spends zero quota. Earlier 15/30-minute state uses a controlled synthetic clock; the host is a disposable manifest, not a live desktop automation.catalog-run-e2e-smoke; its isolated rerun and the final stable-tree run pass. That first failure is retained and is not attributed to a proven root cause.Additional check limit: an ad hoc explicit-file mypy walk for this module (outside the configured target list) fails with 4,942 strict errors across 579 imported files. Its two reported adapter errors are on unchanged lines; the full graph is not differentially attributed and no Python mypy pass is claimed.
Placement/scope review: binding the post-constraint profile at its existing assembly point is the bounded improvement; a broader scheduler-language migration or commit-owner refactor is unnecessary for this repair. Bilingual quota guidance and a public-safe self-repair pattern record the contract. No UI behavior changes.
The reviewed source head
403519cea8a116426093104f292e0f9e6278df9amerged on 2026-10-18 at 09:36:35 UTC as979a0787f7d0e56900e55777929b3eebef89b3e9, which is contained in verified official main3203b55843d2dcca5b77822ed857442adf944587. The source and isolated-wheel validation above is retained. Shared-runtime/App adoption and live scheduler qualification remain separate. Observing the merge does not rewrite historical quality holds or certify broader CI. No active scheduler state was manually edited and no shared runtime was upgraded.中文:截止时间收紧后,允许档位从
[15, 30]变为[15],旧实现仍复用原重置令牌,导致已退避至索引一的 ACK 无法回到索引零。本修复将约束后的实际档位纳入现有令牌计算,同档位内时间流逝保持稳定;保留原生防倒退、宿主回读、回执时效、CAS 与重放保护。上述测试及隔离安装包四条真实 CLI 路径已验证,首次 premerge 烟测失败与之后通过分别保留。当前读回确认最终 head 已合并,且合并提交包含在官方主干中;源码与隔离安装验证保留。现有 App/共享运行时采用和真实宿主调度验收仍独立保留,未改写历史质量限制。