Skip to content

fix(quota): name the workspace move when a selection is deferred for it - #4601

Closed
huangruiteng wants to merge 4 commits into
mainfrom
codex/workspace-repair-reason
Closed

huangruiteng wants to merge 4 commits into
mainfrom
codex/workspace-repair-reason

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

The friction

Binding an action from the shared checkout produces a message that names the wrong thing:

state: quota_action_selection_deferred
reason: explicit action selection was deferred by the current delivery frontier: control_repair
action_selection_qualification.delivery_preemptions: [control_repair, delivery_not_allowed]
effective_action: quota_skip
agent_channel.primary_action: wait for user/owner action after surfacing the blocker or gate

That reads as an unrelated control-plane preemption, so an agent can reasonably conclude it is blocked and go look for other work. The instruction it actually owes is in a different field of the same payload:

execution_obligation.kind = agent_workspace_repair
minimum                   = one_workspace_move_then_guard_rerun
contract                  = "do not edit repository files from a shared checkout; create
                             or switch to an independent worktree/branch, then rerun quota
                             should-run with the same agent id"

Rerunning the identical selection from a lane worktree returns decision=run, effective_action=normal_run immediately. This lane read the frontier wording as a blocker three times in one wake before inspecting the obligation field; each misreading cost a turn of real work, and the deferral is the only one whose recovery is a single mechanical move rather than a frontier question.

Change

When the deferral is a workspace repair, the reason names the move and carries the contract text, and the recommendation names the worktree rerun with the same --turn-instance-id and --todo-id. The two branches are precomputed before payload.update instead of nested inside it, which is what the diff mostly is.

Every other message is byte-identical, including autonomous-replan deferrals, the auxiliary-monitor rejection, and the generic rejection. The only behavior change is which sentence a workspace-repair deferral prints.

Validation

uv run --extra test python -m pytest tests/control_plane/test_quota_settlement_cli.py -q
# 65 passed

Two new tests pin both sides: a workspace-repair deferral must say "move the workspace first" / "independent worktree or branch" / --turn-instance-id and must not say "delivery frontier"; an unrelated autonomous_replan deferral must keep the existing frontier wording and the existing recommendation.

Two test_prior_host_closeout_survives_hidden_todo_lifecycle[sqlite] failures come from canonical_authority_fixture.py failing to run Node.js in this environment and reproduce unchanged on a worktree without this change.

Boundary

Public-safe: no private state, credentials, local paths or raw evidence. No change to selection eligibility, settlement, spend, or which action the guard picks — only what the deferral message tells the agent to do next.

huangruiteng and others added 2 commits September 17, 2026 04:31
Selecting an action from the shared checkout returns
`state=quota_action_selection_deferred` with
`reason="explicit action selection was deferred by the current delivery
frontier: control_repair"` and `delivery_preemptions=[control_repair,
delivery_not_allowed]`. Read literally, that points at an unrelated
control-plane preemption.

The instruction the agent actually owes is in a different field of the same
payload:

    execution_obligation.kind  = agent_workspace_repair
    minimum                    = one_workspace_move_then_guard_rerun
    contract                   = "do not edit repository files from a shared
                                  checkout; create or switch to an independent
                                  worktree/branch, then rerun quota should-run
                                  with the same agent id"

Rerunning the identical selection from a lane worktree returns
`decision=run, effective_action=normal_run` immediately. This lane read the
frontier wording as a blocker three times in one wake before inspecting the
obligation field, and each reading cost a turn of real work.

When the deferral is a workspace repair, the reason now names the move and the
recommendation names the worktree rerun with the same `--turn-instance-id` and
`--todo-id`. Every other deferral and rejection keeps its existing wording,
including the auxiliary-monitor and autonomous-replan messages; the only
behavior change is which sentence a workspace-repair deferral prints.

Validated: `pytest tests/control_plane/test_quota_settlement_cli.py` reports 65
passed. Two `test_prior_host_closeout_survives_hidden_todo_lifecycle[sqlite]`
failures come from `canonical_authority_fixture.py` failing to run Node.js and
reproduce unchanged on a worktree without this change.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng
huangruiteng force-pushed the codex/workspace-repair-reason branch from 4442293 to e377399 Compare September 16, 2026 21:56
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.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.

动机

从共享 checkout 选 Todo 时,配额守卫返回 state=quota_action_selection_deferred、reason=control_repair。这条文本读起来像是无关的控制面前置条件,而真正要做的动作是“把工作移出共享 checkout”,于是 agent 容易多花几轮反复读 portfolio 才找到 execution_obligation.kind=agent_workspace_repair。这是本车道今天亲身遇到的一类“提示没有指向可执行动作”的摩擦。

改动思路

不动选绑逻辑,只让这条 deferral 的文案与建议对齐它唯一可执行的动作:workspace_repair_allowed 且义务为 workspace repair 时,reason 直接指出必须先把工作区移出共享 checkout,并附上契约原文;recommended_action 指明带着同一 --turn-instance-id 与 --todo-id 在独立 worktree 重跑守卫。其余 deferral 保持可读但更具体的原因与建议。

具体改动

  • loopx/cli_commands/quota.py:_apply_requested_quota_action_selection_preflight 增加 workspace-repair 分支,并为其它 deferral/不合格分支给出更具体的 reason 与 recommended_action(含 auxiliary monitor 的场景)。
  • tests/control_plane/test_quota_settlement_cli.py:新增 _deferred_selection_payload 构造器与命名测试,覆盖 workspace repair 与普通 frontier deferral 两条路径。

对主干的风险

只改错误摘要字段,不改 decision/state/error_code 或选绑判定:payload 的机器字段保持原样,附加的是人读文本,因此不会改变调用方的分支逻辑。测试用构造 payload 而不是真实运行,覆盖的是渲染规则本身;这类文案分支的回归风险由命名测试固定。

我的整体评价

把“某类 deferral 其实有明确下一步”显式化,方向和最小性都对,且用命名测试固定两条分支。建议在该 head 的必过检查转绿后合并。

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Head: 842828f

English verdict: APPROVE

@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Closing as superseded by current main (e160b1b). The real quota CLI already preserves a selected Todo through agent_workspace_repair, permits creating an independent worktree, and resumes normal_run on the same Turn without rebinding settlement. Revalidated the existing workspace-repair and action-selection preemption tests: 3 passed. This PR changes rejection-path wording using an incomplete fixture, while the production path is already covered; keeping it would add a second explanation without repairing a remaining behavior gap. The other recovery PRs are being reconciled separately.

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