fix(quota): qualify explicit selection before final guard - #4350
Conversation
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
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)
动机
评审 exact head:4350@3f1a7c0bd94aa9d7dfc1bfdb049250d372944c82。本 PR 从 #4332 的旧七文件批次中收敛出当前 main 仍缺失的真实边界:首轮 quota guard 后新增一个显式 Todo,随后该 Todo 因最终 workspace/scope guard 暂不可交付。修复前 should_run_packet 在 guarded-agent-lane 分支提前返回,CLI 丢失 action_selection_qualification,最终退化为 quota_unexpected_collection_error。这会掩盖可恢复的 control_repair,让 Agent 不能按同一 Turn 可靠重入。
改动思路
入口是 quota should-run --codex-app --todo-id。TypeScript action-selection reducer 仍是资格语义 owner,should_run_packet.py 负责从 canonical Todo inventory 组装 route,quota.py 负责 CLI 错误与 heartbeat receipt。修复只把现有 qualify_action_selection_from_inventory 调用置于 final guarded-route return 之前;selected identity 仍由 guard 冻结,资格仍是 derived projection。deferred/rejected 继续走现有 typed preflight,已有 identity-less receipt 只 replay,不创建 settlement identity、不写新 rollout event、不扣 quota。没有新增模块、schema、authority、provider 或 capability activation。
具体改动
关键代码讲解
loopx/control_plane/quota/should_run_packet.py::_resolve_agent_lane_delivery_route:先计算请求 Todo 的 qualification,再返回 workspace/scope guarded route;非 guard 的现有 qualified/candidate 路径复用保存的 qualification,避免重复计算。loopx/cli_commands/quota.py::_apply_requested_quota_action_selection_preflight:未改动,继续作为 deferred/rejected 到 CLI/receipt 的唯一 transport owner;本 PR 验证它现在能收到 final-guard 的 typed qualification。loopx/control_plane/work_items/action_portfolio.py::qualify_action_selection_from_inventory:未改动,继续从 canonical inventory 推导 task class 并调用 TS reducer。tests/control_plane/test_quota_settlement_cli.py::test_selection_added_after_pending_guard_reports_final_boundary:新增 real File-authority CLI 回归,覆盖首轮 identity-less receipt、Todo late add、control_repairtyped defer、receipt replay/no mutation、确定性 retry 和最终 guard flags。
对主干的风险
这是局部 ordering 修复,运行时 diff 仅 8 行,覆盖现有 explicit-selection 调用,不触及默认选择、monitor polling、autonomous replan、user gate、workspace repair 已有路径或 quota settlement。负向 counterexample 在 baseline 上复现了 quota_unexpected_collection_error;exact head 返回 quota_action_selection_deferred,reason=control_repair,receipt status=replayed,rollout event 数量保持不变。新测试使用真实 quota CLI、File canonical authority 与持久化 receipt readback,不依赖 mock postcondition。
验证结果:focused regression 1 passed;workspace/monitor/replan/user-gate 邻近场景共 5 passed;完整 test_quota_settlement_cli.py 58 passed;boundary/CLI 两个相关 suite 25 passed;Ruff、Python compile、diff checks 通过。loopx canary premerge --from-git-diff --git-diff-base origin/main 的 direct checks 和大多数 selected smokes 通过;quota-plan-smoke 在 baseline 与 head 都受 capability-memory effect-runtime fixture 影响,catalog-run-e2e-smoke 使用项目 Python 直接运行通过但 wrapper 报失败,属于已复现的环境/基线证据,GitHub CI 仍是最终门禁。代码和测试均不改变 public/private boundary。
我的整体评价
当前 two-commit exact head 已完成 #4332 的 rebase 增量收敛:保留唯一仍有价值的 final-guard qualification 修复,删除并行 transport、重复协议文档和重复 monitor 测试。repository reuse、typed state、authority semantics、default-off isolation、domain neutrality 与 guidance-vs-obligation 均通过;没有 blocking finding。由于这是 author-owned PR,GitHub 不允许正式 self-approve,以下 COMMENTED 结论等价记录本次 APPROVE;合并前仍需 exact-head readiness 为 ready 且 GitHub 必需 checks 通过。
English verdict: APPROVE for 4350@3f1a7c0bd94aa9d7dfc1bfdb049250d372944c82 (author-owned PR; recorded as a comment because GitHub blocks self-approval). The focused ordering fix preserves typed quota_action_selection_deferred/control_repair and receipt replay for late explicit selections under the final boundary guard. Existing TS/Python owners, defaults, authority, and settlement paths remain unchanged. Focused, full quota, boundary/CLI, Ruff, compile, and diff checks pass; two canary failures are reproducible baseline/environment gaps and require GitHub CI confirmation.
Summary
Follow-up to #4332, rebased against
mainat0933d48efc058d9e1585bd30058942416aa258a9.The original seven-file head is mostly superseded by #4335 and conflicts with the current quota transport. The remaining incremental bug is narrower: when a Todo is added after the first guard and explicit selection then hits a final workspace/scope boundary guard, qualification was skipped and the CLI degraded to
quota_unexpected_collection_error.This PR keeps the existing TypeScript qualification and Python quota/receipt owners, computes qualification before returning the final guarded route, and adds one real CLI regression. Deferred selection remains typed (
quota_action_selection_deferred,control_repair), the identity-less receipt is replayed without mutation, and retry re-enters the current guard. No new schema, authority, quota spend, provider, or capability activation is introduced.Validation
python -m pytest -q tests/control_plane/test_quota_settlement_cli.py::test_selection_added_after_pending_guard_reports_final_boundary— passed.python -m pytest -q tests/control_plane/test_quota_settlement_cli.py— 58 passed.python -m pytest -q tests/control_plane/test_quota_boundary_projection_selection.py tests/control_plane/test_cli_output_budget.py— 25 passed.loopx canary premerge --from-git-diff --git-diff-base origin/main— direct checks and most selected smokes passed.quota-plan-smokereproduces an existing capability-memory effect-runtime fixture failure on both baseline and this branch;catalog-run-e2e-smokepasses when invoked directly with the project Python but is reported failed by the canary wrapper. These are environment/baseline gaps, not failures in the changed path.UI impact: none. This is a quota CLI/control-plane behavior fix.