fix(quota): isolate recovery from current replan - #4329
Conversation
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
PR #4329 — fix(quota): isolate recovery from current replan
Reviewed exact head: 28498ae9d6c99f9d3734886bb5386e91de579f6e
动机
这个 PR 解决的是一个真实的 heartbeat settlement 身份串线问题。旧逻辑在 effective_action=unsettled_host_turn_recovery 时仍会继续读取 replan_action_packet,因此同一响应里的当前 replan 可能被写成前一 Turn recovery receipt 的 settlement_identity。这样后续 closeout/readback 会追错 effect identity,主机持续得到 required_closeout_receipt_missing,而本应在 typed lifecycle closeout 之后选择的 successor 无法安全重入。变更只涉及现有 quota settlement owner 和它的回归测试;没有新增 quota、delivery、merge 或其他 authority。更小的 caller-side 修补无法覆盖所有 receipt reconciliation 路径,因此在 quota_rollout_settlement_binding 设定恢复边界是合适的最小修复。
改动思路
apply_unsettled_host_turn_recovery_if_required 已经把 recovery 明确建模为 delivery 不允许的 repair Turn,并移除 selected_todo/action_portfolio。本 PR 在随后的 quota_rollout_settlement_binding 再加一道同样的 authority boundary:只要 effective action 是 recovery,就返回 (None, None),不把并行投影的 replan obligation 当成 durable settlement identity。replan id 仍通过 semantic_replan_obligation_id 保持可见,便于诊断和后续独立动作;只有 typed external-wait/blocker closeout 完成后,新的 should-run 才能绑定 successor Todo。该实现复用了已有 receipt append/readback、Todo lifecycle 和 same-Turn reconciliation,没有引入第二个状态机或 provider。
具体改动
关键代码讲解
loopx/control_plane/quota/settlement_cli.py:185的quota_rollout_settlement_binding增加 recovery exact-action guard。普通显式 Todo/replan 参数、typed settlement plan 和 selected Todo 的既有顺序保持不变;恢复分支不会消费replan_action_packet的 obligation id。loopx/control_plane/quota/unsettled_host_turn.py:127的apply_unsettled_host_turn_recovery_if_required是上游 recovery 投影 owner,继续输出delivery_allowed=false、must_attempt=true和 typed lifecycle closeout contract;本 PR 只修复它下游的 receipt 身份绑定。tests/control_plane/test_quota_settlement_cli.py:1036将原 recovery 用例扩展为 15 项 replan backlog、identity-less recovery、typed wait 和同 Turn successor reentry 的完整序列,断言 semantic replan id 可见但不进入 settlement identity。
变更分类为 1 个生产 Python helper 加 1 个测试文件,共 +37/-1;没有生成文件、文档协议或私有材料。
对主干的风险
我先用基线 7eb4b7bb1661bd5eff63a8725a33169792d5964b 和 exact head 做反例对照:同一临时 registry/runtime 注入 15 个 replan Todo,基线 recovery receipt 写入了 binding_kind=autonomous_replan;exact head 仅保留 semantic_replan_obligation_id,不再写 settlement_identity。基线原有 recovery smoke 通过,说明普通用例本身不足以证明修复;新增 backlog 反例覆盖了真正的串线条件。exact head 的完整 quota settlement suite 为 45 passed, 0 failed, 0 skipped。
无 blocking finding。唯一的 P2 建议是:guard 位于显式参数解析之前,直接调用该 helper 时即使传入显式 todo_id/replan_obligation_id 也会得到 (None, None),这与函数 docstring 对“explicit settlement arguments are causal identities”的表述不完全一致。正常 public recovery path 会在进入 reconciliation 前拒绝显式 action selection,因此当前没有复现为用户可达 blocker;建议后续要么把 guard 放到显式参数处理之后,要么加测试明确 recovery 中显式 identity 的拒绝语义。
我的整体评价
这是一个范围很小但针对性很强的修复:它把“修复前一 Turn”与“当前 replan/下一独立动作”重新分开,并以独立 base/head CLI 反例证明了 observable delta。authority、typed state、domain-neutrality、guidance-vs-obligation 和 default-off 检查均通过;没有发现 blocking finding。GitHub 当前 exact head 28498ae9d6c99f9d3734886bb5386e91de579f6e 保持不变,mergeable=MERGEABLE、mergeStateStatus=BEHIND,23 个非 skipped hosted checks 全部成功。建议 APPROVE;合并前只需按仓库流程更新到最新 main(如维护者要求),并保留该 P2 contract follow-up。
English verdict: APPROVE — exact head 28498ae9d6c99f9d3734886bb5386e91de579f6e; the recovery receipt no longer binds a concurrent replan, while the independent base/head CLI counterexample and 45-test exact-head quota suite pass. One non-blocking P2 notes explicit-identity precedence in the helper contract.
Summary
摘要
Delivery contract / 交付契约
Target capability / 目标能力: LoopX Core quota settlement and heartbeat recovery.
Repository / 仓库: git:github.com/huangruiteng/loopx.
Write scope / 写范围: quota settlement binding and its integration test only.
Product entry points / 产品入口: CLI, managed Turn and Lark scheduler consume the same typed should-run receipt. No separate frontend control is needed.
Validation / 验证
Safety / 安全边界
Existing prior-Turn closeout evidence is still required. This grants no delivery, message, merge, signing, transfer or trading authority. Current replan projection remains visible without being recorded as the repaired prior Turn.
仍需既有前一 Turn closeout 证据。本变更不授予交付、消息、合并、签名、转账或交易权限;当前 replan 保持可见,但不再被记录为被修复的前一 Turn。