Repository navigation
fix(scheduler): preserve cadence after settled Turn replay - #5403
Conversation
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
本次完整评审结论:APPROVE,对应 head 5d43394ca0fc17176c5b1e2be4135b23cc5db54e,对比 base db3672f3c6646a75878158d66c79a1a5a32ceb32。没有发现阻塞问题。结论覆盖当前 9 个文件的完整改动;这是控制面修复,等待维护者合并。
动机
完成一个 Turn,只能证明这个 Turn 的回执已经结清。它不能证明整个 Goal 进入等待,更不能证明新 successor 不需要及时推进。旧实现将 heartbeat_settled_skip 映射成 QUIET_WAIT,随后生成 backoff_until_state_change。因此同一个已结算 Turn 的读回,会给仍有后续工作的自动化发出降频指令。
这对应 overall roadmap S2/S7 的回放可靠性与调度边界。我用相同合成场景,在不可变 base/head 上运行真实 CLI 和独立的 File、SQLite 权威存储:原 Turn 结算一次、新 successor 仍 open、另一注册 peer 的任务也存在时,base 的三个已结算读回仍返回 backoff;head 均只保留当前调度。两边重复 spend 都不会再次扣费,fresh Turn 才重新获得推进资格。
改动思路
改动复用既有 scheduler.state_transition.evaluate TypeScript owner,增加只读、无调度副作用的 settled_replay 分支。Python 负责最终交互契约的仲裁和传输适配,既有 execution context 负责区分 App 与 native Goal。没有新增持久标志、配置项、capability、provider 或第二个 cadence 决策源。
我对比了三个方案:维持旧行为会错误减慢后续工作;统一固定成三分钟会覆盖真实等待、未知 cadence 和用户原有设置;本方案仅让已关闭 Turn 保持既有调度,下一个 fresh Turn 再评估当前 frontier,范围更准确。仅替换枚举还不够:如果仍经过 backoff builder,它仍会生成 reset、目标 RRULE 或 ACK。因此在既有 owner 中产出完整 hold-only packet 并绕开 backoff builder,是必要的有界修复。
具体改动
arbitration.py将精确的 settled mode 分类为SETTLED_REPLAY。若这个关闭契约仍声称需要用户处理、需要执行或允许交付,则进入 consistency repair。scheduler_hint.py在明确的 Goal stop/quota pause 和 terminal 处理之后,选择 typed replay projection;state_transition_rules.py只调用已有 TS effect runtime 并检查响应形状。state_transition_rules.ts增加SchedulerSettledReplayResult和projectSettledReplay:返回preserve_current_schedule、next_trigger=fresh_turn_identity,没有目标 interval/RRULE、reset、backoff progression、ACK、failure 或 fallback 指令。已有 cadence/host/backoff/monitor 操作保持原状。execution_context.py让 native Goal 的这个结果进入continue_now;它仍需下一轮的 fresh identity 和 quota guard,并没有给当前关闭 Turn 新的执行权限。- 两组 Python 回归与 TS transition 测试覆盖已结算读回、未知/三分钟/六十分钟 cadence、App/native Goal、矛盾契约,以及真实 successor 创建、结算、重复 spend、fresh guard。
docs/quota-allocation.md明确披露旧默认与新默认的区别。
关键代码讲解
build_scheduler_arbitration 读取最终 interaction_contract。settled 判定来自精确 mode 和关闭义务,不依赖 Todo 标题、显示条数或“看起来没工作”的文字推断。
projectSettledReplay 把已观察到的 RRULE 当作信息保留;不知道 cadence 时仍是 null,不会猜一个目标。host_action=none 和 ack_required=false 与没有任何目标字段共同构成无副作用契约。
build_scheduler_hint 的 settled 分支 提前返回,因此不会进入 stateful backoff。build_goal_runtime_continuation 只改变下一次 guard 的继续方式;原有 receipt、Todo admission 和 spend owner 仍负责防止同一 Turn 重复执行。
对主干的风险
主要风险是把“保留调度”误读成“当前 Turn 可以继续执行”,或者让明确 stop/pause、真实 fresh-Turn 等待也失去退避。独立验证没有发现这些变化:72 组相同 base/head builder 场景中,45 组非 settled 或 pause 的完整语义对象及 Markdown guidance 完全一致;其余 27 组仅具有已披露的 settled 调度变化。真实 CLI 的 File/SQLite 两组生命周期均保持单次扣费、重复 spend 不追加、关闭回放不追加 history、不改 scheduler state、不选择其他 Todo;新 successor 和独立 peer 工作不会被关闭 Turn 吞并,fresh guard 返回 run_now。
独立反例的 oracle 来自“一个关闭回执不授权重设宿主调度”:base 上六次真实 CLI replay 均使断言失败,当前 head 全部通过。矛盾 flag 的负例由已有仲裁进入 repair。这个证明包含真实 backend 和 Python→TS 边界;不把 mock 状态或作者提交的验证声明作为通过依据。
本次本地验证:315 个相关 Python 测试、8 个 TS transition 测试通过;TS typecheck、Ruff、配置内 Mypy 19 个文件、完整 semantic vocabulary smoke、diff/compile 检查以及 risk-based premerge 选出的 19 个 canary/public-boundary 检查通过。新增局部仲裁枚举的 advisory 已检查:它是 scheduler 内部 disposition,与 agent frontier vocabulary 的用途不同,不能仅因字符串重叠合并。最初一次测试命令引用了不存在的旧文件名,没有执行测试;修正命令后上述 315 个测试全部通过。独立 probe 最初的合成 Todo ID 不符合既有 schema,两边同样拒绝;换为合法合成 ID 后才进行对照。没有查询或等待 CI,也没有调整验证阈值。
没有前端或配置编辑器配套缺口:这次修复的是已有 CLI/heartbeat/native Goal 输出,自动加载的 heartbeat guidance 已按现有字段执行 host action;hold-only packet 不携带 update/ACK 指令。源代码验证不等于已安装 App/外部 native host 升级,合并后的安装与实际宿主调度读回仍属于部署边界。本 PR 也不宣称完成分布式调度、公平性或整个 S7。
我的整体评价
这是一个有实际后续推进价值、范围适当且可回退的修复。新增约束没有放宽执行或扣费 authority,也没有引入手工同步的新状态。未来改动的边界检查已完成:保留既有 typed scheduler owner,避免增加固定 cadence 框架;现有 Python 适配和 host context 有明确调用者,当前不需要额外拆分。批准当前 exact head,等待维护者合并。
English verdict: APPROVE - head 5d43394. Settled replay preserves host cadence without reopening work or charging twice; independent base/head File/SQLite CLI counterexamples, 315 Python tests, 8 TS tests and 19 selected canaries passed. Installed-host adoption remains outside this source qualification.
Delivery brief
Goal: preserve useful wakeups after a bounded Turn finishes. Owning acceptance: roadmap S7 scheduling / S2 receipt replay reliability. A closed heartbeat receipt currently selects
QUIET_WAITand a 30/60-minute host backoff even when the Goal still has unfinished work. Turn idempotency is not evidence that the live frontier is waiting.Changes
heartbeat_settled_skipto a localSETTLED_REPLAYdisposition, not quiet waiting. Reject contradictory closed contracts carrying execution or user-action obligations.scheduler.state_transition.evaluateowner with a typed, hold-onlysettled_replayprojection. Python adapts its transport and applies existing host scoping; no new capability, provider, policy editor or parallel cadence owner is introduced.preserve_current_schedule, with no target RRULE/interval, scheduler reset, backoff progression, ACK, host failure/fallback or update instruction. Unknown host cadence stays unknown. The next trigger must evaluate the live frontier with a fresh Turn identity; replay still cannot execute or spend again.Entry points: CLI
quota should-run, Codex/Trae heartbeat hints, native Goal continuation. There is no new configuration or frontend editor field: this corrects the existing host projection consumed by the automation host. The installed host schedule is outside the repository diff.Validation
backoff_until_state_change).cqr_4a9676e66836940f05e5(9 files), verified. No safe-fix pass; no unresolved quality blockers.Future-facing pass: retain one typed hold-only owner and bypass the unrelated backoff builder; do not add a speculative fixed-cadence framework or infer admission from raw Todo counts. This is a bounded replay/projection repair, not qualification of distributed scheduling or an entire roadmap milestone.
Merge boundary
Control-plane behavior changes require maintainer merge. No self-merge or admin bypass is requested. Source qualification is not an installed-runtime upgrade; deployment/readback follows maintainer acceptance.