Skip to content

fix(scheduler): preserve cadence after settled Turn replay - #5403

Merged
huangruiteng merged 2 commits into
mainfrom
codex/settled-turn-cadence
Oct 1, 2026
Merged

huangruiteng merged 2 commits into
mainfrom
codex/settled-turn-cadence

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

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_WAIT and 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

  • Route heartbeat_settled_skip to a local SETTLED_REPLAY disposition, not quiet waiting. Reject contradictory closed contracts carrying execution or user-action obligations.
  • Extend the existing built-in TypeScript scheduler.state_transition.evaluate owner with a typed, hold-only settled_replay projection. Python adapts its transport and applies existing host scoping; no new capability, provider, policy editor or parallel cadence owner is introduced.
  • Emit 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.
  • Native Goal runtimes continue to a fresh guard rather than blocking/completing the Goal. Explicit Goal stop/quota pause remains higher precedence. Genuine fresh-Turn gate, monitor and quiet waits retain their existing policy.
  • Document this changed default in the quota contract. This does not globally pin heartbeat intervals or disable adaptive scheduling for actual waits.

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

  • Regression-first: all 9 known/unknown cadence × App/native Goal cases fail against the previous behavior (backoff_until_state_change).
  • 245 focused Python execution-context/replay cases; 93 scheduler arbitration/state/convergence/monitor cases passed.
  • Real source CLI with isolated synthetic File and SQLite authorities: create successor, settle original Turn once, duplicate spend remains idempotent, settled read emits no host cadence effects while successor remains open, fresh Turn admits work at 3 minutes (2 integration cases passed).
  • TypeScript typecheck and 8 transition tests passed, including the unchanged 19-case pinned characterization and malformed input/no-mutation checks.
  • Ruff, full-tree semantic vocabulary smoke, diff hygiene, compile checks and all 19 risk-selected native premerge checks passed. Public/private boundary scan clean; no required checks skipped.
  • Exact-scope CQA: 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.

Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@huangruiteng
huangruiteng merged commit d0d1d99 into main Oct 1, 2026
29 of 30 checks passed
@huangruiteng
huangruiteng deleted the codex/settled-turn-cadence branch October 1, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant