fix(quota): settle receipt-bound dependency waits without rebinding - #5363
Conversation
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
|
CI attribution at exact head
So #5367 should remove the shard failure, but the output-budget regression still needs a separate adjustment on this branch. |
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed exact head: ab0f8a0daefce10f8219582cd624cf9ebaca9eaa; immutable pre-change base: 3b73108e32acfe6657204b902a037171797e3e5c. 使用 pull_request_review_execution_contract_v2 当前 policy revision 12,重新判断整个 base-to-head,而非继承作者的验证结论。当前没有阻塞发现。
动机
真实问题不是 receipt 应允许换绑,而是同一 Turn 已绑定 A、A 后来进入已登记依赖等待时,quota 仍展示 B 的推进动作;显式选择 B 又正确拒绝,却误报为 receipt 写入失败。这样的恢复路径反复消耗操作,并不能到达后续独立工作。这个 PR 闭合的范围是“原 Turn 真实无 spend 收尾,然后下一真实 Turn 选独立任务”,不宣称完成整体协作路线图。
最小可行修复应继续使用现有依赖判定和结算 owner。放宽 receipt identity 会破坏幂等/计费归属;只改错误文案仍没有可执行 closeout。当前实现没有新增并行 Python 决策源或手工同步状态。
改动思路
公开路径是 quota should-run → live_decision → apply_receipt_bound_wait_recovery → quota.settlement.read → projectReceiptBoundWait/prepareBlockedWait → interaction_contract → refresh-state。权威输入是已提交 Turn receipt 和当前完整 canonical Todo snapshot;TS 重用 resume_condition/blocked-wait 规则,Python 仅传输绑定行及其引用目标、清除 B/replan 的动作投影并渲染命令。观察等待本身不提交收尾,也不授予交付权限。
正向实际走通:绑定 A → 更新 monitor_changed 或 todo_done 等待 → 同 Turn 重入保留原 event_id/A → 执行返回的 registry/runtime/Todo/Turn 精确绑定 refresh 命令 → typed blocked writeback settled → 重放只保留一条分类 → spend 不追加 → 下一 Turn 选 B。负向:显式 B 换绑仍拒绝、保留原 receipt;done/archived 引用与不可读权威源不能被当成 pending 证明。新建 Todo 也不被原 Turn 捕获。
具体改动
完整 13 文件 diff 为 +519/-7,生产恢复/渲染约 148 行新增,主体增量是可重复回归用例,并非新框架。
quota.py与quota_failure_report.py分离身份冲突和真正 receipt-write failure,明确原 Turn 重试与新 Turn 选择的区别。blocked_wait.ts:projectReceiptBoundWait只接受 exact owned/open/active advancement Todo 的 typed registered dependency;目标存在、归档状态、代际均复用prepareBlockedWait,并保留 timer、PR-merge、capacity 旧路由。live_decision.py、unsettled_host_turn.py与settlement_readback.ts接入纯投影。完整 canonical 读取不依赖展示上限;_apply_recovery_projection合并 prior/current-Turn 的渲染,但 prior-Turn replan 语义仍独立。interaction_contract.py与unsettled_host_turn_contract.py输出原 registry、runtime、agent、Todo 和 Turn 的可执行无 spend 收尾。恢复是机器义务:must_attempt_work=true、delivery_allowed=false;DONT_NOTIFY仅控制输出,不是执行门。- vocabulary/IO manifest 更新既有协议归属与位置;旧误诊断测试改为正确合同,新 TS/Python 用例覆盖两个 canonical 后端的实际 CLI 结算和后续选择。
相邻 future-facing pass 已应用在共享恢复投影和现有 typed wait owner;不需要再拆一个共享 helper 或做语言迁移。没有 frontend/Lark 配置或渲染变化:这是现有 managed CLI 的错误/恢复命令合同,未声称安装后 UI 验收。
对主干的风险
最强反例是“目标已经完成/归档、来源不完整、或只是一个长 timer,却被拿来无 spend 收尾”,以及原 receipt 仍绑定 A 时 B 偷渡进合同。实际公开路径的 22 项审查探针通过:包括两个后端、60 条无关完成项置前、展示重排、新增 subject、done/archived 引用、权威文件暂不可读、timer/PR/capacity 对照、精确换绑拒绝、返回命令执行、重放和下一 Turn 的真实进展。相同核心 harness 在 immutable base 的 8 个 causal 用例均按预期失败,head 8 个通过,证明不是“两个新 provider 相互印证”。
本次独立验证:183 项 TS tests、control-plane typecheck、changed-file Ruff、focused Mypy 通过;最终 premerge 的 10 catalog + 8 risk smokes 全通过,无 manual hold。Python settlement 集原先 101 pass/1 fail 中,唯一 Gitless 失败来自审查临时目录错误地放进 Git checkout,基线/head 在仓库外同命令都通过;保留了该失败,不把它归为 PR 缺陷。同一六文件完整 settlement/consumer 集在最终隔离下重跑 102 项全部通过。
hot-path 在普通相同工作负载下从 13901 到 14217 chars(预算 14500),增量是必须的 registry 绑定,不是重复说明;nested/top-level 键数分别为 base 353/48、head 360/52。较长的审查临时目录曾导致预算失败,最终标准短目录隔离下整套 premerge 通过;没有改预算或缩小扫描范围。剩余风险是路径长度与现有预算的余量、真实部署环境行为;不把局部用例当成长周期 soak 或安装升级证明。wait_for_ci=false,未获取/等待远端 CI。
我的整体评价
APPROVE。已有 caller 可以按返回的动作完成真实 closeout,并在后续 Turn 接着工作;收尾不授予新的 delivery/bypass 权限,不放宽 receipt identity、不修改 Todo completion declaration,也不强迫轮询 monitor。能力/错误文案保持 goal-neutral;有界增量和维护成本匹配这个明确的恢复缺口。
这是 exact-head 评审结论,不是合并权限。该 PR 修改控制面,按仓库政策留给维护者合并;合并前仍需独立 exact-head readiness。未宣布整体 Goal 完成。
English verdict: APPROVE - exact head ab0f8a0; immutable binding, typed no-spend closeout, idempotent replay and next-Turn progress independently verified on real File/SQLite, with attributed harness failures and passing final premerge. Control-plane merge authority remains with the maintainer.
Signed-off-by: huangruiteng <huangrt01@163.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Post-merge exact-head audit: actionable budget regression
Reviewed head: df0bbaaaab08c938df78e025e96ffd12c1b13cb5; immutable base: 04bf6b6c1c1d37d3b19194c91efe5b4c4d9daa7d. Current pull_request_review_execution_contract_v2, policy revision 12. This PR was already merged when audited; this is a new repair finding, not a pre-merge approval or a merge performed by this reviewer.
[P2] Shared output budgets regress at interaction_contract.py:720. On the same ordinary crowded fixture, loopx_turn_plan/crowded/json grows 14482 → 14557 chars, exceeding the existing 14500 ceiling. The base passes the absolute suite and the head fails; a short TMPDIR repeat also fails. Whole102-row receipts compared by the repository differential owner fail five rows: diagnose/small +300, turn-plan/crowded +75, turn-plan/multi_agent +150, turn-plan/small +151, quota/small +150. This is independently reproduced local PR-induced drift, not a reason inferred from unrelated remote CI.
动机
原问题确实存在:同一 Turn 已绑定 A,A 后来进入已登记依赖等待,却展示 B 的推进动作;显式选择 B 又因 immutable receipt 被拒绝且误报写入失败。正确结果是保留 A,真实无 spend 收尾,再由下一真实 Turn 选独立工作。放宽绑定会破坏幂等与归属,仅改报错也不能恢复后续工作。
本轮重新判断整个 integrated base-to-head。恢复能力有价值,但默认 agent-facing 输出也属于交付结果;现有成本约束不能因局部恢复验证通过而被忽略。
改动思路
quota should-run → live_decision → apply_receipt_bound_wait_recovery → quota.settlement.read → projectReceiptBoundWait/prepareBlockedWait → interaction_contract → refresh-state。TS 复用 typed dependency、目标代际和结算规则;Python 读取完整 canonical facts、传输与渲染,不建立第二个决策源。查询不写状态,原 closeout writer 才能结算。
正向走通原 receipt/A 保留、返回的 registry/runtime/Todo/Turn 精确绑定命令实际执行、无 spend、重放幂等、下一 Turn 选 B。负向换绑、terminal/archived 引用、不可读权威源、新增 subject 和 timer/PR/capacity 对照也实际验证。必须保留精确 registry 绑定,不能为绿预算删掉它而恢复写错状态的风险。
具体改动
完整13文件 +519/-7 的恢复/诊断/协议归属和测试改动与先前 patch 内容一致;但这次 base 集成了 shared CLI dispatch、输出捕获和 usage observation 等调用方变动,所以旧 head 的 APPROVE 与预算结果不能继承。
blocked_wait.ts:projectReceiptBoundWait复用既有prepareBlockedWait,只为 exact owned/open/active Todo 的已登记依赖投影收尾。live_decision.py、unsettled_host_turn.py、settlement_readback.ts保留原 identity,清除竞争动作,完整 canonical 读取不依赖展示上限。- CLI 错误区分身份冲突和真正 receipt-write failure;
must_attempt_work是机器义务,DONT_NOTIFY仅控制通知,恢复不授予 delivery/bypass。 interaction_next_cli_actions给共享 prefix 加--registry。crowded JSON 中实际增长落在turn_envelope.writeback.next_cli_actions[0],该 registry 参数为75字符;没有增加 JSON shape。action fingerprint 的相等不能替代命令绑定语义和成本审查。
相邻简化 pass 复用了 typed wait owner 和 prior/current 共有投影;下一步应比较共享命令重复成本、精简可行性与必要 registry 成本的明示预算。没有 frontend/Lark 配置或渲染改动,此次不宣称已安装主机/界面验收。
对主干的风险
当前 head 独立验证:22个实际 File/SQLite CLI 恢复、隔离和反例通过;188个 native TS 用例、control-plane typecheck、focused Mypy 通过;premerge10 catalog +8 risk smokes 通过。它们没有覆盖这套完整 real-CLI 输出矩阵,不能抵销其失败。
复现命令(各自 immutable checkout,仓库外临时状态):
uv run --extra test python -m pytest -q tests/control_plane/test_cli_output_budget.py::test_real_cli_output_stays_inside_the_characterized_baseline
uv run --extra test python examples/control_plane/cli-output-base-head-differential-smoke.py --base-ref 04bf6b6c1c1d37d3b19194c91efe5b4c4d9daa7d --main-ref 04bf6b6c1c1d37d3b19194c91efe5b4c4d9daa7d
绝对 budget 与当前增量 gate 均保留原值。完整 paired102-row measurement-only 仅用于诊断归因,不是通过 qualification。第一次诊断选错 semantics module 在产出前失败,改为实际 cli_output_semantics.py 后两边完整读回;它不改变独立 absolute suite 的 basepass/headfail。strict change-quality 将真实 required-validation failure 记为 non-passing,不豁免。
最小修复:保留必要 registry 安全性,按现有 budget decision guide 比较精简重复与有证据的 regression-budget 调整;若选择后者,说明同工作负载的成本/consumer value/余量,联动 owning contract、文档与防止 registry 丢失的断言,并重跑绝对和全部增量 gates。只把14500改大并不能解决其余4类增量失败。不要缩小工作负载、删除决定语义或把必要绑定当成无价值字符串。wait_for_ci=false,没有获取/等待远端 CI。
我的整体评价
REQUEST_CHANGES(post-merge repair required)。恢复路径和 authority 边界通过,但新 integrated head 尚未满足当前 shared output 成本合同。旧 head 的13901→14217结果被当前 base/caller变化失效;本轮新发现已给出可复现命令和具体修复边界,不扩大为整体路线图重写。合并权限、安装升级及整体 Goal 完成均不由本评审授予。
English verdict: REQUEST_CHANGES - post-merge actionable repair for exact head df0bbaa. Recovery is independently verified, but the immutable-base comparison proves an introduced absolute output-budget violation and five differential regressions. Preserve registry binding and qualify the bounded cost repair; no remote-CI inference or budget waiver.
A heartbeat can bind Todo A, then observe A waiting on a registered Todo dependency while another Todo is runnable. Re-entering the same Turn previously advertised successor work, but explicitly selecting it correctly failed the immutable receipt check and was misleadingly reported as a receipt write failure.
This change projects the existing typed blocked closeout for the original Todo and Turn. It preserves the committed receipt, disallows successor delivery and quota spending during recovery, and renders the actual registry-bound refresh command. Independent work is selected on the next real host Turn. The shared TypeScript wait validator remains the decision owner; Python transports provider facts and renders the recovery contract.
Validation covers the production CLI with real File and SQLite providers, both monitor_changed and todo_done dependencies, rejected rebinding, receipt replay, idempotent blocked closeout, unchanged completion-validation declarations, and subsequent-Turn selection. Native TypeScript negative cases cover foreign, archived, runnable, missing and changed dependency facts. The full settlement run passed 82 cases with one obsolete diagnostic assertion; after correcting that assertion, the final focused rerun passed all five cases. TypeScript: 129 native cases and full control-plane typecheck pass. The final refinement explicitly preserves timer, PR-merge and capacity wait routes instead of sending them through Todo-dependency recovery; all four File/SQLite causal cases were rerun successfully. Changed-file Ruff, focused Mypy, semantic catalog/structure checks and exact-scope change-quality verification pass. Final risk-based premerge passed all ten selected catalog canaries and eight risk-profile smokes. Public-boundary scanning passed, and exact-scope quality receipt cqr_7143f0016f9c033f5a59 is valid after the route-isolation refinement. No CI waiting is claimed.
No frontend companion is needed: this changes the managed CLI recovery contract and diagnostic, not frontend data or rendering. No persisted receipt migration or provider format change is introduced. The related refactor shares the recovery projection while preserving prior-Turn replan observation.
Integration refresh: merged current main without manual conflict resolution; the recovery diff is unchanged. Four real File/SQLite CLI cases, full control-plane TypeScript typecheck and all 10 catalog plus 8 risk-profile premerge checks passed again on the integrated head. Independent exact-head review is being refreshed before merge.