Skip to content

fix(quota): preserve action and monitor receipt identities - #4335

Merged
huangruiteng merged 4 commits into
mainfrom
codex/quota-action-selection-preflight
Sep 13, 2026
Merged

huangruiteng merged 4 commits into
mainfrom
codex/quota-action-selection-preflight

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Why

After a healthy identity-less heartbeat guard, a legitimate same-Turn action selection can be preempted by a newly due hard lane or autonomous replan. The current CLI collapses that unbound preflight outcome into heartbeat_receipt_identity_conflict and then projects a false write_failed receipt. That makes a safe refusal look like corrupt settlement identity and gives the agent no deterministic recovery path.

Closes #4327.

Behavior

  • Preserve the TypeScript action-selection reducer as the single eligibility/arbitration owner and expose its existing action_selection_qualification_v0 result in the full quota payload.
  • Return quota_action_selection_deferred or quota_action_selection_rejected for unbound choices, with the exact typed reason and a same-Turn refresh/re-entry instruction.
  • Replay an existing identity-less receipt without mutation; for a first-call rejection, report status=not_committed and append no receipt event.
  • Keep an already bound Todo/replan mismatch as the existing hard heartbeat_receipt_identity_conflict.
  • Fail closed if the TypeScript qualification is absent or contradicts the selected projection.

Product entry points

The managed/Codex CLI selection journey changes directly. Frontend and Lark need no separate component or source change: they consume the same interaction/receipt projection, and this patch changes only the second-guard failure/readback packet rather than adding a setting or alternate UI authority. The protocol reference documents the new states and retry behavior.

Validation

  • tests/control_plane/test_quota_settlement_cli.py: 47 passed (full file before the non-overlapping latest-main fast-forward).
  • Post-fast-forward exact regression: first-call rejection, autonomous-replan preemption, and true bound-receipt conflict: 3 passed.
  • Action portfolio/boundary/effect-turn Python suites: 19 passed.
  • TypeScript action-portfolio reducer: 11 passed.
  • Quota CLI projection: 9 passed.
  • Ruff, git diff --check, and docs governance smoke passed.

Scope and follow-up

This is a bounded quota/receipt correction. The future-facing pass kept policy in the existing TypeScript reducer and avoided a second Python eligibility rule or a new receipt store. Broader manager handoff work remains under RFC #4330 and its existing milestones.

@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Added a regression from an installed finance Goal: a due monitor can be visible as auxiliary context while the hard lane remains advancement work. The preflight now returns auxiliary_monitor_not_selectable_in_advancement_lane with a recoverable action, replays the identity-less receipt unchanged, and never projects a false receipt write failure. The existing hard-lane monitor selection path remains qualified. Validation: 3 focused CLI cases passed; TypeScript action-portfolio suite passed 12/12; Ruff and diff checks passed. Updated head: a97825d.

@huangruiteng
huangruiteng force-pushed the codex/quota-action-selection-preflight branch from a97825d to 21fd41d Compare September 13, 2026 11:47
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Updated head: 21fd41da89213aaf551411c345020df55891968d.

A real finance-research same-Turn selection exposed a second preflight misclassification: the requested Todo was projected and capability-ready, but its repository write had to move from the canonical checkout to an independent worktree. The typed quota decision correctly returned agent_workspace_repair; the legacy explicit-selection check required normal_delivery_allowed, threw an identity-conflict exception, and the outer heartbeat wrapper hid the actionable workspace recovery.

This update accepts only the narrowly typed workspace-repair case: exact requested/selected Todo, pending selection binding, workspace_repair_allowed=true, matching effective action and execution obligation, and a must-attempt/non-delivery agent channel. It binds the receipt to that Todo, preserves the original worktree recovery action, and lets the same Turn replay as normal_run after switching to an independent worktree. Capability rejection, hard-lane preemption, auxiliary monitor rejection, and true receipt identity conflicts remain fail-closed.

Validation:

  • 49 passed — complete tests/control_plane/test_quota_settlement_cli.py
  • 12 passed — tests/control_plane_ts/action_portfolio.test.ts
  • Ruff passed for the changed Python/test files
  • git diff --check passed

The regression exercises canonical-checkout repair → independent-worktree same-Turn recovery and verifies the receipt remains bound to the exact selected Todo.

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/quota-action-selection-preflight branch from 21fd41d to bc0e079 Compare September 13, 2026 12:35
@huangruiteng huangruiteng changed the title fix(quota): distinguish unbound action-selection preflight fix(quota): preserve action and monitor receipt identities Sep 13, 2026
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Updated head: bc0e079512e7102ac8147e75d86916d38a877682.

A real finance-research Turn exposed that monitor-poll reused one Goal/Agent/Turn effect id for every observation. The first due monitor committed, the second collided with that immutable request, and a third monitor outside the bounded projection failed the auxiliary-receipt check.

This update keeps the heartbeat settlement identity fixed on the original P0 advancement Todo while scoping each auxiliary no-spend observation by monitor Todo (or target digest when no Todo id exists). Exact retries replay only that observation; changed content conflicts only with the same monitor; shipped turn-only receipts remain replayable. Auxiliary admission now checks the Todo authority as well as the compact projection, so bounded display truncation cannot reject a genuinely due monitor.

The end-to-end regression commits three independent monitor observations in one settlement Turn, including one request with only --todo-id; proves three distinct receipts, exact replay, per-monitor conflict, unchanged P0 settlement selection, and zero quota spend. It also closes the prior maintainability-ratchet failure by moving task-class resolution into the existing action-portfolio owner instead of adding an exception.

Validation on the pushed head:

  • focused quota/selection CLI regressions: 7 passed
  • maintainability ratchet: 8 passed
  • TypeScript action-portfolio and monitor transaction suites: 27 passed
  • Python monitor runtime/legacy identity tests: 3 passed
  • Ruff and git diff --check: passed

User entry points: CLI/managed Turn semantics changed. No frontend or Lark control was added because the existing typed receipt projection already exposes settlement Todo, observed Todo/target, replay and error state; this patch changes identity/admission correctness beneath that shared projection.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Follow-up fix for the real same-Turn action-selection reproduction is now in b1381349d:

  • exact --todo-id selection now consumes the same capability-ready planning inventory that the action portfolio projects; a late-created Todo can no longer be visible as eligible yet rejected as unprojected
  • preserves the unbound receipt upgrade path and does not add a second spend
  • regression reproduces first unbound receipt → create Todo → same-Turn reentry → exact selection → upgraded receipt

Validation:

  • quota/monitor Python suite: 61 passed
  • targeted late-selection cases: 3 passed
  • maintainability ratchet: 8 passed
  • control-plane import boundaries: 15 passed
  • TypeScript action portfolio + monitor transaction: 27 passed
  • Ruff and git diff --check: passed

This PR remains review-required because it changes quota/receipt identity semantics; I am not using the concierge self-merge authorization for it.

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.

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

评审对象:PR #4335,精确 head 0c4628c752710ecbf83bf112002bac59825d20a7(base ba6ba1b6e3a2788d188f36e012eb795e2616800e)。结论为 APPROVE;未发现 P0/P1 阻塞项。当前新 head 的远端 CI 仍有 4 个 test-shard pending,合并前需等待并确认它们通过。

动机

旧路径把“同一 Turn 中显式选择已不再满足当前交付前沿的 Todo”误报成 heartbeat_receipt_identity_conflict,并可能把一次尚未提交的拒绝投影成 write_failed,导致 agent 无法用同一 Turn id 进行确定性重入。另一个问题是多个到期 monitor 共用 Turn-only effect identity:第一个观察成功后,第二个合法观察会被当成冲突。PR 将这两类语义拆开:选择资格由现有 TypeScript reducer 判定,receipt 仍只绑定一个 settlement Todo,monitor 观察按 Todo 或 target-key 建立独立的 no-spend 操作身份。

改动思路

入口仍是 quota should-run 与 quota monitor-poll。Python 负责读取当前 Todo、能力和 registry 权威源,并组装已有投影;TypeScript qualifyActionSelection 负责 qualified/deferred/rejected 状态与 typed reason。CLI 只有在精确选择通过资格、交付和 workspace 条件后才升级 heartbeat receipt;不通过时返回 quota_action_selection_deferred/rejected,首调用只返回 status=not_committed,不写 receipt event。monitor-poll 继续经过既有 TypeScript effect transaction 与 Todo writeback provider,新增 effect-id 仅隔离不同观察,不改变 settlement_todo_id 或 quota spend authority;已存在的 turn-only receipt 仍可按原 monitor replay。

具体改动

涉及 12 个文件(运行时 7、协议文档 2、测试 3,+901/-62)。协议文档同步写明 preflight 与 settlement conflict 的边界、旧回执兼容和多 monitor no-spend 规则。

关键代码讲解

  • loopx/cli_commands/quota.py:_apply_requested_quota_action_selection_preflight:校验 selected Todo、execution/interaction gate 和 action_selection_qualification_v0;无资格时只改内存 payload,避免伪造 durable receipt,并支持同 Turn replay/re-entry。
  • loopx/control_plane/work_items/action_portfolio.ts:qualifyActionSelection:候选为空时区分普通不可选与 auxiliary_monitor_not_selectable_in_advancement_lane;候选存在时仍强制 open + advancement_task,没有新增写权限。
  • loopx/control_plane/quota/monitor_poll.py:_monitor_poll_effect_id:同一 monitor 的精确重试复用既有 effect;Todo id 或 target-key digest 隔离不同观察,同时读取旧 turn-only effect 保障升级兼容。
  • loopx/quota.py:record_quota_monitor_poll:同时检查完整 registry Todo 与有界决策投影,只允许同 agent 的到期/重放 monitor 作为 advancement receipt 的 auxiliary observation;receipt-bound monitor 替换仍严格冲突。

对主干的风险

正向路径验证了:晚到且合格的 Todo 可以在同一 Turn 绑定;三个独立 monitor 各自写入一次 no-spend observation,精确重试不追加,改 result hash 只与对应 monitor 冲突。负向路径验证了:不存在/能力不足/硬 lane 或 autonomous replan 抢占时返回 typed reject/defer;首调用 receipt 数为零;错误 settlement Todo 继续返回 heartbeat_receipt_identity_conflict。基线 ba6ba1b6e3a2788d188f36e012eb795e2616800e 的反事实显示旧实现第二个 monitor 会复用 Turn-only effect 并冲突,head 已修复。

验证结果:新 head 12 个 quota settlement 回归、3 个 monitor runtime 测试、12 个 TypeScript action-portfolio 测试、Ruff、Python compile、diff check 均通过;与当前 origin/main 临时合并无冲突。风险仅剩本机缺少 tsc,以及远端 4 个 test-shard 尚 pending;本地 canary 的 direct checks、compile 和前 7 个 catalog canary 已通过。没有新增 frontend/Lark 入口、skill 自动加载或 quota spend 路径。

我的整体评价

这是围绕同一 receipt/selection identity 边界的三段式修复,复用了现有 TypeScript action reducer、monitor transaction 和 Python provider adapter,没有引入第二套 authority。新增状态是 typed、可观测且有明确 replay/rollback 语义;测试断言了 receipt 数、effect id、conflict fields 和 spend count,而非只比较 decision code。建议等待新 head 的 4 个 test-shard 结束并确认全绿后再合并;在该条件满足前,不把当前 BLOCKED 视为代码阻塞,也不要在旧 head 上复用本结论。

English verdict: APPROVE for exact head 0c4628c752710ecbf83bf112002bac59825d20a7. The change correctly separates typed action-selection preflight from settlement identity and scopes independent monitor observations without expanding quota authority. Focused Python/TypeScript tests and static checks pass; four remote test-shards are still pending and must pass before merge.

@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)

评审 exact head:0c4628c752710ecbf83bf112002bac59825d20a7

动机

这个 PR 修复两类共享同一根边界的问题:显式选择尚未绑定 receipt 时,当前门禁给出的 deferred、rejected 或 agent_workspace_repair 被 CLI 外层误报成 heartbeat_receipt_identity_conflict;同一结算 Turn 内多个到期 monitor 又复用了一个 Turn-only effect id,第二次观察会与第一次碰撞。前者会把可恢复的 Turn 卡死并诱导重启,后者会漏掉后续 monitor 观察。仅改错误文案不够,因为 receipt 是否写入、同 Turn 如何重入以及 effect 的幂等键都会继续错误。

改动思路

改动没有新增第二套权威:action_portfolio.ts::qualifyActionSelection 仍是显式选择资格的唯一决策者,Python 只补齐 canonical task class、完整可运行候选并把 typed 结果投影到 CLI/managed Turn。CLI 只允许三种精确路径继续绑定:正常交付、字段完整且精确 Todo 匹配的 workspace repair、当前硬义务;其余路径保留 deferred/rejected,首调用不写假 receipt,已有无身份 receipt 仅重放。monitor 侧保持 heartbeat 的 settlement Todo 唯一,同时把辅助观察的 operation identity 按 monitor Todo(无 Todo 时按 target 摘要)拆开;旧 Turn-only receipt 只对其原观察保持兼容重放。

具体改动

  • loopx/cli_commands/quota.py 将旧的统一抛 identity conflict 改成 typed preflight 映射,补 not_committed 准确回执,并保留真实 bound-receipt conflict。
  • should_run_prepare.py、should_run_packet.py 与 action_portfolio.py/.ts 让显式选择和 action portfolio 消费同一 capability-ready inventory,并为 advancement lane 中的 auxiliary monitor 返回明确原因。
  • control_plane/quota/monitor_poll.py 为每个观察派生/恢复稳定 effect id;loopx/quota.py 从 canonical Todo authority 验证有界列表之外的 due monitor,且始终保留原 settlement Todo。
  • 两份双语协议说明了 preflight/workspace recovery 与多 monitor/no-spend 语义;新增 Python/TS 回归覆盖了首调用拒绝、同 Turn 新 Todo、replan/workspace 抢占、辅助 monitor、legacy replay 和三个 monitor 同 Turn 写入。

关键代码讲解

  1. _apply_requested_quota_action_selection_preflight 只在 exact requested/selected Todo 且机器门禁完整时放行;typed qualification 缺失或自相矛盾会 fail closed。
  2. qualifyActionSelection 保持纯 reducer,不触碰 receipt;continuous_monitor 仅获得更准确的 rejection reason,不会获得 advancement 权限。
  3. _monitor_poll_effect_id 先查原观察的持久 receipt,再按 Todo/target 派生新 key,避免同 Turn 不同 monitor 互相覆盖。
  4. record_quota_monitor_poll 只把 canonical due、同 Agent 可见的 monitor 当作辅助观察;wrong Todo 仍是硬冲突。

对主干的风险

最大风险是把辅助观察误当第二个交付身份,或让 workspace repair 绕过 capability/work-lane 门禁。代码和测试都把两者钉死:settlement_todo_id 始终是原 advancement Todo,monitor receipt 不计费;workspace repair 必须同时满足 exact Todo、workspace_repair_allowed、匹配的 execution obligation 与 agent-channel flags。以同一 fixture 在 ba6ba1b6e 基线强制运行 7 个新增回归均失败,在该 head 全部通过;完整 quota/monitor Python 为 60 passed,架构/维护性边界 26 passed,TS reducer 12 passed,Ruff/diff check 通过。远端 DCO、构建、静态检查、四分片、installed/e2e、mutation、Windows 与 merge-gate 全绿;仅剩明确 non-blocking 的 Sonar。残余兼容成本是旧 Turn-only monitor receipt 的读取分支,可在受支持发布窗口结束后单独清理。

我的整体评价

没有阻断 finding。这个 exact head 修复的是已在真实 finance Goal 与可重复 fixture 中出现的高成本卡死/漏观察问题,且复用了现有 TS 决策、canonical Todo、receipt 与 effect owner,没有引入新队列、新 store 或新 CLI 权威。虽然 PR 覆盖 action 与 monitor 两条路径,但二者共同收敛“结算身份与预检/观察身份不得混淆”的同一契约,生产改动与兼容成本相称。结论:APPROVE;由于是 author-owned PR,以 COMMENTED review 记录,不构成 GitHub 自审批或合并授权。

English verdict: APPROVE for exact head 0c4628c752710ecbf83bf112002bac59825d20a7. The typed action-selection owner and single settlement identity remain intact; seven base/head regressions plus the full local and required remote checks validate deterministic re-entry, workspace recovery, and independent no-spend monitor receipts. No blocking finding remains; this author-owned COMMENTED review is not a self-approval or merge authorization.

@huangruiteng
huangruiteng merged commit b0f7aa0 into main Sep 13, 2026
28 checks passed
@huangruiteng
huangruiteng deleted the codex/quota-action-selection-preflight branch September 13, 2026 15:21
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.

quota: explicit Todo selection can reject the canonical runnable successor

1 participant