fix(quota): preserve action and monitor receipt identities - #4335
Conversation
eb8930d to
a97825d
Compare
|
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 |
a97825d to
21fd41d
Compare
|
Updated head: A real This update accepts only the narrowly typed workspace-repair case: exact requested/selected Todo, pending selection binding, Validation:
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>
21fd41d to
bc0e079
Compare
|
Updated head: 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 Validation on the pushed head:
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>
|
Follow-up fix for the real same-Turn action-selection reproduction is now in
Validation:
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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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 写入。
关键代码讲解
_apply_requested_quota_action_selection_preflight只在 exact requested/selected Todo 且机器门禁完整时放行;typed qualification 缺失或自相矛盾会 fail closed。qualifyActionSelection保持纯 reducer,不触碰 receipt;continuous_monitor仅获得更准确的 rejection reason,不会获得 advancement 权限。_monitor_poll_effect_id先查原观察的持久 receipt,再按 Todo/target 派生新 key,避免同 Turn 不同 monitor 互相覆盖。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.
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
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
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.