fix(coordination): let reviewed pauses retire expired hard leases - #5035
Conversation
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
评审对象:#5035,精确 head 75cf9c45d58770c7b7e1348dc43ed7613f8361a9。结论:无阻断性发现,认可此代码修复;这不是合并授权。
动机
既有 hard-lease 路径把过期租约仍当成执行谱系:owner 要暂停已晋升的 Agent Todo 时,普通规划更新会因缺少当前租约证明而失败,Todo 仍为 open,下一轮也无法如实反映暂停。相同合成 File-provider CLI 输入在基线得到 handoff_mode_requires_lease,在本 head 得到已提交的 blocked 与已释放租约。因此本 PR 完成的是一项可独立验证的暂停/恢复代码修复;上线后真正的目标操作仍应由有权 owner 另行执行,不能把阻塞记录当成交付完成。长程推进与用户体验均有改善:不再因过期执行权卡在 open,且状态、原因、恢复路径可读回。
改动思路
入口沿用现有 todo update 和 reviewed Chat action;File/SQLite 的 canonical projection、registered actor / lifecycle grant 判定与 provider CAS 仍是权威边界。新规则只识别 Agent Todo 的 open↔blocked、非空 reason、显式 clear-resume-when 且不夹带 copy/ownership/work patch 的窄生命周期操作。活跃租约拒绝;非活跃租约与 Todo 在同一 CAS 中更新,返回不授予执行权的 transition receipt。reviewed Chat 的 block note 转为 canonical reason,不再当作普通 note 补丁。对比既有 deferred-reopen 后,二者共用租约释放记录和提交边界,但状态前提与恢复语义不同;目前分开保留谓词比引入参数化生命周期框架更易审查。
具体改动
todo_blocked_lifecycle.ts增加窄操作识别、注册主体/执行证明/活跃租约拒绝,以及对过期或已释放租约的 transition 规划。todo_update_admission.ts在既有 owner/grant 判定之后接入该特例;普通 leased metadata/status edit 仍走原执行证明 fence。todo_update.ts将 Todo upsert 与 lease release 合成一次 provider CAS,并把blocked_lifecycle_transition写入正常提交及幂等读回;todo_update_intent.ts在显式清除等待条件时一并清掉旧的resume_condition/resume_ready派生观察。chat_todo_actions.py仅把 reviewed block 的 note 用作 reason;文档解释暂停、重开及新租约要求,TypeScript File/SQLite 与 Python reviewed-Chat 测试覆盖正反路径。
关键代码讲解
isBlockedLifecycleTransition把可越过过期租约 metadata fence 的输入缩为状态、原因与清等待条件三项;夹带 note 的请求不会命中。blockedLifecycleRejection先拒绝非注册主体、旧租约 proof 和仍活跃的租约;因此 controller grant 不能撤销正在执行的 holder。planBlockedLifecycleTransition复用releasedTaskLeaseRecord,由executeCoordinationTodoUpdate与 Todo 变更一并提交;重开不直接授予执行权。prepareUpdatedTodo清除 canonical wait 的派生观察,避免旧resume_ready在新读回中伪装成仍有效的等待。
对主干的风险
主要反例是:暂停错误地跨越活跃租约、把额外 copy edit 混入特例,或只改 Todo 未释放旧租约。File/SQLite 的 6 项目标测试覆盖活跃租约、未授权 controller、夹带编辑、幂等重放、过期租约同次提交及重开;reviewed-Chat 23 项和 TypeScript typecheck、Ruff 均通过。同一合成 public CLI 脚本在 base/head 配对验证了过期租约由失败变为成功,活跃租约在两边均保持 open/active 且无 revision 变化,旁边的 Todo 未改。尚未在真实 PostgreSQL 环境验证此路径,部署前应按仓库规则补齐适用的 provider 检查。
语义与 CI 对齐
本轮 Goal 配置为不读取远端 CI;本地全量 TypeScript 套件为 3059 通过、30 跳过、1 失败。唯一失败是 SQLite capacity 的 matched_fill: AssertionError,同一命令在不可变基线与本 head 均有相同签名;本机磁盘只余约 3 GiB,而该测试要求 >5 GiB reserve,且本 PR 未改 capacity 路径。它不应让无关 PR 得到 REQUEST_CHANGES,但该环境门禁仍需清理空间后重跑,不能当成绿色合并凭据。新 receipt 字段是已有 Todo update 合同的可选扩展,没有赋予新主体或默认执行权。
我的整体评价
APPROVE(作者自评采用 COMMENTED 记录):没有发现阻断性的代码问题;长程工作恢复与用户可见暂停/恢复均比基线可靠。最强剩余风险是未覆盖的真实 PostgreSQL 路径及本机磁盘不足导致的全量 capacity 检查失败,合并就绪应由维护者另行判断。本 PR 更改控制面租约与 Todo 状态合同,仓库规则禁止作者自合并或 admin bypass 自合并;请维护者在独立合并门禁满足后处理。相邻的 deferred-reopen 复用点已审视,本次没有为了相似代码制造新的抽象。
English verdict: APPROVE - exact head 75cf9c4; narrow expired-lease pause is validated, active leases remain fenced; baseline-equivalent disk-reserve failure is unrelated and merge readiness remains separate.
Problem
A promoted Agent Todo can retain an expired hard lease after the owner pauses its lane. Ordinary status=blocked planning is rejected as a leased status edit, leaving the prior managed Turn unsettled even though no delivery should proceed. The derived resume-condition observation can also outlive an explicitly cleared wait.
Change
Verification
This PR does not mutate the live finance Goal or spend quota. The exact old Turn and Todo need readback and a reviewed lifecycle operation after merge and local upgrade.